STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1/**
2 * PT (Pressure Transducer) Hotfire state machine — ESP32 PCB
3 *
4 * Three states: Waiting for Server → Active → Standalone Abort (and back to
5 * Active on clear). Sends heartbeats, streams sensor data to server or to
6 * actuator controller in abort. Uses shared SensorHotfireCore; board-specific:
7 * ADC init, collect_chunk, send_chunks_to.
8 */
9
10#include "main.h"
11
12#include <Arduino.h>
13#include <Ethernet.h>
14#include <EthernetUdp.h>
15#include <SPI.h>
16#include <daq-protocol.h>
17#include <esp_mac.h>
18
19#include <cstring>
20#include <vector>
21
22#include "STAR_ADS126X.h"
23
24// clang-format off
25// MUST be defined before sense_board_pins.h / connector_adc_map.h
26#define PINS_ACTIVE_LAYOUT sense_board_pins::PT_Board
27#include "sense_board_pins.h"
28#include "connector_adc_map.h"
29#include "adc_mappings.h"
30#include "SensorHotfireCore.h"
31#include "SensorSelfTest.h"
32// clang-format on
33
34using namespace sense_board_pins;
35
36//-----------------------------------------------------------------------------
37// ADC config (Stream_ADC_Data / PT_BOARD_Multi style)
38//-----------------------------------------------------------------------------
39#define FILTER ADS126X_SINC5
40#define DATA_RATE ADS126X_RATE_38400
42#define TEST_PIN 1
43
45SPIClass ADC_SPI(HSPI);
46std::vector<daq::SensorDataChunkCollection> dataChunks;
47
50
51static unsigned long g_last_sensor_packet_log_ms = 0;
52
53// Set to true to enable Serial output from core and board; false to disable.
54bool g_verbose = true;
55
56static float convert_code_to_voltage(int32_t code) {
57 return (static_cast<float>(code) * 2.5f) / 2147483648.0f;
58}
59
60static void flush_cycles(int cycles) {
61 for (int i = 0; i < cycles; i++) {
62 while (digitalRead(Pins.ADC_DRDY_1) != LOW)
64 ads126x.readADC1();
65 }
66}
67
68static void collect_chunk_impl() {
69 const uint8_t active_count = coreState.stored_config.num_sensors;
70 if (active_count == 0)
71 return;
72 const uint8_t* active_ids = coreState.stored_config.sensor_ids;
73
75 for (uint8_t i = 0; i < active_count; i++) {
76 ads126x.setInputMux(getAdcChannel(active_ids[i], TEST_PIN),
79 while (digitalRead(Pins.ADC_DRDY_1) != LOW)
81 const auto reading = ads126x.readADC1();
82 const uint32_t value =
83 reading.checksumValid ? static_cast<uint32_t>(reading.value) : 0u;
84 chunk.add_datapoint(active_ids[i], value);
85 }
86 if (chunk.size() == active_count &&
88 dataChunks.push_back(chunk);
89}
90
91static void send_chunks_to_impl(IPAddress dest_ip, int dest_port,
92 bool also_to_abort_controller,
93 IPAddress abort_controller_ip,
94 int abort_controller_port) {
95 if (dataChunks.empty())
96 return;
98 return;
100 const uint8_t num_sensors = dataChunks[0].num_sensors;
101 size_t packetSize = daq::create_sensor_data_packet(
102 dataChunks, num_sensors, millis(), packetBuffer, sizeof(packetBuffer));
103 if (packetSize == 0)
104 return;
105 coreState.udp.beginPacket(dest_ip, dest_port);
106 coreState.udp.write(packetBuffer, packetSize);
107 coreState.udp.endPacket();
108 HF_VERBOSE("Sent: sensor_data to ");
109 HF_VERBOSE(dest_ip);
110 HF_VERBOSE(":");
111 HF_VERBOSELN(dest_port);
112
113 unsigned long now = millis();
114 if (now - g_last_sensor_packet_log_ms >= 1000) {
116 HF_VERBOSELN("SENSOR_DATA contents:");
117 for (size_t i = 0; i < dataChunks.size(); ++i) {
118 const auto& chunk = dataChunks[i];
119 HF_VERBOSE(" chunk ");
120 HF_VERBOSE(i);
121 HF_VERBOSE(" ts=");
122 HF_VERBOSE(chunk.timestamp);
123 HF_VERBOSE(" :");
124 for (const auto& dp : chunk.datapoints) {
125 HF_VERBOSE(" (id=");
126 HF_VERBOSE(static_cast<unsigned>(dp.sensor_id));
127 HF_VERBOSE(", data=");
128 HF_VERBOSE(dp.data);
129 HF_VERBOSE(")");
130 }
132 }
133 }
134
135 if (also_to_abort_controller) {
136 coreState.udp.beginPacket(abort_controller_ip, abort_controller_port);
137 coreState.udp.write(packetBuffer, packetSize);
138 coreState.udp.endPacket();
139 HF_VERBOSE("Sent: sensor_data to ");
140 HF_VERBOSE(abort_controller_ip);
141 HF_VERBOSE(":");
142 HF_VERBOSELN(abort_controller_port);
143 }
144 dataChunks.clear();
145}
146
147static void init_adc_cb(void*) {
148 ADC_SPI.begin(Pins.ADC_SCLK, Pins.ADC_MISO, Pins.ADC_MOSI);
149 ADC_SPI.setDataMode(SPI_MODE1);
150 pinMode(Pins.ADC_DRDY_1, INPUT);
151 ads126x.begin(Pins.ADC_CS_1, &ADC_SPI);
152 ads126x.stopADC1();
153 ads126x.setInputMux(static_cast<uint8_t>(getAdcChannel(1, TEST_PIN)),
155 ads126x.bypassPGA();
156 ads126x.setFilter(FILTER);
157 ads126x.setRate(DATA_RATE);
158 // Reference is not configured here; it must be set from server
159 // SENSOR_CONFIG
160 ads126x.startADC1();
161}
162
163static void on_reference_voltage_cb(void*, uint8_t reference_voltage) {
164 // 0 = internal 2.5V, 1 = VDD, 2 = 5V (ignored; use internal)
165 uint8_t ref_pos =
166 (reference_voltage == 1) ? ADS126X_REF_POS_VDD : ADS126X_REF_POS_INT;
167 ads126x.setReference(ADS126X_REF_NEG_VSS, ref_pos);
168}
169
170static void collect_chunk_cb(void*) {
172}
173
174static void send_chunks_to_cb(void*, IPAddress dest_ip, int dest_port,
175 bool also_to_abort_controller,
176 IPAddress abort_controller_ip,
177 int abort_controller_port) {
178 send_chunks_to_impl(dest_ip, dest_port, also_to_abort_controller,
179 abort_controller_ip, abort_controller_port);
180}
181
183 switch (r) {
185 return "CONNECTED";
187 return "AMBIGUOUS";
189 return "DISCONNECTED";
190 default:
191 return "?";
192 }
193}
194
195static void run_self_test_cb(void*,
197 std::vector<daq::SelfTestResult>& results_out) {
198 // 1. ADC self-test (TDAC internal)
199 auto adc_result = SensorSelfTest::run_adc_self_test(
201 results_out.push_back(daq::SelfTestResult{
202 0u, static_cast<uint8_t>(adc_result.passed ? 1 : 0)});
203
204 HF_LOGLN("=== ADC TDAC Self-Test ===");
205 // Verdict is Tier 1; the numeric detail is verbose-only.
206 HF_VERBOSE(" read code = ");
207 HF_VERBOSELN(adc_result.code);
208 HF_VERBOSE(" expected = ");
210 HF_VERBOSE(" tolerance = ");
212 HF_VERBOSE(" checksum = ");
213 HF_VERBOSELN(adc_result.checksum_valid ? "OK" : "FAIL");
214 HF_LOG(" result = ");
215 HF_LOGLN(adc_result.passed ? "PASS" : "FAIL");
216 Serial.flush();
217
218 // 2. Sensor bias continuity test
219 HF_LOGLN("=== Sensor Bias Test ===");
221 for (uint8_t i = 0; i < cfg.num_sensors; i++) {
222 uint8_t id = cfg.sensor_ids[i];
223 int channel = getAdcChannel(id, TEST_PIN);
224 if (channel < 0) {
225 HF_LOG(" sensor id=");
226 HF_LOG(id);
227 HF_LOGLN(" channel=INVALID result=FAIL");
228 results_out.push_back(daq::SelfTestResult{id, 0u});
229 continue;
230 }
232 ads126x, Pins.ADC_DRDY_1, static_cast<uint8_t>(channel),
234 uint8_t pass =
235 (bias.result == SensorSelfTest::BiasResult::CONNECTED) ? 1u : 0u;
236
237 HF_LOG(" sensor id=");
238 HF_LOG(id);
239 HF_VERBOSE(" ch=");
240 HF_VERBOSE(channel);
241 HF_VERBOSE(" code=");
242 HF_VERBOSE(bias.code);
243 HF_VERBOSE(" chk=");
244 HF_VERBOSE(bias.checksum_valid ? "OK" : "FAIL");
245 HF_LOG(" -> ");
246 HF_LOGLN(bias_result_str(bias.result));
247
248 results_out.push_back(daq::SelfTestResult{id, pass});
249 }
250 Serial.flush();
252}
253
254void setup() {
255 // Do NOT memset coreState -- it contains IPAddress (has vtable via
256 // Printable) and EthernetUDP. memset zeroes vtable pointers, causing
257 // LoadProhibited crashes on virtual calls like Serial.print(IPAddress).
258 // Static variables are already zero-initialized for POD members.
259 coreState.gateway = IPAddress(0, 0, 0, 0);
260 coreState.subnet = IPAddress(255, 255, 255, 0);
261 coreState.dns = IPAddress(192, 168, 2, 1);
262
263 coreConfig.board_name = "PT";
264 coreConfig.pins = &Pins;
265 coreConfig.init_adc = init_adc_cb;
266 coreConfig.collect_chunk = collect_chunk_cb;
267 coreConfig.send_chunks_to = send_chunks_to_cb;
268 // Re-enable self-test now that we guard invalid mappings.
269 coreConfig.run_self_test = run_self_test_cb;
270 coreConfig.on_reference_voltage_config = on_reference_voltage_cb;
271 coreConfig.user_data = nullptr;
272
274}
275
#define FILTER
Definition main.h:4
#define DATA_RATE
Definition main.h:5
static SerialStub Serial
Definition Arduino.h:128
#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:254
SPIClass ADC_SPI(HSPI)
static void run_self_test_cb(void *, const SensorHotfire::StoredSensorConfig &cfg, std::vector< daq::SelfTestResult > &results_out)
Definition main.cpp:195
static void collect_chunk_cb(void *)
Definition main.cpp:170
static void flush_cycles(int cycles)
Definition main.cpp:60
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:91
static float convert_code_to_voltage(int32_t code)
Definition main.cpp:56
static const char * bias_result_str(SensorSelfTest::BiasResult r)
Definition main.cpp:182
static void init_adc_cb(void *)
Definition main.cpp:147
static void on_reference_voltage_cb(void *, uint8_t reference_voltage)
Definition main.cpp:163
static void collect_chunk_impl()
Definition main.cpp:68
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:174
void loop()
Definition main.cpp:276
#define HF_VERBOSE(...)
#define HF_LOG(...)
Definition hotfire_log.h:99
#define HF_VERBOSELN(...)
#define HF_VERBOSELN_()
#define HF_LOGLN(...)
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.
constexpr int32_t ADC_TDAC_EXPECTED_CODE
constexpr int32_t ADC_TDAC_TOLERANCE
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.
static boot_report_t r