Protocol rework
This commit is contained in:
parent
3e77d34ccc
commit
2b713a3e3a
15 changed files with 1347 additions and 1155 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* @file sbc_fw.ino
|
||||
* @brief Main firmware for sensor board controller, YKSA PL/EDU16 RSENSE
|
||||
* @brief Main firmware for sensor board controller, YKSA PL/EDU16 RSENSE.
|
||||
*
|
||||
* Created: 21.09.2025
|
||||
* Author: ThePetrovich
|
||||
|
|
@ -15,15 +15,126 @@
|
|||
#include <SPI.h>
|
||||
#include <SoftwareI2C.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "eeprom.h"
|
||||
#include "iodefs.h"
|
||||
#include "lsense.h"
|
||||
#include "protocol.h"
|
||||
#include "rsense.h"
|
||||
|
||||
/*
|
||||
* Frame format: [CMD 2B][LEN 2B][PAYLOAD LEN bytes][CRC16 2B].
|
||||
* Maximum payload size chosen to fit the largest command (config_t ~22 bytes);
|
||||
* 256 bytes leaves headroom for future commands.
|
||||
*/
|
||||
#define PROTOCOL_MAX_PAYLOAD 256U
|
||||
#define PROTOCOL_HEADER_SIZE ((uint16_t)sizeof(protocol_header_t))
|
||||
#define PROTOCOL_CRC_SIZE 2U
|
||||
#define PROTOCOL_BUF_SIZE (PROTOCOL_HEADER_SIZE + PROTOCOL_MAX_PAYLOAD + PROTOCOL_CRC_SIZE)
|
||||
|
||||
static uint8_t rx_buf[PROTOCOL_BUF_SIZE];
|
||||
static uint16_t rx_pos = 0;
|
||||
|
||||
/**
|
||||
* @brief Dispatch a fully-validated frame to its registered handler.
|
||||
* Generated from the X-macro table in protocol.h.
|
||||
*/
|
||||
static void dispatch_handler(uint16_t cmd, void *payload, uint16_t length)
|
||||
{
|
||||
switch (cmd) {
|
||||
#define X(Enum, Cmd_id, Handler) \
|
||||
case Cmd_id: \
|
||||
Handler(payload, length); \
|
||||
break;
|
||||
X_PROTOCOL_handlerS
|
||||
#undef X
|
||||
default : protocol_send_error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Drain the serial RX buffer and assemble/dispatch any complete frames.
|
||||
* On bad CRC, oversized length, or buffer overflow, an ERR is emitted
|
||||
* and the receive state is reset.
|
||||
*/
|
||||
static void protocol_receive(void)
|
||||
{
|
||||
while (Serial.available()) {
|
||||
uint8_t byte = (uint8_t)Serial.read();
|
||||
|
||||
if (rx_pos < PROTOCOL_BUF_SIZE) {
|
||||
rx_buf[rx_pos++] = byte;
|
||||
} else {
|
||||
/* Buffer overflow — discard and reset. */
|
||||
protocol_send_error();
|
||||
rx_pos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Need a full header before total frame length is known. */
|
||||
if (rx_pos < PROTOCOL_HEADER_SIZE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
protocol_header_t hdr;
|
||||
memcpy(&hdr, rx_buf, sizeof(hdr));
|
||||
|
||||
if (hdr.length > PROTOCOL_MAX_PAYLOAD) {
|
||||
protocol_send_error();
|
||||
rx_pos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t total = PROTOCOL_HEADER_SIZE + hdr.length + PROTOCOL_CRC_SIZE;
|
||||
if (rx_pos < total) {
|
||||
continue; /* not enough bytes yet */
|
||||
}
|
||||
|
||||
uint16_t rx_crc = (uint16_t)rx_buf[total - 2] | ((uint16_t)rx_buf[total - 1] << 8);
|
||||
uint16_t calc_crc = protocol_crc16_xmodem(rx_buf, total - PROTOCOL_CRC_SIZE);
|
||||
|
||||
if (calc_crc != rx_crc) {
|
||||
protocol_send_error();
|
||||
} else {
|
||||
dispatch_handler(hdr.cmd, &rx_buf[PROTOCOL_HEADER_SIZE], hdr.length);
|
||||
}
|
||||
|
||||
/* Shift any back-to-back trailing bytes to the front of the buffer. */
|
||||
uint16_t extra = rx_pos - total;
|
||||
if (extra > 0) {
|
||||
memmove(rx_buf, rx_buf + total, extra);
|
||||
}
|
||||
rx_pos = extra;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t status_led_last_blink = 0;
|
||||
|
||||
/**
|
||||
* @brief Toggle the status LED at #STATUS_LED_BLINK_PERIOD ms intervals.
|
||||
*/
|
||||
static inline void update_status_led(void)
|
||||
{
|
||||
if (millis() - status_led_last_blink > STATUS_LED_BLINK_PERIOD) {
|
||||
digitalWrite(STATUS_LED, !digitalRead(STATUS_LED));
|
||||
status_led_last_blink = millis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bring up the SPI bus shared by the AD5160 digital potentiometers.
|
||||
*/
|
||||
static void initialize_spi(void)
|
||||
{
|
||||
SPI.begin();
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV128); /* 125 kHz @ 16 MHz */
|
||||
SPI.setDataMode(SPI_MODE0); /* AD5160 (Rev C.) */
|
||||
SPI.setBitOrder(MSBFIRST); /* AD5160 (Rev C.), p. 13, fig. 37 */
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
pinMode(STATUS_LED, OUTPUT);
|
||||
|
|
@ -38,102 +149,9 @@ void setup()
|
|||
rsense_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize SPI interface for potentiometer control
|
||||
*/
|
||||
void initialize_spi(void)
|
||||
{
|
||||
SPI.begin();
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV128); // 125 kHz @ 16 MHz
|
||||
SPI.setDataMode(SPI_MODE0); // AD5160 (Rev C.)
|
||||
SPI.setBitOrder(MSBFIRST); // AD5160 (Rev C.), p. 13, fig. 37
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle incoming serial commands
|
||||
* @param command Command character received
|
||||
*/
|
||||
void handle_serial_command(char command)
|
||||
{
|
||||
switch (command) {
|
||||
case 'v': // Firmware version
|
||||
handle_version_command();
|
||||
break;
|
||||
case 'd': // Detect/ping
|
||||
handle_detect_command();
|
||||
break;
|
||||
case 'l': // Light sensor presence
|
||||
lsense_cmd_presence();
|
||||
break;
|
||||
case 'r': // Read sensors
|
||||
lsense_cmd_read();
|
||||
rsense_cmd_get_cpm();
|
||||
break;
|
||||
case 'c': // Dump 128 bytes of channel data
|
||||
rsense_cmd_dump_channels();
|
||||
break;
|
||||
case 'f': // Flush counters
|
||||
rsense_cmd_flush();
|
||||
break;
|
||||
case 'e': // Enable radiation detection
|
||||
rsense_cmd_enable();
|
||||
break;
|
||||
case 's': // Disable radiation detection
|
||||
rsense_cmd_disable();
|
||||
break;
|
||||
case 'p': // Set potentiometers
|
||||
rsense_cmd_set_potentiometers();
|
||||
break;
|
||||
case 't': // Telemetry
|
||||
rsense_cmd_telemetry();
|
||||
break;
|
||||
case 'm': // Set spectrum mode (16-bit or 32-bit)
|
||||
rsense_cmd_set_configuration();
|
||||
break;
|
||||
default:
|
||||
// Unknown command - ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle firmware version command
|
||||
*/
|
||||
void handle_version_command(void)
|
||||
{
|
||||
Serial.write(FIRMWARE_VERSION_MAJOR);
|
||||
Serial.write(FIRMWARE_VERSION_MINOR);
|
||||
Serial.flush();
|
||||
SERIAL_BUFFER_CLEAR();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle detect/ping command
|
||||
*/
|
||||
void handle_detect_command(void) { SERIAL_SEND_OK(); }
|
||||
|
||||
/**
|
||||
* @brief Update status LED (blink)
|
||||
*/
|
||||
inline void update_status_led(void)
|
||||
{
|
||||
if (millis() - status_led_last_blink > STATUS_LED_BLINK_PERIOD) {
|
||||
digitalWrite(STATUS_LED, !digitalRead(STATUS_LED));
|
||||
status_led_last_blink = millis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main program loop
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
// Process incoming serial commands
|
||||
while (Serial.available()) {
|
||||
char command = Serial.read();
|
||||
handle_serial_command(command);
|
||||
}
|
||||
protocol_receive();
|
||||
update_status_led();
|
||||
|
||||
rsense_periodic();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue