3 Commits

Author SHA1 Message Date
84ce56fdde Sprinkler reset blocking when disable 2026-05-24 23:44:12 +03:00
c1937a045b RESET command fix
Co-authored-by: Copilot <copilot@github.com>
2026-05-24 23:30:06 +03:00
fd23dc3567 Sprinkler improvement
Co-authored-by: Copilot <copilot@github.com>
2026-05-24 22:54:21 +03:00
4 changed files with 113 additions and 59 deletions

View File

@@ -91,11 +91,24 @@ DREN\_OPERATE|таймаут 2000 сек |FAULT_DREN|выключить rDren -
Для сброса счетчиков можно использовать как непосредственную установку значения параметра "val" для каждой зоны так и команду RESET, отправленную в нужную зону или в объект sprinkler через суффикс /cmd.
В последнем случае, контроллер итерационно сбросит счетчики в значение 0 для каждой зоны полива.
А также, отключит систему полива, чтобы программа не стартовала в момент сброса счетчиков (например, в полночь)
**Пример:** ```root/name/sprinkler/cmd -> RESET```
В последнем случае, контроллер итерационно сбросит счетчики в значение 0 для каждой зоны полива.
Это инициирует старт программы полива, если система находится в состоянии ON
Команда RESET не отработает если обьект полива находится в состоянии DISABLE или FREEZE
Также, в этих режимах, в принципе, не отработает запуск полива какой-либо зоны (команды set/RESET), если для нее задано время/обьем полива.
Если обьем полива не задан (set==0) - это например водяная розетка для мытья машины, то такая зона запустится даже в режиме DISABLE
Если в процессе полива придет команда DISABLE (начался дождь) - полив прекратится, программа полива возобновится после ENABLE
## Управление
@@ -110,7 +123,9 @@ DREN\_OPERATE|таймаут 2000 сек |FAULT_DREN|выключить rDren -
### Включение/выключение цикла полива:
**Включить** ```root/name/sprinkler/cmd -> ON```
Система начнет или продолжит цикл полива, переходя от зоны к зоне по мере завершения работы с каждой предыдущей зоной. После завершения работы со всеми зонами, sprinkler перейдет в состояние OFF
Система начнет или продолжит цикл полива, переходя от зоны к зоне по мере завершения работы с каждой предыдущей зоной. После завершения работы со всеми зонами, sprinkler НЕ перейдет в состояние OFF автоматически, но полив прекратится для тех зон, где показатель val достиг параметра set
Если не будет ни одной активной зоны - насос обесточится.
Перед включением полива, система убедится что бак наполнен или до-наполнит его до максимума из водопровода.
@@ -162,6 +177,19 @@ root/name/sprinkler/val -> -1 //система перейдет в режим FR
```
Так как команда FREEZE блокирует запуск каких либо насосов и открытие клапанов, это делает невозможным слив системы в процессе подготовки к зиме
Когда требуется слить систему, необходимо отключить дренажный насос, перекрыть водопроводный кран
Данный режим реализован при помощи команды DRY
Дополнительно к перекрытию поступления воды в бак (заблокирован автомат пополнения бака из дренажного насоса и водопровода) данная команда включает помпу.
Далее, требуется слить бак включением выбранных зон полива или через водяные розетки.
Только после осушения бака (насос выключится) - систему необходимо перевести в режим FREEZE
Перевод в этот редим сбрасывает режим DRY (а также, состояния ошибки автомата наполнения бака)
### Передача статусных значений
@@ -225,16 +253,16 @@ sensor:
state_topic: "edem/s_out/sprinkler/trees/val"
- name: "Поливаем юг"
state_topic: "edem/s_out/sprinkler/south/$state"
state_topic: "root/s_out/sprinkler/south/$state"
- name: "Поливаем север"
state_topic: "edem/s_out/sprinkler/nord/$state"
state_topic: "root/s_out/sprinkler/nord/$state"
- name: "Поливаем капельно"
state_topic: "edem/s_out/sprinkler/trees/$state"
state_topic: "root/s_out/sprinkler/trees/$state"
- name: "Полив розетка статус"
state_topic: "edem/s_out/sprinkler/outlets/$state"
state_topic: "root/s_out/sprinkler/outlets/$state"
- name: "Полив блокировки"
state_topic: "root/s_out/sprinkler/ctrl"

View File

@@ -1250,6 +1250,7 @@ bool itemCmd::saveItem(Item * item, uint16_t optionsFlag)
case CMD_ENABLE:
case CMD_FREEZE:
case CMD_UNFREEZE:
case CMD_RESET:
break;
default:
item->setCmd(cmd.cmdCode);

View File

@@ -284,6 +284,10 @@ switch (state) {
strcpy(val,"FULL");
break;
case SP_DRYING:
strcpy(val,"DRYING");
break;
case SP_FAULT_VIN:
strcpy(val,"FAULT_VIN");
fault = 1;
@@ -347,6 +351,11 @@ int out_sprinkler::moveToState(sprinklerState nextState)
dren(false);
vin(false);
break;
case SP_DRYING:
dren(false);
vin(false);
pump(true);
break;
}
//publishBooleanState("/$rDren", nextState == SP_DREN_ON || nextState == SP_DREN_OPERATE);
@@ -624,66 +633,71 @@ int out_sprinkler::Poll(short cause)
pump(false);
return 0;
}
bool isActiveZone = false;
if (item->getCmd() == CMD_ON && tankReady)
{
currentZone = findNextZone();
if (currentZone)
{
long setVal = getIntFromJson(currentZone, "set", 0);
long valVal = getIntFromJson(currentZone, "val", 0);
{
long setVal = getIntFromJson(currentZone, "set", 0);
long valVal = getIntFromJson(currentZone, "val", 0);
// if not active - activate
if (!getIntFromJson(currentZone, "@active", 0))
{
turnOffAllZones();
if (!item->getFlag(FLAG_DISABLED) || !setVal)
{
setZoneActive(currentZone, true);
short zonePin = getIntFromJson(currentZone, "pin", PINS_COUNT);
if (isValidControlPin(zonePin)) setOutput(zonePin, true);
setValToJson(gatesObj, "@flowTimer", (long)now);
isActiveZone = true;
}
}
else isActiveZone = true;
if (!getIntFromJson(currentZone, "@active", 0))
{
turnOffAllZones();
setZoneActive(currentZone, true);
short zonePin = getIntFromJson(currentZone, "pin", PINS_COUNT);
if (isValidControlPin(zonePin)) setOutput(zonePin, true);
setValToJson(gatesObj, "@flowTimer", (long)now);
}
if (isActiveZone)
{
if (abs(wCtrPin) < PINS_COUNT)
{
bool curr = readInPin(wCtrPin);
if (curr && !lastWctrlState)
{
updateZoneValue(currentZone, 1);
}
if (curr) lastVals |= LASTWCTRLSTATE; else lastVals &= ~LASTWCTRLSTATE;
setValToJson(gatesObj, "@lastVals", (long)lastVals);
}
else
{
uint32_t flowTimer = (uint32_t)getIntFromJson(gatesObj, "@flowTimer", now);
if (isTimeOver(flowTimer, now, 1000UL))
{
updateZoneValue(currentZone, 1);
setValToJson(gatesObj, "@flowTimer", (long)now);
}
}
}
if (abs(wCtrPin) < PINS_COUNT)
{
bool curr = readInPin(wCtrPin);
if (curr && !lastWctrlState)
{
updateZoneValue(currentZone, 1);
}
if (curr) lastVals |= LASTWCTRLSTATE; else lastVals &= ~LASTWCTRLSTATE;
setValToJson(gatesObj, "@lastVals", (long)lastVals);
}
else
{
uint32_t flowTimer = (uint32_t)getIntFromJson(gatesObj, "@flowTimer", now);
if (isTimeOver(flowTimer, now, 1000UL))
{
updateZoneValue(currentZone, 1);
setValToJson(gatesObj, "@flowTimer", (long)now);
}
}
if (setVal > 0 && (valVal >= setVal || item->getFlag(FLAG_DISABLED)))
{
short zonePin = getIntFromJson(currentZone, "pin", PINS_COUNT);
if (isValidControlPin(zonePin)) setOutput(zonePin, false);
setZoneActive(currentZone, false);
currentZone = findNextZone();
}
if (setVal > 0 && valVal >= setVal)
{
short zonePin = getIntFromJson(currentZone, "pin", PINS_COUNT);
if (isValidControlPin(zonePin)) setOutput(zonePin, false);
setZoneActive(currentZone, false);
//////setValToJson(currentZone, "cmd", (long)CMD_OFF);
//item->SendStatusImmediate(itemCmd().Cmd(CMD_OFF).setSuffix(S_CMD), FLAG_COMMAND, currentZone->name);
currentZone = findNextZone();
}
if (currentZone)
{
needPump = true;
}
if (currentZone)
{
needPump = true;
}
}
}
if (!needPump)
{
pump(false);
/*
if (item->getCmd() == CMD_ON)
{
aJsonObject * resultZone = findNextZone();
@@ -693,6 +707,7 @@ int out_sprinkler::Poll(short cause)
item->SendStatus(FLAG_COMMAND);
}
}
*/
}
else
{
@@ -764,6 +779,7 @@ int out_sprinkler::Ctrl(itemCmd cmd, char* subItem, bool toExecute, bool authori
return 1;
case CMD_RESET:
if (item->getFlag(FLAG_FREEZED | FLAG_DISABLED)) return -1;
setValToJson(zone, "val", (long)0);
if (sendStatus)
{
@@ -788,11 +804,18 @@ int out_sprinkler::Ctrl(itemCmd cmd, char* subItem, bool toExecute, bool authori
case CMD_OFF:
turnOffAllZones();
pump(false);
//dren(false);
return 1;
case CMD_DRY:
setValToJson(gatesObj, "@state", (long)SP_DRYING);
moveToState(SP_DRYING);
notifyState(SP_DRYING);
return 1;
case CMD_RESET:
{
if (item->getFlag(FLAG_FREEZED | FLAG_DISABLED)) return -1;
aJsonObject * zone = gatesObj->child;
while (zone)
{
@@ -806,9 +829,10 @@ int out_sprinkler::Ctrl(itemCmd cmd, char* subItem, bool toExecute, bool authori
}
zone = zone->next;
}
turnOffAllZones();
pump(false);
item->Off();
// turnOffAllZones();
// pump(false);
// item->Off();
}
return 1;

View File

@@ -28,6 +28,7 @@ enum sprinklerState {
SP_DREN_EMPTY = 4,
SP_VIN = 5,
SP_FULL = 6,
SP_DRYING = 7,
SP_FAULT_VIN = -1,
SP_FAULT_DREN = -2
};