537 lines
17 KiB
C++
537 lines
17 KiB
C++
/*************************************************************
|
||
* ARDUINO NANO – MODBUS SLAVE 6× PWM + LED + EEPROM + CRC
|
||
* -----------------------------------------------------------
|
||
* Verze FW: 2.2
|
||
*
|
||
* TODO:
|
||
* - případné rozšíření registrů a chybových kódů
|
||
*
|
||
* Funkce:
|
||
* - 6× slow PWM pro termo hlavice (SSR)
|
||
* - LED D13 řízená přes Modbus (LED_SWITCH)
|
||
* - Alive LED (pin 10)
|
||
* - EEPROM uložení konfigurace (perioda, duty, typy, skupiny)
|
||
* - CRC kontrola konfigurace
|
||
* - Versioning konfigurace (CONFIG_VERSION)
|
||
* - Last-Modbus-Age registr (čas od poslední OK Modbus komunikace)
|
||
* - Test mode (sekvenční blikání 6 PWM)
|
||
* - Skupiny PWM:
|
||
* - Každý PWM kanál má svoji skupinu (0–4), nastavovanou z ESPHome
|
||
* - Každá skupina má svůj "virtuální" PWM duty registr
|
||
* - Zápis do GROUPx_PWM se propíše do všech PWM, které mají danou skupinu
|
||
*
|
||
* -----------------------------------------------------------
|
||
* MODBUS – MAPA REGISTRŮ (Holding Registers)
|
||
* -----------------------------------------------------------
|
||
* Index Adresa Název R/W Popis
|
||
* ----- ------ ---------------- --- ---------------------------------------
|
||
* 0 0 PWM_PERIOD R/W Společná perioda PWM (v sekundách), min. 1 s
|
||
* 1 1 TEST_MODE R/W 0 = normální režim, !=0 = test sekvence PWM
|
||
* 2 2 STATUS_FLAGS R Stavové bity:
|
||
* bit0 = config načten (platná konfigurace v RAM)
|
||
* bit1 = CRC OK (1) / CRC chyba (0)
|
||
* bit2 = verze configu nesouhlasila (použity defaulty)
|
||
* 3 3 LAST_ERROR R Rezervováno (zatím 0)
|
||
* 4 4 LAST_MODBUS_AGE R Čas od poslední korektní Modbus komunikace (ms / 65k wrap)
|
||
* 5 5 TEMPERATURE1 R Teplotní čidlo DS18B20 1
|
||
* 6 6 TEMPERATURE2 R Teplotní čidlo DS18B20 2
|
||
* 7 7 TEMPERATURE3 R Teplotní čidlo DS18B20 3
|
||
* 8 8 TEMPERATURE4 R Teplotní čidlo DS18B20 4
|
||
* 9 9 LED_SWITCH R/W 0 = LED D13 vyp, !=0 = LED D13 zap
|
||
* 10 10 REZERVA1 R Rezerva 1
|
||
*
|
||
* -- "virtuální" skupinové PWM (NEUKLÁDAJÍ se do EEPROM) --
|
||
*
|
||
* 11 11 GROUP1_PWM R/W Duty (%) pro skupinu 1:
|
||
* zápis se propíše do všech PWM,
|
||
* které mají PWMx_GROUP == 1
|
||
* 12 12 GROUP2_PWM R/W Stejně jako GROUP1_PWM, pro skupinu 2
|
||
* 13 13 GROUP3_PWM R/W Stejně jako GROUP1_PWM, pro skupinu 3
|
||
* 14 14 GROUP4_PWM R/W Stejně jako GROUP1_PWM, pro skupinu 4
|
||
* 15 15 REZERVA2 R Rezerva2
|
||
*
|
||
* -- Per-kanál konfigurace PWM (duty, typ, skupina) --
|
||
*
|
||
* 16 16 PWM1_DUTY R/W PWM1 - výkon v % (0–100)
|
||
* 17 17 PWM1_TYPE R/W Typ výstupu PWM1: 0 = NC (normálně vyp.), 1 = NO (invert)
|
||
* 18 18 PWM1_GROUP R/W Skupina PWM1: 0 = žádná, 1–4 = GROUPx_PWM
|
||
* 19 19 PWM2_DUTY R/W
|
||
* 20 20 PWM2_TYPE R/W
|
||
* 21 21 PWM2_GROUP R/W
|
||
* 22 22 PWM3_DUTY R/W
|
||
* 23 23 PWM3_TYPE R/W
|
||
* 24 24 PWM3_GROUP R/W
|
||
* 25 25 PWM4_DUTY R/W
|
||
* 26 26 PWM4_TYPE R/W
|
||
* 27 27 PWM4_GROUP R/W
|
||
* 28 28 PWM5_DUTY R/W
|
||
* 29 29 PWM5_TYPE R/W
|
||
* 30 30 PWM5_GROUP R/W
|
||
* 31 31 PWM6_DUTY R/W
|
||
* 32 32 PWM6_TYPE R/W
|
||
* 33 33 PWM6_GROUP R/W
|
||
*
|
||
* Celkový počet registrů: TOTAL_REGS_SIZE (34)
|
||
*
|
||
* EEPROM:
|
||
* - Ukládají se registry:
|
||
* PWM_PERIOD (0)
|
||
* PWM1–PWM6_DUTY (16–31, po 3 skoky)
|
||
* PWM1–PWM6_TYPE
|
||
* PWM1–PWM6_GROUP
|
||
* - Neukládají se:
|
||
* GROUPx_PWM, TEST_MODE, LED_SWITCH, teploty, status, atd.
|
||
*
|
||
* EEPROM layout (uint16_t):
|
||
* adresa 0–1 : CONFIG_VERSION
|
||
* adresa 2–3 : CRC konfiguračních registrů
|
||
* adresa 4–... : hodnoty registrů z pole CONFIG_REGS[] (každý 2 bajty)
|
||
*************************************************************/
|
||
|
||
#include <SoftwareSerial.h>
|
||
#include <EEPROM.h>
|
||
#include <SimpleModbusSlaveSoftwareSerial.h>
|
||
#include <GyverDS18.h>
|
||
|
||
// ================= PINY ======================
|
||
#define PIN_RX 3
|
||
#define PIN_TX 2
|
||
#define LED_PIN 13
|
||
#define ALIVE_PIN 10
|
||
#define PWM1_PIN 4
|
||
#define PWM2_PIN 5
|
||
#define PWM3_PIN 6
|
||
#define PWM4_PIN 7
|
||
#define PWM5_PIN 8
|
||
#define PWM6_PIN 9
|
||
|
||
#define DALLAS1 A2
|
||
#define DALLAS2 A3
|
||
#define DALLAS3 A4
|
||
#define DALLAS4 A5
|
||
|
||
// =============== MODBUS ======================
|
||
#define DEVICE_ID 3
|
||
#define BAUD_RATE 9600
|
||
#define RS485_EN -1 // -1 = nepoužíváme DE/RE řízení (přímo RX/TX RS485 převodníku)
|
||
|
||
// =============== DS18B20 ======================
|
||
#define DALLAS_RESOLUTION 9
|
||
#define DALLAS_PERIOD 5000
|
||
|
||
GyverDS18Single ds1(DALLAS1);
|
||
GyverDS18Single ds2(DALLAS2);
|
||
GyverDS18Single ds3(DALLAS3);
|
||
GyverDS18Single ds4(DALLAS4);
|
||
|
||
// =============== EEPROM CONFIG ===============
|
||
// Pozn.: změna CONFIG_VERSION zajistí ignorování staré EEPROM
|
||
#define CONFIG_VERSION 1 // verze layoutu konfigurace v EEPROM
|
||
#define EEPROM_WRITE_DELAY 200 // ms – po změně configu počkáme a pak uložíme celý blok
|
||
|
||
// Příznak, že se nějaký konfigurační registr změnil (pro odložený zápis)
|
||
bool configDirty = false;
|
||
unsigned long lastConfigChange = 0;
|
||
|
||
// =============== REGISTR MAPA ===============
|
||
#define TOTAL_REGISTERS 34
|
||
|
||
enum {
|
||
PWM_PERIOD = 0, // 0
|
||
TEST_MODE, // 1
|
||
STATUS_FLAGS, // 2
|
||
LAST_ERROR, // 3
|
||
LAST_MODBUS_AGE, // 4
|
||
TEMPERATURE1, // 5
|
||
TEMPERATURE2, // 6
|
||
TEMPERATURE3, // 7
|
||
TEMPERATURE4, // 8
|
||
LED_SWITCH, // 9
|
||
REZERVA1, // 10
|
||
GROUP1_PWM, // 11
|
||
GROUP2_PWM, // 12
|
||
GROUP3_PWM, // 13
|
||
GROUP4_PWM, // 14
|
||
REZERVA2, // 15
|
||
|
||
// PWM 1
|
||
PWM1_DUTY, // 16
|
||
PWM1_TYPE, // 17
|
||
PWM1_GROUP, // 18
|
||
|
||
// PWM 2
|
||
PWM2_DUTY, // 19
|
||
PWM2_TYPE, // 20
|
||
PWM2_GROUP, // 21
|
||
|
||
// PWM 3
|
||
PWM3_DUTY, // 22
|
||
PWM3_TYPE, // 23
|
||
PWM3_GROUP, // 24
|
||
|
||
// PWM 4
|
||
PWM4_DUTY, // 25
|
||
PWM4_TYPE, // 26
|
||
PWM4_GROUP, // 27
|
||
|
||
// PWM 5
|
||
PWM5_DUTY, // 28
|
||
PWM5_TYPE, // 29
|
||
PWM5_GROUP, // 30
|
||
|
||
// PWM 6
|
||
PWM6_DUTY, // 31
|
||
PWM6_TYPE, // 32
|
||
PWM6_GROUP, // 33
|
||
|
||
TOTAL_REGS_SIZE // 34
|
||
};
|
||
|
||
unsigned int holdingRegs[TOTAL_REGS_SIZE];
|
||
|
||
// Seznam registrů, které patří do konfigurace (EEPROM)
|
||
const uint8_t CONFIG_REGS[] = {
|
||
// PWM duty
|
||
PWM1_DUTY,
|
||
PWM2_DUTY,
|
||
PWM3_DUTY,
|
||
PWM4_DUTY,
|
||
PWM5_DUTY,
|
||
PWM6_DUTY,
|
||
// společná perioda
|
||
PWM_PERIOD,
|
||
// typy
|
||
PWM1_TYPE,
|
||
PWM2_TYPE,
|
||
PWM3_TYPE,
|
||
PWM4_TYPE,
|
||
PWM5_TYPE,
|
||
PWM6_TYPE,
|
||
// skupiny
|
||
PWM1_GROUP,
|
||
PWM2_GROUP,
|
||
PWM3_GROUP,
|
||
PWM4_GROUP,
|
||
PWM5_GROUP,
|
||
PWM6_GROUP
|
||
};
|
||
const uint8_t CONFIG_REGS_COUNT = sizeof(CONFIG_REGS) / sizeof(CONFIG_REGS[0]);
|
||
|
||
// =============== MODBUS SERIAL ===============
|
||
SoftwareSerial modbusSerial(PIN_RX, PIN_TX);
|
||
unsigned long cycleStart = 0;
|
||
unsigned long lastModbusTime = 0;
|
||
|
||
// =============== EEPROM MAPA ==================
|
||
int eeAddrVersion = 0; // uint16_t verze configu
|
||
int eeAddrCRC = 2; // uint16_t CRC
|
||
int eeAddrRegs = 4; // začátek oblasti s registry (2 bajty na každý reg z CONFIG_REGS[])
|
||
|
||
// =============== CRC16 PRO KONFIG =============
|
||
// Standardní Modbus CRC16 (polynom 0xA001)
|
||
uint16_t crc16_update(uint16_t crc, uint8_t a) {
|
||
crc ^= a;
|
||
for (int i = 0; i < 8; ++i) {
|
||
if (crc & 1)
|
||
crc = (crc >> 1) ^ 0xA001;
|
||
else
|
||
crc = (crc >> 1);
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
// Výpočet CRC přes všechny konfigurační registry z CONFIG_REGS[]
|
||
uint16_t calcConfigCRC() {
|
||
uint16_t crc = 0xFFFF;
|
||
for (uint8_t i = 0; i < CONFIG_REGS_COUNT; i++) {
|
||
uint16_t v = holdingRegs[CONFIG_REGS[i]];
|
||
crc = crc16_update(crc, v & 0xFF);
|
||
crc = crc16_update(crc, (v >> 8) & 0xFF);
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
// Uložení konfigurace do EEPROM (jen pokud se změní / voláno z kódu)
|
||
void saveConfigToEEPROM() {
|
||
uint16_t version = CONFIG_VERSION;
|
||
EEPROM.put(eeAddrVersion, version);
|
||
|
||
// uložit všechny config registry postupně za sebou
|
||
for (uint8_t i = 0; i < CONFIG_REGS_COUNT; i++) {
|
||
uint16_t v = holdingRegs[CONFIG_REGS[i]];
|
||
EEPROM.put(eeAddrRegs + i * 2, v);
|
||
}
|
||
|
||
uint16_t crc = calcConfigCRC();
|
||
EEPROM.put(eeAddrCRC, crc);
|
||
|
||
// nastavit příznaky – konfigurace je platná a CRC sedí
|
||
holdingRegs[STATUS_FLAGS] |= (1 << 0); // bit0 = config načten/platný
|
||
holdingRegs[STATUS_FLAGS] |= (1 << 1); // bit1 = CRC OK
|
||
// bit2 (verze nesouhlasila) případně ponecháme beze změny jako "historii"
|
||
}
|
||
|
||
// Načtení konfigurace z EEPROM
|
||
// Vrací true = konfigurace z EEPROM byla úspěšně načtena a CRC sedí
|
||
// Vrací false = verze nesedí NEBO CRC nesedí -> v setup() se použijí defaulty a přepíšou EEPROM
|
||
bool loadConfigFromEEPROM() {
|
||
uint16_t storedVersion;
|
||
uint16_t storedCRC;
|
||
|
||
EEPROM.get(eeAddrVersion, storedVersion);
|
||
EEPROM.get(eeAddrCRC, storedCRC);
|
||
|
||
// Kontrola verze layoutu
|
||
if (storedVersion != CONFIG_VERSION) {
|
||
// Verze nesouhlasí – uložená data ignorujeme, v setupu se použijí defaulty
|
||
holdingRegs[STATUS_FLAGS] |= (1 << 2); // bit2 = verze configu nesouhlasila
|
||
return false;
|
||
}
|
||
|
||
// Načíst registry z EEPROM do RAM
|
||
for (uint8_t i = 0; i < CONFIG_REGS_COUNT; i++) {
|
||
uint16_t v;
|
||
EEPROM.get(eeAddrRegs + i * 2, v);
|
||
holdingRegs[CONFIG_REGS[i]] = v;
|
||
}
|
||
|
||
// Kontrola CRC
|
||
uint16_t calc = calcConfigCRC();
|
||
if (calc != storedCRC) {
|
||
// CRC chyba – konfiguraci považujeme za neplatnou
|
||
holdingRegs[STATUS_FLAGS] &= ~(1 << 1); // bit1 = 0 (CRC error)
|
||
return false;
|
||
}
|
||
|
||
// Úspěšně načteno
|
||
holdingRegs[STATUS_FLAGS] |= (1 << 0); // bit0 = config načten
|
||
holdingRegs[STATUS_FLAGS] |= (1 << 1); // bit1 = CRC OK
|
||
holdingRegs[STATUS_FLAGS] &= ~(1 << 2); // bit2 = verze v pořádku
|
||
return true;
|
||
}
|
||
|
||
// =============== DEFAULTY =====================
|
||
void loadDefaults() {
|
||
// Perioda v sekundách
|
||
holdingRegs[PWM_PERIOD] = 30; // 30 s
|
||
|
||
// Typy výstupů: výchozí NC (0)
|
||
holdingRegs[PWM1_TYPE] = 0;
|
||
holdingRegs[PWM2_TYPE] = 0;
|
||
holdingRegs[PWM3_TYPE] = 0;
|
||
holdingRegs[PWM4_TYPE] = 0;
|
||
holdingRegs[PWM5_TYPE] = 0;
|
||
holdingRegs[PWM6_TYPE] = 0;
|
||
|
||
// Výchozí PWM duty = 0 %
|
||
holdingRegs[PWM1_DUTY] = 0;
|
||
holdingRegs[PWM2_DUTY] = 0;
|
||
holdingRegs[PWM3_DUTY] = 0;
|
||
holdingRegs[PWM4_DUTY] = 0;
|
||
holdingRegs[PWM5_DUTY] = 0;
|
||
holdingRegs[PWM6_DUTY] = 0;
|
||
|
||
// Skupiny: výchozí = 0 (žádná skupina)
|
||
holdingRegs[PWM1_GROUP] = 0;
|
||
holdingRegs[PWM2_GROUP] = 0;
|
||
holdingRegs[PWM3_GROUP] = 0;
|
||
holdingRegs[PWM4_GROUP] = 0;
|
||
holdingRegs[PWM5_GROUP] = 0;
|
||
holdingRegs[PWM6_GROUP] = 0;
|
||
|
||
// Test mode, LED, group PWM, status atd.
|
||
holdingRegs[TEST_MODE] = 0;
|
||
holdingRegs[LED_SWITCH] = 0;
|
||
|
||
holdingRegs[STATUS_FLAGS] = 0;
|
||
holdingRegs[LAST_ERROR] = 0;
|
||
holdingRegs[LAST_MODBUS_AGE] = 0;
|
||
|
||
holdingRegs[GROUP1_PWM] = 0;
|
||
holdingRegs[GROUP2_PWM] = 0;
|
||
holdingRegs[GROUP3_PWM] = 0;
|
||
holdingRegs[GROUP4_PWM] = 0;
|
||
}
|
||
|
||
// =============== SLOW PWM ======================
|
||
void slowPwm(int pin, int duty, int typeNO, unsigned long periodMs, unsigned long offset) {
|
||
bool on;
|
||
|
||
if (duty <= 0) {
|
||
on = false;
|
||
} else if (duty >= 100) {
|
||
on = true;
|
||
} else {
|
||
on = offset < (periodMs * (unsigned long)duty) / 100UL;
|
||
}
|
||
|
||
// typeNO = 1 → NO (invertované chování)
|
||
if (typeNO) on = !on;
|
||
|
||
digitalWrite(pin, on);
|
||
}
|
||
|
||
// =============== TEST MODE ======================
|
||
// Sekvenční test 6 PWM výstupů – každý má 1/6 periody ON
|
||
void testModeRun(unsigned long offset, unsigned long periodMs) {
|
||
const uint8_t pwmCount = 6;
|
||
unsigned long segment = periodMs / pwmCount;
|
||
|
||
uint8_t active = offset / segment;
|
||
if (active >= pwmCount) active = pwmCount - 1;
|
||
|
||
digitalWrite(PWM1_PIN, active == 0);
|
||
digitalWrite(PWM2_PIN, active == 1);
|
||
digitalWrite(PWM3_PIN, active == 2);
|
||
digitalWrite(PWM4_PIN, active == 3);
|
||
digitalWrite(PWM5_PIN, active == 4);
|
||
digitalWrite(PWM6_PIN, active == 5);
|
||
}
|
||
|
||
// =============== SETUP =========================
|
||
void setup() {
|
||
pinMode(LED_PIN, OUTPUT);
|
||
pinMode(ALIVE_PIN, OUTPUT);
|
||
|
||
pinMode(PWM1_PIN, OUTPUT);
|
||
pinMode(PWM2_PIN, OUTPUT);
|
||
pinMode(PWM3_PIN, OUTPUT);
|
||
pinMode(PWM4_PIN, OUTPUT);
|
||
pinMode(PWM5_PIN, OUTPUT);
|
||
pinMode(PWM6_PIN, OUTPUT);
|
||
|
||
// Nejprve nastavíme defaulty do RAM
|
||
loadDefaults();
|
||
|
||
// Pokus o načtení konfigurace z EEPROM
|
||
bool ok = loadConfigFromEEPROM();
|
||
|
||
if (!ok) {
|
||
// Pokud verze neseděla (bit2 = 1), máme už v RAM defaulty a jen je uložíme.
|
||
// Pokud verze seděla, ale CRC nesedělo, znovu nahrajeme defaulty.
|
||
if (!(holdingRegs[STATUS_FLAGS] & (1 << 2))) {
|
||
// žádná verze-mismatch vlajka -> CRC chyba nebo prázdná EEPROM
|
||
loadDefaults();
|
||
}
|
||
// V obou případech teď v RAM jsou defaulty -> zapíšeme je do EEPROM
|
||
saveConfigToEEPROM();
|
||
}
|
||
|
||
ds1.setResolution(DALLAS_RESOLUTION);
|
||
ds2.setResolution(DALLAS_RESOLUTION);
|
||
ds3.setResolution(DALLAS_RESOLUTION);
|
||
ds4.setResolution(DALLAS_RESOLUTION);
|
||
|
||
ds1.setPeriod(DALLAS_PERIOD);
|
||
ds2.setPeriod(DALLAS_PERIOD);
|
||
ds3.setPeriod(DALLAS_PERIOD);
|
||
ds4.setPeriod(DALLAS_PERIOD);
|
||
|
||
modbusSerial.begin(BAUD_RATE);
|
||
modbus_configure(&modbusSerial, BAUD_RATE, DEVICE_ID, RS485_EN, TOTAL_REGS_SIZE);
|
||
}
|
||
|
||
// =============== LOOP ==========================
|
||
void loop() {
|
||
// Čtení teplot (GyverDS18Single – tick() spouští měření, po doběhu vrací false)
|
||
if (!ds1.tick()) {
|
||
holdingRegs[TEMPERATURE1] = ds1.getTemp() * 100;
|
||
}
|
||
if (!ds2.tick()) {
|
||
holdingRegs[TEMPERATURE2] = ds2.getTemp() * 100;
|
||
}
|
||
if (!ds3.tick()) {
|
||
holdingRegs[TEMPERATURE3] = ds3.getTemp() * 100;
|
||
}
|
||
if (!ds4.tick()) {
|
||
holdingRegs[TEMPERATURE4] = ds4.getTemp() * 100;
|
||
}
|
||
|
||
unsigned long now = millis();
|
||
holdingRegs[LAST_MODBUS_AGE] = (uint16_t)(now - lastModbusTime);
|
||
|
||
// Alive LED – cca 1 Hz
|
||
digitalWrite(ALIVE_PIN, (now / 500) % 2);
|
||
|
||
// Kopie registrů před Modbusem – pro zjištění změn
|
||
uint16_t before[TOTAL_REGS_SIZE];
|
||
memcpy(before, holdingRegs, sizeof(holdingRegs));
|
||
|
||
int errors = modbus_update(holdingRegs);
|
||
if (errors == 0) {
|
||
lastModbusTime = now;
|
||
}
|
||
|
||
// ==========================================================
|
||
// SKUPINOVÉ PWM – obsluha GROUP1..4
|
||
// Pokud se změní GROUPx_PWM, přepíše se PWMx_DUTY
|
||
// všech kanálů se stejnou hodnotou PWMx_GROUP
|
||
// ==========================================================
|
||
for (uint8_t group = 1; group <= 4; group++) {
|
||
uint8_t groupReg = GROUP1_PWM + (group - 1);
|
||
|
||
// Skupinový registr se změnil?
|
||
if (holdingRegs[groupReg] != before[groupReg]) {
|
||
uint16_t duty = holdingRegs[groupReg];
|
||
if (duty > 100) duty = 100;
|
||
holdingRegs[groupReg] = duty; // ořez na 0–100 %
|
||
|
||
// Projdi všech 6 PWM kanálů
|
||
for (uint8_t i = 0; i < 6; i++) {
|
||
uint8_t chGroupReg = PWM1_GROUP + i * 3;
|
||
uint8_t chDutyReg = PWM1_DUTY + i * 3;
|
||
|
||
// Kanál patří do této skupiny?
|
||
if (holdingRegs[chGroupReg] == group) {
|
||
holdingRegs[chDutyReg] = duty;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ----------------------------------------------------------
|
||
// DETEKCE ZMĚNY KONFIGURAČNÍCH REGISTRŮ PRO EEPROM
|
||
// (porovnání aktuálních hodnot s "before" po obsluze skupin)
|
||
// ----------------------------------------------------------
|
||
for (uint8_t i = 0; i < CONFIG_REGS_COUNT; i++) {
|
||
uint8_t r = CONFIG_REGS[i];
|
||
if (holdingRegs[r] != before[r]) {
|
||
configDirty = true;
|
||
lastConfigChange = now;
|
||
}
|
||
}
|
||
|
||
// Odložený zápis konfigurace do EEPROM
|
||
if (configDirty && (now - lastConfigChange > EEPROM_WRITE_DELAY)) {
|
||
saveConfigToEEPROM();
|
||
configDirty = false;
|
||
}
|
||
|
||
// LED D13 řízená přes Modbus
|
||
digitalWrite(LED_PIN, holdingRegs[LED_SWITCH] ? HIGH : LOW);
|
||
|
||
// Perioda v ms – registr je v sekundách
|
||
unsigned long periodMs = (unsigned long)holdingRegs[PWM_PERIOD] * 1000UL;
|
||
if (periodMs < 1000UL) periodMs = 1000UL; // aspoň 1 s
|
||
|
||
unsigned long offset = now - cycleStart;
|
||
if (offset >= periodMs) {
|
||
cycleStart = now;
|
||
offset = 0;
|
||
}
|
||
|
||
// Test mode
|
||
if (holdingRegs[TEST_MODE]) {
|
||
testModeRun(offset, periodMs);
|
||
return;
|
||
}
|
||
|
||
// Normální režim – slow PWM
|
||
slowPwm(PWM1_PIN, holdingRegs[PWM1_DUTY], holdingRegs[PWM1_TYPE], periodMs, offset);
|
||
slowPwm(PWM2_PIN, holdingRegs[PWM2_DUTY], holdingRegs[PWM2_TYPE], periodMs, offset);
|
||
slowPwm(PWM3_PIN, holdingRegs[PWM3_DUTY], holdingRegs[PWM3_TYPE], periodMs, offset);
|
||
slowPwm(PWM4_PIN, holdingRegs[PWM4_DUTY], holdingRegs[PWM4_TYPE], periodMs, offset);
|
||
slowPwm(PWM5_PIN, holdingRegs[PWM5_DUTY], holdingRegs[PWM5_TYPE], periodMs, offset);
|
||
slowPwm(PWM6_PIN, holdingRegs[PWM6_DUTY], holdingRegs[PWM6_TYPE], periodMs, offset);
|
||
}
|