Может причина в том, что цикл начинается из состояния 0? В состоянии 0 предполагается, что таймер 3ч уже запущен. Надо либо запускать таймер в setup(), либо начинать цикл из состояния 1.
Привет!
Внимательно просмотрел проект. Как-то сложноват он мне показался :(
Я бы не стал ждать, когда воды останется на 15 мин, а, допустим, постоянно подпитывал основной бак при снижении уровня примерно менее 50% V. Не знаю как у вас с электричеством, но пока оно есть, лучше заполнить бак доверху.
Для контроля работоспособности насоса и переполнения бака я себе закупил (поставлю по весне) реле ПОтока. Иногда называется реле ПРОтока. Такие реле бывают и для сильно загрязненной среды. Гугл/Али в помощь.
Про сложность... ну да, есть такой момент. Это я еще дисплей не прикрутил :) Шучу.
Про подкачку воды. У меня есть некоторая идея - лучше выкачать воду, а потом ее закачать, чем она будет постоянно в баке. Некоторый обмен, снижающий риск застоя. Но это теория. На практике - залив сверху, забор снизу. Так что это просто некоторое внутренее желание. Хочу, чтобы вода сначала почти вся ушла из бака, а потом залилась новая.
Перед отъездом на зиму я уже погонял водопровод в ручном режиме. Т.е. бак наполнял вручную. Трехходовой вентиль включил, насос включил, 15 минут подождал, вентиль выключил, бак накачал, насос выключил :)
И вот мой опыт показывает, что при разборе воды из дома скорость наполнения емкости скваженным насосом такова, что не нужно никаких 15-ти минутных запасов воды. Вкачивает больше, чем разбирает. Так что нижний поплавок буду ставить сантиметрах в 10-15 ото дна.
Что касается электричества - бак в котельной в цоколе, без электричества насосная станция ничего по дому не раздаст, самотек невозможен (только то, что выдавид 100 литровый гидроаккумулятор). А если я генератор включаю, то какая разница? А если лефтричества нет долго, я его полюбому включу.
Относительно реле потока (или протока). Я не хочу дожидаться, пока вода пойдет в переливную трубу. Зачем? Поплавковый выключатель (продублированный на всякий случай) вполне себе вариант. А учитывая, что насос останавливается другим поплавком, то вообще предположить, что вода перельется - ну меньше 1%. А на этот процент как раз труба перелива.
Думаю, можно еще усложнить программу и прикрутить таймер, что если насос не выключается в течении 15-ти минут (нравится мне это количество, что уж тут, но можно и 25 :) ) - выключить принудительно и дать аварийную сигнализацию.
По моим соображениям, у твоей системы всего три основных состояния.
Ну и кучка дополнительных.
Опять-таки, на мой взгляд, получается два таймера, два уровнемера.
И еще на схеме забыл вставить запуск таймера по заполнению бака.
Как-то так.
Я тоже бросился было сначала код писать, но потом докумекал, что лучше сначала общий алгоритм прописать. Псоле ряда неудачных попыток остановился на опции "Фигуры горизонтальной функциональной блок-схемы" из меню Бизнес-процессы.
Как-то так.
единственное - картинка очень маленького разрешения, практически ничего не видно, кинь если не сложно на почту bob_shmidt ну там значек собаки и это все на mail.ru
про логику, дык ты посмотри на предыдущей странице темы у нас с Andy был целый эпос начиная с этого сообщения и с блок схемами и т.д. и т.п.
причесал тему слегка.
движок форума, конечно, не совсем мне понятен. некоторые мои посты я не могу отредактировать, нет такой "кнопки" (видимо это просиходит, если не просто написать новый пост, а нажатьь "ответить")
не понимаю, как удалить отдельный пост, приходится писать "заглушки"
очень жалко, что нет механизма личных сообщений...
Это конечно, смешно, но прошло больше, чем год
Когда я сел вспоминать, что я там наваял столько времени спустя, даже учитывая мою почти параноидальную склонность комментировать очевидные вещи, я с трудом справился
Поскольку задача передо мной сейчас была сугубо практическая - таки запустить автоматику, я решил оставить в стороне всякие излишества, типа дисплея (через неделю потеряшек в менюшках, отложил), типа часов реального времени (китайский шедевр при отключении упорно выдавал 0,6в с рабочей батарейки с ожидаемым сбросом времени), записи на SD (я уже и не вспомнил, что я таки хотел записывать )
Итак, вчера я все запустил. Работает. Вместо дисплея - 7 led-ов. Но, конечно, есть ряд нареканий и поводов к улучшению.
1. Логика кода была так жестко завязана на состояние поплавков в жесткой связке с светодиодами, что попытки поменять логику "мигания" подвела меня к необходимости вообще пересмотреть сам принцип сигнализации состояний. Иначе ничего не выходит.
2. При первом включении системы по умолчанию запускается полный цикл с прокачкой скважины и т.д. Но состояние поплавков считывается уже после того, как проходит цикл анализа состояния - в результате, даже если бак полный, полный цикл стартует с риском перелива (сегодня проверю, так ли это). Но почему и где это происходит - загадка. В своем же коде не могу разобраться
Попробую вообще с чистого листа начать.
Ну и плюшки на будущее.
1. Я хотел бы все-таки прикрутить сдвиговый регистр на вход (на выход уже работает). Пока не успешно.
2. Считывать состояние вентиля (там есть концевики), убрав один таймер.
3. Есть потребность летом один раз в сутки по любому прокачивать скважину, наполняя технический пруд.
4. Заказал на Али ультрасоник, очень уж хочется знать точно, сколько воды в баке. А то я его в пенал из ЭППС запаковал (иначе конденсатом заливает пол жестко) - ну и ничего не видно.
5. Дисплей, часы и SD
На память себе код
/*
WaterSystem.cpp
Created by Angbor, January 1, 2016
Released into the public domain
*/
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <WaterSystem.h>
WaterTimer::WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe){
timerName = tName;
timerType = tTipe;
withFunction = withFunc;
ptrOnFuniction = f;
timerLength = tLength;
timerIsOn = false;
previousMillis = 0;
}
WaterTimer::WaterTimer(long tLength, byte withFunc, String tName, String tTipe){
timerName = tName;
timerType = tTipe;
withFunction = withFunc;
timerLength = tLength;
timerIsOn = false;
previousMillis = 0;
}
void WaterTimer::timerOn(unsigned long currentMillis) {
timerOverflow = false;
previousMillis = currentMillis;
timerIsOn = true;
//Serial.print(timerName);
//Serial.println(" ON (msg from timer)");
}
void WaterTimer :: timerOff() {
timerIsOn = false;
//Serial.print(timerName);
//Serial.println(" OFF (msg from timer)");
}
bool WaterTimer :: getIsTimerOn(unsigned long currentMillis){
if ((timerIsOn) && (currentMillis - previousMillis >= timerLength))
{
if (timerType == "loop") {
if (withFunction == 1) {
ptrOnFuniction();
}
previousMillis = currentMillis;
}
if (timerType == "line"){
//Serial.println("overflow (msg from timer)");
timerOverflow = true;
timerOff();
}
return true;
}
else {
return false;
}
}
/*
WaterSystem.h
Created by Angbor, January 1, 2016
Released into the public domain
*/
#ifndef WaterSystem_h
#define WaterSystem_h
#include "Arduino.h"
class WaterTimer
{
private:
long timerLength; // длительность таймера
unsigned long previousMillis;
String timerName = "nill";
void (*ptrOnFuniction)();
byte withFunction = 0;
public:
WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe);
WaterTimer(long tLength, byte withFunc, String tName, String tTipe);
void timerOn(unsigned long currentMillis);
void timerOff();
bool getIsTimerOn(unsigned long currentMillis);
bool timerOverflow = false;
String timerType = "loop"; // or "line"
bool timerIsOn = false; // Текущее состояние On или Off
};
#endif
#include <WaterSystem.h>
// определяем пины для переключателей
#define BOTTOM_SWITCH 2
#define MIDDLE_SWITCH 3
#define TOP_SWITCH 4
#define OVERFLOW_SWITCH 5
//=== замедления циклов ======
#define READ_PIN_DELAY 100
#define REGISTR_DELAY 150
#define BLINK_DELAY 300
//=== реле ======
#define PUMP 9
#define PUMP_ON 1
#define PUMP_OFF 0
#define THREE_WAY_VALWE 10
#define TWV_ON 1
#define TWV_OFF 0
//потом надо перевести это дело как-то в часы и минуты (соответственно таймеру). сейчас пока в милисекундах
#define DOWNTIME 10800000// 3 часа, таймер 3H время простоя, после которого обязательна прокачка.
#define TWV_DELAY 1200000// 20 мин., таймер 15M время прокачки
#define TWV_OPENING 20000// 20 сек., таймер 1M время на открытие вентиля (потом надо заменить это на чтение концевика 3W
// прототипы и укзатели функций
void blinkS();
void pinReading();
void regWork();
void (*ptrBlinkS)() = blinkS;
void (*ptrPinReading)() = pinReading;
void (*ptrRegWork)() = regWork;
// -- таймеры ---------------
#define WITH_FUNCTION 1
#define WITHOUT_FUNCTION 0
WaterTimer timerBlink(BLINK_DELAY, ptrBlinkS, WITH_FUNCTION, "BLINK_DELAY", "loop");
WaterTimer timerPinRead(READ_PIN_DELAY, ptrPinReading, WITH_FUNCTION, "READ_PIN_DELAY", "loop");
WaterTimer timerRegistrDelay(REGISTR_DELAY, ptrRegWork, WITH_FUNCTION, "REGISTR_DELAY", "loop");
WaterTimer timer3H(DOWNTIME, WITHOUT_FUNCTION, "3H", "line");
WaterTimer timer1M(TWV_OPENING, WITHOUT_FUNCTION, "1M", "line");
WaterTimer timer15M(TWV_DELAY, WITHOUT_FUNCTION, "15M", "line");
bool fPumpIsOn = false;
bool f3wIsOpen = false;
bool firstSTart = true; // отслеживание первого включения платы и изменение флага в 3-х часовом таймере
// описание состояний
#define TANK_IS_EMPTY 0
#define TANK_LESS_THAN_A_HALF 1
#define TANK_MORE_THAN_A_HALF 2
#define TANK_IS_FULL 3
#define WRONG_SENSOR_COMB 4
#define TANK_OVERFLOW 5
#define SENSORS_IS_OK 6 // заглушка для функции проверки аварии, когда в прошлом вызове была авария, а сейчас нет и еще нет сигнала от сенсоров, чтобы переписать состояние
byte fSystemState = 0;
// ====== для цикла чтения пинов
#define NUM 4
#define GET(b) digitalRead(arr[b])
const byte arr[] = {BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
// устанавливаем управление сдвиговым регистром
const int latchPin = 8;
const int clockPin = 12;
const int dataPin = 11;
uint8_t ledsBurn = 0; //переменная для передачи в регистр
// =========================
byte switchState[] = {0, 0, 0, 0}; // состояние переключателей
byte lastSwitchState[] = {0, 0, 0, 0}; // предыдущее состояние
byte blinkF[] = {1, 0, 0, 1}; /*флаги для включения blink, здесь указываем по умолчанию какие диоды будут мигать (1),
а какие гореть постоянно (0)*/
byte ledS[] = {B00000001, B00000010, B00000100, B00001000}; //значения, соответствующие включенному LED
/*================================================
=============== SETUP ==========================
================================================
*/
void setup() {
{
// Timer0 уже используется функцией millis()
// чтобы не мешать, прерывемся примерно посередине и вызываем ниже функцию "Compare A"
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
}
/*Serial.begin(9600);
while (!Serial) {
}*/
unsigned long currentMillis = millis();
timerBlink.timerOn(currentMillis);
timerPinRead.timerOn(currentMillis);
timerRegistrDelay.timerOn(currentMillis);
timer3H.timerOn(currentMillis);
timer3H.timerOff();
// настройка сдвигового регистра
pinMode(latchPin, OUTPUT);//8
pinMode(clockPin, OUTPUT);//12
pinMode(dataPin, OUTPUT);//11
// инициализация реле
pinMode(PUMP, OUTPUT);//9
pinMode(THREE_WAY_VALWE, OUTPUT);//10
for (byte i = 0; i < NUM; i++) {// 2,3,4,5
pinMode(GET(i), INPUT);
}
ledsBurn = ledsBurn ^ B10000000; // включаем диод "питание ON"
}
/*================================================
=============== FUNCTIONS =====================
================================================
*/
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
timerBlink.getIsTimerOn(currentMillis);
timerPinRead.getIsTimerOn(currentMillis);
timerRegistrDelay.getIsTimerOn(currentMillis);
timer3H.getIsTimerOn(currentMillis);
timer1M.getIsTimerOn(currentMillis);
timer15M.getIsTimerOn(currentMillis);
}
void blinkS() { // отвечает за мигание
for (byte i = 0; i < NUM /*4*/ ; i++) {
if (blinkF[i] == 1 && switchState[i] == 1) {
ledsBurn = ledsBurn ^ ledS[i];
}
}
}
void systemStateChanging (byte inInt) {
static byte st = 0;
switch (st)
{
case 0:
if ( fSystemState == TANK_IS_EMPTY && !timer3H.timerOverflow && !firstSTart)
/*бак пустой полностью && 3ч не истек и это не первое включение платы*/
{
st = 1;
timer3H.timerOff();
pumpOnOff(PUMP_ON);
}
if ((fSystemState == TANK_IS_EMPTY && timer3H.timerOverflow) || firstSTart)
/*бак пустой полностью && 3ч истек*/
{
st = 2;
firstSTart = false;
timer3H.timerOff();
threeWayOnOff(TWV_ON);
timer1M.timerOn(millis());
}
if (caseOfEmergency ()) {
st = 4;
}
break;
//==============================================
case 1:
if (fSystemState == TANK_IS_FULL) {
/*бак полный*/
st = 0;
pumpOnOff(PUMP_OFF);
timer3H.timerOn(millis());
}
if (caseOfEmergency ()) {
st = 4;
}
break;
//==============================================
case 2:
if (timer1M.timerOverflow) {
/*таймер 1М истек*/
st = 3;
timer15M.timerOn(millis());
pumpOnOff(PUMP_ON);
}
if (caseOfEmergency ()) {
st = 4;
}
break;
//==============================================
case 3:
if (timer15M.timerOverflow) {
/*таймер 15м истек*/
st = 1;
threeWayOnOff(TWV_OFF);
}
if (caseOfEmergency ()) {
st = 4;
}
break;
//==============================================
case 4:
if (inInt == 0/*сброс*/) {
st = 0;
// просто переход в обычное состояние
}
break;
}
}
bool caseOfEmergency () {
if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) {
timer1M.timerOff();
timer15M.timerOff();
timer3H.timerOn(millis());
threeWayOnOff(TWV_OFF);
pumpOnOff(PUMP_OFF);
if (fSystemState == WRONG_SENSOR_COMB) {
/*неверная комбинация датчиков*/
return true;
}
if (fSystemState == TANK_OVERFLOW) {
/* перелив*/
return true;
}
}
else return false;
}
void pumpOnOff (int onOrOff) {
if ((onOrOff == 1) && !fPumpIsOn) {
digitalWrite(PUMP, PUMP_ON);
ledsBurn = ledsBurn ^ B00010000;
fPumpIsOn = true;
}
if ((onOrOff == 0) && fPumpIsOn) {
digitalWrite(PUMP, PUMP_OFF);
ledsBurn = ledsBurn ^ B00010000;
fPumpIsOn = false;
}
}
void threeWayOnOff (int onOrOff) {
if ((onOrOff == 1) && !f3wIsOpen) {
digitalWrite(THREE_WAY_VALWE, TWV_ON);
ledsBurn = ledsBurn ^ B00100000;
f3wIsOpen = true;
}
if ((onOrOff == 0) && f3wIsOpen) {
digitalWrite(THREE_WAY_VALWE, TWV_OFF);
ledsBurn = ledsBurn ^ B00100000;
f3wIsOpen = false;
}
}
void sensorsRead() {
if (checkSwitchesCombination()) {
if ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) {
fSystemState = TANK_IS_EMPTY;
}
else if ((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) {
fSystemState = TANK_LESS_THAN_A_HALF;
}
else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0)) {
fSystemState = TANK_MORE_THAN_A_HALF;
// в этом состоянии насос не включаем
}
else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) {
fSystemState = TANK_IS_FULL;
}
}
}
void led_switching (int switchNumber) {
if (switchState[switchNumber] == HIGH) {
bitWrite(ledsBurn, switchNumber, 1);
}
else {
if (switchState[switchNumber] == LOW) {
bitWrite(ledsBurn, switchNumber, 0);
}
}
}
bool checkSwitchesCombination() {
if (switchState[3] == 1) { // если переполнение
if (fSystemState != TANK_OVERFLOW) {
fSystemState = TANK_OVERFLOW;
}
return false;
}
else if (((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) ||
((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) ||
((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) ||
((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0))) {
if (fSystemState != WRONG_SENSOR_COMB) {
fSystemState = WRONG_SENSOR_COMB;
}
return false;
}
else { // если все ОК
if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) {// если в прошлом вызове была аварийная ситуация
fSystemState = SENSORS_IS_OK;
systemStateChanging(0);// сброс
}
return true;
}
}
void regWork() {
// использует подготовленную другими функциями перменную ledsBurn
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, ledsBurn);
digitalWrite(latchPin, HIGH);
}
void pinReading() {
for (byte i = 0; i < NUM; i++) {
switchState[i] = GET(i);
if (switchState[i] != lastSwitchState[i]) {
led_switching(i);
sensorsRead();
}
lastSwitchState[i] = switchState[i];//обновляем значение последнего состояния переключателя
systemStateChanging(1);
}
}
void loop() {
}
Перелива нет, если после подачи напряжения с полным баком нчинается полный цикл, то сначала идет прокачка на улицу, потом в дом и сразу же срабатывает отключение по верхнему поплавку.
Так что пока полет нормальный, всем доволен.
Не уверен, что в ближайшее время полезу что-то там менять. И так дел куча :)
Прошу прощения что вторгаюсь в Вашу тему но хочю предложить варианты работы насоса (у меня он работает в колодце с малым дебетом воды) без ардуино стоимомтью 50 центов а дальше( заисит только от потребляеьой нагрузки) в файле насос есть две схемы одна на реле вторая на оптопарах работа схеты на реле при включении устройства если верхний датчик находится в воде (7) ток через открытый транзистор включает реле и замыкает ево же контакты так как контакты нижнего датчика (8) находятся в воде ток через контакты реле самоблокирует реле до тех пор пока вода не опустится ниже контактов нижнего уровня. Цепь разомкнется и будет ждать контакта на верхнем уровне (схема представлена для электродных датчиков (я использовал стержни от батареек так как они наименее подвержны электролизу при постоянном токе) На схеме обозначено одно реле с одной групой контактов. Для комутации высокого напряжения необходимо второе реле которе подключается паралельно первому или если применять одно то с двумя группами контактов Цена только от стоимости реле На второй схеме практически тот же принцип но с использованием оптопар Я использовал самую рпспространенную РС817 и МОС30,,,, причем МОС30.... это уже управление нагрузкой (его также можно заменить на реле или с его выводов включив симистор управлять более мошной нагрузкой). И цена ниже и габариты Транзисторы могут преминятся любые на напряжение источника питания я применял КТ315В Г также о второй схеме необжодимо увеличить сопротивление резисторов при увелечении напряжения
все до сих пор работает. Из реально актуальных вопросов к доработке только один - таймер по началу закачивания бака. И то только потому, что сделал переключение в ручной режим и вентиль, переводящий вывод насоса на поли. И этот самый вентиль в котельной :/
Окрываешь вентиль на полив, включаешь вручную насос - пошел поливать. Устал, выключил насос, забыл перекрыть вентиль. Включилась автоматика - в котельной все поплыло...
Но сейчас я занялся автоматической кормушкой для кур, это поактуальнее, позже выложу проектик.
Спустя 4 годя я дозрел-таки до переделки автоматики.
Основные задачи (на память):
1. Все временные интервалы должны быть настраиваемые а не записаны намертво.
2. Добавить обработку концевых замыкателей 3-х ходового вентиля
3. Таки дисплей
4. RTC DS3231
5. Добавить связь по nRF24L01 с контрльным модулем на жилом этаже (чтобы каждый раз когда вдруг не идет вода из крана, не бежать в котельную)
Может причина в том, что цикл начинается из состояния 0? В состоянии 0 предполагается, что таймер 3ч уже запущен. Надо либо запускать таймер в setup(), либо начинать цикл из состояния 1.
убрал пост, новый код ниже
убрал пост, новый код ниже
отлично... начал смотреть код библиотеки и нашел я ошибку - я проверяю включен таймер или нет, а надо проверять, был ли он переполнен или нет
т.е. вот так надо
case 0: if ( ((fSystemState == TANK_IS_EMPTY) || (fSystemState == TANK_LESS_THAN_A_HALF)) && (!timer3H.timerOverflow)) /*бак пустой полностью || уровень ниже половины) && 3ч не истек*/ { st = 1; Serial.print("PUMP. st = "); Serial.println (st); timer3H.timerOff();//останавливаем 3ч таймер; //включаем насос; } if ( ((fSystemState == TANK_IS_EMPTY) || (fSystemState == TANK_LESS_THAN_A_HALF)) && (timer3H.timerOverflow)) /*(бак пустой полностью || уровень ниже половины) && 3ч истек*/ { st = 2; Serial.print("3W opening. st = "); Serial.println (st); timer3H.timerOff();// останавливаем 3ч таймер; //открываем клапан; timer1M.timerOn(millis());// запускам таймер 1м; }Ну, вроде все работает.
Пойду почитаю про классы (чего в public, а чего нет), попробую-таки функции вынести в библиотеку (пока безуспешно) и потом поприкручиваю дисплейчик :)
/* WaterSystem.h Created by Angbor, January 1, 2016 Released into the public domain */ #ifndef WaterSystem_h #define WaterSystem_h #include "Arduino.h" class WaterTimer { private: long timerLength; // длительность таймера unsigned long previousMillis; String timerName = "nill"; void (*ptrOnFuniction)(); byte withFunction = 0; public: WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe); WaterTimer(long tLength, byte withFunc, String tName, String tTipe); void timerOn(unsigned long currentMillis); void timerOff(); bool getIsTimerOn(unsigned long currentMillis); bool timerOverflow = false; String timerType = "loop"; // or "line" bool timerIsOn = false; // Текущее состояние On или Off }; #endif/* WaterSystem.cpp Created by Angbor, January 1, 2016 Released into the public domain */ #include <Arduino.h> #include <WaterSystem.h> WaterTimer::WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe){ timerName = tName; timerType = tTipe; withFunction = withFunc; ptrOnFuniction = f; timerLength = tLength; timerIsOn = false; previousMillis = 0; } WaterTimer::WaterTimer(long tLength, byte withFunc, String tName, String tTipe){ timerName = tName; timerType = tTipe; withFunction = withFunc; timerLength = tLength; timerIsOn = false; previousMillis = 0; } void WaterTimer::timerOn(unsigned long currentMillis) { timerOverflow = false; previousMillis = currentMillis; timerIsOn = true; Serial.print(timerName); Serial.println(" ON"); } void WaterTimer :: timerOff() { timerIsOn = false; Serial.print(timerName); Serial.println(" OFF"); } bool WaterTimer :: getIsTimerOn(unsigned long currentMillis){ if ((timerIsOn) && (currentMillis - previousMillis >= timerLength)) { if (timerType == "loop") { if (withFunction == 1) { ptrOnFuniction(); } previousMillis = currentMillis; } if (timerType == "line"){ Serial.print(timerName); Serial.println(" overflow"); timerOverflow = true; timerOff(); } return true; } else { return false; } }#include <WaterSystem.h> // определяем пины для переключателей #define BOTTOM_SWITCH 2 #define MIDDLE_SWITCH 3 #define TOP_SWITCH 4 #define OVERFLOW_SWITCH 5 //=== замедления циклов ====== #define READ_PIN_DELAY 100 #define REGISTR_DELAY 150 #define BLINK_DELAY 300 //=== реле ====== #define PUMP 9 #define PUMP_ON 1 #define PUMP_OFF 0 #define THREE_WAY_VALWE 10 #define TWV_ON 1 #define TWV_OFF 0 //потом надо перевести это дело как-то в часы и минуты (соответственно таймеру). сейчас пока в милисекундах #define DOWNTIME 5000 // 3H время простоя, после которого обязательна прокачка. #define TWV_DELAY 3000 // 15M время прокачки #define TWV_OPENING 2000 // 1M время на открытие вентиля (потом надо заменить это на чтение концевика 3W // прототипы и укзатели функций void blinkS(); void pinReading(); void regWork(); void (*ptrBlinkS)() = blinkS; void (*ptrPinReading)() = pinReading; void (*ptrRegWork)() = regWork; // -- таймеры для снижения частоты смены состояний --------------- #define WITH_FUNCTION 1 #define WITHOUT_FUNCTION 0 WaterTimer timerBlink(BLINK_DELAY, ptrBlinkS, WITH_FUNCTION, "BLINK_DELAY", "loop"); WaterTimer timerPinRead(READ_PIN_DELAY, ptrPinReading, WITH_FUNCTION, "READ_PIN_DELAY", "loop"); WaterTimer timerRegistrDelay(REGISTR_DELAY, ptrRegWork, WITH_FUNCTION, "REGISTR_DELAY", "loop"); WaterTimer timer3H(DOWNTIME, WITHOUT_FUNCTION, "3H", "line"); WaterTimer timer1M(TWV_OPENING, WITHOUT_FUNCTION, "1M", "line"); WaterTimer timer15M(TWV_DELAY, WITHOUT_FUNCTION, "15M", "line"); bool fPumpIsOn = false; bool f3wIsOpen = false; // описание состояний #define TANK_IS_EMPTY 0 #define TANK_LESS_THAN_A_HALF 1 #define TANK_MORE_THAN_A_HALF 2 #define TANK_IS_FULL 3 #define WRONG_SENSOR_COMB 4 #define TANK_OVERFLOW 5 #define SENSORS_IS_OK 6 // заглушка для функции проверки аварии, когда в прошлом вызове была авария, а сейчас нет и еще нет сигнала от сенсоров, чтобы переписать состояние byte fSystemState = 0; String systemStateName[] = {"EMPTY", "LESS THEN A HALF", "MORE THAN A HALF", "FULL", "WRONG SENSORS", "OVERFLOW", "RESET SENSORS"}; // ====== для цикла чтения пинов #define NUM 4 #define GET(b) digitalRead(arr[b]) const int arr[] = {BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH}; // устанавливаем управление сдвиговым регистром const int latchPin = 8; const int clockPin = 12; const int dataPin = 11; uint8_t ledsBurn = 0; //переменная для передачи в регистр // ========================= byte switchState[] = {1, 0, 0, 0}; // состояние переключателей byte lastSwitchState[] = {0, 0, 0, 0}; // предыдущее состояние byte blinkF[] = {1, 0, 0, 1}; /*флаги для включения blink, здесь указываем по умолчанию какие диоды будут мигать (1), а какие гореть постоянно (0)*/ String ledSName[] = {"Bottom switch", "Middle switch", "Top switch", "OverFlow switch"}; byte ledS[] = {B00000001, B00000010, B00000100, B00001000}; //значения, соответствующие включенному LED /*================================================ ================================================ ================================================ =============== SETUP ========================== ================================================ ================================================ ================================================ */ void setup() { { // Timer0 уже используется функцией millis() // чтобы не мешать, прерывемся примерно посередине и вызываем ниже функцию "Compare A" OCR0A = 0xAF; TIMSK0 |= _BV(OCIE0A); } unsigned long currentMillis = millis(); timerBlink.timerOn(currentMillis); timerPinRead.timerOn(currentMillis); timerRegistrDelay.timerOn(currentMillis); timer3H.timerOn(currentMillis); // настройка сдвигового регистра pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); // инициализация реле pinMode(PUMP, OUTPUT); pinMode(THREE_WAY_VALWE, OUTPUT); // инициализация "кнопок" for (byte i = 0; i < NUM; i++) { pinMode(GET(i), INPUT); } Serial.begin(9600); while (!Serial) { } } /*================================================ ================================================ ================================================ =============== FUNCTIONS ===================== ================================================ ================================================ ================================================ */ SIGNAL(TIMER0_COMPA_vect) { unsigned long currentMillis = millis(); timerBlink.getIsTimerOn(currentMillis); timerPinRead.getIsTimerOn(currentMillis); timerRegistrDelay.getIsTimerOn(currentMillis); timer3H.getIsTimerOn(currentMillis); timer1M.getIsTimerOn(currentMillis); timer15M.getIsTimerOn(currentMillis); } void blinkS() { for (int i = 0; i < 4 ; i++) { if ((blinkF[i] == 1) && (switchState[i]) == 1) { ledsBurn = ledsBurn ^ ledS[i]; } } } void systemStateChanging (byte inInt) { static byte st = 0; switch (st) { case 0: if ( ((fSystemState == TANK_IS_EMPTY) || (fSystemState == TANK_LESS_THAN_A_HALF)) && (!timer3H.timerOverflow)) /*бак пустой полностью || уровень ниже половины) && 3ч не истек*/ { st = 1; Serial.println("PUMP"); timer3H.timerOff(); pumpOnOff(PUMP_ON); } if ( ((fSystemState == TANK_IS_EMPTY) || (fSystemState == TANK_LESS_THAN_A_HALF)) && (timer3H.timerOverflow)) /*(бак пустой полностью || уровень ниже половины) && 3ч истек*/ { st = 2; Serial.println("3W opening"); timer3H.timerOff(); threeWayOnOff(TWV_ON); timer1M.timerOn(millis()); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 1: if (fSystemState == TANK_IS_FULL) { /*бак полный*/ st = 0; Serial.println("FULL"); pumpOnOff(PUMP_OFF); timer3H.timerOn(millis()); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 2: if (timer1M.timerOverflow) { /*таймер 1М истек*/ st = 3; Serial.println("3w is open start PUMP OUT"); timer15M.timerOn(millis()); pumpOnOff(PUMP_ON); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 3: if (timer15M.timerOverflow) { /*таймер 15м истек*/ st = 1; Serial.println("3w close start pump IN"); threeWayOnOff(TWV_OFF); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 4: if (inInt == 0/*сброс*/) { st = 0; Serial.println("DROP"); // просто переход в обчныое состояние } break; } } bool caseOfEmergency () { if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) { timer1M.timerOff(); timer15M.timerOff(); timer3H.timerOn(millis()); threeWayOnOff(TWV_OFF); pumpOnOff(PUMP_OFF); if (fSystemState == WRONG_SENSOR_COMB) { /*неверная комбинация датчиков*/ Serial.println("WRONG COMB"); return true; } if (fSystemState == TANK_OVERFLOW) { /* перелив*/ Serial.println("OVERFLOW"); return true; } } else return false; } void pumpOnOff (int onOrOff) { if ((onOrOff == 1) && !fPumpIsOn) { digitalWrite(PUMP, PUMP_ON); fPumpIsOn = true; Serial.println("PUMP IS ON"); } if ((onOrOff == 0) && fPumpIsOn) { digitalWrite(PUMP, PUMP_OFF); fPumpIsOn = false; Serial.println("PUMP IS OFF"); } } void threeWayOnOff (int onOrOff) { if ((onOrOff == 1) && !f3wIsOpen) { digitalWrite(THREE_WAY_VALWE, TWV_ON); f3wIsOpen = true; Serial.println("3W IS ON"); } if ((onOrOff == 0) && f3wIsOpen) { digitalWrite(THREE_WAY_VALWE, TWV_OFF); f3wIsOpen = false; Serial.println("3W IS OFF"); } } void sensorsRead() { if (checkSwitchesCombination()) { if ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) { fSystemState = TANK_IS_EMPTY; // Serial.println("EMPTY"); } else if ((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) { // Serial.println("LESS THAN A HALF"); fSystemState = TANK_LESS_THAN_A_HALF; } else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0)) { fSystemState = TANK_MORE_THAN_A_HALF; // Serial.println("MORE THAN A HALF"); // в этом состоянии насос не включаем } else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) { fSystemState = TANK_IS_FULL; // Serial.println("FULL"); } } } void led_switching (int switchNumber) { if (switchState[switchNumber] == HIGH) { bitWrite(ledsBurn, switchNumber, 1); } else { if (switchState[switchNumber] == LOW) { bitWrite(ledsBurn, switchNumber, 0); } } } bool checkSwitchesCombination() { if (switchState[3] == 1) { // если переполнение if (fSystemState != TANK_OVERFLOW) { fSystemState = TANK_OVERFLOW; } return false; } else if (((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) || ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) || ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) || ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0))) { // Serial.println("WRONG COMBINATION"); if (fSystemState != WRONG_SENSOR_COMB) { fSystemState = WRONG_SENSOR_COMB; } return false; } else { // если все ОК if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) {// если в прошлом вызове была аварийная ситуация fSystemState = SENSORS_IS_OK; systemStateChanging(0);// сброс } return true; } } void regWork() { // использует подготовленную другими функциями перменную ledsBurn digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, ledsBurn); digitalWrite(latchPin, HIGH); } void pinReading() { for (byte i = 0; i < NUM; i++) { switchState[i] = GET(i); if (switchState[i] != lastSwitchState[i]) { led_switching(i); sensorsRead(); } lastSwitchState[i] = switchState[i];//обновляем значение последнего состояния переключателя systemStateChanging(1); } } void loop() { while (Serial.available() > 0) { byte sensors = Serial.parseInt(); if (Serial.read() == '\n') { if (sensors == 9) { // } } } }Привет!
Внимательно просмотрел проект. Как-то сложноват он мне показался :(
Я бы не стал ждать, когда воды останется на 15 мин, а, допустим, постоянно подпитывал основной бак при снижении уровня примерно менее 50% V. Не знаю как у вас с электричеством, но пока оно есть, лучше заполнить бак доверху.
Для контроля работоспособности насоса и переполнения бака я себе закупил (поставлю по весне) реле ПОтока. Иногда называется реле ПРОтока. Такие реле бывают и для сильно загрязненной среды. Гугл/Али в помощь.
Может, инфа пригодится.
Удачи.
Олег, и тебе привет. Спасибо за советы.
Про сложность... ну да, есть такой момент. Это я еще дисплей не прикрутил :) Шучу.
Про подкачку воды. У меня есть некоторая идея - лучше выкачать воду, а потом ее закачать, чем она будет постоянно в баке. Некоторый обмен, снижающий риск застоя. Но это теория. На практике - залив сверху, забор снизу. Так что это просто некоторое внутренее желание. Хочу, чтобы вода сначала почти вся ушла из бака, а потом залилась новая.
Перед отъездом на зиму я уже погонял водопровод в ручном режиме. Т.е. бак наполнял вручную. Трехходовой вентиль включил, насос включил, 15 минут подождал, вентиль выключил, бак накачал, насос выключил :)
И вот мой опыт показывает, что при разборе воды из дома скорость наполнения емкости скваженным насосом такова, что не нужно никаких 15-ти минутных запасов воды. Вкачивает больше, чем разбирает. Так что нижний поплавок буду ставить сантиметрах в 10-15 ото дна.
Что касается электричества - бак в котельной в цоколе, без электричества насосная станция ничего по дому не раздаст, самотек невозможен (только то, что выдавид 100 литровый гидроаккумулятор). А если я генератор включаю, то какая разница? А если лефтричества нет долго, я его полюбому включу.
Относительно реле потока (или протока). Я не хочу дожидаться, пока вода пойдет в переливную трубу. Зачем? Поплавковый выключатель (продублированный на всякий случай) вполне себе вариант. А учитывая, что насос останавливается другим поплавком, то вообще предположить, что вода перельется - ну меньше 1%. А на этот процент как раз труба перелива.
Думаю, можно еще усложнить программу и прикрутить таймер, что если насос не выключается в течении 15-ти минут (нравится мне это количество, что уж тут, но можно и 25 :) ) - выключить принудительно и дать аварийную сигнализацию.
Навскидку такая схема получилась:
Вполне возможно,что я не совсем не прав.
PS. Картинка сделана в MS Visio,но какая-то серая :(
PPS. Не получается вставить pdf.
По моим соображениям, у твоей системы всего три основных состояния.
Ну и кучка дополнительных.
Опять-таки, на мой взгляд, получается два таймера, два уровнемера.
И еще на схеме забыл вставить запуск таймера по заполнению бака.
Как-то так.
Я тоже бросился было сначала код писать, но потом докумекал, что лучше сначала общий алгоритм прописать. Псоле ряда неудачных попыток остановился на опции "Фигуры горизонтальной функциональной блок-схемы" из меню Бизнес-процессы.
Как-то так.
круто! спасибо за участие
единственное - картинка очень маленького разрешения, практически ничего не видно, кинь если не сложно на почту bob_shmidt ну там значек собаки и это все на mail.ru
про логику, дык ты посмотри на предыдущей странице темы у нас с Andy был целый эпос начиная с этого сообщения и с блок схемами и т.д. и т.п.
Вроде, исходник картинки беру высокого качества, но где-то внутри оно пережимается :(
Сейчас скину на почту.
PS. Я всю вашу ветку смотрел.
Очень тяжело читать эту тему, много развернутого кода и неформатных картинок, пожалуйста прочитайте эти посты
Пост для кода, самый первый (как свернуть код и другое)
Пост для картинок (без потери качества изображений!!!!!) пост 18
причесал тему слегка.
движок форума, конечно, не совсем мне понятен. некоторые мои посты я не могу отредактировать, нет такой "кнопки" (видимо это просиходит, если не просто написать новый пост, а нажатьь "ответить")
не понимаю, как удалить отдельный пост, приходится писать "заглушки"
очень жалко, что нет механизма личных сообщений...
Это конечно, смешно, но прошло больше, чем год


)
Когда я сел вспоминать, что я там наваял столько времени спустя, даже учитывая мою почти параноидальную склонность комментировать очевидные вещи, я с трудом справился
Поскольку задача передо мной сейчас была сугубо практическая - таки запустить автоматику, я решил оставить в стороне всякие излишества, типа дисплея (через неделю потеряшек в менюшках, отложил), типа часов реального времени (китайский шедевр при отключении упорно выдавал 0,6в с рабочей батарейки с ожидаемым сбросом времени), записи на SD (я уже и не вспомнил, что я таки хотел записывать
Итак, вчера я все запустил. Работает. Вместо дисплея - 7 led-ов. Но, конечно, есть ряд нареканий и поводов к улучшению.
1. Логика кода была так жестко завязана на состояние поплавков в жесткой связке с светодиодами, что попытки поменять логику "мигания" подвела меня к необходимости вообще пересмотреть сам принцип сигнализации состояний. Иначе ничего не выходит.
2. При первом включении системы по умолчанию запускается полный цикл с прокачкой скважины и т.д. Но состояние поплавков считывается уже после того, как проходит цикл анализа состояния - в результате, даже если бак полный, полный цикл стартует с риском перелива (сегодня проверю, так ли это). Но почему и где это происходит - загадка. В своем же коде не могу разобраться
Попробую вообще с чистого листа начать.
Ну и плюшки на будущее.
1. Я хотел бы все-таки прикрутить сдвиговый регистр на вход (на выход уже работает). Пока не успешно.
2. Считывать состояние вентиля (там есть концевики), убрав один таймер.
3. Есть потребность летом один раз в сутки по любому прокачивать скважину, наполняя технический пруд.
4. Заказал на Али ультрасоник, очень уж хочется знать точно, сколько воды в баке. А то я его в пенал из ЭППС запаковал (иначе конденсатом заливает пол жестко) - ну и ничего не видно.
5. Дисплей, часы и SD
На память себе код
/* WaterSystem.cpp Created by Angbor, January 1, 2016 Released into the public domain */ #if defined(ARDUINO) && ARDUINO >= 100 #include "Arduino.h" #else #include "WProgram.h" #endif #include <WaterSystem.h> WaterTimer::WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe){ timerName = tName; timerType = tTipe; withFunction = withFunc; ptrOnFuniction = f; timerLength = tLength; timerIsOn = false; previousMillis = 0; } WaterTimer::WaterTimer(long tLength, byte withFunc, String tName, String tTipe){ timerName = tName; timerType = tTipe; withFunction = withFunc; timerLength = tLength; timerIsOn = false; previousMillis = 0; } void WaterTimer::timerOn(unsigned long currentMillis) { timerOverflow = false; previousMillis = currentMillis; timerIsOn = true; //Serial.print(timerName); //Serial.println(" ON (msg from timer)"); } void WaterTimer :: timerOff() { timerIsOn = false; //Serial.print(timerName); //Serial.println(" OFF (msg from timer)"); } bool WaterTimer :: getIsTimerOn(unsigned long currentMillis){ if ((timerIsOn) && (currentMillis - previousMillis >= timerLength)) { if (timerType == "loop") { if (withFunction == 1) { ptrOnFuniction(); } previousMillis = currentMillis; } if (timerType == "line"){ //Serial.println("overflow (msg from timer)"); timerOverflow = true; timerOff(); } return true; } else { return false; } }/* WaterSystem.h Created by Angbor, January 1, 2016 Released into the public domain */ #ifndef WaterSystem_h #define WaterSystem_h #include "Arduino.h" class WaterTimer { private: long timerLength; // длительность таймера unsigned long previousMillis; String timerName = "nill"; void (*ptrOnFuniction)(); byte withFunction = 0; public: WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe); WaterTimer(long tLength, byte withFunc, String tName, String tTipe); void timerOn(unsigned long currentMillis); void timerOff(); bool getIsTimerOn(unsigned long currentMillis); bool timerOverflow = false; String timerType = "loop"; // or "line" bool timerIsOn = false; // Текущее состояние On или Off }; #endif#include <WaterSystem.h> // определяем пины для переключателей #define BOTTOM_SWITCH 2 #define MIDDLE_SWITCH 3 #define TOP_SWITCH 4 #define OVERFLOW_SWITCH 5 //=== замедления циклов ====== #define READ_PIN_DELAY 100 #define REGISTR_DELAY 150 #define BLINK_DELAY 300 //=== реле ====== #define PUMP 9 #define PUMP_ON 1 #define PUMP_OFF 0 #define THREE_WAY_VALWE 10 #define TWV_ON 1 #define TWV_OFF 0 //потом надо перевести это дело как-то в часы и минуты (соответственно таймеру). сейчас пока в милисекундах #define DOWNTIME 10800000// 3 часа, таймер 3H время простоя, после которого обязательна прокачка. #define TWV_DELAY 1200000// 20 мин., таймер 15M время прокачки #define TWV_OPENING 20000// 20 сек., таймер 1M время на открытие вентиля (потом надо заменить это на чтение концевика 3W // прототипы и укзатели функций void blinkS(); void pinReading(); void regWork(); void (*ptrBlinkS)() = blinkS; void (*ptrPinReading)() = pinReading; void (*ptrRegWork)() = regWork; // -- таймеры --------------- #define WITH_FUNCTION 1 #define WITHOUT_FUNCTION 0 WaterTimer timerBlink(BLINK_DELAY, ptrBlinkS, WITH_FUNCTION, "BLINK_DELAY", "loop"); WaterTimer timerPinRead(READ_PIN_DELAY, ptrPinReading, WITH_FUNCTION, "READ_PIN_DELAY", "loop"); WaterTimer timerRegistrDelay(REGISTR_DELAY, ptrRegWork, WITH_FUNCTION, "REGISTR_DELAY", "loop"); WaterTimer timer3H(DOWNTIME, WITHOUT_FUNCTION, "3H", "line"); WaterTimer timer1M(TWV_OPENING, WITHOUT_FUNCTION, "1M", "line"); WaterTimer timer15M(TWV_DELAY, WITHOUT_FUNCTION, "15M", "line"); bool fPumpIsOn = false; bool f3wIsOpen = false; bool firstSTart = true; // отслеживание первого включения платы и изменение флага в 3-х часовом таймере // описание состояний #define TANK_IS_EMPTY 0 #define TANK_LESS_THAN_A_HALF 1 #define TANK_MORE_THAN_A_HALF 2 #define TANK_IS_FULL 3 #define WRONG_SENSOR_COMB 4 #define TANK_OVERFLOW 5 #define SENSORS_IS_OK 6 // заглушка для функции проверки аварии, когда в прошлом вызове была авария, а сейчас нет и еще нет сигнала от сенсоров, чтобы переписать состояние byte fSystemState = 0; // ====== для цикла чтения пинов #define NUM 4 #define GET(b) digitalRead(arr[b]) const byte arr[] = {BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH}; // устанавливаем управление сдвиговым регистром const int latchPin = 8; const int clockPin = 12; const int dataPin = 11; uint8_t ledsBurn = 0; //переменная для передачи в регистр // ========================= byte switchState[] = {0, 0, 0, 0}; // состояние переключателей byte lastSwitchState[] = {0, 0, 0, 0}; // предыдущее состояние byte blinkF[] = {1, 0, 0, 1}; /*флаги для включения blink, здесь указываем по умолчанию какие диоды будут мигать (1), а какие гореть постоянно (0)*/ byte ledS[] = {B00000001, B00000010, B00000100, B00001000}; //значения, соответствующие включенному LED /*================================================ =============== SETUP ========================== ================================================ */ void setup() { { // Timer0 уже используется функцией millis() // чтобы не мешать, прерывемся примерно посередине и вызываем ниже функцию "Compare A" OCR0A = 0xAF; TIMSK0 |= _BV(OCIE0A); } /*Serial.begin(9600); while (!Serial) { }*/ unsigned long currentMillis = millis(); timerBlink.timerOn(currentMillis); timerPinRead.timerOn(currentMillis); timerRegistrDelay.timerOn(currentMillis); timer3H.timerOn(currentMillis); timer3H.timerOff(); // настройка сдвигового регистра pinMode(latchPin, OUTPUT);//8 pinMode(clockPin, OUTPUT);//12 pinMode(dataPin, OUTPUT);//11 // инициализация реле pinMode(PUMP, OUTPUT);//9 pinMode(THREE_WAY_VALWE, OUTPUT);//10 for (byte i = 0; i < NUM; i++) {// 2,3,4,5 pinMode(GET(i), INPUT); } ledsBurn = ledsBurn ^ B10000000; // включаем диод "питание ON" } /*================================================ =============== FUNCTIONS ===================== ================================================ */ SIGNAL(TIMER0_COMPA_vect) { unsigned long currentMillis = millis(); timerBlink.getIsTimerOn(currentMillis); timerPinRead.getIsTimerOn(currentMillis); timerRegistrDelay.getIsTimerOn(currentMillis); timer3H.getIsTimerOn(currentMillis); timer1M.getIsTimerOn(currentMillis); timer15M.getIsTimerOn(currentMillis); } void blinkS() { // отвечает за мигание for (byte i = 0; i < NUM /*4*/ ; i++) { if (blinkF[i] == 1 && switchState[i] == 1) { ledsBurn = ledsBurn ^ ledS[i]; } } } void systemStateChanging (byte inInt) { static byte st = 0; switch (st) { case 0: if ( fSystemState == TANK_IS_EMPTY && !timer3H.timerOverflow && !firstSTart) /*бак пустой полностью && 3ч не истек и это не первое включение платы*/ { st = 1; timer3H.timerOff(); pumpOnOff(PUMP_ON); } if ((fSystemState == TANK_IS_EMPTY && timer3H.timerOverflow) || firstSTart) /*бак пустой полностью && 3ч истек*/ { st = 2; firstSTart = false; timer3H.timerOff(); threeWayOnOff(TWV_ON); timer1M.timerOn(millis()); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 1: if (fSystemState == TANK_IS_FULL) { /*бак полный*/ st = 0; pumpOnOff(PUMP_OFF); timer3H.timerOn(millis()); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 2: if (timer1M.timerOverflow) { /*таймер 1М истек*/ st = 3; timer15M.timerOn(millis()); pumpOnOff(PUMP_ON); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 3: if (timer15M.timerOverflow) { /*таймер 15м истек*/ st = 1; threeWayOnOff(TWV_OFF); } if (caseOfEmergency ()) { st = 4; } break; //============================================== case 4: if (inInt == 0/*сброс*/) { st = 0; // просто переход в обычное состояние } break; } } bool caseOfEmergency () { if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) { timer1M.timerOff(); timer15M.timerOff(); timer3H.timerOn(millis()); threeWayOnOff(TWV_OFF); pumpOnOff(PUMP_OFF); if (fSystemState == WRONG_SENSOR_COMB) { /*неверная комбинация датчиков*/ return true; } if (fSystemState == TANK_OVERFLOW) { /* перелив*/ return true; } } else return false; } void pumpOnOff (int onOrOff) { if ((onOrOff == 1) && !fPumpIsOn) { digitalWrite(PUMP, PUMP_ON); ledsBurn = ledsBurn ^ B00010000; fPumpIsOn = true; } if ((onOrOff == 0) && fPumpIsOn) { digitalWrite(PUMP, PUMP_OFF); ledsBurn = ledsBurn ^ B00010000; fPumpIsOn = false; } } void threeWayOnOff (int onOrOff) { if ((onOrOff == 1) && !f3wIsOpen) { digitalWrite(THREE_WAY_VALWE, TWV_ON); ledsBurn = ledsBurn ^ B00100000; f3wIsOpen = true; } if ((onOrOff == 0) && f3wIsOpen) { digitalWrite(THREE_WAY_VALWE, TWV_OFF); ledsBurn = ledsBurn ^ B00100000; f3wIsOpen = false; } } void sensorsRead() { if (checkSwitchesCombination()) { if ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) { fSystemState = TANK_IS_EMPTY; } else if ((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) { fSystemState = TANK_LESS_THAN_A_HALF; } else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0)) { fSystemState = TANK_MORE_THAN_A_HALF; // в этом состоянии насос не включаем } else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) { fSystemState = TANK_IS_FULL; } } } void led_switching (int switchNumber) { if (switchState[switchNumber] == HIGH) { bitWrite(ledsBurn, switchNumber, 1); } else { if (switchState[switchNumber] == LOW) { bitWrite(ledsBurn, switchNumber, 0); } } } bool checkSwitchesCombination() { if (switchState[3] == 1) { // если переполнение if (fSystemState != TANK_OVERFLOW) { fSystemState = TANK_OVERFLOW; } return false; } else if (((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) || ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) || ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) || ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0))) { if (fSystemState != WRONG_SENSOR_COMB) { fSystemState = WRONG_SENSOR_COMB; } return false; } else { // если все ОК if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) {// если в прошлом вызове была аварийная ситуация fSystemState = SENSORS_IS_OK; systemStateChanging(0);// сброс } return true; } } void regWork() { // использует подготовленную другими функциями перменную ledsBurn digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, ledsBurn); digitalWrite(latchPin, HIGH); } void pinReading() { for (byte i = 0; i < NUM; i++) { switchState[i] = GET(i); if (switchState[i] != lastSwitchState[i]) { led_switching(i); sensorsRead(); } lastSwitchState[i] = switchState[i];//обновляем значение последнего состояния переключателя systemStateChanging(1); } } void loop() { }В догонку.
Перелива нет, если после подачи напряжения с полным баком нчинается полный цикл, то сначала идет прокачка на улицу, потом в дом и сразу же срабатывает отключение по верхнему поплавку.
Так что пока полет нормальный, всем доволен.
Не уверен, что в ближайшее время полезу что-то там менять. И так дел куча :)
Прошу прощения что вторгаюсь в Вашу тему но хочю предложить варианты работы насоса (у меня он работает в колодце с малым дебетом воды) без ардуино стоимомтью 50 центов а дальше( заисит только от потребляеьой нагрузки) в файле насос есть две схемы одна на реле вторая на оптопарах работа схеты на реле при включении устройства если верхний датчик находится в воде (7) ток через открытый транзистор включает реле и замыкает ево же контакты так как контакты нижнего датчика (8) находятся в воде ток через контакты реле самоблокирует реле до тех пор пока вода не опустится ниже контактов нижнего уровня. Цепь разомкнется и будет ждать контакта на верхнем уровне (схема представлена для электродных датчиков (я использовал стержни от батареек так как они наименее подвержны электролизу при постоянном токе) На схеме обозначено одно реле с одной групой контактов. Для комутации высокого напряжения необходимо второе реле которе подключается паралельно первому или если применять одно то с двумя группами контактов Цена только от стоимости реле На второй схеме практически тот же принцип но с использованием оптопар Я использовал самую рпспространенную РС817 и МОС30,,,, причем МОС30.... это уже управление нагрузкой (его также можно заменить на реле или с его выводов включив симистор управлять более мошной нагрузкой). И цена ниже и габариты Транзисторы могут преминятся любые на напряжение источника питания я применял КТ315В Г также о второй схеме необжодимо увеличить сопротивление резисторов при увелечении напряжения
схемы и многое другое можнонайтм по ссылке
http://flprog.ru/forum/18-2165-2
базиба :)
как обычно, спустя год .... :)
все до сих пор работает. Из реально актуальных вопросов к доработке только один - таймер по началу закачивания бака. И то только потому, что сделал переключение в ручной режим и вентиль, переводящий вывод насоса на поли. И этот самый вентиль в котельной :/
Окрываешь вентиль на полив, включаешь вручную насос - пошел поливать. Устал, выключил насос, забыл перекрыть вентиль. Включилась автоматика - в котельной все поплыло...
Но сейчас я занялся автоматической кормушкой для кур, это поактуальнее, позже выложу проектик.
Спустя 4 годя я дозрел-таки до переделки автоматики.
Основные задачи (на память):
1. Все временные интервалы должны быть настраиваемые а не записаны намертво.
2. Добавить обработку концевых замыкателей 3-х ходового вентиля
3. Таки дисплей
4. RTC DS3231
5. Добавить связь по nRF24L01 с контрльным модулем на жилом этаже (чтобы каждый раз когда вдруг не идет вода из крана, не бежать в котельную)
Сейчас заканчиваю разводку новой платы в KiCAD