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

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

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

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

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

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:

Регистрация

Вход через
Разделы
  • Пожарная сигнализация123
    • Инструкции13
    • Проекты2
  • Автоматика54
  • Отопление33
  • Мониторинг30
  • Электрика16
  • Пожаротушение31
  • Умный дом32
  • Arduino12
  • Гаджеты32
  • MaxSite CMS26
Актуальное
  • СП 484
  • Автоматизация теплого пола
  • Болид vs Рубеж
  • С2000-АСПТ
  • ПЦН в кармане
  • Системы противопожарной зашиты
  • Cloud IoT
  • Проектирование
  • Программирование
  • Ссылки
Последние комментарии
Идеальное техническое решение по противопожарной автоматике в разделе ЭОМ
  • Виктор Чекавин » У исполнительных модулей Рубежа МДУ-1 какая то конструктивная болячка с залипшими реле. Такому давать сертификат подсудное дело. Если программно добавлено...
  • Андрей » Ну так санкции. В таких условиях, когда снипы-хрипы накладывают требования, невыполнимые в текущем социально-экономичес ком развитии общества, я считаю вообще надо...
Контроллер теплых полов Beok CCT-10 с AliExpress для зонального отопления
  • Андрей » Здравствуйте. Видимо неправильно собраны контроллеры по логической схеме ИЛИ в цепь управления котлом. Надо нормально разомкнутые контакты выхода на котел соединить...
Можно ли использование адресных пожарных датчиков с блоком пожаротушения "С2000-АСПТ" по СП 484
  • Дмитрий » 1. Блок С2000-АСПТ сам по себе работать не может согласно РЭ (только под управлением сетевого контроллера "С2000М исп. 02" и...
  • Андрей » Не до конца понял почему нельзя запускать АСПТ от адресных? От С2000М нельзя запускать адресными датчиками РЭ. Более того,...
Последние загрузки
Все загрузки
Дискуссии на форуме
  • Выбор ОПС для коттеджа
  • Отключение питания раздвижных дверей при пожарном сценарии.
  • Подключение люков дымоудаления Mercor
Реклама

Coffee machine Nivona CafeRomatica NICR 520 capuchinator maker automatic kitchen appliances goods Kapuchinator for kitchen

30/50/100pcs Universal Cable wire Connectors 222 TYPE Fast Home Compact wire Connection push in Wiring Terminal Block PCT-212

Mileseey laser distance meter electronic roulette laser digital tape rangefinder trena metro laser range finder measuring tape

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