• Обратная связь
  • Карта сайта
  • Отзыв
  • Комментарии
  • Форум
discord
Технические заметки
  • Главная
  • Лекции
  • Практикум
  • Обзоры
  • Сравнение
  • Нормы
  • События
  • Вход
ГлавнаяArduinoУмный домМониторингОтопление
Правильное не-подключение модулей пожаротушения к С2000-АСПТ
Получаем уровень связи WiFi модуля ESP8266

Отправляем состояния теплых полов из Arduino UNO ESP8266 WiFi на сервер ThingSpeak

18 января 2020 г.Просмотров: 2464Комментарии: 0
ArduinoУмный домМониторингОтоплениеAliExpressArduinoESP8266ThingSpeakWiFiИнтернет ВещейМониторингОблачный СервисОтоплениеТеплый пол

Используем совмещенные на одной плате Arduino UNO и ESP8266 WiFi для мониторинга работы центрального контроллера водяных теплых полов.

Ранее научился отправлять показания датчиков при помощи связки Arduino UNO и ESP8266 WiFi в облачные сервисы RemoteXY и Cayenne.

Сервис ThingSpeak показался более интересным именно для сбора и обработки данных.

Постановка задачи мониторинга теплого пола.

Достигнутая цель выглядит так:

Использую вот такую плату Arduino UNO с модулем ESP-1 ESP8266 WiFi:

UNO R3 + WiFi ATmega328P+ESP8266 (32Mb memory) USB-TTL CH340G For Arduino Uno NodeMCU WeMos ESP8266 One New Arrival

В предыдущей статье описывал то, как добился отправки этой платой POST запроса на сервер регистрации данных.

Научим эту плату отправлять данные на сервис ThingSpeak.

Возможно имело бы смысл воспользоваться собственной библиотекой сервиса для работы с ESP8266 thingspeak-arduino - так поступил в случае с RemoteXY и Cayenne, использовав библиотеки для работы ESP8266 каждого из этих сервисов.

github.com/mathworks/thingspeak-arduino

Но будем использовать AT-команды и попытаемся разобраться с AT-командами самостоятельно.

Тем более что есть более-менее понятные примеры:

Updating Data In Thingspeak.

ESP8266 Wifi Temperature Logger.

AT+CIPSEND = Not returning.

Будет решаться конкретная практическая задача мониторинга состояния системы зонального отопления водяными теплыми полами, постановку задачи которого осуществил в статье Сервер теплого пола на MaxSite CMS.

До написания плагина в рамках MaxSite CMS руки еще не дошли, но, похоже, сервис ThingSpeak дает возможности не только собирать информацию, но и получать собранные данные в годном для автоматизированной обработки виде, а также обрабатывать собранные данные собственными средствами MATLAB.

Описание системы дистанционного мониторинга теплого пола в облачном сервисе.

Плата, совмещающая в себе два устройства: Arduino UNO и ESP8266 WiFi, накладывает ограничения на вывод диагностической информации в SerialPort, поскольку SerialPort обеих в работе должны быть объединены переключателями на плате.

Поэтому для вывода будем использовать Lcd 2004A - поле символов 20*4 позволит вывести всю диагностическую информацию.

Будем принимать сигналы о наличии 220В на открытие направления теплого пола от четырех направлений на центральном хабе-контроллере теплых полов.

Сигнал управления на головки коллектора теплого пола в виде 220В выдают комнатные терморегуляторы.

Наличие напряжения управления головками 220В будем определять при помощи вот этой платы:

AC 220V 8 Channel MCU TTL Level 8 Ch Optocoupler Isolation Test Board Isolated Detection Tester Module PLC Processors

Работа с платой описывалась в статье о контроле 220В при помощи Arduino, в том числе и описывались проблемы с использованием входов A4, A5.

Говоря коротко, не получилось использовать A4, A5, но это не значит что их нельзя использовать.

Наверное можно использовать и часть цифровых входов.

Но у меня 4 направления и мне хватит A0-A3.

Отправку показаний входов будем осуществлять только при изменении значений одного из входов.

Если входы не меняются в течении заданного времени - будем делать тестовую передачу не измененных значений входов и текущего RSS WiFi.

Текущие значения входов будем отображать на дисплее.

Также будем отображать состояние пяти шагов отправки данных на сервер.

Кроме того, будем контролировать уровень связи с точкой доступа.

Способ получения RSSI WiFi точки доступа, к которой подключен ESP8266 в качестве клиента, описывается в статье Получаем уровень связи WiFi модуля ESP8266.

RSSI WiFi будем отображать на дисплее и посылать на сервер вместе с измененными значениями входов.

Если начинается череда ошибок будем перезагружать ESP8266.

