Автор Тема: Продвинутый контроллер для гидропоники на Arduino Mega + Wemos  (Прочитано 1141 раз)

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
Ну, продвинутый - конечно громко сказано, просто чуть сложнее, чем простое клацанье релюхами и отображение температуры.
Решил я для баттла чуть переделать свой коллайдер, и поскольку там будет добавлено несколько плюх, требующих для своей реализации дополнительных пинов (а они уже кончились на Wemos), то решил сделать его на связке Arduino Mega + Wemos (как вариант, можно было на сдвиговых регистрах расширить пины), первая будет все замерять и управлять, второй - только взаимодествовать с пользователем и сервером для отображения информации и управления.
В принципе для меня здесь будет мало что нового, просто переделка старых наработок, но поскольку некоторые просили подробностей (в частности alex_step подобное тоже собирается делать), то постараюсь подробно описать все этапы.

Для совсем новичков сразу оговорюсь, что ардуиновский скетч можно условно разделить на 4 части:
А. Включение библиотек и объявление необходимых переменных.
В. Функция Setup. Выполняется один раз при включении.
С. Функция Loop. Выполняется после Setup бесконечно (как только выполнилась 1 раз, начинает выполняться заново).
D. Пользовательские функции.
Следовательно, если мы добавляем в скетч очередную плюху, то изменения нужно вносить во все 4 части, как правило. Следовательно и добавление плюх я тоже буду описывать исходя из этих частей: A, B, C, D.

Еще пару ремарок.
1. Названия переменных стараемся даватm такие, чтобы через 3 года глянув на скетч мы сразу же понимали, что делает эта переменная. Да, они могут быть длинными, но читабельность кода , особенно через прошедшее время, увеличивается значительно.
2. В Loop у меня всегда содержится только перечень вызываемых функций, чтоб глянув на него, сразу было понятно, что в программе происходит. Выглядит это так:

long data_last_time;
//========================================================
void setup() {
}
//========================================================
void loop() {
   Data(1000);                  //вызываю функцию Data с параметром, обозначающим промежуток времени в мс, через которые эта функция должна выполняться. В данном случае 1 раз в секунду.
}
//========================================================
void Data(float freq){
  if ((millis()-data_last_time)>freq){       
      //
      // здесь тело функции
      //
      data_last_time = millis();
  }
}

В общем это культура кода каждого конкретного кодера, я пришел к такой схеме, мне так удобней, кто-то делает по-другому, дело вкуса.

Пара слов о ТЗ:
1. Управление релюхами света (4-5 штук)
2. Управление твердотельным реле помпы (1 штука)
3. Климат (температура, влажность, давление, температура раствора)
4. Управление 12-вольтовым вентилятором вытяжки с ШИМ.
5. Подогрев раствора с ШИМ.
6. Отправка статистики на сервер
7. Отображение статистики в приятном глазу виде с графиками.
8. Управление через Веб-морду Вемоса.
9. Прога на Андроиде (опционально)

Итак, поехали.




« Последнее редактирование: 19 Ноябрь 2017, 16:07:39 от Ann »

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
Да-да-да!!!  :dance:
Ждём-с...
всё железо уже в ожидании обновления кода  ;)

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
Этап 1. Протокол общения между Arduino Mega и Wemos

Подключаем по Serial. У Меги их аж 4, я выбрал 1-й.
Rx Меги в Tx Вемоса, соответственно Tx Меги в Rx Вемоса.
Я подключаю напрямую, что есть неправильно! Поскольку Вемос работает на 3.3вольтах, а Мега на 5-ти, соответственно Вемос может сгореть от 5-ти вольт Меги.
Я это делаю на свой страх и риск, запасной Вемос у меня есть. По науке нужно использовать преобразователь логических уровней.
Либо Tx от Меги пускать через делитель напряжения, можно использовать переменный резистор (крайнюю ногу на Tx Меги, среднюю на Rx Вемоса, другую крайнюю на землю. Выкрутить так, чтоб между ногой Tx Меги и средней ногой было сопротивление 1/3 от общего сопротивления переменного резистора, т.е. между крайними ногами). Обратный канал преобразовывать необязательно, Мега и 3.3 вольта тоже прекрасно понимает.

