 #undef OTSIM900LINK_SUBCYCLE_SEND_TIMEOUT_TEST
  */
 
-// GLOBAL flags that alter system build and behaviour.
-//#define DEBUG // If defined, do extra checks and serial logging.  Will take more code space and power.
-
-// Ensure that OpenTRV 'standard' UART speed is set unless explicitly overridden.
-#define BAUD 4800
-// Global flag for REV10 secure BHR
-#define CONFIG_REV10_SECURE_BHR // REV10: secure stats relay and boiler hub.
-
-// Get defaults for valve applications.
-#include <OTV0p2_valve_ENABLE_defaults.h>
-// REV8 + GSM Arduino shield + I2CEXT, see TODO-551.
-#include <OTV0p2_CONFIG_REV10.h>
+/******************************************************************************
+ * INCLUDES
+ *****************************************************************************/
+
+////// GLOBAL flags that alter system build and behaviour.
+
+// If defined, do extra checks and serial logging.  Will take more code space and power.
+#undef DEBUG
+
+// *** Global flag for REVx configuration here *** //
+// e.g.
+#define V0p2_REV 10
+
 // --------------------------------------------
-// Fixups to apply after loading the target config.
-#include <OTV0p2_valve_ENABLE_fixups.h>
-
-#include <OTV0p2_Board_IO_Config.h> // I/O pin allocation and setup: include ahead of I/O module headers.
+// I/O pin allocation and setup: include ahead of I/O module headers.
+#include <OTV0p2_Board_IO_Config.h>
+
+
+////// MAIN LIBRARIES
 
 #include <Arduino.h>
 #include <OTV0p2Base.h>
 #include <OTRadioLink.h>
+
+
+////// ADDITIONAL LIBRARIES/PERIPHERALS
 
 #define RFM23B_IRQ_CONTROL  // Enable RFM23B IRQ control code.
 #include <OTRFM23BLink.h>
 #include <OTSIM900Link.h>
 #include <OTAESGCM.h>
 
+
+////// ADDITIONAL USER HEADERS
+
 #include "secondaryRadioConfig.h"  // IP address in separate header to avoid accidentally committing.
 
-/////// RADIOS
-// Pick an appropriate radio config for RFM23 (if it is the primary radio).
-// Nodes talking on fast GFSK channel 0.
-static constexpr uint8_t nPrimaryRadioChannels = 1;
-static const OTRadioLink::OTRadioChannelConfig RFM23BConfigs[nPrimaryRadioChannels] =
-  {
-  // GFSK channel 0 full config, RX/TX, not in itself secure.
-  OTRadioLink::OTRadioChannelConfig(OTRFM23BLink::StandardRegSettingsGFSK57600, true),
-  };
-
-static const OTRadioLink::OTRadioChannelConfig SecondaryRadioConfig(&SIM900Config, true);
-
-// Brings in necessary radio libs.
-static constexpr uint8_t RFM23B_RX_QUEUE_SIZE = OTRFM23BLink::DEFAULT_RFM23B_RX_QUEUE_CAPACITY;
-static constexpr int8_t RFM23B_IRQ_PIN = PIN_RFM_NIRQ;
-static constexpr bool RFM23B_allowRX = true;
-OTRFM23BLink::OTRFM23BLink<OTV0P2BASE::V0p2_PIN_SPI_nSS, RFM23B_IRQ_PIN, RFM23B_RX_QUEUE_SIZE, RFM23B_allowRX> PrimaryRadio;
-OTSIM900Link::OTSIM900Link<8, 5, RADIO_POWER_PIN, OTV0P2BASE::getSecondsLT> SecondaryRadio;
-
-/////// SENSORS
-
-// Sensor for supply (eg battery) voltage in millivolts.
-OTV0P2BASE::SupplyVoltageCentiVolts Supply_cV;
-OTV0P2BASE::RoomTemperatureC16_TMP112 TemperatureC16;
-// Sense ambient lighting level.
-OTV0P2BASE::SensorAmbientLight AmbLight;
-
-/////// ACTUATORS
-static constexpr bool enableDefaultAlwaysRX = true;
-static constexpr bool enableRadioRX = true;
-static constexpr bool allowGetMinBoilerOnMFromEEPROM = false;
-OTRadValve::OTHubManager<enableDefaultAlwaysRX, enableRadioRX, allowGetMinBoilerOnMFromEEPROM> hubManager;
-OTRadValve::BoilerLogic::OnOffBoilerDriverLogic<decltype(hubManager), hubManager, OUT_HEATCALL> BoilerHub;
-
-//////// Constants and variables
+/******************************************************************************
+ * GENERAL CONSTANTS AND VARIABLES
+ * Core constants and variables required for V0p2 loop.
+ *****************************************************************************/
 // Controller's view of Least Significant Digits of the current (local) time, in this case whole seconds.
 // TIME_LSD ranges from 0 to TIME_CYCLE_S-1, also major cycle length.
 static constexpr uint_fast8_t TIME_CYCLE_S = 60;
 // Controller's notion/cache of seconds within major cycle.
 static uint_fast8_t TIME_LSD;
