|
SH2SC-EDT
Self-Healing Hardware and Software Complex for Encrypted Data Transmission
|
SH2SC-EDT — Receiver Node B ("The Synthesizer") firmware. More...
Include dependency graph for ReceiverNode.ino:Go to the source code of this file.
Enumerations | |
| enum | ParseState : uint8_t { WAIT_AA , WAIT_55 , READ_TYPE , READ_PAYLOAD } |
| Four-state byte-level frame assembly FSM states. More... | |
Functions | |
| static LiquidCrystal_AIP31068_I2C | lcd (RX_LCD_ADDR, RX_LCD_COLS, RX_LCD_ROWS) |
| I2C LCD display object (Aip31068, 16×2, address 0x3E). | |
| void | updateRxDisplay (RxState state, uint16_t seqNum, bool macOk) |
| Refresh the RX LCD with the current FSM state and last packet result. | |
| static void | resetParser () |
| Flush the UART RX FIFO and reset the byte-parser FSM to WAIT_AA. | |
| void | processReceivedByte (uint8_t inByte) |
| Consume one incoming UART byte and advance the frame-assembly FSM. | |
| void | processHelloBody () |
| Store the received session nonce and install MASTER_PSK into the cipher. | |
| void | authenticateAndPlay (const DataPacket *pkt) |
| Run the full ChaCha20-Poly1305 pipeline; play the note only on MAC success. | |
| void | processFinPacket (const DataPacket *pkt) |
| Authenticate and process a FLAG_FIN session-teardown packet. | |
| void | startNote (uint16_t frequencyHz, uint16_t durationMs) |
| Start non-blocking note playback on the piezo buzzer. | |
| void | stopNote () |
| Stop the currently playing note and clear the playback flag. | |
| static void | onRingOscPulse () |
| Ring oscillator ISR — increments pulse counter on each RISING edge. | |
| static uint32_t | mixEntropy (uint32_t pool, uint32_t bits) |
| Single-step cryptographic entropy mixer — rotate-left XOR fold. | |
| void | generateEntropyPool (uint8_t *outputSeed) |
Harvest 256 bits of hardware entropy and fill outputSeed (RX variant). | |
| void | rx_setup () |
| Initialise RX hardware, seed the CSPRNG, and display the idle screen. | |
| void | rx_loop () |
| Execute one non-blocking C2P-ARQ FSM tick for the Receiver node. | |
| void | setup () |
| Arduino entry point — delegates to rx_setup(). | |
| void | loop () |
| Arduino main loop — delegates to rx_loop(). | |
Variables | |
| static const uint16_t | universal_notes [NOTE_DICT_SIZE] |
| Universal note frequency dictionary for the C2P-ARQ Receiver. | |
| static ParseState | parseState = WAIT_AA |
| Current byte-parser FSM state; reset to WAIT_AA by resetParser(). | |
| static uint8_t | rx_buffer [sizeof(DataPacket)] |
| Raw receive buffer sized to hold the largest frame body (DataPacket, 15 bytes). | |
| static uint8_t | rx_index = 0 |
| Number of bytes written to rx_buffer for the frame currently being collected. | |
| static uint8_t | expected_length = 0 |
| Total expected bytes for the current frame; set in READ_TYPE state. | |
| static bool | s_sessionActive = false |
| Session active flag — set after a valid HelloPacket handshake; cleared on FIN or watchdog timeout. | |
| static uint8_t | s_sessionNonce [HELLO_NONCE_SIZE] |
| 12-byte session nonce delivered by the last accepted HelloPacket (FLAG_SYN). | |
| static ChaChaPoly | s_cipher |
| ChaCha20-Poly1305 cipher instance; re-initialised per packet via clear(). | |
| static uint32_t | noteStartMs = 0 |
| Timestamp (millis()) when the current note started playing. | |
| static uint16_t | noteLengthMs = 0 |
| Duration in milliseconds that the current note should sound. | |
| static bool | isPlayingNote = false |
| True while a note is actively sounding; cleared by stopNote(). | |
| const uint16_t | CHK_ERR_DISPLAY_MS = 500 |
| Duration of the MAC-error message shown on the LCD before returning to idle display. | |
| static bool | isShowingError = false |
| True when the LCD is currently showing a MAC-error message. | |
| static uint32_t | errorDisplayStart = 0 |
| Timestamp (millis()) when the MAC-error display was last activated. | |
| const uint16_t | RX_PARSER_TIMEOUT_MS = 20 |
| Stale byte accumulation timeout for the byte-level frame parser. | |
| static uint32_t | rxLastByteMs = 0 |
| Timestamp (millis()) of the most recently received UART byte. | |
| const uint32_t | NETWORK_GRACE_PERIOD_MS = 3000 |
| Network grace period added to the last note's duration for the Dynamic Smart Watchdog. | |
| static uint32_t | current_timeout_limit = 5000 |
| Current Dynamic Watchdog timeout in milliseconds. | |
| static uint32_t | last_valid_packet_time = 0 |
| Timestamp (millis()) of the most recently successfully authenticated DataPacket. | |
| static RxState | currentState = RxState::WAITING_SYNC_1 |
| Tracks the current RxState for LCD display updates; independent of the byte-parser FSM. | |
| static volatile uint32_t | s_ringOscPulses = 0 |
| Pulse counter incremented by the ring oscillator ISR on INT0 (pin 2). | |
SH2SC-EDT — Receiver Node B ("The Synthesizer") firmware.
Implements the C2P-ARQ protocol receive side. Responsibilities:
Definition in file ReceiverNode.ino.
| enum ParseState : uint8_t |
Four-state byte-level frame assembly FSM states.
Drives processReceivedByte(). Both HELLO and DATA frames must pass through the 0xAA 0x55 preamble gate before any body bytes are accepted.
| Enumerator | |
|---|---|
| WAIT_AA | |
| WAIT_55 | |
| READ_TYPE | |
| READ_PAYLOAD | |
Definition at line 65 of file ReceiverNode.ino.
| void authenticateAndPlay | ( | const DataPacket * | pkt | ) |
Run the full ChaCha20-Poly1305 pipeline; play the note only on MAC success.
Authenticate and decrypt a DataPacket (FLAG_DAT); play the note on MAC success.
Pipeline (mirrors TX sendPacket()):
seq_num from the packed struct field directly.s_sessionNonce, then packetNonce[10] ^= (seqNum >> 8), packetNonce[11] ^= seqNum.clear() -> setKey(MASTER_PSK, 32) -> setIV(packetNonce, 12).addAuthData({flags, seq_lo, seq_hi}, 3) — 3-byte plaintext AAD.decrypt(pkt->payload, plaintext, DATA_PAYLOAD_SIZE).computeTag(expectedMac, 16), then memcmp with truncated 8-byte tag. Security invariant: startNote() is called ONLY after memcmp returns 0. Also bounds-checks noteIndex < NOTE_DICT_SIZE before indexing universal_notes[] to prevent out-of-bounds reads on ATmega328P. | pkt | Pointer to the fully buffered DataPacket (flags == FLAG_DAT). |
Definition at line 327 of file ReceiverNode.ino.
References ACK_BYTE, current_timeout_limit, currentState, DATA_PAYLOAD_SIZE, errorDisplayStart, EXECUTING_ACTION, DataPacket::flags, HELLO_NONCE_SIZE, isShowingError, last_valid_packet_time, lcd(), DataPacket::mac, MASTER_PSK, NACK_BYTE, NETWORK_GRACE_PERIOD_MS, NOTE_DICT_SIZE, DataPacket::payload, resetParser(), REST_INDEX, s_cipher, s_sessionNonce, DataPacket::seq_num, seqNum, startNote(), stopNote(), TRUNCATED_MAC_SIZE, universal_notes, and updateRxDisplay().
Referenced by processReceivedByte().
Here is the call graph for this function:
Here is the caller graph for this function:| void generateEntropyPool | ( | uint8_t * | outputSeed | ) |
Harvest 256 bits of hardware entropy and fill outputSeed (RX variant).
Harvest 256 bits of hardware entropy and write them to outputSeed (RX variant).
Harvest 256 bits of hardware entropy and write them to outputSeed.
Fills outputSeed[32] as 8 independent 32-bit words, each gathered from six hardware sources (micros() human-timing jitter is omitted — RX has no button):
Total execution time: 8 × 2 ms gate ≈ 16 ms (one-shot in rx_setup()).
| outputSeed | Pointer to a 32-byte buffer for the entropy pool output. Pass directly to initCSPRNG(); scrub with memset() afterwards. |
Definition at line 545 of file ReceiverNode.ino.
References ENTROPY_RING_OSC_PIN, mixEntropy(), mixEntropy(), onRingOscPulse(), onRingOscPulse(), s_ringOscPulses, and s_ringOscPulses.
Referenced by rx_setup().
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
I2C LCD display object (Aip31068, 16×2, address 0x3E).
Referenced by authenticateAndPlay(), processFinPacket(), rx_loop(), rx_setup(), and updateRxDisplay().
Here is the caller graph for this function:| void loop | ( | ) |
Arduino main loop — delegates to rx_loop().
Definition at line 722 of file ReceiverNode.ino.
References rx_loop().
Here is the call graph for this function:
|
inlinestatic |
Single-step cryptographic entropy mixer — rotate-left XOR fold.
Rotates pool left by 1 bit, then XORs in bits. Used iteratively by generateEntropyPool() for each entropy source.
| pool | Accumulated entropy pool value. |
| bits | New entropy bits to fold in. |
Definition at line 524 of file ReceiverNode.ino.
Referenced by generateEntropyPool().
Here is the caller graph for this function:
|
static |
Ring oscillator ISR — increments pulse counter on each RISING edge.
Definition at line 514 of file ReceiverNode.ino.
References s_ringOscPulses.
Referenced by generateEntropyPool().
Here is the caller graph for this function:| void processFinPacket | ( | const DataPacket * | pkt | ) |
Authenticate and process a FLAG_FIN session-teardown packet.
Authenticate a FLAG_FIN teardown packet and close the session on MAC success.
Cryptographic pipeline is identical to authenticateAndPlay() to guarantee that only the legitimate TX (holding MASTER_PSK and s_sessionNonce) can produce a verifiable FIN. A noise-induced FLAG_FIN byte will fail MAC and trigger NACK without touching the session state. Uses discardBuf to receive the zero plaintext — decrypt() MUST run before computeTag() to correctly advance the Poly1305 accumulator. On MAC success: ACK sent, s_sessionNonce zeroed (key erasure), parser reset. On MAC failure: NACK sent, session left active for FIN retransmission.
| pkt | Pointer to the fully buffered DataPacket (flags == FLAG_FIN). |
Definition at line 416 of file ReceiverNode.ino.
References ACK_BYTE, DATA_PAYLOAD_SIZE, FLAG_FIN, HELLO_NONCE_SIZE, lcd(), DataPacket::mac, MASTER_PSK, NACK_BYTE, DataPacket::payload, resetParser(), s_cipher, s_sessionActive, s_sessionNonce, DataPacket::seq_num, seqNum, and TRUNCATED_MAC_SIZE.
Referenced by processReceivedByte().
Here is the call graph for this function:
Here is the caller graph for this function:| void processHelloBody | ( | ) |
Store the received session nonce and install MASTER_PSK into the cipher.
Handle a fully buffered HelloPacket (FLAG_SYN) from the Transmitter.
Copies rx_buffer[1..12] into s_sessionNonce, calls s_cipher.setKey(MASTER_PSK, 32) to pre-install the key, sets s_sessionActive = true, sends ACK_BYTE, and arms the Dynamic Smart Watchdog with current_timeout_limit = 5000 ms.
Definition at line 290 of file ReceiverNode.ino.
References ACK_BYTE, current_timeout_limit, HELLO_NONCE_SIZE, last_valid_packet_time, MASTER_PSK, rx_buffer, s_cipher, s_sessionActive, and s_sessionNonce.
Referenced by processReceivedByte().
Here is the caller graph for this function:| void processReceivedByte | ( | uint8_t | inByte | ) |
Consume one incoming UART byte and advance the frame-assembly FSM.
Implements the WAIT_AA -> WAIT_55 -> READ_TYPE -> READ_PAYLOAD pipeline. Both HELLO and DATA frames must pass the 0xAA 0x55 preamble gate. On buffer completion, dispatches to processHelloBody(), authenticateAndPlay(), or processFinPacket() as appropriate, then returns to WAIT_AA.
| inByte | Byte just read from the UART hardware buffer. |
Definition at line 188 of file ReceiverNode.ino.
References authenticateAndPlay(), currentState, expected_length, FLAG_DAT, FLAG_FIN, FLAG_SYN, NACK_BYTE, parseState, processFinPacket(), processHelloBody(), READ_PAYLOAD, READ_TYPE, READING_DATA, READING_HELLO, resetParser(), rx_buffer, rx_index, s_sessionActive, SYNC_BYTE_1, SYNC_BYTE_2, updateRxDisplay(), WAIT_55, WAIT_AA, and WAITING_SYNC_1.
Referenced by rx_loop().
Here is the call graph for this function:
Here is the caller graph for this function:
|
static |
Flush the UART RX FIFO and reset the byte-parser FSM to WAIT_AA.
Called on MAC failure or parser timeout to prevent a noise burst that corrupted one frame from also corrupting the preamble detection of the next frame. Reads and discards all bytes in the 64-byte hardware FIFO (Serial.flush() only flushes TX; manual drain is required for RX).
Definition at line 170 of file ReceiverNode.ino.
References currentState, parseState, rx_index, WAIT_AA, and WAITING_SYNC_1.
Referenced by authenticateAndPlay(), processFinPacket(), processReceivedByte(), and rx_loop().
Here is the caller graph for this function:| void rx_loop | ( | ) |
Execute one non-blocking C2P-ARQ FSM tick for the Receiver node.
Handles the MAC-error display timer, note-duration timer, Dynamic Smart Watchdog tear-down, non-blocking UART byte reading, and parser timeout. At most one FSM transition per invocation; no delay() calls.
Definition at line 672 of file ReceiverNode.ino.
References CHK_ERR_DISPLAY_MS, current_timeout_limit, currentState, errorDisplayStart, EXECUTING_ACTION, HELLO_NONCE_SIZE, isPlayingNote, isShowingError, last_valid_packet_time, lcd(), noteLengthMs, noteStartMs, parseState, processReceivedByte(), resetParser(), rx_index, RX_PARSER_TIMEOUT_MS, rxLastByteMs, s_sessionActive, s_sessionNonce, stopNote(), updateRxDisplay(), WAIT_AA, and WAITING_SYNC_1.
Referenced by loop().
Here is the call graph for this function:
Here is the caller graph for this function:| void rx_setup | ( | ) |
Initialise RX hardware, seed the CSPRNG, and display the idle screen.
Initialise RX hardware and seed the CSPRNG. Called once from setup().
Configures UART @ 9600 bps, buzzer pin, I2C LCD, and byte-parser FSM. Harvests 256-bit hardware entropy (~16 ms) and seeds the ChaCha20 CSPRNG. The entropy buffer is scrubbed from the stack immediately after use.
Definition at line 615 of file ReceiverNode.ino.
References BAUD_RATE, currentState, generateEntropyPool(), initCSPRNG(), lcd(), parseState, RX_BUZZER_PIN, rx_index, updateRxDisplay(), WAIT_AA, and WAITING_SYNC_1.
Referenced by setup().
Here is the call graph for this function:
Here is the caller graph for this function:| void setup | ( | ) |
Arduino entry point — delegates to rx_setup().
Definition at line 720 of file ReceiverNode.ino.
References rx_setup().
Here is the call graph for this function:| void startNote | ( | uint16_t | frequencyHz, |
| uint16_t | durationMs | ||
| ) |
Start non-blocking note playback on the piezo buzzer.
Start a non-blocking note playback on the piezo buzzer.
Calls tone(RX_BUZZER_PIN, frequencyHz), records noteStartMs = millis(), and sets isPlayingNote = true. rx_loop() will call stopNote() when (millis() - noteStartMs) >= noteLengthMs. For frequencyHz == 0 (REST), no tone is started and isPlayingNote is still set to handle timing.
| frequencyHz | Frequency in Hz to pass to tone(); 0 = silence. |
| durationMs | Duration in milliseconds to hold the note before calling noTone(). |
Definition at line 494 of file ReceiverNode.ino.
References isPlayingNote, noteLengthMs, noteStartMs, and RX_BUZZER_PIN.
Referenced by authenticateAndPlay().
Here is the caller graph for this function:| void stopNote | ( | ) |
Stop the currently playing note and clear the playback flag.
Stop the currently playing note by calling noTone().
Calls noTone(RX_BUZZER_PIN) and sets isPlayingNote = false. Called by rx_loop() when (millis() - noteStartMs) >= noteLengthMs.
Definition at line 506 of file ReceiverNode.ino.
References isPlayingNote, and RX_BUZZER_PIN.
Referenced by authenticateAndPlay(), and rx_loop().
Here is the caller graph for this function:| void updateRxDisplay | ( | RxState | state, |
| uint16_t | seqNum, | ||
| bool | macOk | ||
| ) |
Refresh the RX LCD with the current FSM state and last packet result.
Row 0 shows a human-readable RxState label; Row 1 shows SEQ: <seqNum> OK or SEQ: <seqNum> MAC! depending on macOk.
| state | Current RxState to display on LCD row 0. |
| seqNum | Last received sequence number for LCD row 1. |
| macOk | true if MAC verification passed; false on failure. |
Definition at line 138 of file ReceiverNode.ino.
References EXECUTING_ACTION, GOT_DATA, GOT_HELLO, lcd(), READING_DATA, READING_HELLO, s_sessionActive, seqNum, WAITING_FOR_TYPE, WAITING_SYNC_1, and WAITING_SYNC_2.
Referenced by authenticateAndPlay(), processReceivedByte(), rx_loop(), and rx_setup().
Here is the call graph for this function:
Here is the caller graph for this function:| const uint16_t CHK_ERR_DISPLAY_MS = 500 |
Duration of the MAC-error message shown on the LCD before returning to idle display.
Definition at line 96 of file ReceiverNode.ino.
Referenced by rx_loop().
|
static |
Current Dynamic Watchdog timeout in milliseconds.
Updated after every authenticated DataPacket to (last note duration + NETWORK_GRACE_PERIOD_MS). Default 5000 ms until the first DATA packet establishes a note duration.
Definition at line 123 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), processHelloBody(), and rx_loop().
|
static |
Tracks the current RxState for LCD display updates; independent of the byte-parser FSM.
Definition at line 128 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), processReceivedByte(), resetParser(), rx_loop(), and rx_setup().
|
static |
Timestamp (millis()) when the MAC-error display was last activated.
Definition at line 100 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), and rx_loop().
|
static |
Total expected bytes for the current frame; set in READ_TYPE state.
Definition at line 78 of file ReceiverNode.ino.
Referenced by processReceivedByte().
|
static |
True while a note is actively sounding; cleared by stopNote().
Definition at line 93 of file ReceiverNode.ino.
Referenced by rx_loop(), startNote(), and stopNote().
|
static |
True when the LCD is currently showing a MAC-error message.
Definition at line 98 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), and rx_loop().
|
static |
Timestamp (millis()) of the most recently successfully authenticated DataPacket.
Definition at line 125 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), processHelloBody(), and rx_loop().
| const uint32_t NETWORK_GRACE_PERIOD_MS = 3000 |
Network grace period added to the last note's duration for the Dynamic Smart Watchdog.
Absorbs the worst-case TX retry storm: MAX_RETRIES × ACK_TIMEOUT_MS = 50 × 50 ms = 2500 ms. Value 3000 ms provides 500 ms margin above that maximum.
Definition at line 116 of file ReceiverNode.ino.
Referenced by authenticateAndPlay().
|
static |
Duration in milliseconds that the current note should sound.
Definition at line 91 of file ReceiverNode.ino.
Referenced by rx_loop(), and startNote().
|
static |
Timestamp (millis()) when the current note started playing.
Definition at line 89 of file ReceiverNode.ino.
Referenced by rx_loop(), and startNote().
|
static |
Current byte-parser FSM state; reset to WAIT_AA by resetParser().
Definition at line 67 of file ReceiverNode.ino.
Referenced by processReceivedByte(), resetParser(), rx_loop(), and rx_setup().
|
static |
Raw receive buffer sized to hold the largest frame body (DataPacket, 15 bytes).
HelloPacket (13 bytes) also fits. rx_buffer[0] always holds the flags byte; casting to the appropriate struct pointer gives zero-copy struct access.
Definition at line 74 of file ReceiverNode.ino.
Referenced by processHelloBody(), and processReceivedByte().
|
static |
Number of bytes written to rx_buffer for the frame currently being collected.
Definition at line 76 of file ReceiverNode.ino.
Referenced by processReceivedByte(), resetParser(), rx_loop(), and rx_setup().
| const uint16_t RX_PARSER_TIMEOUT_MS = 20 |
Stale byte accumulation timeout for the byte-level frame parser.
If body bytes stop arriving mid-frame for longer than this value, resetParser() is called to prevent a permanently stalled accumulator.
Definition at line 107 of file ReceiverNode.ino.
Referenced by rx_loop().
|
static |
Timestamp (millis()) of the most recently received UART byte.
Definition at line 109 of file ReceiverNode.ino.
Referenced by rx_loop().
|
static |
ChaCha20-Poly1305 cipher instance; re-initialised per packet via clear().
Definition at line 86 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), processFinPacket(), and processHelloBody().
|
static |
Pulse counter incremented by the ring oscillator ISR on INT0 (pin 2).
Definition at line 512 of file ReceiverNode.ino.
Referenced by generateEntropyPool(), and onRingOscPulse().
|
static |
Session active flag — set after a valid HelloPacket handshake; cleared on FIN or watchdog timeout.
Definition at line 81 of file ReceiverNode.ino.
Referenced by processFinPacket(), processHelloBody(), processReceivedByte(), rx_loop(), and updateRxDisplay().
|
static |
12-byte session nonce delivered by the last accepted HelloPacket (FLAG_SYN).
Definition at line 83 of file ReceiverNode.ino.
Referenced by authenticateAndPlay(), processFinPacket(), processHelloBody(), and rx_loop().
|
static |
Universal note frequency dictionary for the C2P-ARQ Receiver.
Maps an encoded note index (0–20) sent by Transmitter Node A to the corresponding buzzer frequency in Hz. The RX node has no knowledge of which melody is playing — it only translates index -> frequency. Index layout (chromatic scale, C4 … G#5):
REST_INDEX (255) means silence — handled separately in authenticateAndPlay().
Definition at line 33 of file ReceiverNode.ino.
Referenced by authenticateAndPlay().