Пара слов о протоколе.
Здесь нет совершенно никаких оганичений, данные можно отправлять в любой форме.
Мой выбор:
Принцип работы: master-slave. Вемос у нас будет выступать master, т.е. полностью руководить связью. Периодически он будет отслыть в Вемос запрос на получение данных, Мега при получении этого запроса сразу же их отдает. Кроме того, Вемос может отправлять управляющие команды, при получении которых Мега будет менять свои настройки.
Команды из Вемоса будут идти в таком виде:
Т_12>
"Т" - означает, какой именно параметр мы хотим поменять. "_" - сепаратор. "12" - значение. ">" - символ конца строки.
Информация от Меги будет идти в CSV-формате, т.е. данные разделенные запятыми.
<233, 23.4, 45.6> - примерно так выглядит.
Опять же повторюсь, тут ограничений никаких нет, можно импользовать и только первый вариант для обоих плат, и только второй. Второй вариант больше подходит не для одиночых команд, а для мало-мальски ощутимого массива данных, чуть удобней.

Ближе к делу.
Набросали простейший скетч для Меги, который будет эмулировать получение данных с датчиков (функция Data генерирует случайные показания и заносит их масив data) и один раз в секунду отправлять эти данные в Вемос по  Serial1.

float data[10];
long data_last_time;
long transmit_last_time;

//========================================================
void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}
//========================================================
void loop() {
  Data(1000);
  Transmit(1000);
}
//========================================================
void Data(float freq){
  if ((millis()-data_last_time)>freq){       
    for(int n = 0; n < 10; n++ ){
      String m = (String)(random(10000)/100.0);
      data[n] = m.toFloat();
    }
    data_last_time = millis();
  }
}

void Transmit(float freq){
  if ((millis()-transmit_last_time)>freq){   
    float summa = 0;
    String stroka = "<";       
    for(int n = 0; n < 10; n++ ){
      summa += data[n];
      stroka += (String)data[n]+",";
    }
    stroka += (String)summa + ">";
    Serial1.print(stroka);
   transmit_last_time = millis();
  }
}

Думаю все должно быть понятно, названия переменных говорят сами за себя.
Пара моментов:
1. В данные, кроме собственно них, включается еще контрольная сумма (математическая сумма всех значений строки). В функции Transmit есть цикл в котором происходит одновременно формирование строки из массива данных и подсчитывается их сумма. Затем сумма добавляется в конец строки и передается в Вемос с помощью Serial1.print(stroka);
2. Этот протокол не есть идеальным. Более правильно было бы дробные значения передавать в виде двух байт (по байту на целую и дробную составляющую), я же передаю их как символы, что увеличивает объем переданных данных в 2-3 раза и потребляет ресурсы на преобразования строк в числа и обратно. Для данного скетча это не будет критично, данных мало, поэтому в первую очередь удобство. Для бОльших объемов желательно минимизировать и сделать правильно.

Теперь принимаем данные в Вемосе

float receive_array[20];              //массив с данными, в который будем заносить полученные значения от Меги
String receive_string;

//========================================================
void setup() {
   Serial.begin(115200);
}
//========================================================
void loop() {
  Receive_from_Mega();
}
//========================================================
void Receive_from_Mega(){
  while (Serial.available() > 0) {                                  //если в буфере Serial появилось больше нуля принятых байт
    char incomig_char = Serial.read();                          //принимает один байт
    receive_string += (String)incomig_char;                 //добавляем его в строку
    if ((String)incomig_char == ">") {                           //если получили символ конца строки
      String stroka = receive_string;
      receive_string = "";
      stroka = stroka.substring(1,stroka.length()-1);   //обрезаем символы начала и конца строки
     
      int index = 0;                                                        //разбитие строки и занесение в массив. В Ардуино отсутствует такая прекрасная команда как split, поэтому разбивать строку приходится вручную
      char separator = ',';
      int strIndex[] = { 0, -1 };
      int maxIndex = stroka.length() - 1;
      for (int i = 0; i <= maxIndex; i++) {
        if (stroka.charAt(i) == separator || i == maxIndex) {
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
            String value = stroka.substring(strIndex[0], strIndex[1]);
            receive_array[index]= value.toFloat();                                        //заносим в массив полученное значение
            index++;
        }
      }

      float summa = 0.0;                                          //проверка контрольной суммы
      for (int i = 0; i < 10; i++) {
        summa += receive_array;
      }
      if(round(receive_array[10]*100) == round(summa*100)){   // здесь важно сравнивать именно округленные значения, поскольку при работе с float нужно помнить, что занося в переменную значение 2.0, там может оказаться 2.0000001, что не даст равенства между ними
        Serial.println("              success");
      }
    }
  }
}

To be continued...
« Последнее редактирование: 19 Ноябрь 2017, 16:14:48 от Ann »

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
Я подключаю напрямую, что есть неправильно! Поскольку Вемос работает на 3.3вольтах, а Мега на 5-ти, соответственно Вемос может сгореть от 5-ти вольт Меги.
здесь этого можно не опасаться
Цитировать
Огромный плюс этого решения в том, что разработчики позаботились о согласовании логических уровней сигналов всех компонентов системы.
https://geektimes.ru/post/287124/
хотя не совсем понятно что значит подключаю? физически? оно ведь всё уже скоммутировано, нужно лишь установить режим работы переключателями...
или подразумевалось таки две отдельных платы: Мега и Вемос..?
« Последнее редактирование: 19 Ноябрь 2017, 17:31:55 от alex_step »

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
Саш, ты меня доконаешь рано или поздно :D
Я же в начале сказал, что делаю себе коллайдер на двух отдельных платах: Меге и Вемосе :)

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
там про ОТДЕЛЬНЫЕ не сказано
и значит я недопонял т.к.
1.
решил сделать его на связке Arduino Mega + Wemos
2.
(в частности alex_step подобное тоже собирается делать)
1) - я воспринял НЕ как две платы
и
2) я таки это заведу на https://ru.aliexpress.com/item/WEMOS-Mega-WiFi-R3-ATmega2560-ESP8266-32Mb-memory-USB-TTL-CH340G-Compatible-for-Arduino-Mega-NodeMCU/32820282312.html
« Последнее редактирование: 19 Ноябрь 2017, 17:58:34 от alex_step »

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
Ну, та связка все же ATmega2560 + ESP8266 называется, а Вемос вроде как отдельная плата. Ладно, не суть, значит не слишком четко выразился.
В любом случае по коддингу отличий между двумя вариантами никаких.

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
в таком случае я как минимум застрахован от опасений в отношении согласования логических уровней  ;)

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
Продолжение.
Теперь в Вемос осталось добавить периодические запросы на получение информации от Меги и какую-нибудь тестовую команду. А в Мегу - прием этих команд.

Вот итоговый скетч Вемоса:
float receive_array[20];
String receive_string;

long command_last_time;

//========================================================
void setup() {
   Serial.begin(115200);
}
//========================================================
void loop() {
  Receive_from_Mega();
  Command_to_Mega(1000);
}
//========================================================

void Command_to_Mega(float freq){
  if ((millis()-command_last_time)>freq){
    Serial.print("I_1>");
    if(random(100) > 80) Serial.print("T_" + (String)random(100)+">");
    command_last_time = millis();
  }
}

void Receive_from_Mega(){
  while (Serial.available() > 0) {         
    char incomig_char = Serial.read();                          //прием строки
    receive_string += (String)incomig_char;
    if ((String)incomig_char == ">") {
      String stroka = receive_string;
      receive_string = "";
      stroka = stroka.substring(1,stroka.length()-1);
      //Serial.println(stroka);

     
      int index = 0;                                            //разбитие строки и занесение в массив
      char separator = ',';
      int strIndex[] = { 0, -1 };
      int maxIndex = stroka.length() - 1;
      for (int i = 0; i <= maxIndex; i++) {
        if (stroka.charAt(i) == separator || i == maxIndex) {
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
            String value = stroka.substring(strIndex[0], strIndex[1]);
            receive_array[index]= value.toFloat();
            index++;
        }
      }

      float summa = 0.0;                                          //проверка контрольной суммы
      for (int i = 0; i < 10; i++) {
        summa += receive_array[i];
      }
      if(round(receive_array[10]*100) == round(summa*100)){
        Serial.println("              success");
      }
    }
  }
}


Вот Мега:
float data[10];
long data_last_time;
String receive_string;

//========================================================
void setup() {
  Serial.begin(115200);
  Serial1.begin(115200);
}
//========================================================
void loop() {
  Data(1000);
 Receive_from_Wemos();
}
//========================================================
void Data(float freq){
  if ((millis()-data_last_time)>freq){       
    for(int n = 0; n < 10; n++ ){
      String m = (String)(random(10000)/100.0);
      data[n] = m.toFloat();
    }
    data_last_time = millis();
  }
}

void Receive_from_Wemos(){
  while (Serial1.available() > 0) {         
    char incomig_char = Serial1.read();                          //прием строки
    receive_string += (String)incomig_char;
    if ((String)incomig_char == ">") {
      String stroka = receive_string;
      receive_string = "";
      stroka = stroka.substring(0,stroka.length()-1);
         
      String value[2];
      int index = 0;                                            //разбитие строки и занесение в массив
      char separator = '_';
      int strIndex[] = { 0, -1 };
      int maxIndex = stroka.length() - 1;
      for (int i = 0; i <= maxIndex; i++) {
        if (stroka.charAt(i) == separator || i == maxIndex) {
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
            value[index] = stroka.substring(strIndex[0], strIndex[1]);
            index++;
        }
      }

      if(value[0] == "T"){
        Serial.println("COMMAND:"+ value[0] + "-" + value[1]);
      }
       
      if(value[0] == "I"){
        Send_to_Wemos();
        Serial.println("Send to Wemos!!!");
      }
    }
  }
}