Скетч Arduino мониторинга теплого пола.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd( 0x27 , 20 , 4 );
String ssid ="Keenetic-0138";
String password="*******";
String API = "************";   
String HOST = "api.thingspeak.com";
String PORT = "80";
// строки, которые пригодятся для тестирования в мониторе порта
// AT+CWJAP="Keenetic-0138","*********"
// AT+CIPSTART=0,"TCP","api.thingspeak.com",80
// AT+CIPSEND=0,90
// GET /update?api_key=***************&field1=1&field2=1&field3=1&field4=0&field5=0&field6=0
boolean found = false;
boolean sendnook = false;
int valSensor = 1;
// вспомогательные переменные
String data;
String fields;
int value;
boolean current = false;
// какие пины соответствуют каким по счету датчикам
int analog1 = 1;
int analog2 = 2;
int analog3 = 3;
int analog4 = 4;
int analog5 = 5;
// текущее состояние входов
boolean last1 = false;
boolean last2 = false;
boolean last3 = false;
boolean last4 = false;
boolean last5 = false;
boolean outflag = true; // признак изменений, требующих отправки - начнем с отправки значений
#define PIN_SWITCH_1 12
#define PIN_SWITCH_2 13
String curRSSI="";
int crssi=0;
int errorscount=0;
int maxerrorscount=10;
unsigned long lastConnectionTime = 0;   
unsigned long lastInfoTime = 0;
const unsigned long postingInterval = 1200000; // 20 минут 
const unsigned long infoInterval = 1000;      // 1 сек
long day = 86400000; // 86400000 milliseconds in a day
long hour = 3600000; // 3600000 milliseconds in an hour
long minute = 60000; // 60000 milliseconds in a minute
long second =  1000; // 1000 milliseconds in a second
void setup() 
{
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 1);
  lcd.print("S1:" + String(last1));    
  lcd.setCursor(6, 1);
  lcd.print("S2:" + String(last2));  
  lcd.setCursor(12, 1);
  lcd.print("S3:" + String(last3));    
  lcd.setCursor(0, 2);
  lcd.print("S4:" + String(last4));  
  lcd.setCursor(6, 2);
  lcd.print("220V:" + String(last4)); 
  pinMode (PIN_SWITCH_1, OUTPUT);  
  pinMode (PIN_SWITCH_2, OUTPUT); 
  Serial.begin(115200);
  reset();
  connectWifi();
}
void reset() 
{
  sendC("AT+RST", 5, "OK");    
  delay(1000);
}
void connectWifi() 
{
  lcd.setCursor(0, 0);
  lcd.print("WiFi:GO");
  sendC("AT+CWMODE=3", 100, "OK");
  while(!found) sendC("AT+CWJAP=\"" + ssid + "\",\"" + password + "\"", 4000, "OK");
  if (found) 
  {
    digitalWrite(PIN_SWITCH_2, HIGH);
    lcd.setCursor(0, 0);
    lcd.print("WiFi:OK");
  }
  else
  {
    digitalWrite(PIN_SWITCH_2, LOW);
    lcd.setCursor(0, 0);
    lcd.print("WiFi:NO");
  }  
  Serial.println("ATE0"); // не знаю что это, но так работает лучше
}
void loop ()
{
   // вероятно проверки входов можно было бы и свернуть в одну функцию
   delay(500); 
   value = analogRead(analog1);
   if(value < 500) current = true;
   else current = false;
   if(last1 != current)
   {
      outflag = true;
      last1 = current;
      lcd.setCursor(0, 1);
      lcd.print("S1:" + String(last1));
   }
   value = analogRead(analog2);
   if(value < 500) current = true;
   else current = false;
   if(last2 != current)
   {
      outflag = true;
      last2 = current;
      lcd.setCursor(6, 1);
      lcd.print("S2:" + String(last2));
   } 
   value = analogRead(analog3);
   if(value < 500) current = true;
   else current = false;
   if(last3 != current)
   {
      outflag = true;
      last3 = current;
      lcd.setCursor(12, 1);
      lcd.print("S3:" + String(last3));
   } 
   value = analogRead(analog4);
   if(value < 500) current = true;
   else current = false;
   if(last4 != current)
   {
      outflag = true;
      last4 = current;
      lcd.setCursor(0, 2);
      lcd.print("S4:" + String(last4));
   } 
   value = analogRead(analog5);
   if(value < 500) current = true;
   else current = false;
   if(last5 != current)
   {
      outflag = true;
      last5 = current;
      lcd.setCursor(6, 2);
      lcd.print("220V:" + String(last5));
   }    
   // счетчики 
   if (millis() - lastConnectionTime > postingInterval) { outflag = true; }
   if (millis() - lastInfoTime > infoInterval) 
   {
     lastInfoTime = millis();
     time(postingInterval+lastConnectionTime-millis());  
   }  
   
   // отправка на сервер, если надо  
   if (outflag || sendnook) 
   {
     sendnook = true; // пока ничего не отправлено
     httppost();
     outflag = false; // обнуляем флаг изменений входов
   }
    // счетчик ошибок
    lcd.setCursor(7, 3);
    lcd.print("Err:" + String(errorscount)); 
    if (errorscount>maxerrorscount)
    {
      reset();
      connectWifi();
      errorscount = 0;
     }
    delay(1000);
    getRSSI();
}
void httppost () 
{
  int iserror = 1; // предполагаем что будет ошибка
  found = false; // флаг успешной отправки на шаге итерации
  
  fields = "field1=" + String(last1)+"&field2=" + String(last2)+"&field3=" + String(last3)+"&field4=" + String(last4)+"&field5=" + String(last5);
  if (crssi) fields = fields + "&field6=-" + curRSSI; 
  String data = "GET /update?api_key=" + API + "&" + fields  + "\r\n";  
  
  lcd.setCursor(11, 0);
  lcd.print("1Post    "); 
  sendC("AT+CIPMUX=1", 2000, "OK");
  if (found)
  {
      lcd.setCursor(11, 0);
      lcd.print("2Start   ");    
      sendC("AT+CIPSTART=0,\"TCP\",\"" + HOST + "\"," + PORT, 2000, "OK");
      if (found) 
      {
         lcd.setCursor(11, 0);
         lcd.print("3Connect ");
         sendC("AT+CIPSEND=0," + String(data.length()+2), 2000, ">");
         if (found) 
         {
            lcd.setCursor(11, 0);
            lcd.print("4Send    ");          
            Serial.println(data);
            sendnook = false;
            delay(2000);
            lcd.setCursor(11, 0);
            lcd.print("5Send OK ");
            iserror = 0; // ошибки не было
            errorscount = 0; 
            lcd.setCursor(7, 3);
            lcd.print("      ");
          }
         else Serial.println("AT+CIPCLOSE"); 
      }  
  }
  sendC("AT+CIPMUX=0", 100, "OK");
  if (iserror) errorscount++; // счетчик шибок
  else errorscount=0; // обнуляем ошибки
  
  lastConnectionTime = millis();
  lcd.setCursor(0, 3);
  lcd.print("      ");
}
void getRSSI()
{
  char c1;
  String val = "";
  int i;
  curRSSI = "";
  Serial.print("AT+CWLAP=\"" + ssid + "\"\r\n");
  delay(500);
  if (Serial.available()) 
  {
    while (Serial.available() > 0) 
    {      
       c1 = (char)Serial.read(); val = val + c1; delay(2);
    }  
    int pos = val.indexOf(ssid);
    pos = pos+ssid.length()+3;
    curRSSI = val.substring(pos, pos+2);
    
    Serial.println(curRSSI);
    crssi = curRSSI.toInt();
    if (crssi) 
    {
       lcd.setCursor(5, 0);
       lcd.print(-crssi); 
    }       
  }  
}
void sendC(String command, int maxTime, char readReplay[])
{
  found = false;  
 
  Serial.println(command);
  delay(maxTime);
  if (Serial.find(readReplay)) 
  {
      found = true;
      digitalWrite(PIN_SWITCH_1, HIGH);
  }
  else
  {
    digitalWrite(PIN_SWITCH_1, LOW);
  }
}
void time(long timeNow)
{
 int days = timeNow / day ;  /
 int hours = (timeNow % day) / hour;  
 int minutes = ((timeNow % day) % hour) / minute ;  
 int seconds = (((timeNow % day) % hour) % minute) / second;
 lcd.setCursor(0, 3); 
 printDigits(minutes);
 lcd.setCursor(2, 3);
 lcd.print(":");
 printDigits(seconds);
}
void printDigits(byte digits)
{
 if(digits < 10)
   lcd.print('0');
 lcd.print(digits,DEC);  
}

