|
SH2SC-EDT
Self-Healing Hardware and Software Complex for Encrypted Data Transmission
|
SH2SC-EDT — Transmitter Node A ("The Conductor") public interface. More...
Include dependency graph for transmitter.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Enumerations | |
| enum class | TxState : uint8_t { IDLE , RECONNECTING , SENDING_HELLO , WAITING_HELLO_ACK , SENDING , WAITING_ACK , WAIT_BETWEEN_NOTES , SENDING_FIN , WAITING_FIN_ACK } |
| Finite State Machine states for the C2P-ARQ Transmitter (Node A). More... | |
Functions | |
| void | tx_setup () |
| Initialise TX hardware: UART, I2C LCD, button pin, ring-oscillator entropy. | |
| void | tx_loop () |
| Execute one non-blocking FSM tick for the Transmitter node. | |
| void | suspendSession () |
| Suspend the current C2P-ARQ session and enter the Self-Healing reconnect loop. | |
| bool | readButtonPress () |
| Read and debounce the start button using a millis()-based filter. | |
| void | sendHelloPacket () |
| Generate a fresh 12-byte CSPRNG session nonce and transmit it as a HelloPacket. | |
| void | sendPacket (uint8_t noteIndex, uint16_t noteDurationMs, uint8_t seqNum) |
| Encrypt and transmit a single DataPacket for the given note. | |
| void | formAndSendPacket (uint8_t note_idx, uint16_t duration_ms) |
| High-level wrapper called by the SENDING FSM state. | |
| void | sendFinPacket () |
| Construct and transmit the FLAG_FIN session-close packet. | |
| void | updateTxDisplay (TxState state, uint16_t seqNum, uint8_t retries) |
| Refresh the TX LCD with the current ARQ status and FSM state label. | |
| void | generateEntropyPool (uint8_t *outputSeed) |
Harvest 256 bits of hardware entropy and write them to outputSeed. | |
Variables | |
| const uint8_t | TX_BUTTON_PIN = 3 |
| Tactile start button (INPUT_PULLUP, active LOW). | |
| const uint8_t | ENTROPY_RING_OSC_PIN = 2 |
| Hardware ring oscillator entropy source (INT0). | |
| const uint8_t | TX_LCD_ADDR = 0x3E |
| I2C address of the Aip31068 16x2 LCD display. | |
| const uint8_t | TX_LCD_COLS = 16 |
| Number of columns on the TX LCD. | |
| const uint8_t | TX_LCD_ROWS = 2 |
| Number of rows on the TX LCD. | |
| const uint16_t | DEBOUNCE_DELAY_MS = 50 |
| const uint32_t | RECONNECT_INTERVAL_MS = 2000 |
| Auto-reconnect HelloPacket broadcast interval (ms) while in RECONNECTING state. | |
SH2SC-EDT — Transmitter Node A ("The Conductor") public interface.
Part of the SH2SC-EDT project. Implements the C2P-ARQ protocol. Declares the TX FSM states, hardware pin constants, ARQ timing constants, and all public functions used by TransmitterNode.ino. Stores the melody in PROGMEM, encrypts packets via ChaCha20-Poly1305, and drives the full C2P-ARQ session lifecycle (SYN -> DAT -> FIN).
Definition in file transmitter.h.
|
strong |
Finite State Machine states for the C2P-ARQ Transmitter (Node A).
The entire TX logic is driven by this FSM. No blocking delays are permitted; each state transition is handled in a single non-blocking pass through tx_loop().
Canonical lifecycle:
| Enumerator | |
|---|---|
| IDLE | Waiting for a button press to begin melody playback. |
| RECONNECTING | Link lost mid-melody; broadcasting HelloPackets every RECONNECT_INTERVAL_MS. |
| SENDING_HELLO | Generating a CSPRNG nonce and transmitting the SYN handshake packet. |
| WAITING_HELLO_ACK | HelloPacket sent; awaiting RX nonce-acceptance ACK. |
| SENDING | Constructing, encrypting, and transmitting the current DataPacket. |
| WAITING_ACK | DataPacket sent; listening on the feedback line for ACK or NACK. |
| WAIT_BETWEEN_NOTES | ACK received; holding the inter-note gap before advancing melodyIndex. |
| SENDING_FIN | All notes delivered; transmitting the FLAG_FIN teardown packet. |
| WAITING_FIN_ACK | FIN packet sent; awaiting RX acknowledgement before erasing the session nonce. |
Definition at line 72 of file transmitter.h.
| void formAndSendPacket | ( | uint8_t | note_idx, |
| uint16_t | duration_ms | ||
| ) |
High-level wrapper called by the SENDING FSM state.
Reads noteIndex and duration_ms from PROGMEM at the current melodyIndex, snapshots them into module-level retransmit variables, then delegates to sendPacket(). On NACK or timeout the FSM calls sendPacket() again directly (retransmit path reuses the snapshot).
| note_idx | Note index loaded from the PROGMEM melody table. |
| duration_ms | Duration in milliseconds loaded from the PROGMEM melody table. |
High-level wrapper called by the SENDING FSM state.
Stores note_idx and duration_ms as the pending retransmit snapshot so that on NACK or timeout, the FSM can call sendPacket() directly without re-reading PROGMEM.
| note_idx | Note index read from the PROGMEM melody table (0–20 or REST_INDEX=255). |
| duration_ms | Note duration in milliseconds read from the PROGMEM melody table. |
Definition at line 209 of file TransmitterNode.ino.
References pendingNoteDuration, pendingNoteIndex, sendPacket(), and seqNum.
Referenced by tx_loop().
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 write them to outputSeed.
Fills outputSeed[32] (8 × 32-bit words) by XOR-combining seven independent entropy sources per word:
0x0100 + wordIndex*8.micros() at button-press time (human-timing jitter, TX-only source).random() (obfuscation layer).Total harvest time: ~8 × 2 ms gate = ~16 ms (acceptable one-shot cost).
| outputSeed | Pointer to a 32-byte buffer that will receive the entropy pool. Must be valid and writable. Caller should pass the buffer directly to initCSPRNG() and then scrub it if desired. |
true.Harvest 256 bits of hardware entropy and write them to outputSeed.
Fills outputSeed[32] as 8 independent 32-bit words. Each word is produced by a fresh pass over all seven entropy sources so that a biased source in one iteration is compensated by the others through the rotate-XOR mixing chain. Total execution time: 8 iterations x 2 ms gate = ~16 ms (one-shot cost). This is the TX variant — it includes micros() human-timing jitter from the button press as a seventh source not present on the RX node.
| outputSeed | Pointer to a 32-byte output buffer. Must be valid and writable. Pass directly to initCSPRNG(); scrub afterwards if desired. |
true.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 345 of file TransmitterNode.ino.
References ENTROPY_RING_OSC_PIN, mixEntropy(), onRingOscPulse(), and s_ringOscPulses.
Here is the call graph for this function:| bool readButtonPress | ( | ) |
Read and debounce the start button using a millis()-based filter.
Returns true exactly once per physical button press (falling edge on INPUT_PULLUP). Debounce window is DEBOUNCE_DELAY_MS milliseconds.
true on confirmed button press; false otherwise.Read and debounce the start button using a millis()-based filter.
Must be called every iteration of loop() so the debounce timer continues accumulating regardless of the active FSM state.
true exactly once per confirmed physical button press; false otherwise. Definition at line 71 of file TransmitterNode.ino.
References btnLastChangeMs, btnLastRawState, btnStableState, DEBOUNCE_DELAY_MS, and TX_BUTTON_PIN.
Referenced by tx_loop().
Here is the caller graph for this function:| void sendFinPacket | ( | ) |
Construct and transmit the FLAG_FIN session-close packet.
Cryptographically identical to sendPacket(): same per-packet nonce derivation (XOR bytes 10–11 with seqNum), same 3-byte AAD (flags + seq_num), same truncated Poly1305 MAC. The plaintext payload is all-zeros ({0, 0}) — the AEAD pipeline still runs in full. Called from SENDING_FIN state; retransmitted on NACK or timeout.
memset(s_sessionNonce, 0, 12) to complete the forward-secrecy teardown.Construct and transmit the FLAG_FIN session-close packet.
Architecture mirrors sendPacket() exactly: same per-packet nonce derivation (XOR bytes 10–11 with seqNum), same 3-byte AAD (FLAG_FIN + seq_num), same truncated 8-byte Poly1305 MAC. The plaintext payload is all-zeros; the flags byte in the AAD binds the MAC to FLAG_FIN, preventing a bit-flip attack from turning a data packet into a teardown signal. Called from SENDING_FIN; retransmitted on NACK or timeout until MAX_RETRIES is exhausted (which triggers suspendSession()).
Definition at line 266 of file TransmitterNode.ino.
References DATA_PAYLOAD_SIZE, drainRxFifo(), FLAG_FIN, DataPacket::flags, HELLO_NONCE_SIZE, DataPacket::mac, MASTER_PSK, DataPacket::payload, s_cipher, s_sessionNonce, DataPacket::seq_num, seqNum, SYNC_BYTE_1, SYNC_BYTE_2, and TRUNCATED_MAC_SIZE.
Referenced by tx_loop().
Here is the call graph for this function:
Here is the caller graph for this function:| void sendHelloPacket | ( | ) |
Generate a fresh 12-byte CSPRNG session nonce and transmit it as a HelloPacket.
Calls getSecureRandom32() three times to fill s_sessionNonce[12], then broadcasts it in a FLAG_SYN HelloPacket preceded by the sync preamble (0xAA 0x55). Also calls drainRxFifo() before transmitting to discard any stale ACK/NACK bytes accumulated from prior noise bursts. The nonce is stored internally for use by subsequent sendPacket() calls.
Generate a fresh 12-byte CSPRNG session nonce and transmit it as a HelloPacket.
Called once per button press (from IDLE state) and once per reconnect attempt (from RECONNECTING state) — always before any DataPacket is transmitted. Calls drainRxFifo() before writing to the UART to purge stale NACKs. Stores the generated nonce in s_sessionNonce for subsequent per-packet IV derivation by sendPacket() and sendFinPacket().
Definition at line 118 of file TransmitterNode.ino.
References drainRxFifo(), FLAG_SYN, HelloPacket::flags, getSecureRandom32(), HELLO_NONCE_SIZE, HelloPacket::nonce, s_sessionNonce, SYNC_BYTE_1, and SYNC_BYTE_2.
Referenced by tx_loop().
Here is the call graph for this function:
Here is the caller graph for this function:| void sendPacket | ( | uint8_t | noteIndex, |
| uint16_t | noteDurationMs, | ||
| uint8_t | seqNum | ||
| ) |
Encrypt and transmit a single DataPacket for the given note.
Derives the per-packet IV by copying s_sessionNonce and XOR-ing bytes 10–11 with the 16-bit seqNum, then runs the full ChaChaPoly pipeline:
Only the first TRUNCATED_MAC_SIZE bytes of the tag are transmitted. Sends sync preamble (0xAA 0x55) + 15-byte DataPacket body (17 bytes total).
| noteIndex | Note index to encrypt (0–20 for a pitch, REST_INDEX=255 for silence). |
| noteDurationMs | Duration of the note in milliseconds (stored directly as uint16_t). |
| seqNum | 16-bit packet sequence number; used for per-packet nonce derivation. |
| void suspendSession | ( | ) |
Suspend the current C2P-ARQ session and enter the Self-Healing reconnect loop.
Called automatically when retryCount reaches MAX_RETRIES in any WAITING_* FSM state. Performs the following operations:
memset(s_sessionNonce, 0x00, HELLO_NONCE_SIZE) — erases the session nonce from RAM (forward secrecy; prevents key material leakage).retryCount to 0.melodyIndex — the critical Self-Healing state that allows transmission to resume from the exact note of failure.TxState::RECONNECTING. melodyIndex is the defining "self-healing" property of SH2SC-EDT.Called when retryCount reaches MAX_RETRIES in any WAITING_* FSM state. Unlike a clean FIN close, this is an unclean abort — the receiver vanished without sending FLAG_FIN. Actions performed:
s_sessionNonce via memset() (forward secrecy — the old nonce must not be reused after an unclean close).seqNum and retryCount to 0.melodyIndex — the defining Self-Healing property of SH2SC-EDT; transmission will resume from the exact note of failure on reconnect.TxState::RECONNECTING.lastReconnectAttempt so the first auto-ping fires after a full RECONNECT_INTERVAL_MS, giving the receiver time to reboot. delay(2000) inside this function is the only permitted blocking call outside of setup() — it holds the LCD error message visible to the operator. Definition at line 426 of file TransmitterNode.ino.
References currentState, HELLO_NONCE_SIZE, lastReconnectAttempt, lcd(), RECONNECTING, retryCount, s_sessionNonce, and seqNum.
Referenced by tx_loop().
Here is the call graph for this function:
Here is the caller graph for this function:| void tx_loop | ( | ) |
Execute one non-blocking FSM tick for the Transmitter node.
Dispatches to the handler for the current TxState. Each invocation performs at most one state transition and returns immediately — no delay() calls are permitted anywhere in this function or its callees.
loop().Execute one non-blocking FSM tick for the Transmitter node.
Dispatches to the handler for the active TxState. Each invocation performs at most one FSM transition and returns immediately. No delay() calls are permitted in this function or any callee (except the intentional 2-second error display in suspendSession()).
Definition at line 486 of file TransmitterNode.ino.
References ACK_BYTE, ACK_TIMEOUT_MS, ackWaitStart, currentState, formAndSendPacket(), HELLO_NONCE_SIZE, IDLE, lastReconnectAttempt, MAX_RETRIES, melody, MELODY_LENGTH, melodyIndex, NACK_BYTE, noteWaitStart, pendingNoteDuration, pendingNoteIndex, readButtonPress(), RECONNECT_INTERVAL_MS, RECONNECTING, retryCount, s_sessionNonce, sendFinPacket(), sendHelloPacket(), SENDING, SENDING_FIN, SENDING_HELLO, seqNum, suspendSession(), updateTxDisplay(), WAIT_BETWEEN_NOTES, WAITING_ACK, WAITING_FIN_ACK, and WAITING_HELLO_ACK.
Referenced by loop().
Here is the call graph for this function:
Here is the caller graph for this function:| void tx_setup | ( | ) |
Initialise TX hardware: UART, I2C LCD, button pin, ring-oscillator entropy.
setup().Initialise TX hardware: UART, I2C LCD, button pin, ring-oscillator entropy.
Initialises UART at BAUD_RATE (9600), configures TX_BUTTON_PIN as INPUT_PULLUP, initialises the I2C LCD, harvests 256-bit hardware entropy with generateEntropyPool(), seeds the ChaCha20 CSPRNG with initCSPRNG(), scrubs the entropy seed buffer from the stack, and sets the initial FSM state to IDLE.
Definition at line 454 of file TransmitterNode.ino.
References BAUD_RATE, currentState, generateEntropyPool(), IDLE, initCSPRNG(), lcd(), retryCount, seqNum, TX_BUTTON_PIN, and updateTxDisplay().
Referenced by setup().
Here is the call graph for this function:
Here is the caller graph for this function:| void updateTxDisplay | ( | TxState | state, |
| uint16_t | seqNumber, | ||
| uint8_t | retries | ||
| ) |
Refresh the TX LCD with the current ARQ status and FSM state label.
PKT: <seqNum> RTY:<retries>| state | Current TxState to display on row 1. |
| seqNum | Current packet sequence number to display on row 0. |
| retries | Current consecutive retry count to display on row 0. |
PKT: <seqNumber> RTY:<retries> (live ARQ visibility for the operator). Row 1: Human-readable FSM state name. Must only be called on FSM state transitions — NOT in a tight loop — to avoid I2C bus saturation. | state | Current TxState to display on row 1. |
| seqNumber | Current packet sequence number to display on row 0. |
| retries | Current consecutive retry count to display on row 0. |
Definition at line 228 of file TransmitterNode.ino.
References IDLE, lcd(), RECONNECTING, SENDING, SENDING_FIN, SENDING_HELLO, WAIT_BETWEEN_NOTES, WAITING_ACK, WAITING_FIN_ACK, and WAITING_HELLO_ACK.
Referenced by tx_loop(), and tx_setup().
Here is the call graph for this function:
Here is the caller graph for this function: