Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions firmware/include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,20 @@ extern Timing timing;
extern PID relay1PID;
extern PID relay2PID;

// Command watchdog (BREAD_OP_SET/GET_WATCHDOG): boots disarmed (timeout 0)
// unless RLHT_WATCHDOG_BOOT_MS is defined. ISR handlers write these; main-loop
// access goes through short masked windows (multi-byte volatiles on AVR).
extern volatile uint16_t wdTimeoutMs;
extern volatile unsigned long wdLastRxMs;
extern volatile bool wdTripped;
extern volatile uint8_t wdTripCount;

void setupSlice();
void setupRLHT();
void pollEStop();
void estopISR();
void processEStop();
void watchdogLogic();
void measureThermocouples();
void relayControlLogic();
void setRelayPeriod(uint8_t relayId, uint16_t periodMs);
Expand All @@ -74,8 +83,10 @@ void handler_set_pid(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data,
void handler_set_periods(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data, uint8_t data_len, void *user_data);
void handler_set_tc_select(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data, uint8_t data_len, void *user_data);
void handler_set_open_duty(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data, uint8_t data_len, void *user_data);
void handler_set_watchdog(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data, uint8_t data_len, void *user_data);
void reply_version(crumbs_context_t *ctx, crumbs_message_t *reply, void *user_data);
void reply_get_state(crumbs_context_t *ctx, crumbs_message_t *reply, void *user_data);
void reply_get_caps(crumbs_context_t *ctx, crumbs_message_t *reply, void *user_data);
void reply_get_watchdog(crumbs_context_t *ctx, crumbs_message_t *reply, void *user_data);

#endif // GLOBALS_H
4 changes: 3 additions & 1 deletion firmware/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ monitor_speed = 115200
lib_deps =
fastled/FastLED @ ^3.6.0
cameronbrooks11/CRUMBS @ ^0.12.4
cameronbrooks11/bread-crumbs-contracts @ ^0.4.3
cameronbrooks11/bread-crumbs-contracts @ ^0.4.5
br3ttb/PID @ ^1.2.1
https://github.com/adafruit/MAX6675-library.git
build_flags =
Expand All @@ -21,6 +21,8 @@ build_flags =
; -DI2C_ADR=10
; enable debug serial output:
; -DSLICE_DEBUG=1
; boot with the command watchdog armed (ms); default: boots disarmed:
; -DRLHT_WATCHDOG_BOOT_MS=5000

[env:nano_base]
platform = atmelavr
Expand Down
73 changes: 72 additions & 1 deletion firmware/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ static crumbs_context_t ctx;
volatile bool estopTriggered = false;
CRGB led;

// Command watchdog state (see globals.h). ISR-written; main loop snapshots
// under short masked windows.
volatile uint16_t wdTimeoutMs = 0;
volatile unsigned long wdLastRxMs = 0;
volatile bool wdTripped = false;
volatile uint8_t wdTripCount = 0;

static const unsigned long ESTOP_DEBOUNCE_MS = 25;
static bool estopDebouncePending = false;
static unsigned long estopDebounceStartMs = 0;
Expand Down Expand Up @@ -168,19 +175,66 @@ void loop()
{
wdt_reset();
pollEStop();
watchdogLogic();
measureThermocouples();
relayControlLogic();
serialCommands();
printSerialOutput();
}

// Fires for every CRC-valid inbound command frame (SET_REPLY excluded by
// CRUMBS): any valid command proves a live master and clears a trip.
static void on_crumbs_message(crumbs_context_t *c, const crumbs_message_t *msg)
{
(void)c;
(void)msg;
wdLastRxMs = millis();
wdTripped = false;
}

void watchdogLogic()
{
uint16_t timeout;
unsigned long lastRx;
bool tripped;

noInterrupts();
timeout = wdTimeoutMs;
lastRx = wdLastRxMs;
tripped = wdTripped;
interrupts();

if (timeout == 0 || tripped)
return; // relayControlLogic holds relays LOW while tripped

if (millis() - lastRx < timeout)
return;

digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, LOW);

// Same safe-state fields as processEStop, without touching eStop.
noInterrupts();
wdTripped = true;
wdTripCount++;
slice.relayHeater1.setpointTemperature = 0;
slice.relayHeater2.setpointTemperature = 0;
slice.relayHeater1.relayOnTime = 0;
slice.relayHeater2.relayOnTime = 0;
slice.relay1State = false;
slice.relay2State = false;
interrupts();
SLICE_DEBUG_PRINTLN(F("WATCHDOG TRIPPED: bus silent, relays off"));
}