Оборудование для мониторинга теплого пола.

Подключим теперь платы снятия показаний и передачи на сервер к центру управления коллектором теплого пола Beok CCT-10.

Beok CCT-10 порадовал вместимостью, позволяющей принять и скоммутировать все провода.

Добавим трехцветный светодиод отображения состояния связи/передачи/ошибок.

Добавим кнопку включения/выключения подсветки дисплея и кнопку принудительной (тестовой) отправки данных.

Переключатель слева внизу будет осуществлять выбор режима работы котла: ручной/автоматический.

Если будет отсутствовать 220В, то котел будет работать от источника бесперебойного питания.

Но без 220В контакты управления котлом будут разомкнуты внутри Beok CCT-10 и их необходимо принудительно замкнуть переключателем.

Результаты мониторинга водяного теплого пола.

Скетч с кнопками и светодиодом будет такой.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd( 0x27 , 20 , 4 );
#define BUTTON_PIN1 3
#define BUTTON_PIN2 4
#define PIN_SWITCH_1 11 // ошибка red
#define PIN_SWITCH_2 12 // связь установлена blue
#define PIN_SWITCH_3 13 // передача прошла green
boolean buttonWasUp1 = true;  // была ли кнопка отпущена?
boolean buttonWasUp2 = true;  // была ли кнопка отпущена?
boolean ledEnabled = false;  // включен ли свет?
boolean sendbutton = false;
boolean sendtimer = false;
String ssid ="Keenetic-0138";
String password="***********";
String API = "************";   
String HOST = "api.thingspeak.com";
String PORT = "80";
// строки, которые пригодятся для тестирования в мониторе порта
// AT+CWJAP="Keenetic-0138","**********"
// AT+CIPSTART=0,"TCP","api.thingspeak.com",80
// AT+CIPSEND=0,90
// GET /update?api_key=***************&field1=1&field2=1&field3=1&field4=0&field5=0&field6=0
boolean found = false;
boolean sendnook = false;
int valSensor = 1;
// вспомогательные переменные
String data;
String fields;
int value;
boolean current = false;
// какие пины соответствуют каким по счету датчикам
int analog1 = 1;
int analog2 = 2;
int analog3 = 3;
int analog4 = 4;
int analog5 = 5;
// текущее состояние входов
boolean last1 = false;
boolean last2 = false;
boolean last3 = false;
boolean last4 = false;
boolean last5 = false;
boolean outflag = true; // признак изменений, требующих отправки - начнем с отправки значений
String curRSSI="";
int crssi=0;
int errorscount=0;
int maxerrorscount=10;
unsigned long lastConnectionTime = 0;  
 unsigned long lastOkConnectionTime = 0;  
