Protocol rework

This commit is contained in:
ThePetrovich 2026-05-10 15:33:08 +08:00
parent 3e77d34ccc
commit 2b713a3e3a
15 changed files with 1347 additions and 1155 deletions

328
sbc_fw/protocol.cpp Normal file
View file

@ -0,0 +1,328 @@
/*
* @file protocol.cpp
* @brief Protocol framing, CRC, and command handler implementations.
*
* Created: 07.05.2026
* Author: ThePetrovich
*
* Copyright YKSA - Sakha Aerospace Systems, LLC.
* See the LICENSE file for details.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <Arduino.h>
#include <stdint.h>
#include <string.h>
#include <util/atomic.h>
#include "adc.h"
#include "config.h"
#include "eeprom.h"
#include "iodefs.h"
#include "lsense.h"
#include "protocol.h"
#include "rsense.h"
extern bool g_rsense_enabled; /* from rsense.cpp */
uint16_t protocol_crc16_update(uint16_t crc, const uint8_t *data, uint16_t len)
{
if (data == NULL) {
return crc;
}
while (len--) {
crc ^= (uint16_t)(*data++) << 8;
for (uint8_t i = 0; i < 8; i++) {
crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : crc << 1;
}
}
return crc;
}
uint16_t protocol_crc16_xmodem(const uint8_t *data, uint16_t len) { return protocol_crc16_update(0, data, len); }
/**
* @brief Write a frame header (CMD + LEN) to the serial port.
* Internal helper, not exported.
*/
static void send_header_bytes(uint16_t cmd, uint16_t length)
{
Serial.write((uint8_t)(cmd & 0xFF));
Serial.write((uint8_t)(cmd >> 8));
Serial.write((uint8_t)(length & 0xFF));
Serial.write((uint8_t)(length >> 8));
}
/**
* @brief Write a 16-bit CRC trailer (LSB first) and flush the TX queue.
*/
static void send_crc_trailer(uint16_t crc)
{
Serial.write((uint8_t)(crc & 0xFF));
Serial.write((uint8_t)(crc >> 8));
Serial.flush();
}
void protocol_send_message_v2(uint16_t cmd, const void *frag1, uint16_t len1, const void *frag2, uint16_t len2)
{
protocol_header_t hdr;
hdr.cmd = cmd;
hdr.length = (uint16_t)(len1 + len2);
uint16_t crc = protocol_crc16_update(0, (const uint8_t *)&hdr, sizeof(hdr));
crc = protocol_crc16_update(crc, (const uint8_t *)frag1, len1);
crc = protocol_crc16_update(crc, (const uint8_t *)frag2, len2);
send_header_bytes(hdr.cmd, hdr.length);
if (frag1 != NULL && len1 > 0) {
Serial.write((const uint8_t *)frag1, len1);
}
if (frag2 != NULL && len2 > 0) {
Serial.write((const uint8_t *)frag2, len2);
}
send_crc_trailer(crc);
}
void protocol_send_message(uint16_t cmd, const void *payload, uint16_t length)
{
protocol_send_message_v2(cmd, payload, length, NULL, 0);
}
void protocol_send_ack(void) { protocol_send_message(PROTOCOL_RESP_ACK, NULL, 0); }
void protocol_send_nak(void) { protocol_send_message(PROTOCOL_RESP_NAK, NULL, 0); }
void protocol_send_error(void) { protocol_send_message(PROTOCOL_RESP_ERR, NULL, 0); }
/**
* @brief Validate that a received payload is at least @p expected bytes long.
* Sends a NAK on the wire and returns false if the check fails.
*/
static bool require_payload(uint16_t length, uint16_t expected)
{
if (length < expected) {
protocol_send_nak();
return false;
}
return true;
}
void protocol_version_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
protocol_firmware_version_response_t resp;
resp.major = FIRMWARE_VERSION_MAJOR;
resp.minor = FIRMWARE_VERSION_MINOR;
protocol_send_message(CMD_FIRMWARE_VERSION, &resp, sizeof(resp));
}
void protocol_light_sensor_presence_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
lsense_detect_all();
protocol_light_sensor_presence_response_t resp;
lsense_get_presence(&resp);
protocol_send_message(CMD_LIGHT_SENSOR_PRESENCE, &resp, sizeof(resp));
}
void protocol_read_light_sensors_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_read_light_sensors_handler_t))) {
return;
}
protocol_read_light_sensors_handler_t *cmd = (protocol_read_light_sensors_handler_t *)payload;
if (cmd->channel > 6) {
protocol_send_nak();
return;
}
protocol_read_light_sensors_response_t resp;
lsense_read_channel(cmd->channel, &resp);
protocol_send_message(CMD_READ_LIGHT_SENSORS, &resp, sizeof(resp));
}
void protocol_get_configuration_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
protocol_send_message(CMD_GET_CONFIGURATION, &config, sizeof(config));
}
void protocol_set_configuration_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_set_configuration_handler_t))) {
return;
}
protocol_set_configuration_handler_t *cmd = (protocol_set_configuration_handler_t *)payload;
if (cmd->magic1 != 0xA5 || cmd->magic2 != 0x5A) {
protocol_send_nak();
return;
}
config = *cmd;
eeprom_save_config();
rsense_apply_potentiometers();
protocol_send_ack();
}
void protocol_set_potentiometers_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_set_potentiometers_handler_t))) {
return;
}
config.pots = *(protocol_set_potentiometers_handler_t *)payload;
rsense_apply_potentiometers();
eeprom_save_config();
protocol_send_ack();
}
void protocol_set_calibration_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_set_calibration_handler_t))) {
return;
}
config.calibration = *(protocol_set_calibration_handler_t *)payload;
eeprom_save_config();
protocol_send_ack();
}
void protocol_set_flags_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_set_flags_handler_t))) {
return;
}
config.flags = *(protocol_set_flags_handler_t *)payload;
eeprom_save_config();
protocol_send_ack();
}
void protocol_set_temp_drift_compensation_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_set_temp_drift_compensation_handler_t))) {
return;
}
config.temp_drift_compensation = *(protocol_set_temp_drift_compensation_handler_t *)payload;
eeprom_save_config();
protocol_send_ack();
}
void protocol_set_drift_compensation_enable_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_set_drift_compensation_enable_handler_t))) {
return;
}
protocol_set_drift_compensation_enable_handler_t *cmd =
(protocol_set_drift_compensation_enable_handler_t *)payload;
config.flags.fields.temperature_drift_compensation = (cmd->enable != 0) ? 1 : 0;
eeprom_save_config();
protocol_send_ack();
}
void protocol_get_telemetry_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
adc_restore_default();
protocol_get_telemetry_response_t resp;
resp.hv_temp_c = (uint16_t)adc_to_temperature_c(adc_read_oversampled(HV_TEMP_PIN, 16));
resp.amp_temp_c = (uint16_t)adc_to_temperature_c(adc_read_oversampled(AMP_TEMP_PIN, 16));
resp.sipm_temp_c = (uint16_t)adc_to_temperature_c(adc_read_oversampled(DET_TEMP_PIN, 16));
resp.mcu_temp_c = adc_read_tempsense();
resp.vbias_mv = adc_to_voltage_mv(adc_read_oversampled(V28V0_FB_PIN, 16));
resp.pots = config.pots;
resp.cps = rsense_get_cps() / 60;
resp.cp10s = rsense_get_cp10s();
resp.total_counts = rsense_get_total_counts();
resp.flags = (1 << 0) | /* power on ok */
((g_rsense_enabled ? 1 : 0) << 1) |
((config.flags.fields.temperature_drift_compensation ? 1 : 0) << 2);
adc_enable_fast();
protocol_send_message(CMD_GET_TELEMETRY, &resp, sizeof(resp));
}
void protocol_flush_counters_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
rsense_flush_counters();
protocol_send_ack();
}
void protocol_flush_spectrum_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
rsense_flush_spectrum();
protocol_send_ack();
}
void protocol_rsense_enable_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
rsense_enable();
protocol_send_ack();
}
void protocol_rsense_disable_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
rsense_disable();
protocol_send_ack();
}
void protocol_spectrum_freeze_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
/* Disable ADC result-ready interrupt so the ISR stops accumulating. */
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ADC0.INTCTRL &= ~ADC_RESRDY_bm; }
protocol_send_ack();
}
void protocol_spectrum_unfreeze_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ADC0.INTCTRL |= ADC_RESRDY_bm; }
protocol_send_ack();
}
void protocol_get_counts_handler(void *payload, uint16_t length)
{
(void)payload;
(void)length;
protocol_get_counts_response_t resp;
resp.counts = rsense_get_counts_since_last();
protocol_send_message(CMD_GET_COUNTS, &resp, sizeof(resp));
}
void protocol_read_spectrum_chunk_handler(void *payload, uint16_t length)
{
if (!require_payload(length, sizeof(protocol_read_spectrum_chunk_handler_t))) {
return;
}
protocol_read_spectrum_chunk_handler_t *cmd = (protocol_read_spectrum_chunk_handler_t *)payload;
if (cmd->offset >= RSENSE_CHANNEL_COUNT * sizeof(uint16_t)) {
protocol_send_nak();
return;
}
uint16_t available = (RSENSE_CHANNEL_COUNT * sizeof(uint16_t)) - cmd->offset;
uint16_t send_len = (cmd->length < available) ? cmd->length : available;
const uint8_t *spectrum = (const uint8_t *)rsense_get_spectrum_ptr();
protocol_read_spectrum_chunk_response_t resp_hdr;
resp_hdr.offset = cmd->offset;
resp_hdr.length = send_len;
protocol_send_message_v2(CMD_READ_SPECTRUM_DATA, &resp_hdr, sizeof(resp_hdr), spectrum + cmd->offset, send_len);
}