Автоклав — Arduino Nano simulation

An interactive Arduino Nano circuit simulation you can run free in your browser on Velxio, by popov-vtc.

Components

Sketch code

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include <max6675.h>
#include <math.h>

hd44780_I2Cexp lcd;

// ---- Термопара ----
const int THERMO_SCK = 13;
const int THERMO_CS  = 10;
const int THERMO_SO  = 12;
MAX6675 thermocouple(THERMO_SCK, THERMO_CS, THERMO_SO);

// ---- Кнопки ----
const int BTN_S1 = 2;   // уставка температуры −
const int BTN_S2 = 3;   // уставка температуры +
const int BTN_S3 = 4;   // уставка таймера −
const int BTN_S4 = 5;   // уставка таймера +
const int BTN_S5 = 6;   // пуск/стоп программы

// ---- Реле ----
const int SSR_K1 = 8;   // нагрев
const int SSR_K2 = 9;   // клапан продувки

// ---- Параметры кнопок ----
const uint32_t DEBOUNCE_MS  = 30;
const uint32_t HOLD_MS      = 1500;
const uint32_t REPEAT_MS    = 100;
const uint32_t STARTUP_LOCK = 500;

const float SET_MIN = 40.0f;
const float SET_MAX = 122.0f;

float setpoint = 115.0f;

// ---- Таймер готовки ----
const int32_t TIMER_MIN_SEC = 60;      //  1 мин
const int32_t TIMER_MAX_SEC = 7200;    // 120 мин
int32_t       timerSec       = 2700;   // 45 мин по умолчанию
unsigned long timerElapsedMs = 0;
unsigned long lastTimerTickMs = 0;

// ---- Продувка ----
const float    PURGE_TRIGGER_TEMP = 100.0f;
const uint32_t PURGE_HOLD_MS      = 60000UL;
bool     purgeValveOpen   = false;
bool     purge100Reached  = false;
uint32_t purgeHoldStartMs = 0;

// ---- PID ----
float Kp = 25.0f;
float Ki = 0.20f;
float Kd = 100.0f;

const float         INTEGRAL_MAX  = 500.0f;
const unsigned long PID_PERIOD_MS = 100;
const unsigned long SSR_WINDOW_MS = 2000;

float         integral       = 0.0f;
float         lastInput      = 0.0f;
float         pidOutput      = 0.0f;
unsigned long lastPidMs      = 0;
unsigned long ssrWindowStart = 0;
bool          ssrOn          = false;
float         currentTemp    = NAN;

// ---- Состояние программы готовки ----
enum CookState { CS_IDLE, CS_HEATING, CS_COUNTDOWN, CS_DONE };
CookState cookState = CS_IDLE;

// ---- Кнопки ----
struct Button {
  uint8_t  pin;
  bool     lastRaw;
  bool     stable;
  uint32_t lastChangeMs;
  uint32_t pressMs;
  bool     repeatStarted;
};

Button btnS1 = { BTN_S1, HIGH, HIGH, 0, 0, false };
Button btnS2 = { BTN_S2, HIGH, HIGH, 0, 0, false };
Button btnS3 = { BTN_S3, HIGH, HIGH, 0, 0, false };
Button btnS4 = { BTN_S4, HIGH, HIGH, 0, 0, false };
Button btnS5 = { BTN_S5, HIGH, HIGH, 0, 0, false };

// ---- Тайминги отрисовки ----
uint32_t lastReadMs     = 0;
uint32_t bootMs         = 0;
uint32_t lastBlinkMs    = 0;
bool     blinkPhase     = false;
bool     needRedrawRow0 = true;
bool     needRedrawRow1 = true;
bool     lastSsrDrawn   = false;

// ---- Формат ±xxx,x°C ----
void formatTemp(char* buf, float t) {
  if (isnan(t)) { strcpy(buf, "+---,-\337C"); return; }
  char  sign  = (t < 0.0f) ? '-' : '+';
  float a     = fabsf(t);
  int   whole = (int)a;
  int   frac  = (int)((a - whole) * 10.0f + 0.5f);
  if (frac > 9) { frac = 0; whole++; }
  snprintf(buf, 9, "%c%03d,%d\337C", sign, w