unsigned long lastInfoTime = 0;
const unsigned long postingInterval = 1200000; // 20 минут 
const unsigned long infoInterval = 1000;      // 1 сек
long day = 86400000; // 86400000 milliseconds in a day
long hour = 3600000; // 3600000 milliseconds in an hour
long minute = 60000; // 60000 milliseconds in a minute
long second =  1000; // 1000 milliseconds in a second
void setup() 
{
  pinMode(BUTTON_PIN1, INPUT_PULLUP);  
  pinMode(BUTTON_PIN2, INPUT_PULLUP);    
  lcd.init();
  lcd.setCursor(0, 1);
  lcd.print("S1:" + String(last1));    
  lcd.setCursor(6, 1);
  lcd.print("S2:" + String(last2));  
  lcd.setCursor(12, 1);
  lcd.print("S3:" + String(last3));    
  lcd.setCursor(0, 2);
  lcd.print("S4:" + String(last4));  
  lcd.setCursor(6, 2);
  lcd.print("220V:" + String(last4)); 
  pinMode (PIN_SWITCH_1, OUTPUT);  
  pinMode (PIN_SWITCH_2, OUTPUT); 
  pinMode (PIN_SWITCH_3, OUTPUT); 
  Serial.begin(115200);
  reset();
  connectWifi();
}
void reset() 
{
  sendC("AT+RST", 5, "OK");    
  delay(1000);
}
void connectWifi() 
{
  lcd.setCursor(0, 0);
  lcd.print("WiFi:GO");
  sendC("AT+CWMODE=3", 100, "OK");
  while(!found) sendC("AT+CWJAP=\"" + ssid + "\",\"" + password + "\"", 4000, "OK");
  if (found) 
  {
    digitalWrite(PIN_SWITCH_1, LOW);    
    digitalWrite(PIN_SWITCH_2, HIGH);
    digitalWrite(PIN_SWITCH_3, LOW);    
    lcd.setCursor(0, 0);
    lcd.print("WiFi:OK");
  }
  else
  {
    digitalWrite(PIN_SWITCH_1, LOW);     
    digitalWrite(PIN_SWITCH_2, LOW);
    digitalWrite(PIN_SWITCH_3, LOW);    
    lcd.setCursor(0, 0);
    lcd.print("WiFi:NO");
  }  
  Serial.println("ATE0"); // не знаю что это, но так работает лучше
}
void loop ()
{
  boolean buttonIsUp1 = digitalRead(BUTTON_PIN1);
  if (buttonWasUp1 && !buttonIsUp1) 
  {
    delay(10);
    buttonIsUp1 = digitalRead(BUTTON_PIN1);
    if (!buttonIsUp1) 
    {  
      ledEnabled = !ledEnabled;
      if (ledEnabled) lcd.backlight();
      else lcd.noBacklight();
    }
  }
  buttonWasUp1 = buttonIsUp1;
  boolean buttonIsUp2 = digitalRead(BUTTON_PIN2);
  if (buttonWasUp2 && !buttonIsUp2) 
  {
    delay(10);
    buttonIsUp2 = digitalRead(BUTTON_PIN2);
    if (!buttonIsUp2) 
    {  
       sendbutton = true;
    }
  }
  buttonWasUp2 = buttonIsUp2;
    
   // вероятно проверки входов можно было бы и свернуть в одну функцию
   delay(500); 
   value = analogRead(analog1);
   if(value < 500) current = true;
   else current = false;
   if(last1 != current)
   {
      outflag = true;
      last1 = current;
      lcd.setCursor(0, 1);
      lcd.print("S1:" + String(last1));
   }
   value = analogRead(analog2);
   if(value < 500) current = true;
   else current = false;
   if(last2 != current)
   {
      outflag = true;
      last2 = current;
      lcd.setCursor(6, 1);
      lcd.print("S2:" + String(last2));
   } 
   value = analogRead(analog3);
   if(value < 500) current = true;
   else current = false;
   if(last3 != current)
   {
      outflag = true;
      last3 = current;
      lcd.setCursor(12, 1);
      lcd.print("S3:" + String(last3));
   } 
   value = analogRead(analog4);
   if(value < 500) current = true;
   else current = false;
   if(last4 != current)
   {
      outflag = true;
      last4 = current;
      lcd.setCursor(0, 2);
      lcd.print("S4:" + String(last4));
   } 
   value = analogRead(analog5);
   if(value < 500) current = true;
   else current = false;
   if(last5 != current)
   {
      outflag = true;
      last5 = current;
      lcd.setCursor(6, 2);
      lcd.print("220V:" + String(last5));
   }    
   // счетчики 
   if (millis() - lastConnectionTime > postingInterval) { sendtimer = true; }
   if (millis() - lastInfoTime > infoInterval) 
   {
     lastInfoTime = millis();
     time(postingInterval+lastConnectionTime-millis());  
   }  
   
   // отправка на сервер, если надо  
   if (outflag || sendnook || sendbutton) 
   {
     sendnook = true; // пока ничего не отправлено
     httppost();
     outflag = false; // обнуляем флаг изменений входов
     sendbutton = false; // обнуляем флаг принудительной отправки
     sendtimer = false; // обнуляем флаг отправки по времени
   }
    // счетчик ошибок
    lcd.setCursor(7, 3);
    lcd.print("Err:" + String(errorscount)); 
    if (errorscount>maxerrorscount)
    {
      reset();
      connectWifi();
      errorscount = 0;
     }
    delay(1000);
    getRSSI();
}
void httppost () 
{
  int iserror = 1; // предполагаем что будет ошибка
  found = false; // флаг успешной отправки на шаге итерации
  digitalWrite(PIN_SWITCH_2, LOW);  
  digitalWrite(PIN_SWITCH_3, LOW);  
    
  fields = "field1=" + String(last1)+"&field2=" + String(last2)+"&field3=" + String(last3)+"&field4=" + String(last4)+"&field5=" + String(last5);
//  if (sendtimer) fields = fields + "&field0=timer";
//  else if (sendbutton) fields = fields + "&field0=button";
//  else fields = fields + "&field0=custom";
  
  if (crssi) fields = fields + "&field6=" + String(-crssi); 
  
  String data = "GET /update?api_key=" + API + "&" + fields  + "\r\n";  
  
  lcd.setCursor(11, 0);
  lcd.print("1Post    "); 
  sendC("AT+CIPMUX=1", 2000, "OK");
  if (found)
  {
      lcd.setCursor(11, 0);
      lcd.print("2Start   ");    
      sendC("AT+CIPSTART=0,\"TCP\",\"" + HOST + "\"," + PORT, 2000, "OK");
      if (found) 
      {
         lcd.setCursor(11, 0);
         lcd.print("3Connect ");
         sendC("AT+CIPSEND=0," + String(data.length()+2), 2000, ">");
         if (found) 
         {
            lcd.setCursor(11, 0);
            lcd.print("4Send    ");          
            Serial.println(data);
            sendnook = false;
            delay(2000);
            lcd.setCursor(11, 0);
            lcd.print("5Send OK ");
            iserror = 0; // ошибки не было
            errorscount = 0; 
            lcd.setCursor(7, 3);
            lcd.print("      ");
            digitalWrite(PIN_SWITCH_1, LOW); 
            digitalWrite(PIN_SWITCH_3, HIGH); 
            lastOkConnectionTime = millis();  
          }
         else Serial.println("AT+CIPCLOSE"); 
      }  
  }
  sendC("AT+CIPMUX=0", 100, "OK");
  if (iserror)
  {
     errorscount++; // счетчик шибок
     digitalWrite(PIN_SWITCH_1, HIGH);  
  }
  else errorscount=0; // обнуляем ошибки
  
  lastConnectionTime = millis();
  lcd.setCursor(0, 3);
  lcd.print("      ");
}
void getRSSI()
{
  char c1;
  String val = "";
  int i;
  curRSSI = "";
  Serial.print("AT+CWLAP=\"" + ssid + "\"\r\n");
  delay(500);
  if (Serial.available()) 
  {
    while (Serial.available() > 0) 
    {      
       c1 = (char)Serial.read(); val = val + c1; delay(2);
    }  
    int pos = val.indexOf(ssid);
    pos = pos+ssid.length()+3;
    curRSSI = val.substring(pos, pos+2);
    
    Serial.println(curRSSI);
    crssi = curRSSI.toInt();
    if (crssi) 
    {
       lcd.setCursor(5, 0);
       lcd.print(-crssi); 
    }       
  }  
}
void sendC(String command, int maxTime, char readReplay[])
{
  found = false;  
 
  Serial.println(command);
  delay(maxTime);
  if (Serial.find(readReplay)) 
  {
      found = true;
     // digitalWrite(PIN_SWITCH_1, HIGH);
  }
  else
  {
    // digitalWrite(PIN_SWITCH_1, LOW);
  }
}
void time(long timeNow)
{
 int days = timeNow / day ;   
 int hours = (timeNow % day) / hour;  
 int minutes = ((timeNow % day) % hour) / minute ;  
 int seconds = (((timeNow % day) % hour) % minute) / second;
 lcd.setCursor(0, 3); 
 printDigits(minutes);
 lcd.setCursor(2, 3);
 lcd.print(":");
 printDigits(seconds);
}
void printDigits(byte digits)
{
 if(digits < 10)
   lcd.print('0');
 lcd.print(digits,DEC);  
}

