44#include "actuator_config.h"
46#include "firmware_hash.h"
96static EthernetUDP
udp;
123 return "WaitingForServer";
132 if (
state != new_state) {
145 if (v.actuator_id == actuator_id)
152 if (v.
state != new_state) {
157 HF_LOG(
static_cast<unsigned>(new_state));
170 const std::vector<daq::ActuatorCommand>&
commands) {
175 uint8_t new_state = (cmd.actuator_state != 0) ? 1 : 0;
182 HF_LOG(
static_cast<unsigned>(new_state));
183 HF_LOGLN(
" ignored -- no current SERVER_HEARTBEAT");
210 daq::BoardHeartbeatPacket hb;
222 udp.beginPacket(dest_ip, dest_port);
233 for (
const auto& v :
valves)
237 std::vector<daq::SensorDataChunkCollection>
chunks;
256 static uint8_t pkt[
sizeof(daq::PacketHeader) + 3 +
LOG_BUF_SIZE];
273 if (len <
sizeof(daq::PacketHeader))
275 daq::PacketHeader hdr;
276 memcpy(&hdr, buffer,
sizeof(hdr));
278 switch (hdr.packet_type) {
280 daq::PacketHeader dummy;
281 daq::ServerHeartbeatPacket data;
284#ifdef SENSOR_ETH_USE_DHCP
294 daq::PacketHeader dummy;
295 uint8_t is_controller = 0;
296 uint8_t enable_serial = 0;
297 std::vector<daq::AbortActuatorLocation> act_locs;
298 std::vector<daq::AbortPTLocation> pt_locs;
300 is_controller, act_locs,
301 pt_locs, enable_serial)) {
304 (enable_serial >= 2) ? (enable_serial - 1) : 0;
309 daq::PacketHeader dummy;
310 std::vector<daq::ActuatorCommand>
commands;
341 SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
346 uint8_t version = SPI.transfer(0x00);
348 SPI.endTransaction();
350 Serial.print(
"[ETH] W5500 VERSIONR read: 0x");
355 Serial.println(
" -- chip answers; init failed, try a reset");
356 else if (version == 0x00)
358 " -- MISO stuck low: check MISO (19), SCK (18), CS (5)"
359 " and the module's GND");
360 else if (version == 0xFF)
362 " -- MISO floating/high: check MISO (19), CS (5) and"
363 " the module's 3.3 V");
365 Serial.println(
" -- garbled: check MOSI (23), SCK (18), loose jumpers");
376 analogSetPinAttenuation(v.position_pin, ADC_11db);
387 Serial.println(
"Ball Valve Hotfire starting...");
389 ESP_ERROR_CHECK(esp_read_mac(
mac, ESP_MAC_ETH));
396#ifdef SENSOR_ETH_USE_DHCP
404 Serial.print(
"Board ID: ");
411 Serial.println(
"[ETH] Ethernet initialized (SPI WIZnet)");
412 Serial.print(
"Address source: ");
414 Serial.print(
"Stack IP (Ethernet.localIP): ");
416 Serial.print(
"Hardware: ");
418 :
"no chip detected — check CS / SPI wiring. "
419 "Ethernet disabled; valves held at 0.");
423 Serial.print(
"Link status: ");
429 Serial.print(
"UDP listening on port ");
433 for (
const auto& v :
valves) {
434 Serial.print(
"Valve: actuator_id ");
435 Serial.print(
static_cast<unsigned>(v.actuator_id));
437 Serial.print(v.enable_pin);
438 Serial.print(
"), position sensor_id ");
439 Serial.print(
static_cast<unsigned>(v.sensor_id));
441 Serial.print(v.position_pin);
442 Serial.println(
"), state 0");
444 Serial.println(
"State -> WaitingForServer");
457#ifdef SENSOR_ETH_USE_DHCP
475 int packetSize =
udp.parsePacket();
476 if (packetSize > 0) {
485#ifdef SENSOR_ETH_USE_DHCP
489 net.serverLearned =
false;
491 Serial.println(
"[NET] server silent -- resuming discovery");
496 unsigned long now =
millis();
505#ifdef SENSOR_ETH_USE_DHCP
507 if (!
net.serverLearned)
514 static unsigned long s_last_log_flush_ms = 0;
516 (
g_logbuf.nearFull() || now - s_last_log_flush_ms >= 1000)) {
517 s_last_log_flush_ms = now;
void digitalWrite(int, int)
#define ETHERNET_INIT_DELAY_MS
#define ETHERNET_BEGIN_DELAY_MS
#define HEARTBEAT_TIMEOUT_MS
#define ADC_READ_INTERVAL_MS
#define BOARD_HEARTBEAT_INTERVAL_MS
#define ETHERNET_SPI_DELAY_MS
static EthernetClass Ethernet
std::vector< daq::SensorDataChunkCollection > chunks
static unsigned long lastHeartbeatMillis
static unsigned long last_server_heartbeat_ms
IPAddress dns(192, 168, 2, 1)
IPAddress subnet(255, 255, 255, 0)
IPAddress serverIP(192, 168, 2, 20)
IPAddress gateway(0, 0, 0, 0)
static ActuatorControllerState state
static IPAddress subnet(255, 255, 255, 0)
static IPAddress gateway(192, 168, 2, 1)
StarOTA::Server otaServer(OTA_TCP_PORT)
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]
IPAddress dns(192, 168, 2, 1)
static BoardNet::State net
static void sendBoardHeartbeatTo(IPAddress dest_ip, int dest_port)
static void setValve(Valve &v, uint8_t new_state, const char *reason)
static constexpr uint8_t SAFE_STATE
static void setState(BallValveState new_state)
static void pollNetwork()
static constexpr int ETH_CS
Ball Valve Hotfire — motorized ball valve prototype board.
static uint32_t readPositionCounts(const Valve &v)
Raw 12-bit ADC counts, averaged over POSITION_SAMPLES.
static void probeW5500Version()
Read the W5500's VERSIONR (common register 0x0039, always 0x04) straight over SPI and say what the an...
static void setAllValvesSafe(const char *reason)
static constexpr int POSITION_SAMPLES
static unsigned long last_position_read_ms
static const char * stateName(BallValveState s)
static void otaStart(void *, uint32_t)
An OTA blocks the loop until the board reboots, so the heartbeat watchdog cannot run: put every valve...
static void initializeValves()
static void processIncomingPacket(const uint8_t *buffer, size_t len, IPAddress remoteIP)
static constexpr int ETH_SCLK
static Valve * findValve(uint8_t actuator_id)
static constexpr int ETH_MISO
static constexpr int ETH_MOSI
static void processActuatorCommands(const std::vector< daq::ActuatorCommand > &commands)
static constexpr uint8_t NUM_VALVES
static daq::BoardState getBoardStateForHeartbeat()
static void sendPositions()
static IPAddress serverIP(192, 168, 2, HOTFIRE_SERVER_IP_OCTET_4)
#define HOTFIRE_SERVER_PORT
#define HOTFIRE_SERVER_IP_OCTET_4
#define SERIAL_MONITOR_READY_DELAY_MS
uint8_t g_log_stream_level
#define HF_VERBOSELN(...)
static osMessageQueueId_t commands
void configure(State &s, const byte mac[6])
AddressSource begin(State &s, IPAddress dns, IPAddress gateway)
Bring the interface up: ask the ground station for an address, and take the recovery address only if ...
void printMac(const State &s)
Print the MAC as aa:bb:cc:dd:ee:ff.
uint8_t boardId(const State &s)
The board's ID: the last octet of the address the server assigned (192.168.2.31 -> 31).
void beginOtaHold(State &s)
Called when an OTA transfer starts, so the address cannot move under it.
bool retryDhcp(State &s, IPAddress dns, IPAddress gateway)
On the recovery address, ask for a lease again every SENSOR_ETH_DHCP_RETRY_INTERVAL_MS.
bool serverWentSilent(const State &s)
Server has stopped talking — forget it and resume discovery broadcasts.
bool onServerPacket(State &s, IPAddress remote_ip, IPAddress &serverIP)
A server packet arrived from remote_ip; adopt it as the server address.
void maintainLease(State &s)
Renew the lease as it expires.
const char * sourceName(AddressSource s)
void printTestMessage()
Bench-test helper: prints STAR_OTA_TEST_MESSAGE on a timer as.
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_log_packet(uint8_t flags, const uint8_t *text, uint16_t text_len, uint32_t timestamp_ms, uint8_t *buffer, size_t buffer_size)
Creates a LOGS packet (type 15): buffered board log text for the server.
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.
bool parse_actuator_command_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, std::vector< ActuatorCommand > &commands_out)
Parses an Actuator Command packet from buffer.
bool parse_actuator_config_packet(const uint8_t *buffer, size_t buffer_size, PacketHeader &header_out, uint8_t &is_abort_controller_out, std::vector< AbortActuatorLocation > &abort_actuators_out, std::vector< AbortPTLocation > &abort_pts_out, uint8_t &enable_serial_printing_out)
Parses an Actuator Config packet from buffer.
*brief user defined string literal for JSON values *sa std::size_t n
Represents a single data chunk with timestamp and sensor datapoints.