void setupSlice()
{
int rc;

Serial.begin(115200);

crumbs_arduino_init_peripheral(&ctx, I2C_ADR);
crumbs_set_callbacks(&ctx, on_crumbs_message, nullptr, nullptr);

rc = crumbs_register_handler(&ctx, RLHT_OP_SET_MODE, handler_set_mode, nullptr);
if (rc != 0)
Expand All @@ -206,6 +260,10 @@ void setupSlice()
if (rc != 0)
SLICE_DEBUG_PRINTLN(F("CRUMBS: Failed to register RLHT_OP_SET_OPEN_DUTY"));

rc = crumbs_register_handler(&ctx, BREAD_OP_SET_WATCHDOG, handler_set_watchdog, nullptr);
if (rc != 0)
SLICE_DEBUG_PRINTLN(F("CRUMBS: Failed to register BREAD_OP_SET_WATCHDOG"));

rc = crumbs_register_reply_handler(&ctx, 0x00, reply_version, nullptr);
if (rc != 0)
SLICE_DEBUG_PRINTLN(F("CRUMBS: Failed to register version reply handler"));
Expand All @@ -218,6 +276,17 @@ void setupSlice()
if (rc != 0)
SLICE_DEBUG_PRINTLN(F("CRUMBS: Failed to register BREAD_OP_GET_CAPS reply handler"));

rc = crumbs_register_reply_handler(&ctx, BREAD_OP_GET_WATCHDOG, reply_get_watchdog, nullptr);
if (rc != 0)
SLICE_DEBUG_PRINTLN(F("CRUMBS: Failed to register BREAD_OP_GET_WATCHDOG reply handler"));

#ifdef RLHT_WATCHDOG_BOOT_MS
// Integration opt-in: come up armed (e.g. e-stop wirings that power-cycle
// the board). Default builds boot disarmed.
wdTimeoutMs = (uint16_t)RLHT_WATCHDOG_BOOT_MS;
wdLastRxMs = millis();
#endif

#if RLHT_HAS_STATUS_LED
FastLED.addLeds<NEOPIXEL, LED_PIN>(&led, 1);
FastLED.setBrightness(50);
Expand Down Expand Up @@ -359,7 +428,7 @@ void relayControlLogic()
// one window so a heater's tunings are never torn; a torn view across
// windows self-corrects next iteration.
noInterrupts();
bool localEStop = slice.eStop;
bool localEStop = slice.eStop || wdTripped;
ControlMode localMode = slice.mode;
double sp1 = slice.relayHeater1.setpointTemperature;
double sp2 = slice.relayHeater2.setpointTemperature;
Expand Down Expand Up @@ -393,6 +462,8 @@ void relayControlLogic()
slice.relayHeater1.thermocoupleSelect = tc1;
slice.relayHeater2.thermocoupleSelect = tc2;

// A tripped command watchdog holds the same safe state as e-stop until
// fresh traffic clears the trip (ISR side).
if (localEStop)
{
digitalWrite(RELAY1, LOW);
Expand Down
20 changes: 19 additions & 1 deletion firmware/src/printSerialOutputRLHT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,25 @@ void printSliceState(Print &out)
out.print(F(", Thermo Select Relay 2:"));
out.print(slice.relayHeater2.thermocoupleSelect);
out.print(F(", ESTOP:"));
out.println(slice.eStop);
out.print(slice.eStop);

out.print(F(", WDOG:"));
uint16_t wdTimeout;
bool wdTrip;
uint8_t wdTrips;
noInterrupts();
wdTimeout = wdTimeoutMs;
wdTrip = wdTripped;
wdTrips = wdTripCount;
interrupts();
if (wdTimeout == 0)
out.print(F("off"));
else
out.print(wdTimeout);
out.print(F(", WDTRIP:"));
out.print(wdTrip);
out.print(F("/"));
out.println(wdTrips);
}

void printSerialOutput()
Expand Down
31 changes: 30 additions & 1 deletion firmware/src/rlht_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@ static double deci_c_to_temp(int16_t t)
return ((double)t) / 10.0;
}