+
+// Send the underlying stats binary/text 'whitened' message.
+// This must be terminated with an 0xff (which is not sent),
+// and no longer than STATS_MSG_MAX_LEN bytes long in total (excluding the terminating 0xff).
+// This must not contain any 0xff and should not contain long runs of 0x00 bytes.
+// The message to be sent must be written at an offset of STATS_MSG_START_OFFSET from the start of the buffer.
+// This routine will alter the content of the buffer for transmission,
+// and the buffer should not be re-used as is.
+//   * doubleTX  double TX to increase chance of successful reception
+//   * RFM23BfriendlyPremable  if true then add an extra preamble
+//     to allow RFM23B-based receiver to RX this
+// This will use whichever transmission medium/carrier/etc is available.
+static constexpr uint8_t RFM22_PREAMBLE_BYTES = 5; // Recommended number of preamble bytes for reliable reception.
+static constexpr uint8_t RFM22_SYNC_MIN_BYTES = 3; // Minimum number of sync bytes.
+static constexpr uint8_t STATS_MSG_START_OFFSET = (RFM22_PREAMBLE_BYTES + RFM22_SYNC_MIN_BYTES);
+static constexpr uint8_t STATS_MSG_MAX_LEN = (64 - STATS_MSG_START_OFFSET);
 
 // 'Elapsed minutes' count of minute/major cycles; cheaper than accessing RTC and not tied to real time.
 // Starts at or just above zero (within the first 4-minute cycle) to help avoid collisions between units after mass power-up.

                                    decodeAndHandleSecureFrame, OTRadioLink::decodeAndHandleDummyFrame  // only interested in single frame type.
                                   > messageQueue;

//========================================
// INTERRUPT SERVICE ROUTINES
//========================================
// Controller's view of Least Significant Digits of the current (local) time, in this case whole seconds.
// Previous state of port B pins to help detect changes.
//
// NOTE: PrimaryRadio may switch PCINT0 on and off during operation! This pin bank should not be relied
//       upon for pin change interrupts!
static volatile uint8_t prevStatePB;
// Interrupt service routine for PB I/O port transition changes.
ISR(PCINT0_vect)
{
    // Basic profiling info
    OTV0P2BASE::MemoryChecks::recordPC();

    const uint8_t pins = PINB;
    const uint8_t changes = pins ^ prevStatePB;
    prevStatePB = pins;

    // RFM23B nIRQ falling edge is of interest.
    // Handler routine not required/expected to 'clear' this interrupt.
    if((changes & RFM23B_INT_MASK) && !(pins & RFM23B_INT_MASK))
        { PrimaryRadio._handleInterruptNonVirtual(); }
}

// Previous state of port D pins to help detect changes.
static volatile uint8_t prevStatePD;
// Interrupt service routine for PD I/O port transition changes (including RX).
// (DE20170810) Just does a store on an atomic_t. Low ram cost.
ISR(PCINT2_vect)
{
    const uint8_t pins = PIND;
    const uint8_t changes = pins ^ prevStatePD;
    prevStatePD = pins;
    
    // If an interrupt arrived from the serial RX then wake up the CLI.
    // Use a nominally rising edge to avoid spurious trigger when other interrupts are handled.
    // The will ensure that it is possible to wake the CLI subsystem with an extra CR or LF.
    // It is OK to trigger this from other things such as button presses.
    // TODO: ensure that resetCLIActiveTimer() is inlineable to minimise ISR prologue/epilogue time and space.
    if((changes & SERIALRX_INT_MASK) && !(pins & SERIALRX_INT_MASK))
    { OTV0P2BASE::CLI::resetCLIActiveTimer(); }
}


// Send the underlying stats binary/text 'whitened' message.
// This must be terminated with an 0xff (which is not sent),
// and no longer than STATS_MSG_MAX_LEN bytes long in total (excluding the terminating 0xff).
// This must not contain any 0xff and should not contain long runs of 0x00 bytes.
// The message to be sent must be written at an offset of STATS_MSG_START_OFFSET from the start of the buffer.
// This routine will alter the content of the buffer for transmission,
// and the buffer should not be re-used as is.
//   * doubleTX  double TX to increase chance of successful reception
//   * RFM23BfriendlyPremable  if true then add an extra preamble
//     to allow RFM23B-based receiver to RX this
// This will use whichever transmission medium/carrier/etc is available.
static constexpr uint8_t RFM22_PREAMBLE_BYTES = 5; // Recommended number of preamble bytes for reliable reception.
static constexpr uint8_t RFM22_SYNC_MIN_BYTES = 3; // Minimum number of sync bytes.
static constexpr uint8_t STATS_MSG_START_OFFSET = (RFM22_PREAMBLE_BYTES + RFM22_SYNC_MIN_BYTES);
static constexpr uint8_t STATS_MSG_MAX_LEN = (64 - STATS_MSG_START_OFFSET);
// Managed JSON stats.
// Number of JSON stats to TX:
// Note: each stat statically allocates 6 bytes to RAM.
// - Internal temp
// - Boiler state
// TODO: Consider removing entirely.
constexpr uint8_t nTXStats = 2;
static OTV0P2BASE::SimpleStatsRotation<nTXStats> ss1; // Configured for maximum different stats.
// Do bare stats transmission.
// Output should be filtered for items appropriate
// to current channel security and sensitivity level.

// Sends stats on primary radio channel 0 with possible duplicate to secondary channel.
// If sending encrypted then ID/counter fields (eg @ and + for JSON) are omitted
// as assumed supplied by security layer to remote recipent.
void bareStatsTX()
{
    // Note if radio/comms channel is itself framed.
    const bool neededWaking = OTV0P2BASE::powerUpSerialIfDisabled<>();
