Помогите поправить программу для контроллера теплицы

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Всем привет. Начал с нуля делать минимальную автоматизацию теплицы. Организовал простое меню состоящее из трёх пунктов. Программист из меня ну никакой. Лепил из того что было, библиотеки от AlexGyver. Все почти работает. Но в память не сохраняет значения. Может кто объяснит, что делать
 

Вложения

bort707

★★★★★★✩
21 Сен 2020
3,058
910
хотел посмотреть код - а во вложении файл "программа txt". На всякий случай открывать не стал...
Я не знаю, что у вас там - но выкладывать надо скетч для ардуино с расширением .ino , а не какие-то тексты...
 

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
В скетче реализовано только чтение настроек из памяти и сброс настроек если зажать кнопку энкодера при подачи питания.
Чтобы сохранить настройки при их изменении, надо использовать функцию EEPROM.writeLong в нужном месте.
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
там вроде есть процедура update_EEPROM() вызываемая из меню в строке 355. Но глубже не вникал..
 
  • Лойс +1
Реакции: Roden

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Спасибо. Только нужно адрес поменять, потому что данные записываются друг на друга
 

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
@RamasTex, адреса там вроде как одинаковые (при загрузке и при записи)
 

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
void update_EEPROM() {
EEPROM.updateLong(8 * current_pump, period_time[current_pump]);
EEPROM.updateLong(8 * current_pump + 4, pumping_time[current_pump]);
}
Вот эта функция для записи в память. Почему умножаем на 8 переменную выбора насоса

Она же итак меняет своё значение когда мы делаем настройки
 

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
@RamasTex, почему на 8? Всё легко :) Для одного канала у нас 2 настройки типа long, то есть 4 байта, 2 умножаем на 4 получаем 8 байтов на один канал.
Этот множитель на 8 это сдвиг по байтам, первый канал имеет индекс 0, умножая на 8 получаем адрес 0 и 4, именно то что нам надо
 
  • Лойс +1
Реакции: RamasTex

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Спасибо:)Ну теперь я понял. Ок, тогда настройки освещения можно записать на 6 канал допустим, или 5. просто вместо переменной current_pump поставим число. Но всё равно не сохраняет. Да еще и подсветку дисплея отключает:LOL:
 

Вложения

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Всем привет. Я поправил программу. Подсветку не отключает. Решил заносить в память настройки температуры через адреса. Но почему-то Тmin и Tmax становятся одинаковыми. А настройки освещения не заносит в память. Вот последняя версия. Может кто объяснит в чем может быть дело
 

Вложения

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
Привет, открыл скетч и сразу заметил одну вещь из-за которой всё может не работать.
Смотри, вот твой код setup:
C++:
for (byte i = 0; i < PUPM_AMOUNT; i++) { // пробегаем по всем помпам
        period_time[i] = EEPROM.readLong(8 * i); // читаем данные из памяти. На чётных - период (ч)
        pumping_time[i] = EEPROM.readLong(8 * i + 4); // на нечётных - полив (с)
    }
    for (byte i = 4; i <= 4; i++) { // читаем данные записанные по адресу 5 канала
        period_timel[i] = EEPROM.readLong(8 * i); // читаем данные из памяти. На чётных - период освещения(ч)
        pumping_timel[i] = EEPROM.readLong(8 * i + 4); // на нечётных - работа освещения (с)
    }
    val1 = EEPROM.read(40);
    val2 = EEPROM.read(45);
}
В нём я выделил пару строк, в чём же дело спросишь ты? Смотри, в програмировании есть некоторые типы данных, отличаются они тем, сколько место в байтах выдаётся каждому типу.
Есть переменные типа byte, которые как следует из названия, занимают ровно один байт, есть тип int, он занимает два байта.
Для наглядности вот таблица примитивных типов и сколько места они занимают в памяти:
ТипОписаниеРазмер
boolИмеет два значения, true или false, да или нет, 1 или 0 и т.д1 байт
byte, char, uint8_t Может принимать значение от 0 до 255 (включая), годится для сохранения буквы (у каждой буквы по таблице ASCII значение от 0 до 255), яркости если яркость управляется в диапазоне от 0 до 255 и т.д1 байт
short, int, int16_tИспользуется когда надо сохранить значения чуть который не входят в диапазон 0 до 255.
Валидный диапазон значений: -32768 до 32767
2 байта
unsigned short, unsigned int, uint16_tТо же самое как и short и int, разница в том что переменные этого типа не могут иметь отрицательные значения.
То есть, диапазон значений: 0 до 65535
2 байта
long, int32_tВ переменную такого типа можно записывать большие числа, но и занимают они не мало так памяти.
Валидный диапазон значений: -2147483648 до 2147483647
4 байта
unsigned long, uint32_tТот же long только без отрицательных значений.
0 до 4294967295
4 байта
Найдя у тебя в коде переменную val1, я сразу пошёл смотреть какой у неё тип, оказалось что это int.
Открыл документацию к библиотеки EEPROMEx которая используется в скетче, функция read возвращает тип данных byte, то есть большинство значений ты просто не можешь получить из-за того что читаешь из памяти только часть данных.
Чтобы прочитать тип данных int, надо использовать функцию readInt (учти, что функция возвращает значения типа uint16_t, то есть значения типа unsigned int, отрицательные значения прочитать этой функцией ты не сможешь).
Вот список функций библиотеки:
1605896506357.png
Тоже самое и при изменении данных в памяти, увидел что ты обновляешь данные функцией update, которая также принимает byte, полагаю что туда просто сохраняется первый байт из числа, надо также update заменить на updateInt, и ещё раз заметь что функция принимает тип uint16_t и отрицательные значения ты записать не сможешь.