void handler_set_watchdog(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data, uint8_t data_len, void *user_data)
{
uint16_t timeout_ms = 0;
(void)ctx;
(void)opcode;
(void)user_data;

if (crumbs_msg_read_u16(data, data_len, 0, &timeout_ms) != 0)
return;

wdTimeoutMs = timeout_ms;
wdLastRxMs = millis();
wdTripped = false;
}

void reply_get_watchdog(crumbs_context_t *ctx, crumbs_message_t *reply, void *user_data)
{
uint16_t timeout_ms = wdTimeoutMs;
(void)ctx;
(void)user_data;

(void)bread_watchdog_build_reply(reply, RLHT_TYPE_ID,
timeout_ms != 0 ? 1 : 0, timeout_ms,
wdTripped ? 1 : 0, wdTripCount);
// A reply build proves a live master too.
wdLastRxMs = millis();
}

void handler_set_mode(crumbs_context_t *ctx, uint8_t opcode, const uint8_t *data, uint8_t data_len, void *user_data)
{
uint8_t mode = RLHT_MODE_CLOSED_LOOP;
Expand Down Expand Up @@ -218,5 +246,6 @@ void reply_get_caps(crumbs_context_t *ctx, crumbs_message_t *reply, void *user_d
(void)ctx;
(void)user_data;

(void)bread_caps_build_reply(reply, RLHT_TYPE_ID, RLHT_CAP_LEVEL_1, RLHT_CAP_BASELINE_FLAGS);
(void)bread_caps_build_reply(reply, RLHT_TYPE_ID, RLHT_CAP_LEVEL_1,
RLHT_CAP_BASELINE_FLAGS | RLHT_CAP_CMD_WATCHDOG);
}
28 changes: 28 additions & 0 deletions firmware/src/serialCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ static void processCommand(char *cmd)
if (*cmd == '\0')
return;

// A serial operator is a live master too: feed the command watchdog.
noInterrupts();
wdLastRxMs = millis();
wdTripped = false;
interrupts();

if (starts_with_P(cmd, PSTR("MODE=")))
{
char *mode = (char *)after_prefix_P(cmd, PSTR("MODE="));
Expand Down Expand Up @@ -290,6 +296,27 @@ static void processCommand(char *cmd)
Serial.println(F("Period range 100-10000ms"));
}
}
else if (starts_with_P(cmd, PSTR("WDOG=")))
{
long v = atol(after_prefix_P(cmd, PSTR("WDOG=")));
if (v < 0)
v = 0;
if (v > 65535)
v = 65535;
noInterrupts();
wdTimeoutMs = (uint16_t)v;
wdLastRxMs = millis();
wdTripped = false;
interrupts();
Serial.print(F("WDOG-> "));
if (v == 0)
Serial.println(F("disarmed"));
else
{
Serial.print(v);
Serial.println(F(" ms"));
}
}
else if (starts_with_P(cmd, PSTR("HELP")) || starts_with_P(cmd, PSTR("?")))
{
Serial.println(F("Commands:"));
Expand All @@ -304,6 +331,7 @@ static void processCommand(char *cmd)
Serial.println(F("R2KP/KI/KD=<val> - R2 PID"));
Serial.println(F("R1PERIOD=<val> - R1 period ms"));
Serial.println(F("R2PERIOD=<val> - R2 period ms"));
Serial.println(F("WDOG=<ms> - command watchdog (0=off)"));
}
else
{
Expand Down
Loading