TinyML Arduino Posture Classifier/ Sida 4 av 4
Federated Learning
Federated learning over Bluetooth Low Energy, sharing and aggregating model weights between Arduino devices using weighted averaging based on training data.
The Setup
Two Arduino devices can share their trained output weights over Bluetooth Low Energy. One device acts as the central (initiates the connection), the other as the peripheral (waits for connection).
The central device is determined by checking if it has a valid UUID:
if (Coms.getUUID()) {
// THIS DEV IS CENTRAL
} else {
// THIS DEV IS PERIPHERAL
}
The Protocol
The weight sharing protocol in federated.cpp works differently for central and peripheral:
Central device:
- Send its weights (W_a) to peripheral
- Send its batch count (n_a)
- Receive peripheral’s weights (W_b)
- Receive peripheral’s batch count (n_b)
Peripheral device:
- Wait for central to connect
- Receive central’s weights (W_b)
- Receive central’s batch count (n_b)
- Send its weights (W_a)
- Send its batch count (n_a)
The timing is critical: there are delays between operations to ensure the BLE stack has time to complete each transfer:
// Central sends first
if (!Coms.sendModel((float *)W_a.weights, sizeof(float) * W_out_length)) {
return false;
}
delay(5000); // Wait before sending batch count
if (!Coms.sendNBatches((const uint16_t)n_a, sizeof(uint16_t))) {
return false;
}
// Then receive
if (!Coms.receiveModel((float *)W_b.weights, sizeof(float) * W_out_length)) {
return false;
}
if (!Coms.receiveNBatches(&n_b, sizeof(uint16_t))) {
return false;
}
The peripheral has longer delays (8 seconds) because it needs to wait for the central to initiate the connection and start sending.
Weighted Averaging
After exchanging weights, both devices compute a weighted average:
n_tot = n_a + n_b;
for (size_t i = 0; i < OUTPUT_SIZE; i++) {
for (size_t j = 0; j < RESERVOIR_SIZE; j++) {
W_a.weights[i][j] =
(W_a.weights[i][j] * (float)n_a + W_b.weights[i][j] * (float)n_b) / (float)n_tot;
}
}
If device A trained on 10 batches and device B trained on 5 batches, the aggregated weights give 2/3 weight to A and 1/3 to B. This ensures devices that have seen more data have more influence on the shared model.
Both devices end up with the same aggregated weights, so they’re synchronized after the exchange.
The Trigger
Federated weight sharing is triggered by the CMD_SHARE_WEIGHTS command in engine.cpp:
case CMD_SHARE_WEIGHTS: {
turnOffLED();
// Get total batches processed (including current session)
uint16_t mem;
getNProcessedBatches(&mem);
uint16_t curr = mem + nBatchesProcessed;
// Perform weight sharing
shareW_out(&curr);
// Update batch count
nBatchesProcessed = curr - mem;
// Visual feedback via LED
communicateUSBMode();
delay(1000);
communicateSuccess();
delay(1000);
communicateBLEMode();
delay(1000);
turnOffLED();
// Reset communication mode
CommunicationMode coms = getCommunicationMode();
Coms.setBackend(coms);
break;
}
The LED blinks to show the mode (USB → success → BLE) during the exchange, then the device prompts for a new communication mode selection.
What Gets Shared
Only the output weights (W_out) are shared. The reservoir weights (W_in, W_res) remain fixed and identical across devices (they were initialized with the same random seed). The EMA values are not shared, so each device maintains its own normalization parameters.
The shared structure is defined in esn.h:
typedef struct {
float weights[OUTPUT_SIZE][RESERVOIR_SIZE];
} shareableWeights;
This is 3 × 20 = 60 floats = 240 bytes -> small enough to transfer quickly over BLE.
The Python Helper
The python_helper/ble_shell.py script provides a command-line interface for managing federated learning. It can:
- Connect to devices over BLE
- Send training commands
- Trigger weight sharing
- Monitor the exchange process
The script uses the bleak library for cross-platform BLE support.
Limitations
The current implementation has some constraints:
- Pairwise only: Only two devices can exchange weights at a time. Multi-device federation would require multiple rounds.
- Manual triggering: Weight sharing must be explicitly triggered - it doesn’t happen automatically.
- No conflict resolution: If devices have diverged significantly, the weighted average might not produce a better model.
- BLE reliability: Connection drops can interrupt the exchange, requiring retry logic.
Despite these limitations, the implementation demonstrates that federated learning is feasible on resource-constrained microcontrollers using only BLE communication.