Вернёмся в функцию setup и посмотрим уже на другие строки
C++:
for (byte i = 4; i <= 4; i++) { // читаем данные записанные по адресу 5 канала
    period_timel[i] = EEPROM.readLong(8 * i); // читаем данные из памяти. На чётных - период освещения(ч)
    pumping_timel[i] = EEPROM.readLong(8 * i + 4); // на нечётных - работа освещения (с)
}
Мы тут видим цикл, который выполняется один раз с значением i = 4, цикл тут не нужен и просто так будет тратить время микроконтроллера на выполнение ненужного кода.
Куда проще заменить цикл на две строки:
C++:
period_timel[4] = EEPROM.readLong(8 * 4); // читаем данные из памяти. На чётных - период освещения(ч)
pumping_timel[4] = EEPROM.readLong(8 * 4 + 4); // на нечётных - работа освещения (с)
 
  • Лойс +1
Реакции: RamasTex

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Спасибо вам за помощь:) Замена функций действительно необходима. Теперь настройки температуры сохраняются в памяти. А мне отрицательные значения и не пригодятся) теплица же. Убрал цикл for. Но данные по освещению все равно не сохраняет. Менял номер с 4 на 5. тоже самое. После перезагрузки одни нули. Может память закончилась)Менял положение вызова функции void changeSetLIGHT()
 

Вложения

  • Лойс +1
Реакции: Roden

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
Попробуйте перед изменением данных в память выывести их в порт, посмотрите через компьютер, какое значение должно сохраниться, а также после загрузки этих данных из памяти желательно вывести в порт.
 

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Всем привет:) Продолжаю работать над программой небольшой автоматизации теплицы. Получилось полностью организовать меню. Данные заносятся в память при обновлении. Только остались некоторые моменты. Во вложении "Настройка температуры" и"Настройка свет" некорректно отображаются числа. Данное условие не помогло. Может кто знает решение:rolleyes:
C++:
 if (val3<10)
    {
    lcd.setCursor(4, 1);
    lcd.print(0);
    lcd.print(val3);
    }
    if (val4<10)
    {
    lcd.setCursor(12, 1);
    lcd.print(0);
    lcd.print(val4);
    }
 

Вложения

Изменено:

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Я исправил, но все-равно показывает дичь. Отображение некорректно должно быть 2 цифры вместо 3
C++:
 if (val3<10)
    {
    lcd.setCursor(4, 1);
    lcd.print(0);
    lcd.print(val3);
    }
    if(val3>10)
    {
     lcd.setCursor(4, 1);
     lcd.print(val3); 
    }
    if (val4<10)
    {
    lcd.setCursor(12, 1);
    lcd.print(0);
    lcd.print(val4); 
    }
     if(val4>10)
    {
     lcd.setCursor(4, 1);
     lcd.print(val4); 
    }
 

