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

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

18 января 2020 г.Просмотров: 2047Комментарии: 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

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

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

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


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

     

  

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

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

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

Регистрация

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

Email:

Последние комментарии
"Двухголовый" Рубеж-20П в соответствие с СП 484
  • Alex Rybin » Я думаю из-за ТР ЕАЭС 043/2017 еще пару лет отрасль лихорадить будет.
Способы отключение вентиляции при пожаре
  • artymiron » Спасибо за информацию! Очень полезно и разжёвано
  • Андрей » Пожалуйста.
Краткая инструкция действий дежурного персонала с панелью управления С2000М
  • Комментатор 77 » Пожалуйста Подскажите панель с2000 бки при нажатии не берётся с2000 прописывается попытка снятия доступ запрещён. На с2000 могу взять снять...
  • Андрей » Для того чтобы можно было управлять разделами с С2000-БКИ нужно: 1. В программе PPROG на вкладке "Привязка управления" привязать управление разделами...
Последние записи
Концепция  IoT, которая будет работать.
Концепция IoT, которая будет работать.
Скрытые параметры комнатных терморегуляторов.
Скрытые параметры комнатных терморегуляторов.
Может ли telegram-канал профессионального сообщества  стать профсоюзом или кооперативом?
Может ли telegram-канал профессионального сообщества стать профсоюзом или кооперативом?
"Двухголовый" Рубеж-20П в соответствие с СП 484
"Двухголовый" Рубеж-20П в соответствие с СП 484
Популярное
9+10 ошибок  при монтаже автоматической системы пожаротушения на основе С2000-АСПТ
9+10 ошибок при монтаже автоматической системы пожаротушения на основе С2000-АСПТ
Обзор моделей терморегуляторов с WiFi и облачным сервисом.
Обзор моделей терморегуляторов с WiFi и облачным сервисом.
Как настроить байпас  смесительного узла TIM JH-1036
Как настроить байпас смесительного узла TIM JH-1036
Правильное и не очень подключение С2000М к компьютеру.
Правильное и не очень подключение С2000М к компьютеру.
Прочее
Законодательный запрет на нормальную систему пожаротушения.
Законодательный запрет на нормальную систему пожаротушения.
Ремонт плоских светодиодных светильников комплектующими с AliExpress
Ремонт плоских светодиодных светильников комплектующими с AliExpress
Обзор приемо-контрольных приборов с пожарным сертификатом и облачным сервисом.
Обзор приемо-контрольных приборов с пожарным сертификатом и облачным сервисом.
Обзор  адресных систем охранной и пожарной сигнализации
Обзор адресных систем охранной и пожарной сигнализации
Последние загрузки
Все загрузки
Дискуссии на форуме
  • Не работает Сигнал 20м.
  • Используем для проектирования RubezhCAD
  • С2000-АСПТ и соленоидный клапан
Реклама

Cable Organizer Clips Cable Management Desktop & Workstation ABS Wire Manager Cord Holder USB Charging Data Line Bobbin Winder

XH-W1411 Thermostat LED Digital Temperature Controller AC 110V 220V 10A Switch Thermometer Smart Temperature Regulator DC 12V

Socket Set Universal Car Repair Tool Ratchet Set Torque Wrench Combination Bit A Set Of Keys Multifunction DIY toos

Round Magnetic Cable plug Type C Micro USB C 8 pin Plugs Fast Charging Adapter Phone Microusb Type-C Magnet Charger Plug

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