STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1/**
2 * TC (Thermocouple) Hotfire state machine — ESP32 PCB
3 *
4 * Same state machine as PT Hotfire: Waiting for Server → Active → Standalone
5 * Abort (and back to Active on clear). Sends heartbeats, streams
6 * thermocouple/sensor data to server or to actuator controller in abort. Uses
7 * TC_Board pin layout and BoardType::THERMOCOUPLE. Uses shared
8 * SensorHotfireCore; board-specific: ADC init, collect_chunk, send_chunks_to.
9 */
10
11#include "main.h"
12
13#include <Arduino.h>
14#include <Ethernet.h>
15#include <EthernetUdp.h>
16#include <SPI.h>
17#include <daq-protocol.h>
18#include <esp_mac.h>
19
20#include <cstring>
21#include <vector>
22
23#include "STAR_ADS126X.h"
24
25// clang-format off
26// MUST be defined before sense_board_pins.h / connector_adc_map.h
27#define PINS_ACTIVE_LAYOUT sense_board_pins::TC_Board
28#include "sense_board_pins.h"
29#include "connector_adc_map.h"
30#include "adc_mappings.h"
31#include "SensorHotfireCore.h"
32#include "SensorSelfTest.h"
33// clang-format on
34
35using namespace sense_board_pins;
36
37//-----------------------------------------------------------------------------
38// ADC config (Stream_ADC_Data / multi-channel style)
39//-----------------------------------------------------------------------------
40// FIR @ 20 SPS: the ADS1263's quietest mode, and its FIR has fixed 50/60 Hz
41// rejection nulls (datasheet Table 9-13 / p93 — FIR is available only at
42// 2.5/5/10/20 SPS). This sensor is slow enough that 20 SPS is ample.
43#define FILTER ADS126X_FIR
44#define DATA_RATE ADS126X_RATE_20
46#define TEST_PIN 1
47#define NUM_CHANNELS 10
48
50SPIClass ADC_SPI(HSPI);
51std::vector<daq::SensorDataChunkCollection> dataChunks;
52
55
56static unsigned long g_last_sensor_packet_log_ms = 0;
57
58// Set to true to enable Serial output from core and board; false to disable.
59bool g_verbose = true;
60
61static void flush_cycles(int cycles) {
62 for (int i = 0; i < cycles; i++) {
63 while (digitalRead(Pins.ADC_DRDY_1) != LOW)
65 ads126x.readADC1();
66 }
67}
68
69static void collect_chunk_impl() {
70 const uint8_t active_count = coreState.stored_config.num_sensors;
71 if (active_count == 0)
72 return;
73 const uint8_t* active_ids = coreState.stored_config.sensor_ids;
74
76 for (uint8_t i = 0; i < active_count; i++) {
77 ads126x.setInputMux(getAdcChannel(active_ids[i], TEST_PIN),
80 while (digitalRead(Pins.ADC_DRDY_1) != LOW)
82 const auto reading = ads126x.readADC1();
83 const uint32_t value =
84 reading.checksumValid ? static_cast<uint32_t>(reading.value) : 0u;
85 chunk.add_datapoint(active_ids[i], value);
86 }
87 if (chunk.size() == active_count &&
89 dataChunks.push_back(chunk);
90}
91
92static void send_chunks_to_impl(IPAddress dest_ip, int dest_port,
93 bool also_to_abort_controller,
94 IPAddress abort_controller_ip,
95 int abort_controller_port) {
96 if (dataChunks.empty())
97 return;
99 return;
101 const uint8_t num_sensors = dataChunks[0].num_sensors;
102 size_t packetSize = daq::create_sensor_data_packet(
103 dataChunks, num_sensors, millis(), packetBuffer, sizeof(packetBuffer));
104 if (packetSize == 0) {
105 HF_VERBOSE("Send FAIL: create_sensor_data_packet returned 0 (n=");
106 HF_VERBOSE(dataChunks.size());
107 HF_VERBOSE(", buf=");
109 HF_VERBOSELN(")");
110 return;
111 }
112 coreState.udp.beginPacket(dest_ip, dest_port);
113 coreState.udp.write(packetBuffer, packetSize);
114 coreState.udp.endPacket();
115 HF_VERBOSE("Sent: sensor_data to ");
116 HF_VERBOSE(dest_ip);
117 HF_VERBOSE(":");
118 HF_VERBOSELN(dest_port);
119
120 unsigned long now = millis();
121 if (now - g_last_sensor_packet_log_ms >= 1000) {
123 HF_VERBOSELN("SENSOR_DATA contents:");
124 for (size_t i = 0; i < dataChunks.size(); ++i) {
125 const auto& chunk = dataChunks[i];
126 HF_VERBOSE(" chunk ");
127 HF_VERBOSE(i);
128 HF_VERBOSE(" ts=");
129 HF_VERBOSE(chunk.timestamp);
130 HF_VERBOSE(" :");
131 for (const auto& dp : chunk.datapoints) {
132 HF_VERBOSE(" (id=");
133 HF_VERBOSE(static_cast<unsigned>(dp.sensor_id));
134 HF_VERBOSE(", data=");
135 HF_VERBOSE(dp.data);
136 HF_VERBOSE(")");
137 }
139 }
140 }
141
142 if (also_to_abort_controller) {
143 coreState.udp.beginPacket(abort_controller_ip, abort_controller_port);
144 coreState.udp.write(packetBuffer, packetSize);
145 coreState.udp.endPacket();
146 HF_VERBOSE("Sent: sensor_data to ");
147 HF_VERBOSE(abort_controller_ip);
148 HF_VERBOSE(":");
149 HF_VERBOSELN(abort_controller_port);
150 }
151 dataChunks.clear();
152}
153
154static void init_adc_cb(void*) {
155 ADC_SPI.begin(Pins.ADC_SCLK, Pins.ADC_MISO, Pins.ADC_MOSI, Pins.ADC_CS_1);
156 ADC_SPI.setDataMode(SPI_MODE1);
157 pinMode(Pins.ADC_DRDY_1, INPUT);
158 ads126x.begin(Pins.ADC_CS_1, &ADC_SPI);
159 ads126x.stopADC1();
160 ads126x.setInputMux(static_cast<uint8_t>(getAdcChannel(1, TEST_PIN)),
162 ads126x.bypassPGA();
163 ads126x.setFilter(FILTER);
164 ads126x.setRate(DATA_RATE);
166 ads126x.startADC1();
167}
168
169static void on_reference_voltage_cb(void*, uint8_t reference_voltage) {
170 uint8_t ref_pos =
171 (reference_voltage == 1) ? ADS126X_REF_POS_VDD : ADS126X_REF_POS_INT;
172 ads126x.setReference(ADS126X_REF_NEG_VSS, ref_pos);
173}
174
175static void collect_chunk_cb(void*) {
177}
178
179static void send_chunks_to_cb(void*, IPAddress dest_ip, int dest_port,
180 bool also_to_abort_controller,
181 IPAddress abort_controller_ip,
182 int abort_controller_port) {
183 if (dataChunks.size() >= 1)
184 send_chunks_to_impl(dest_ip, dest_port, also_to_abort_controller,
185 abort_controller_ip, abort_controller_port);
186}
187
188static void run_self_test_cb(void*,
190 std::vector<daq::SelfTestResult>& results_out) {
191 // 1. ADC self-test (TDAC internal)
192 auto adc_result = SensorSelfTest::run_adc_self_test(
194 results_out.push_back(daq::SelfTestResult{
195 0u, static_cast<uint8_t>(adc_result.passed ? 1 : 0)});
196
197 // 2. Sensor bias continuity test
199 for (uint8_t i = 0; i < cfg.num_sensors; i++) {
200 uint8_t id = cfg.sensor_ids[i];
202 ads126x, Pins.ADC_DRDY_1, getAdcChannel(id, TEST_PIN),
204 uint8_t pass =
205 (bias.result == SensorSelfTest::BiasResult::CONNECTED) ? 1u : 0u;
206 results_out.push_back(daq::SelfTestResult{id, pass});
207 }
209}
210
211void setup() {
212 // Do NOT memset coreState -- it contains IPAddress (has vtable via
213 // Printable) and EthernetUDP. memset zeroes vtable pointers, causing
214 // LoadProhibited crashes on virtual calls like Serial.print(IPAddress).
215 coreState.gateway = IPAddress(0, 0, 0, 0);
216 coreState.subnet = IPAddress(255, 255, 255, 0);
217 coreState.dns = IPAddress(192, 168, 2, 1);
218
219 coreConfig.board_name = "TC";
220 coreConfig.pins = &Pins;
221 coreConfig.init_adc = init_adc_cb;
222 coreConfig.collect_chunk = collect_chunk_cb;
223 coreConfig.send_chunks_to = send_chunks_to_cb;
224 coreConfig.run_self_test = run_self_test_cb;
225 coreConfig.on_reference_voltage_config = on_reference_voltage_cb;
226 coreConfig.user_data = nullptr;
227
229}
230
#define FILTER
Definition main.h:4
#define DATA_RATE
Definition main.h:5
#define INPUT
Definition Arduino.h:21
#define HSPI
Definition Arduino.h:132
int digitalRead(int)
Definition Arduino.h:50
void pinMode(int, int)
Definition Arduino.h:46
unsigned long millis()
Definition Arduino.h:29
#define SPI_MODE1
Definition Arduino.h:131
#define LOW
Definition Arduino.h:20
void delayMicroseconds(unsigned int)
Definition Arduino.h:58
#define ADS126X_REF_POS_VDD
Definition STAR_ADS126X.h:9
#define ADS126X_REF_NEG_VSS
Definition STAR_ADS126X.h:8
#define ADS126X_REF_POS_INT
#define ADS126X_AINCOM
#define SENSOR_HOTFIRE_MAX_PACKET_SIZE
Shared sensor hotfire state machine core — PT / TC / LC boards.
const int IPAddress
#define ADS126X_ASSERT_FILTER_RATE(filter, rate)
static uint8_t chunk[64]
Definition board_uart9.c:12
static ADS126X ads126x
Definition main.cpp:20
#define TEST_PIN
Definition main.cpp:23
SPIClass ADC_SPI(HSPI)
std::vector< daq::SensorDataChunkCollection > dataChunks
Definition main.cpp:48
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]
Definition main.cpp:15
bool g_verbose
Definition main.cpp:65
static unsigned long g_last_sensor_packet_log_ms
Definition main.cpp:62
static void run_self_test_cb(void *, const SensorHotfire::StoredSensorConfig &cfg, std::vector< daq::SelfTestResult > &results_out)
Definition main.cpp:219
static void collect_chunk_cb(void *)
Definition main.cpp:206
static SensorHotfire::Config coreConfig
Definition main.cpp:60
static void init_adc_cb(void *)
Definition main.cpp:184
static SensorHotfire::CoreState coreState
Definition main.cpp:59
static void on_reference_voltage_cb(void *, uint8_t reference_voltage)
Definition main.cpp:200
static void send_chunks_to_cb(void *, IPAddress dest_ip, int dest_port, bool also_to_abort_controller, IPAddress abort_controller_ip, int abort_controller_port)
Definition main.cpp:210
void setup()
Definition main.cpp:211
SPIClass ADC_SPI(HSPI)
static void run_self_test_cb(void *, const SensorHotfire::StoredSensorConfig &cfg, std::vector< daq::SelfTestResult > &results_out)
Definition main.cpp:188
static void collect_chunk_cb(void *)
Definition main.cpp:175
static void flush_cycles(int cycles)
Definition main.cpp:61
static void send_chunks_to_impl(IPAddress dest_ip, int dest_port, bool also_to_abort_controller, IPAddress abort_controller_ip, int abort_controller_port)
Definition main.cpp:92
static void init_adc_cb(void *)
Definition main.cpp:154
static void on_reference_voltage_cb(void *, uint8_t reference_voltage)
Definition main.cpp:169
static void collect_chunk_impl()
Definition main.cpp:69
static void send_chunks_to_cb(void *, IPAddress dest_ip, int dest_port, bool also_to_abort_controller, IPAddress abort_controller_ip, int abort_controller_port)
Definition main.cpp:179
void loop()
Definition main.cpp:231
#define HF_VERBOSE(...)
#define HF_VERBOSELN(...)
#define HF_VERBOSELN_()
void setup(CoreState &s, const Config &cfg)
void loop(CoreState &s, const Config &cfg)
void sensor_bias_disable(ADS126X &adc)
Call once after the connector loop.
BiasReadResult read_sensor_bias(ADS126X &adc, uint8_t drdy_pin, uint8_t adc_channel, uint8_t aincom, uint8_t settle_cycles=5)
Read one connector with sensor bias already active.
void sensor_bias_enable(ADS126X &adc)
Call once before the connector loop.
AdcSelfTestResult run_adc_self_test(ADS126X &adc, uint8_t drdy_pin, uint8_t original_ref_neg, uint8_t original_ref_pos)
size_t create_sensor_data_packet(const std::vector< SensorDataChunkCollection > &chunks, const uint8_t num_sensors, uint32_t timestamp_ms, uint8_t *buffer, size_t buffer_size)
Creates a complete Sensor Data packet in the provided buffer.
constexpr int getAdcChannel(uint8_t connector, uint8_t pin)
constexpr const Layout & Pins
#define SENSOR_MAX_CHUNKS_BEFORE_SEND
Represents a single data chunk with timestamp and sensor datapoints.