#include <Arduino.h>
const char* DEVICE_TYPE = "hatch";
const char* FW_VERSION = "1.0 dev";
const uint8_t CHANNEL_NUM = 2; // 0: открыть, 1: закрыть
#include <sh_core.h>
#include "HatchLogic.h"
// -------------------- EEPROM --------------------
const uint16_t HATCH_EEPROM_BASE = getDeviceEepromStart();
uint16_t HATCH_EEPROM_ADDR = HATCH_EEPROM_BASE; // float, 4 байта
// -------------------- Состояние --------------------
float hatchPosition = 0.0f;
// -------------------- JSON helpers --------------------
static String extractJsonStringValue(const String &body, const String &key) {
String pattern = "\"" + key + "\"";
int keyIndex = body.indexOf(pattern);
if (keyIndex < 0) return "";
int colonIndex = body.indexOf(':', keyIndex);
if (colonIndex < 0) return "";
int firstQuote = body.indexOf('"', colonIndex + 1);
if (firstQuote < 0) return "";
int secondQuote = body.indexOf('"', firstQuote + 1);
if (secondQuote < 0) return "";
return body.substring(firstQuote + 1, secondQuote);
}
static float extractJsonFloatValue(const String &body, const String &key) {
String pattern = "\"" + key + "\"";
int keyIndex = body.indexOf(pattern);
if (keyIndex < 0) return -1.0f;
int colonIndex = body.indexOf(':', keyIndex);
if (colonIndex < 0) return -1.0f;
int i = colonIndex + 1;
while (i < (int)body.length() &&
(body[i] == ' ' || body[i] == '\t' || body[i] == '\n' || body[i] == '\r')) i++;
bool quoted = (i < (int)body.length() && body[i] == '"');
if (quoted) i++;
String numStr = "";
while (i < (int)body.length() &&
(body[i] == '-' || body[i] == '.' || (body[i] >= '0' && body[i] <= '9'))) {
numStr += body[i++];
}
if (numStr.length() == 0) return -1.0f;
return numStr.toFloat();
}
// -------------------- appendStatusJsonFields --------------------
void appendStatusJsonFields(String &json) {
uint8_t pct = (uint8_t)((hatchPosition / HATCH_LIMIT_MAX) * 100.0f + 0.5f);
if (pct > 100) pct = 100;
json += ",\"hatch\":{";
json += "\"state\":\"";
json += (hatchPosition > 0.0f) ? "open" : "closed";
json += "\",";
json += "\"position_sec\":";
json += String(hatchPosition, 2);
json += ",\"position_pct\":";
json += String(pct);
json += "}";
}
// -------------------- appendAboutJsonFields --------------------
void appendAboutJsonFields(String &json) {
json += ",\"channels\":" + String(CHANNEL_NUM);
}
// -------------------- deviceHandleAction --------------------
bool deviceHandleAction(const String &action,
const String ¶msJson,
String &errorCode,
String &errorMessage)
{
if (action == "open") {
float timeSec = extractJsonFloatValue(paramsJson, "time");
if (timeSec <= 0.0f) {
errorCode = "IllegalActionOrParams";
errorMessage = "Parameter 'time' must be > 0";
return false;
}
HatchActionResult res = hatchOpen(timeSec);
switch (res) {
case HATCH_OK:
return true;
case HATCH_ERR_ALREADY_AT_LIMIT:
errorCode = "IllegalActionOrParams";
errorMessage = "Hatch is already fully open";
return false;
case HATCH_ERR_CALIBRATION_FAILED:
errorCode = "CalibrationFailed";
errorMessage = "Limit switch did not trigger during calibration";
return false;
default:
errorCode = "IllegalActionOrParams";
errorMessage = "Open failed";
return false;
}
}
if (action == "close") {
float timeSec = extractJsonFloatValue(paramsJson, "time");
if (timeSec <= 0.0f) {
errorCode = "IllegalActionOrParams";
errorMessage = "Parameter 'time' must be > 0";
return false;
}
HatchActionResult res = hatchClose(timeSec);
switch (res) {
case HATCH_OK:
return true;
case HATCH_ERR_ALREADY_CLOSED:
errorCode = "IllegalActionOrParams";
errorMessage = "Hatch is already closed";
return false;
default:
errorCode = "IllegalActionOrParams";
errorMessage = "Close failed";
return false;
}
}
errorCode = "IllegalActionOrParams";
errorMessage = "Unknown action. Supported: open, close";
return false;
}
// -------------------- deviceHandleReset --------------------
void deviceHandleReset() {
hatchPosition = 0.0f;
hatchSavePosition();
// Сбрасываем EEPROM устройства
EEPROM.begin(EEPROM_SIZE);
for (uint16_t addr = DEVICE_EEPROM_START; addr < EEPROM_SIZE; addr++) {
EEPROM.write(addr, 0xFF);
}
EEPROM.commit();
EEPROM.end();
}
// -------------------- setup / loop --------------------
void setup() {
coreSetup();
// Инициализация пинов управления
for (uint8_t ch = 0; ch < CHANNEL_NUM; ch++) {
uint8_t pin = sh_channel_pin(ch);
if (pin == SH_PIN_UNUSED) continue;
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
}
// Инициализация пина концевика
uint8_t fbPin = sh_channel_feedback_pin(HATCH_CH_CLOSE);
if (fbPin != SH_PIN_UNUSED) {
pinMode(fbPin, INPUT_PULLDOWN);
}
// Загрузка сохранённой позиции
hatchLoadPosition();
Serial.print(F("Hatch position loaded: "));
Serial.print(hatchPosition, 2);
Serial.println(F(" sec"));
}
void loop() {
coreLoop();
}