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.
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 <SPIFFS.h>
17#include <daq-protocol.h>
18#include <esp_mac.h>
19
20#include <cstring>
21#include <vector>
22#define PINS_ACTIVE_LAYOUT sense_board_pins::TC_Board
23#include "STAR_ADS126X.h"
24#include "adc_mappings.h"
25#include "connector_adc_map.h"
26#include "sense_board_pins.h"
27
28using namespace sense_board_pins;
29
30//-----------------------------------------------------------------------------
31// ADC config (Stream_ADC_Data / PT_BOARD_Multi style)
32//-----------------------------------------------------------------------------
33#define FILTER ADS126X_SINC4
34#define DATA_RATE ADS126X_RATE_7200
35#define TEST_PIN 1
36#define NUM_PTS 10
37#define READINGS_PER_MUX 5
38#define MAX_CHUNKS 9 // 9 chunks fit in 512-byte packet with 10 sensors
39
41SPIClass ADC_SPI(HSPI);
42static uint8_t currentConnector = 1;
43
44//-----------------------------------------------------------------------------
45// Ethernet and network (IP 192.168.2.XXX and board_id = XXX from SPIFFS)
46//-----------------------------------------------------------------------------
47static uint8_t board_id = BOARD_ID_DEFAULT;
48byte mac[6];
50IPAddress gateway(0, 0, 0, 0);
51IPAddress subnet(255, 255, 255, 0);
52IPAddress dns(192, 168, 2, 1);
53const int udpListenPort = 5005;
54const int serverPortDefault = 5006;
55EthernetUDP udp;
56
57// Server address: updated when we receive a server heartbeat (store server
58// address in server heartbeat)
59IPAddress serverIP(192, 168, 2, 20);
61
62//-----------------------------------------------------------------------------
63// State machine
64//-----------------------------------------------------------------------------
66
68
69// Stored config from SENSOR_CONFIG packet (necessary_for_abort, actuator
70// controller IP)
72 bool valid;
74 uint32_t actuator_controller_ip; // IPv4 as uint32_t (e.g. network order)
75};
76static StoredSensorConfig stored_config = {false, false, 0};
77
78// Sensor data collection (Stream_ADC_Data style)
79std::vector<daq::SensorDataChunkCollection> dataChunks;
80uint8_t sensorId = 0;
81
82// Heartbeat at fixed interval (main.h: BOARD_HEARTBEAT_INTERVAL_MS)
83static unsigned long lastHeartbeatMillis = 0;
84
85//-----------------------------------------------------------------------------
86// Incoming packet kinds (for transitions)
87//-----------------------------------------------------------------------------
95
96//-----------------------------------------------------------------------------
97// Helpers: ADC and voltage
98//-----------------------------------------------------------------------------
99static float convert_code_to_voltage(int32_t code) {
100 return (static_cast<float>(code) * 2.5f) / 2147483648.0f;
101}
102
103static void flush_cycles(int cycles) {
104 for (int i = 0; i < cycles; i++) {
105 while (digitalRead(Pins.ADC_DRDY_1) != LOW)
107 ads126x.readADC1();
108 }
109}
110
111//-----------------------------------------------------------------------------
112// Read packet header (for type dispatch)
113//-----------------------------------------------------------------------------
114static bool readPacketHeader(const uint8_t* buffer, size_t len,
115 daq::PacketHeader& hdr_out) {
116 if (len < sizeof(daq::PacketHeader))
117 return false;
118 memcpy(&hdr_out, buffer, sizeof(daq::PacketHeader));
119 return true;
120}
121
122//-----------------------------------------------------------------------------
123// Process one incoming packet; return kind and optionally store server address
124// remote_ip/remote_port: set when we receive SERVER_HEARTBEAT (store server
125// address)
126//-----------------------------------------------------------------------------
127static IncomingPacketKind processIncomingPacket(const uint8_t* buffer,
128 size_t len, IPAddress remote_ip,
129 int remote_port) {
130 daq::PacketHeader hdr;
131 if (!readPacketHeader(buffer, len, hdr))
133
134 switch (hdr.packet_type) {
136 daq::PacketHeader dummy;
137 daq::ServerHeartbeatPacket data;
138 if (daq::parse_server_heartbeat_packet(buffer, len, dummy, data)) {
139 serverIP = remote_ip;
140 serverPort = remote_port;
142 }
144 }
146 // Store config: layout per generate_packets (necessary_for_abort
147 // byte, then optional controller_ip)
148 stored_config.valid = true;
149 if (len >= 6 + 1) {
150 stored_config.necessary_for_abort = (buffer[6] != 0);
151 if (stored_config.necessary_for_abort && len >= 6 + 5) {
152 stored_config.actuator_controller_ip =
153 (static_cast<uint32_t>(buffer[7]) << 24) |
154 (static_cast<uint32_t>(buffer[8]) << 16) |
155 (static_cast<uint32_t>(buffer[9]) << 8) |
156 static_cast<uint32_t>(buffer[10]);
157 } else {
158 stored_config.actuator_controller_ip = 0;
159 }
160 }
165 if (len >= sizeof(daq::PacketHeader))
168 }
169 default:
171 }
172}
173
174//-----------------------------------------------------------------------------
175// Send board heartbeat (board ID + boardState), like actuator_c /
176// Stream_ADC_Data
177//-----------------------------------------------------------------------------
178static void sendBoardHeartbeat(daq::BoardState board_state, IPAddress dest_ip,
179 int dest_port) {
180 daq::BoardHeartbeatPacket hb;
181 memset(hb.firmware_hash, 0, sizeof(hb.firmware_hash));
182 hb.board_id = board_id;
183 hb.engine_state = daq::EngineState::SAFE;
184 hb.board_state = board_state;
185
188 sizeof(packetBuffer));
189 if (n == 0)
190 return;
191 udp.beginPacket(dest_ip, dest_port);
192 udp.write(packetBuffer, n);
193 udp.endPacket();
194}
195
196//-----------------------------------------------------------------------------
197// Stream sensor data to a destination (Stream_ADC_Data style)
198//-----------------------------------------------------------------------------
199static void sendSensorDataPacketTo(IPAddress dest_ip, int dest_port) {
200 if (dataChunks.empty())
201 return;
203 size_t packetSize = daq::create_sensor_data_packet(
204 dataChunks, static_cast<uint8_t>(NUM_PTS), millis(), packetBuffer,
205 sizeof(packetBuffer));
206 if (packetSize == 0)
207 return;
208 udp.beginPacket(dest_ip, dest_port);
209 udp.write(packetBuffer, packetSize);
210 udp.endPacket();
211 dataChunks.clear();
212}
213
214//-----------------------------------------------------------------------------
215// Read ADC data for current connector and push chunk (Stream_ADC_Data style)
216//-----------------------------------------------------------------------------
217static void read_data(int count) {
219 for (int i = 0; i < count; i++) {
220 while (digitalRead(Pins.ADC_DRDY_1) != LOW)
222 const auto reading = ads126x.readADC1();
223 if (!reading.checksumValid)
224 continue;
225 chunk.add_datapoint(sensorId, static_cast<uint32_t>(reading.value));
226 }
227 if (!chunk.empty())
228 dataChunks.push_back(chunk);
229}
230
231//-----------------------------------------------------------------------------
232// State handlers
233//-----------------------------------------------------------------------------
234
235// 1: Waiting for Server — heartbeat sent at fixed interval in loop; on config
236// -> Active
237static void run_WaitingForServer() {
238 // No per-state work; heartbeat sent in loop
239}
240
241// 2: Active — stream sensor packets to server; heartbeat at fixed interval
242static void run_Active() {
243 if (dataChunks.size() >= MAX_CHUNKS)
245}
246
247// 3: Standalone Abort — stream sensor data to actuator controller; heartbeat at
248// fixed interval
249static void run_StandaloneAbort() {
250 if (!stored_config.valid || stored_config.actuator_controller_ip == 0)
251 return;
252 IPAddress actuatorIP((stored_config.actuator_controller_ip >> 24) & 0xFF,
253 (stored_config.actuator_controller_ip >> 16) & 0xFF,
254 (stored_config.actuator_controller_ip >> 8) & 0xFF,
255 stored_config.actuator_controller_ip & 0xFF);
256 if (dataChunks.size() >= MAX_CHUNKS)
258}
259
260//-----------------------------------------------------------------------------
261// Apply packet-driven transitions
262//-----------------------------------------------------------------------------
264 switch (state) {
268 }
269 break;
272 stored_config.necessary_for_abort)
274 break;
276 if (kind == IncomingPacketKind::ClearAbort) {
278 }
279 break;
280 }
281}
282
283//-----------------------------------------------------------------------------
284// Setup
285//-----------------------------------------------------------------------------
286void setup() {
287 Serial.begin(115200);
288 Serial.println("TC Hotfire state machine starting...");
289
290 bool spiffs_ok = false;
291 // Mount read-only: do not format on fail, so we never overwrite burned
292 // value
293 if (SPIFFS.begin(false)) {
294 File f = SPIFFS.open(SPIFFS_BOARD_VALUE_PATH, "r"); // path from main.h
295 if (f && f.available() >= 1) {
296 uint8_t b;
297 if (f.read(&b, 1) == 1) {
298 board_id = b;
299 staticIP = IPAddress(192, 168, 2, b);
300 spiffs_ok = true;
301 Serial.print("Board ID and IP from SPIFFS: ");
302 Serial.print(static_cast<unsigned>(board_id));
303 Serial.print(" / 192.168.2.");
304 Serial.println(static_cast<unsigned>(b));
305 }
306 }
307 if (f)
308 f.close();
309 SPIFFS.end();
310 }
311 if (!spiffs_ok)
312 Serial.println(
313 "SPIFFS read skipped or failed, using default board ID 1 / "
314 "192.168.2.1");
315
316 ADC_SPI.begin(Pins.ADC_SCLK, Pins.ADC_MISO, Pins.ADC_MOSI, Pins.ADC_CS_1);
317 ADC_SPI.setDataMode(SPI_MODE1);
318 pinMode(Pins.ADC_DRDY_1, INPUT);
319
320 ads126x.begin(Pins.ADC_CS_1, &ADC_SPI);
321 ads126x.stopADC1();
322 ads126x.setInputMux(static_cast<uint8_t>(getAdcChannel(1, TEST_PIN)),
324 ads126x.bypassPGA();
325 ads126x.setFilter(FILTER);
326 ads126x.setRate(DATA_RATE);
328 ads126x.startADC1();
329
331
332 ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_ETH));
333 SPI.begin(Pins.ETH_SCLK, Pins.ETH_MISO, Pins.ETH_MOSI);
335 Ethernet.init(Pins.ETH_CS);
339 udp.begin(udpListenPort);
340
342 Serial.print("Ethernet IP: ");
343 Serial.println(Ethernet.localIP());
344 Serial.println("Setup complete. State: WaitingForServer (TC Board)");
345}
346
347//-----------------------------------------------------------------------------
348// Loop
349//-----------------------------------------------------------------------------
350void loop() {
351 int packetSize = udp.parsePacket();
352 if (packetSize > 0) {
353 IPAddress remoteIP = udp.remoteIP();
354 int remotePort = udp.remotePort();
356 int bytesRead = udp.read(packetBuffer, sizeof(packetBuffer));
357 if (bytesRead > 0) {
359 packetBuffer, bytesRead, remoteIP, remotePort);
361 }
362 }
363
370 if (ch >= 0)
371 ads126x.setInputMux(static_cast<uint8_t>(ch), ADS126X_AINCOM);
373
374 switch (state) {
377 break;
379 run_Active();
380 break;
383 break;
384 }
385
386 // Send board heartbeat at fixed interval (e.g. once per second)
387 unsigned long now = millis();
390 switch (state) {
393 serverPort);
394 break;
397 serverPort);
398 break;
400 if (!stored_config.valid ||
401 stored_config.actuator_controller_ip == 0)
404 else {
405 IPAddress actuatorIP(
406 (stored_config.actuator_controller_ip >> 24) & 0xFF,
407 (stored_config.actuator_controller_ip >> 16) & 0xFF,
408 (stored_config.actuator_controller_ip >> 8) & 0xFF,
409 stored_config.actuator_controller_ip & 0xFF);
411 actuatorIP, serverPortDefault);
412 }
413 break;
414 }
415 }
416
418}
#define FILTER
Definition main.h:4
#define DATA_RATE
Definition main.h:5
#define READINGS_PER_MUX
Definition main.h:3
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 BOARD_ID_DEFAULT
#define ETHERNET_INIT_DELAY_MS
#define LOOP_DELAY_MS
#define SPIFFS_BOARD_VALUE_PATH
#define ETHERNET_BEGIN_DELAY_MS
#define BOARD_HEARTBEAT_INTERVAL_MS
#define ETHERNET_SPI_DELAY_MS
static EthernetClass Ethernet
Definition Ethernet.h:116
#define MAX_PACKET_SIZE
Definition main.h:6
#define ADS126X_REF_POS_VDD
Definition STAR_ADS126X.h:9
#define ADS126X_REF_NEG_VSS
Definition STAR_ADS126X.h:8
#define ADS126X_AINCOM
static StoredConfig stored_config
const int IPAddress
void delay(int ms)
constexpr uint8_t settlePulses(uint8_t filter, uint8_t data_rate, bool chop_enabled=false, bool idac_rotation_enabled=false)
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)
#define MAX_CHUNKS
Definition main.cpp:47
std::vector< daq::SensorDataChunkCollection > dataChunks
Definition main.cpp:48
static unsigned long lastHeartbeatMillis
Definition main.cpp:81
const int serverPort
Definition main.cpp:38
const int udpListenPort
Definition main.cpp:37
IPAddress dns(192, 168, 2, 1)
IncomingPacketKind
Definition main.cpp:237
byte mac[6]
Definition main.cpp:31
IPAddress subnet(255, 255, 255, 0)
IPAddress staticIP(192, 168, 2, BOARD_ID_DEFAULT)
IPAddress serverIP(192, 168, 2, 20)
static uint8_t board_id
Definition main.cpp:30
EthernetUDP udp
Definition main.cpp:40
static void sendBoardHeartbeat()
Definition main.cpp:192
IPAddress gateway(0, 0, 0, 0)
static ActuatorControllerState state
Definition main.cpp:57
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]
Definition main.cpp:15
#define NUM_PTS
Definition main.cpp:36
static void read_data(int count)
Definition main.cpp:217
static void run_Active()
Definition main.cpp:242
static void run_WaitingForServer()
Definition main.cpp:237
static uint8_t currentConnector
Definition main.cpp:42
void setup()
Definition main.cpp:286
static void sendSensorDataPacketTo(IPAddress dest_ip, int dest_port)
Definition main.cpp:199
static void applyPacketTransition(IncomingPacketKind kind)
Definition main.cpp:263
uint8_t sensorId
Definition main.cpp:80
IPAddress dns(192, 168, 2, 1)
SPIClass ADC_SPI(HSPI)
IPAddress subnet(255, 255, 255, 0)
IPAddress staticIP(192, 168, 2, BOARD_ID_DEFAULT)
IPAddress serverIP(192, 168, 2, 20)
PTHotfireState
Definition main.cpp:65
@ WaitingForServer
Definition main.cpp:65
@ StandaloneAbort
Definition main.cpp:65
const int serverPortDefault
Definition main.cpp:54
static void flush_cycles(int cycles)
Definition main.cpp:103
static float convert_code_to_voltage(int32_t code)
Definition main.cpp:99
static bool readPacketHeader(const uint8_t *buffer, size_t len, daq::PacketHeader &hdr_out)
Definition main.cpp:114
static void run_StandaloneAbort()
Definition main.cpp:249
static IncomingPacketKind processIncomingPacket(const uint8_t *buffer, size_t len, IPAddress remote_ip, int remote_port)
Definition main.cpp:127
IPAddress gateway(0, 0, 0, 0)
void loop()
Definition main.cpp:350
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
Definition json.hpp:13878
size_type count(const key_type &key) const
Definition json.hpp:19264
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.
bool parse_server_heartbeat_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, ServerHeartbeatPacket &data_out)
Parses a Server Heartbeat packet from buffer.
size_t create_board_heartbeat_packet(const BoardHeartbeatPacket &data, uint32_t timestamp_ms, uint8_t *buffer, size_t buffer_size)
Creates a complete Board Heartbeat packet in the provided buffer.
BoardState
Defines the operational state of a board.
Definition DiabloEnums.h:31
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
constexpr int getAdcChannel(uint8_t connector, uint8_t pin)
constexpr const Layout & Pins
uint32_t actuator_controller_ip
Definition main.cpp:74
bool necessary_for_abort
Definition main.cpp:73
Represents a single data chunk with timestamp and sensor datapoints.
static std::string ch(int ain, const std::string &kind, const std::string &extra="", const std::string &board="lj")
Definition test_t7.cpp:98