Достоверность отправки данных из Arduino на облачный сервер.

Как видно из скетча, мы после отправки каждой AT команды проверяем, что пришел требуемый ответ:

sendC("AT+CIPMUX=1", 2000, "OK");
if (found)
{
  ....
  sendC("AT+CIPSTART=0,\"TCP\",\"" + HOST + "\"," + PORT, 2000, "OK");
  if (found) 
  {
    ....
    sendC("AT+CIPSEND=0," + String(data.length()+2), 2000, ">");
    if (found) 
    { 
      Serial.println(data);
      ....
      iserror = 0; // ошибки не было
      ....

После того как получили символ ожидания данных ">" отправляем данные

Serial.println(data);

Но после отправки самих данных мы автоматически считаем, что данные отправлены, хотя нужно было бы и тут дождаться получения "OK" в ответе в Serial:

busy s...


SEND OK
CLOSED

Решение проблем получения busy s...

Оказывается все дело в неправильно определяемой длине строки, ведь println() добавляет еще символы конца строки.

Поэтому функцию отправки надо переписать с применением print() вместо println(), а в AT команды, передаваемые функции отправки, добавить "\r\n".

Итоговый скетч с правками и с проверкой всех шагов и без busy s... будет такой:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd( 0x27 , 20 , 4 );
#define BUTTON_PIN1 3
#define BUTTON_PIN2 4
#define PIN_SWITCH_1 11 // ошибка red
#define PIN_SWITCH_2 12 // связь установлена blue
#define PIN_SWITCH_3 13 // передача прошла green
boolean buttonWasUp1 = true;  // была ли кнопка отпущена?
boolean buttonWasUp2 = true;  // была ли кнопка отпущена?
boolean ledEnabled = false;  // включен ли свет?
boolean sendbutton = false;
boolean sendtimer = false;
boolean senderror = false;
String ssid ="Keenetic-0138";
String password="***********";
String API = "*****************";   
String HOST = "api.thingspeak.com";
String PORT = "80";
// строки, которые пригодятся для тестирования в мониторе порта
// AT+CWJAP="Keenetic-0138","**********"
// AT+CIPSTART=0,"TCP","api.thingspeak.com",80
// AT+CIPSEND=0,90
// GET /update?api_key=**************&field1=1&field2=1&field3=1&field4=0&field5=0&field6=0
boolean found = false;
boolean sendnook = false;
int valSensor = 1;
// вспомогательные переменные
String data;
String fields;
int value;
boolean current = false;
// какие пины соответствуют каким по счету датчикам
int analog1 = 1;
int analog2 = 2;
int analog3 = 3;
int analog4 = 4;
int analog5 = 5;
// текущее состояние входов
boolean last1 = false;
boolean last2 = false;
boolean last3 = false;
boolean last4 = false;
boolean last5 = false;
boolean outflag = true; // признак изменений, требующих отправки - начнем с отправки значений
String curRSSI="";
int crssi=0;
int errorscount=0;
int maxerrorscount=10;
unsigned long lastConnectionTime = 0;  
unsigned long lastOkConnectionTime = 0;  
unsigned long lastInfoTime = 0;
unsigned long lastrssTime = 0;
unsigned long lasterrorTime = 0;
const unsigned long postingInterval = 600000; // 10 минут 
const unsigned long infoInterval = 1000;      // 1 сек
const unsigned long rssiInterval = 300000;      // 5 минут  
const unsigned long errorInterval = 10000;      // 10 сек 
long day = 86400000; // 86400000 milliseconds in a day
long hour = 3600000; // 3600000 milliseconds in an hour
long minute = 60000; // 60000 milliseconds in a minute
long second =  1000; // 1000 milliseconds in a second
void setup() 
{
  pinMode(BUTTON_PIN1, INPUT_PULLUP);  
  pinMode(BUTTON_PIN2, INPUT_PULLUP);    
  lcd.init();
  lcd.setCursor(0, 1);
  lcd.print("S1:" + String(last1));    
  lcd.setCursor(6, 1);
  lcd.print("S2:" + String(last2));  
  lcd.setCursor(12, 1);
  lcd.print("S3:" + String(last3));    
  lcd.setCursor(0, 2);
  lcd.print("S4:" + String(last4));  
  lcd.setCursor(6, 2);
  lcd.print("220V:" + String(last4)); 
  pinMode (PIN_SWITCH_1, OUTPUT);  
  pinMode (PIN_SWITCH_2, OUTPUT); 
  pinMode (PIN_SWITCH_3, OUTPUT); 
  Serial.begin(115200);
  reset();
  connectWifi();
  delay(2000);
  getRSSI();// первый раз не всегда работает при включении
  getRSSI();
}
void reset() 
{
  sendC("AT+RST\r\n", 5, "OK");    
  delay(1000);
}
void connectWifi() 
{
  int i=0;
  lcd.setCursor(0, 0);
  lcd.print("WiFi:GO");
  sendC("AT+CWMODE=3\r\n", 100, "OK");
  while(!found)
  {
     i++;
     if (i>10) { reset(); i=0; }  
     sendC("AT+CWJAP=\"" + ssid + "\",\"" + password + "\"\r\n", 4000, "OK");
  }
  if (found) 
  {
    digitalWrite(PIN_SWITCH_1, LOW);    
    digitalWrite(PIN_SWITCH_2, HIGH);
    digitalWrite(PIN_SWITCH_3, LOW);    
    lcd.setCursor(0, 0);
    lcd.print("WiFi:OK");
  }
  else
  {
    digitalWrite(PIN_SWITCH_1, LOW);     
    digitalWrite(PIN_SWITCH_2, LOW);
    digitalWrite(PIN_SWITCH_3, LOW);    
    lcd.setCursor(0, 0);
    lcd.print("WiFi:NO");
  }  
  Serial.println("ATE0"); // не знаю что это, но так работает лучше
}
void loop ()
{
  boolean buttonIsUp1 = digitalRead(BUTTON_PIN1);
  if (buttonWasUp1 && !buttonIsUp1) 
  {
    delay(10);
    buttonIsUp1 = digitalRead(BUTTON_PIN1);
    if (!buttonIsUp1) 
    {  
      ledEnabled = !ledEnabled;
      if (ledEnabled) lcd.backlight();
      else lcd.noBacklight();
    }
  }
  buttonWasUp1 = buttonIsUp1;
  boolean buttonIsUp2 = digitalRead(BUTTON_PIN2);
  if (buttonWasUp2 && !buttonIsUp2) 
  {
    delay(10);
    buttonIsUp2 = digitalRead(BUTTON_PIN2);
    if (!buttonIsUp2) 
    {  
       sendbutton = true;
    }
  }
  buttonWasUp2 = buttonIsUp2;
    
   // вероятно проверки входов можно было бы и свернуть в одну функцию
   value = analogRead(analog1);
   if(value < 500) current = true;
   else current = false;
   if(last1 != current)
   {
      outflag = true;
      last1 = current;
      lcd.setCursor(0, 1);
      lcd.print("S1:" + String(last1));
   }
   value = analogRead(analog2);
   if(value < 500) current = true;
   else current = false;
   if(last2 != current)
   {
      outflag = true;
      last2 = current;
      lcd.setCursor(6, 1);
      lcd.print("S2:" + String(last2));
   } 
   value = analogRead(analog3);
   if(value < 500) current = true;
   else current = false;
   if(last3 != current)
   {
      outflag = true;
      last3 = current;
      lcd.setCursor(12, 1);
      lcd.print("S3:" + String(last3));
   } 
   value = analogRead(analog4);
   if(value < 500) current = true;
   else current = false;
   if(last4 != current)
   {
      outflag = true;
      last4 = current;
      lcd.setCursor(0, 2);
      lcd.print("S4:" + String(last4));
   } 
   value = analogRead(analog5);
   if(value < 500) current = true;
   else current = false;
   if(last5 != current)
   {
      outflag = true;
      last5 = current;
      lcd.setCursor(6, 2);
      lcd.print("220V:" + String(last5));
   }    
   // счетчики 
   if (millis() - lastConnectionTime > postingInterval) 
   { 
       sendtimer = true; 
   }
   
   if (millis() - lastInfoTime > infoInterval) 
   {
     lastInfoTime = millis();
     time(postingInterval+lastConnectionTime-millis());  
   } 
   // пропуск времени если ошибка 
   if ( sendnook and (millis() - lasterrorTime > errorInterval) )
   {
      senderror = true;
   }
   
   // отправка на сервер, если надо  
   if ( (outflag || senderror || sendbutton || sendtimer) & (millis()>10000) ) 
   {
     sendnook = true; // пока ничего не отправлено
     httppost();
     outflag = false; // обнуляем флаг изменений входов
     sendbutton = false; // обнуляем флаг принудительной отправки
     sendtimer = false; // обнуляем флаг отправки по времени
     senderror = false;
   }
   if ( (millis() - lastrssTime > rssiInterval)) 
   {
     lastrssTime = millis();
     getRSSI();
   }  
   
    // счетчик ошибок
    lcd.setCursor(7, 3);
    lcd.print("Err:" + String(errorscount)); 
    if (errorscount>maxerrorscount)
    {
      reset();
      connectWifi();
      errorscount = 0;
     }
}
void httppost () 
{
  int iserror = 1; // предполагаем что будет ошибка
  found = false; // флаг успешной отправки на шаге итерации
  digitalWrite(PIN_SWITCH_2, LOW);  
  digitalWrite(PIN_SWITCH_3, LOW);  
    
  fields = "field1=" + String(last1)+"&field2=" + String(last2)+"&field3=" + String(last3)+"&field4=" + String(last4)+"&field5=" + String(last5);
//  if (sendtimer) fields = fields + "&field0=timer";
//  else if (sendbutton) fields = fields + "&field0=button";
//  else fields = fields + "&field0=custom";
  
  if (crssi) fields = fields + "&field6=" + String(-crssi); 
  
  String data = "GET /update?api_key=" + API + "&" + fields  + "\r\n\r\n";  
  
  lcd.setCursor(11, 0);
  lcd.print("1Post    "); 
  sendC("AT+CIPMUX=1\r\n", 2000, "OK");
  if (found)
  {
      lcd.setCursor(11, 0);
      lcd.print("2Start   ");    
      sendC("AT+CIPSTART=0,\"TCP\",\"" + HOST + "\"," + PORT + "\r\n", 2000, "OK");
      if (found) 
      {
         lcd.setCursor(11, 0);
         lcd.print("3Connect ");
         sendC("AT+CIPSEND=0," + String(data.length()) + "\r\n", 2000, ">");
         if (found) 
         {
              lcd.setCursor(11, 0);
              lcd.print("4Send    "); 
              sendC(data, 3000, "OK");         
              if (found) 
              {
                sendnook = false;
                lcd.setCursor(11, 0);
                lcd.print("5Send OK ");
                iserror = 0; // ошибки не было
                errorscount = 0; 
                lcd.setCursor(7, 3);
                lcd.print("      ");
                digitalWrite(PIN_SWITCH_1, LOW); 
                digitalWrite(PIN_SWITCH_3, HIGH); 
                lastOkConnectionTime = millis();
              }  
              else Serial.println("AT+CIPCLOSE");
          }
          else Serial.println("AT+CIPCLOSE"); 
      }  
  }
  sendC("AT+CIPMUX=0\r\n", 100, "OK");
  if (iserror)
  {
     errorscount++; // счетчик шибок
     digitalWrite(PIN_SWITCH_1, HIGH); 
     lasterrorTime = millis();
  }
  else errorscount=0; // обнуляем ошибки
  
  lastConnectionTime = millis();
  lcd.setCursor(0, 3);
  lcd.print("      ");
 // Serial.println("AT+CIPCLOSE"); // потому что не всегда само закрывается
}
void getRSSI()
{
  char c1;
  String val = "";
  curRSSI = "";
  int i;
  delay(3000);
  Serial.print("AT+CWLAP=\"" + ssid + "\"\r\n");
  delay(2000);
  if (Serial.available()) 
  {
    while (Serial.available() > 0) 
    {      
       c1 = (char)Serial.read(); val = val + c1; delay(2);
    }  
    int pos = val.indexOf(ssid);
    pos = pos+ssid.length()+3;
    curRSSI = val.substring(pos, pos+2);
    
    i = curRSSI.toInt();
    if (i) 
    {
       crssi = i;
       lcd.setCursor(5, 0);
       lcd.print(-crssi); 
    }       
  }
  delay(2000); 
}
void sendC(String command, int maxTime, char readReplay[])
{
  found = false;  
  Serial.print(command);
  delay(maxTime);
  if (Serial.find(readReplay)) 
  {
    found = true;
  }
}
void time(long timeNow)
{
 int days = timeNow / day ;   
 int hours = (timeNow % day) / hour;  
 int minutes = ((timeNow % day) % hour) / minute ;  
 int seconds = (((timeNow % day) % hour) % minute) / second;
 lcd.setCursor(0, 3); 
 printDigits(minutes);
 lcd.setCursor(2, 3);
 lcd.print(":");
 printDigits(seconds);
}
void printDigits(byte digits)
{
 if(digits < 10)
   lcd.print('0');
 lcd.print(digits,DEC);  
}

Достоверность отправки тут уже 100%.

Кроме того,в последнем скетче поправлены задержки на такие, с которыми стабильно работает.

Вот только существует вероятность, что после символов "OK" в ответе будет содержаться еще и такое:

Это возможно, если, строка данных не будет понята сервером.

Вот результаты ручной отправки команд серверу для тестирования работы, в которых видна опечатка в одной из строк данных для отправки, в результате которой возвращается ошибка 400:

(Для удобства строки команд для ручной отправки храню в теле скетча - нужно только убедиться что они копируются без пробелов в конце).

Так вот, не мешало бы содержимое ответа сервера в Serial проверить на отсутствие символов "400".

Можно поступить проще и проверить чтобы размер ответа в Serial был не больше чем "SEND OK" и "0,CLOSED".

Но это не реализовано.

Страница канала с данными работы теплого пола: thingspeak.com/channels/958186

Встраиваемые Chart IFrame для отображения данных ThingSpeak на сайте.

Сравнение уровня сигнала для ESP1 и ESP12.

Показалось что уровень связи в месте установки оборудования слишком низкий и заменил совместную плату с ESP1 и UNO на отдельную плату с модулем ESP12E, которую применял в предыдущих экспериментах совместно с платой Arduino UNO.

WeMos D1 WiFi uno based ESP8266 for arduino Compatible

Уровень связи оказался еще меньше и пришлось заменить обратно.

На графике хорошо видна обратная замена на совмещенную плату.

  • Коллектор водяных теплых полов с автоматическим зональным регулированием
    • Как я приспособил смесительный узел TIM JH-1036 для теплого пола.
      • Сколько стоит и где купить оборудование для теплого поля TIM - Смета
      • Как настроить байпас смесительного узла TIM JH-1036
    • Соленоидные клапана с AliExpress
    • Обзор центральных блоков зонального управления водяным теплым полом
    • Контроллер теплых полов Beok CCT-10 с AliExpress для зонального отопления
    • Выбор и тестирование дешёвых сервоприводов коллектора теплого пола с AliExpress
    • Какие бывают терморегуляторы - типы и виды
    • Отправляем состояния теплых полов из Arduino UNO ESP8266 WiFi на сервер ThingSpeak
    • Схема подключения к контроллеру теплых полов Beok CCT-10

Еще записи по теме

Сервер  теплого пола на MaxSite CMS - задумка.
Сервер теплого пола на MaxSite CMS - задумка.
Подключаем Arduino UNO при помощи ESP8266-12E  к облаку Cayenne
Подключаем Arduino UNO при помощи ESP8266-12E к облаку Cayenne
Комнатные терморегуляторы с Aliexpress 2020
Комнатные терморегуляторы с Aliexpress 2020
Wi-Fi терморегулятор за 1630р в ближайшую Черную Пятницу
Wi-Fi терморегулятор за 1630р в ближайшую Черную Пятницу
Версия 02 плагина My_ESP8266 для MaxSiteCMS.
Версия 02 плагина My_ESP8266 для MaxSiteCMS.
Отправка данных из ESP8266WiFi на сервер с CMS MaxSite
Отправка данных из ESP8266WiFi на сервер с CMS MaxSite
Оставьте комментарий.

grin LOL cheese smile wink smirk rolleyes confused surprised big surprise tongue laugh tongue rolleye tongue wink raspberry blank stare long face ohh grrr gulp oh oh downer red face sick shut eye hmmm mad angry zipper kiss shock cool smile cool smirk cool grin cool hmm cool mad cool cheese vampire snake excaim question


Выберите для анонимного комментирования (комментарий будет опубликован после проверки).

     

  

Выберите если нужно войти или зарегистрироваться и оставить комментарий от своего аккаунта.

Войти, используя

(обязательно)

Подписка на новости
discord

Email:

Разделы
  • Пожарная сигнализация107
    • Инструкции13
    • Проекты1
  • Автоматика47
  • Отопление33
  • Мониторинг29
  • Электрика15
  • Пожаротушение29
  • Умный дом31
  • Arduino12
  • Гаджеты31
  • MaxSite CMS26
Актуальное
  • СП 484
  • Автоматизация теплого пола
  • Болид vs Рубеж
  • С2000-АСПТ
  • ПЦН в кармане
  • Системы противопожарной зашиты
  • Cloud IoT
  • Проектирование
  • Программирование
  • Ссылки

Регистрация

Вход через
Последние комментарии
Управление приводами фрамуг «Giesse Varia UNI 220» при помощи «С2000-СП4».
  • АгностиГ » А как подключить varia 24в и СП4/24?
  • Андрей » Думаю что 24В подключаются также. Но мы же можем цепь 24В контролировать и С2000-КПБ. Я бы пробовал через С2000-КПБ или С2000-СП2 ИСП.2. Цепь...
От распределённых систем к сетевым.
  • T-72 » >Также очень удачной возможностью просоответствовать СП 484 оказался протокол LON(S2), который можно применить в системе "Аргус-Спектр". Но почему тогда эта возможность...
В какие пожарные датчики не нужно устанавливать добавочный резистор
  • Назар » А как же "ИП 212-90 Один дома", у которого на борту уже есть резистор 1 кОм между клеммами 1 и...
  • Андрей » А как же "ИП 212-90 Один дома" Да, действительно. Цитата из РЭ: В ИП212-90 дополнительный резистор номиналом 1,5кОм установлен внутри извещателя и...
Последние записи
Тестирование новых СП 484 на сложном объекте
Тестирование новых СП 484 на сложном объекте
Практикум по автоматизации системы медицинских газов на оборудовании Болид.
Практикум по автоматизации системы медицинских газов на оборудовании Болид.
Воспитание, как компонента профессионального технического образования.
Воспитание, как компонента профессионального технического образования.
Выбор прибора для охранной и пожарной сигнализации с GSM для частного объекта
Выбор прибора для охранной и пожарной сигнализации с GSM для частного объекта
Популярное
9+10 ошибок  при монтаже автоматической системы пожаротушения на основе С2000-АСПТ
9+10 ошибок при монтаже автоматической системы пожаротушения на основе С2000-АСПТ
Обзор моделей терморегуляторов с WiFi и облачным сервисом.
Обзор моделей терморегуляторов с WiFi и облачным сервисом.
Как настроить байпас  смесительного узла TIM JH-1036
Как настроить байпас смесительного узла TIM JH-1036
Правильное и не очень подключение С2000М к компьютеру.
Правильное и не очень подключение С2000М к компьютеру.
Прочее
Сравнение стоимости системы пожаротушения из 300 модулей ТРВ на различных системах
Сравнение стоимости системы пожаротушения из 300 модулей ТРВ на различных системах
Какой Wi-Fi терморегулятор купить на AliExpress?
Какой Wi-Fi терморегулятор купить на AliExpress?
Азартные игры в техническое обслуживание пожарной сигнализации
Азартные игры в техническое обслуживание пожарной сигнализации
Какой емкости АКБ нужен для обеспечения требуемого времени работы пожарной сигнализации на резерве
Какой емкости АКБ нужен для обеспечения требуемого времени работы пожарной сигнализации на резерве
Последние загрузки
Все загрузки
Дискуссии на форуме
  • Противопожарные шторы.
  • Не работает Сигнал 20м.
  • Используем для проектирования RubezhCAD
Реклама

Power Adapter Supply AC 110V/220V To DC 5V 12V 24V Lighting Transformer 1A 2A 3A 5A 6A 8A 10A LED Strip Power Adapter For CCTV

Tuya WiFi GAS LPG Leak detector alarm Security APP Control Safety smart home Leakage sensor

Aokin For Raspberry Pi Camera Module With Automatic Ir-cut Night Vision Camera 5mp 1080p Hd Webcam For Raspberry Pi 3 Model B

48W Quick Charger 3.0 USB Charger for Samsung A50 A30 iPhone 7 8 Xiaomi mi9 Tablet QC 3.0 Fast Wall Charger US EU UK Plug Adapte

Пожарка 108 Проектирование 81 Болид 57 Лекции 55 Рубеж 44 Социальное 44 Обзор 42 Автоматика 40 Адресные системы 34 Отопление 33 Инженерные системы 32 Пожаротушение 30 Курьез 30 Интернет Вещей 29 Практикум 29 MaxSite 28 Терморегуляторы 24 Облачный Сервис 23 Плагин 21 Теплый пол 21 AliExpress 20 Мониторинг 20 ППУ 20 Сравнение 18 Законы Ома 18 ППК 17 ГОСТ 17 Астра 16 Техобслуживание 15 Электрика 14 Arduino 13 Клапана 12 С2000-АСПТ 12 Оповещение 12 Вентиляция 12 Рубикон 12 WiFi 12 С2000М 11 Гранд Магистр 11 СП 484 11 Насосная Станция 10 Ritm 10 ESP8266 10 ВЭРС 9 Датчики 9 ПЦН 9 Авария 8 Сценарии 8 Жилой дом 8 Сообщества 7 Taggallery 7 Программирование 7 Юнитест 7 Отзыв 6 Диспетчеризация 6 Освещение 6 ПО 5 Спрут-2 5 С2000-СП4 5 МПН 4 МПТ 4 GeoRITM 4 Плазма-Т 4 Рубеж-2ОП 3 Радиоканал 3 Баня 3 Гранит 3 Firesec3 3 My_ESP8266 3 Гаджеты 3 Visio 3 РИП 3 Zigbee 2 Версет 2 Security Hub 2 Учет ресурсов 2 Navigard 2
  • Обратная связь
  • Карта сайта
  • Отзыв
  • События
  • Комментарии
  • Форум
© Технические заметки 2021. Работает на MaxSite CMS. ( Вход )
Автор не несет ответственность за последствия применения материалов сайта на практике.