Вложения

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Вот для одного варианта все вот это:
C++:
if (val3<10)
    {
    lcd.setCursor(4, 1);
    lcd.print(0);
    lcd.print(val3);
    }
    if(val3>10)
    {
     lcd.setCursor(4, 1);
     lcd.print(val3);
    }
Заменить на это:
C++:
lcd.setCursor(4, 1);
if (val3<10) lcd.print(0);
lcd.print(val3);
 
  • Лойс +1
Реакции: RamasTex

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Странно, как только число меньше 10 то значения дублируются. 99 ;88;77;.... и т.д

А в памяти сохраняет нормально
 

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Вроде как работает, но когда число меньше 10 то, все наоборот 90;80;70. Пробую менять координаты отображения , чушь выходит. Так самый лучший вариант
C++:
enc1.tick();
lcd.setCursor(1, 1);
lcd.print("Tр=");
lcd.print(val3);
lcd.setCursor(9, 1);
lcd.print("Tп=");
lcd.print(val4);
lcd.setCursor(5, 0);
lcd.print("НАЗАД");
if (enc1.isTurn())
    {
int increment = 0;  // локальная переменная направления
    // получаем направление   
    if (enc1.isRight()) increment = 1;
    if (enc1.isLeft()) increment = -1;
    arrowPosLamp += increment;  // двигаем курсор
    arrowPosLamp = constrain(arrowPosLamp, 0, SETTINGS_AMOUNT_LAMP - 1); // ограничиваем
    increment = 0;  // обнуляем инкремент
    // изменение позиции стрелки и вывод данных
changeSetLamp();
// при нажатом повороте меняем переменные ++
    if (enc1.isRightH()) {
      switch (arrowPosLamp) {
        case 0:
        val3++;
        EEPROM. updateByte(60,val3); //записываем значения времени освещения в память
        break;
        case 1:
        val4++;
        EEPROM. updateByte(65,val4); //записываем значения времени освещения в память
        break;
      }
    }
     // при нажатом повороте меняем переменные --
    if (enc1.isLeftH()) {
       switch (arrowPosLamp) {
       case 0:
       val3--;
       EEPROM. updateByte(60,val3);
       break;
       case 1:
       val4--;
       EEPROM. updateByte(65,val4);
       break;
      }
    }
       lcd.setCursor(3, 1);
       if (val3<10) lcd.print(0);
       lcd.print(val3);
        lcd.setCursor(11, 1);
       if (val4<10) lcd.print(0);
       lcd.print(val4);
  }
 

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Всем привет:). После долгих мучений и бессонных ночей. Мне наконец-то удалось поправить программу. Всё более менее работает как надо. И думал уже финиш. Данные сохраняются в память, клапаны открываются по таймеру. Проект завершён, круть:). Но есть проблемка, когда находишься во вложении меню, то все работает. Как только выходишь в основное меню, перестают срабатывать клапаны по времени. И все останавливается. Как решить данную проблему? Я знаю, что нужно поставить функцию которая отвечает за включение клапанов по таймерам в цикл void loop() но как правильно это сделать не знаю. Вот скетч
 

Вложения

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
Привет 👋
Конструкция меню не совсем удобная и правильная,опрос клапанов происходит только на одном экране, в идеале надо было бы избавиться от цикла while, а так думаю что можно просто добавить вызов функций periodTickWAT и flowTickWAT в каждую функцию экрана (drawM....)
1606934499057.png
 

RamasTex

✩✩✩✩✩✩✩
11 Ноя 2020
23
3
Спасибо вам огроменное за помощь😊. Все начало работать как надо. Даже если электричество пропадет, и снова появится все сразу заработает. Не только в каждой функции, но и нужно drawMWAT(); дописать в void setup. А как иначе делать меню? Если не через wihile:unsure:. Неужели есть решения проще
 
  • Лойс +1
Реакции: Roden

Roden

✩✩✩✩✩✩✩
19 Апр 2019
31
5
@RamasTex, лучше всего использовать блоки свитч-кейс, если будет время постараюсь переделать скетч на них