void Send_to_Wemos(){
    float summa = 0;
    String stroka = "<";       
    for(int n = 0; n < 10; n++ ){
      summa += data[n];
      stroka += (String)data[n]+",";
    }
    stroka += (String)summa + ">";
    Serial1.print(stroka);
}

Раз в секунду Вемос посылает Меге запрос вида I_12> (число не имеет значения), и изредка, в случайном порядке. Т_12>
Мега принимает I_12> и сразу же отдает информацию, принимая Т_12>, выводит в монитор, что получило эту команду и её значение.
В принципе всё. В обе стороны отдаем и принимаем.

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
можно подробнее про I_12 и Т_12? это про
Цитировать
запросы на получение информации от Меги и какую-нибудь тестовую команду
или хде?

Или все таки лучше пождать скетча ближе к итоговому?..

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
можно подробнее про I_12 и Т_12?
I_12 (или с любым числом) - запрос от Вемоса в Мегу на получение информации. Как только Мега его получила, она сразу же отправляет пакет информации Вемосу.
Т_12 - абстрактная команда для изменения настроек. К примеру, продолжительность работы красных диодов - 12 часов.
Префикс (буква) обозначает какую именно настройку будем менять. Цифра - значение новой настройки.
По мере обрастания коллайдера модулями, буду уже вводить реальные команды.

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
ага, значит на данный момент присутствует только ради информации "как-и-шо"

печально что никому больше интереса нет... так у нас тут просто диалог получается...
или если кто заинтересован, то тихо ожидает..?

Онлайн Ann

  • Глобальный модератор
  • Habanero
  • *****
  • Сообщений: 1527
  • Лойсы: +118/-0
    • Просмотр профиля
печально что никому больше интереса нет... так у нас тут просто диалог получается...
или если кто заинтересован, то тихо ожидает..?
Ну не все же прямо сейчас коллайдеры собирают :)
Когда-то я единственный был на форуме, кто страдал ардуиной. Через год-два уже с десяток человек появилось.

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
Через год-два уже с десяток человек появилось.
так хде же фсе???  ???

Оффлайн PowerTech

  • Serrano
  • ***
  • Сообщений: 173
  • Лойсы: +11/-0
  • Василий
    • Просмотр профиля
Очень интересный проект, тоже хотел полностью автоматизировать баттл, но лень взяла своё и остановился на очень урезанном варианте.
Буду с удовольствием следить за развитием темы, но для себя присматриваю esp32s без посредников.

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
слава яйцам!!! мы здесь не они!

по поводу esp32 - я тож туда целился, но Андрей таки меня надоумил => необкатанная она, от слова "совсем"...

Оффлайн PowerTech

  • Serrano
  • ***
  • Сообщений: 173
  • Лойсы: +11/-0
  • Василий
    • Просмотр профиля
по поводу esp32 - я тож туда целился, но Андрей таки меня надоумил => необкатанная она, от слова "совсем"...
Вот и буду её пытать как лень поборю  :D

Оффлайн alex_step

  • Serrano
  • ***
  • Сообщений: 177
  • Лойсы: +5/-1
  • Александр, Украина, Жмеринка
    • Просмотр профиля
а я таки свернул на альтернативу -> одноплатный Mega + ESP8266

Оффлайн Ouroboros

  • Jalapeno
  • **
  • Сообщений: 50
  • Лойсы: +0/-0
    • Просмотр профиля
А мне вот одной ESP'шки достаточно. Сеть - есть, управление релюхами - есть, датчики - есть. Чего еще надо?
За сдвиговые регистры 74HC595 - спасибо, не знал. До этой проблемы правда пока что далеко, но она маячила на горизонте с самого момента покупки платы.

Оффлайн eddy

  • Habanero
  • *****
  • Сообщений: 643
  • Лойсы: +32/-0
  • 300 не надо хватит и одного
    • Просмотр профиля
печально что никому больше интереса нет... так у нас тут просто диалог получается...
или если кто заинтересован, то тихо ожидает..?

Да интерес то есть. Сказать по существу вопроса нечего. Вот и сидим тихо. Ожидаем. Но пожелания могу высказать.
Было бы неплохо если бы законченный проект легко клонировался не особо продвинутыми личностями.  Ну типа как Arduino Mega Server, управление подключение отключение доступных плюшек через веб морду.
 В зимнем батле у кого автоматизация у того и козыри. Дофига чего очень быстро меняется. Вручную не уследить, особенно на подоконнике.
— Товарищ генерал-лейтенант, я давно хотел спросить. Как с йети быть?
— Йети? Надо чаще мыть.