Проблема стабильности с ведением журнала веб-сервера и смартметра

relay web-server logging

За последние несколько месяцев я успешно записал данные со счетчиков электроэнергии и газа в моем доме на удаленный веб-сервер.

Теперь я пытаюсь добавить возможность включать и отключать дверной звонок через Интернет.

Несмотря на то, что это работает, я столкнулся с двумя проблемами.

Проблемы

  • Arduino зависает через несколько часов

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

Вопросы

Есть ли способ сделать управление дверным звонком приоритетным?

и ..

есть ли способ выяснить, почему Arduino через некоторое время зависает?

Настройка:

  • Плата Arduino Mega 2560
  • Экран Ethernet
  • ЖК-дисплей
  • Ретрансляция
  • датчик освещенности(для управления подсветкой)
  • последовательное подключение к интеллектуальному счетчику электроэнергии/газа
//V1.1: Cleaned up messy code
#include <LiquidCrystal_I2C.h>
#include <Wire.h>  // Comes with Arduino IDE
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#include <AltSoftSerial.h>
#include <SPI.h>
#include <Ethernet.h>


//setup ethernet server
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; //assign arduino mac address
byte ip[] = { 192, 168,  178, 95}; // ip in lan assigned to arduino
byte gateway[] = { 192,  168, 178,  1}; // internet access via router
byte subnet[] = {  255,  255,  255,  0}; //subnet mask
EthernetServer server(84); //server port arduino server will use
char thingSpeakAddress[] = "";
IPAddress myDns(8, 8, 8, 8);
EthernetClient client;
String writeAPIKey = "XXXMX2WYYR0EVZZZ";
String tsData = "hello"; //dummy data
String readString; //used by server to capture POST request
const int updateThingSpeakInterval = 20 * 1000; // Time interval in milliseconds to update ThingSpeak (number of seconds * 1000 = interval)
// Variable Setup
long lastConnectionTime = 0;
boolean lastConnected = false;
int failedCounter = 0;
unsigned long int updateCounter = 0; //set refresh counter to 0
//////////////////////
//slimme meter////////
/////////////////////
char c;
AltSoftSerial altSerial;
const int requestPin = 48; //data pin connected to smartmeter
char input; // incoming serial data (byte)
bool readnextLine = false;
#define BUFSIZE 75
char buffer[BUFSIZE]; //Buffer for serial data to find \n .
int bufpos = 0;
long mEVLT = 0; //Meter reading Electrics - consumption low tariff
long mEVHT = 0; //Meter reading Electrics - consumption low tariff
long mEAV = 0; //Meter reading Electrics - Actual consumption
long mG = 0; //Meter reading Gas
//unsigned long lastConnectionTime = 0;             // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds
// the "L" is needed to use long type numbers

//const unsigned long postingInterval = 10 L * 1000 L; // delay between updates, in milliseconds, the "L" is needed to use long type numbers
/////////////////////////////////////////
//photcel////////controls lcd backlight//
////////////////////////////////////////
int photocellPin = 5; // the cell and 10K pulldown are connected to a0
int photocellReading; // the analog reading from the analog resistor divider
//boolean doorbell = true;

int creset = 43; //Telling arduino that you are connecting pin 43 with reset



//////////
//setup///
//////////
void setup() {
  Wire.begin();
  digitalWrite(22, LOW);
  digitalWrite(creset, HIGH);
  pinMode(creset, OUTPUT);
  pinMode(22, OUTPUT); //pin selected to control
  pinMode(6, OUTPUT); //pin selected to control
  pinMode(7, OUTPUT); //pin selected to control
  pinMode(8, OUTPUT); //pin selected to control
  //pinMode(5, OUTPUT); //pin 5 selected to control
  Ethernet.begin(mac, ip, myDns, gateway, subnet);
  server.begin();
  Serial.begin(9600);
  altSerial.begin(9600);
  Serial.println(F("Smarthome controller")); // keep track of what is loaded
  Serial.println(F("Send a g in serial monitor to test client")); // what to do to test client
  lcd.begin(20, 4); // initialize the lcd for 16 chars 2 lines, turn on backlight
  // sensorValue = analogRead(sensorPin);
  lcd.backlight(); // finish with backlight on
  //-------- Write characters on the display ------------------
  // NOTE: Cursor Position: Lines and Characters start at 0
  lcd.setCursor(0, 0); //Start at character 4 on line 0
  lcd.print("Smarthome controller");
  delay(1000);
  lcd.setCursor(0, 1);
  lcd.print("powerd by arduino");
  delay(1000);
  lcd.print("                  ");
  lcd.setCursor(0, 2);
  lcd.print("");
  lcd.setCursor(0, 3);
  delay(1000);
  lcd.print("Slimmemeter:     ");
  //lcd.backlight(); // turn on backlight.
  lcd.setCursor(0, 2);
  lcd.print("Doorbell: onn      ");
}
void loop() {
  if (updateCounter > 400) {
    digitalWrite(creset, LOW);  // Reset arduino
  }
  // Update ThingSpeak
  if (!client.connected() && (millis() - lastConnectionTime > updateThingSpeakInterval)) {
    decodeTelegram();
  }
  // Check if Arduino Ethernet needs to be restarted
  if (failedCounter > 3) {
    startEthernet();
  }
  lastConnected = client.connected();
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char cl = client.read();
        //read char by char HTTP request
        if (readString.length() < 100) {
          //store characters to string
          readString += cl;
          //Serial.print(c);
        }
        //if HTTP request has ended
        if (cl == '\n') {
          ///////////////
          Serial.print(readString); //print to serial monitor for debuging
          //now output HTML data header
          if (readString.indexOf('?') >= 0) { //don't send new page
            client.println("HTTP/1.1 204 Zoomkat");
            client.println();
            client.println();
          } else {
            client.println(F("HTTP/1.1 200 OK")); //send new page on browser request
            client.println(F("Content-Type: text/html"));
            client.println();
            client.println(F("<HTML>"));
            client.println(F("<HEAD>"));
            client.println(F("<TITLE>Smarthome</TITLE>"));
            client.println(F("</HEAD>"));
            client.println(F("<BODY>"));
            client.println(F("<H1>Smarthome control page</H1>"));
            // DIY buttons
            client.println(F("Deurbel"));
            client.println(F("<a href=/?on2 target=inlineframe>ON</a>"));
            client.println(F("<a href=/?off3 target=inlineframe>OFF</a><br><br>"));

            client.println(F("Pins"));
            client.println(F("&nbsp;<a href=/?off2468 target=inlineframe>ALL ON</a>"));
            client.println(F("&nbsp;<a href=/?off3579 target=inlineframe>ALL OFF</a>"));
            client.println(F("<IFRAME name=inlineframe style='display:none'>"));
            client.println(F("</IFRAME>"));
            client.println(F("</BODY>"));
            client.println(F("</HTML>"));
          }
          delay(1);
          //stopping client
          client.stop();
          if (readString.indexOf("**secretcode**") > 0) //checks for secretkey
          {
            ///////////////////// control arduino pin
            if (readString.indexOf("on2") > 0) //checks for 2
            {
              digitalWrite(22, LOW); // set pin 5 high
              //Serial.println(F("Led 5 On"));
              //Serial.println();
              lcd.setCursor(0, 2);
              lcd.print("Doorbell: on      ");
              //doorbell = true;
            }
            if (readString.indexOf("off3") > 0) //checks for 3
            {
              digitalWrite(22, HIGH); // set pin 5 low
              //Serial.println(F("Led 5 Off"));
              //Serial.println();
              lcd.setCursor(0, 2);
              lcd.print("Doorbell: off      ");
              //doorbell = false;
            }

            //clearing string for next read
            readString = "";
          }
        }
      }
    }
  }

  //lcd.setCursor(0, 0); //Start at character 4 on line 0
  //lcd.print("SMARTHOME CONTROLLER;.");
  //String analogValue = String(analogRead(A5), DEC);
  photocellReading = analogRead(photocellPin);

  //lcd.setCursor(0, 1);
  //lcd.print(photocellReading);
  if (photocellReading < 800) {
    lcd.backlight(); // turn on backlight.
  } else {
    lcd.noBacklight(); // turn on backlight.
  }



}


////////////////////////////////////////////////////////////////////////////
//////////////////////////FUNCTIONS/////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
void updateThingSpeak(String tsData)
//void sendGET() //client function to send and receive GET data from external server.
{
  //lcd.setCursor(0, 2);
  // lcd.print("                   ");
  lcd.setCursor(13, 3);
  lcd.print("verstur");
  // delay(1000);
  if (client.connect(thingSpeakAddress, 80)) {
    client.print("GET /smarthome/log.php?stroomLaag=");
    client.print(mEVLT);
    client.print("&stroomHoog=");
    client.print(mEVHT);
    client.print("&stroomVerbruik=");
    client.print(mEAV);
    client.print("&gasStand=");
    client.print(mG);
    client.println(" HTTP/1.1");
    client.println("Host: .nl");
    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();
    lastConnectionTime = millis();
    updateCounter++;
    Serial.print("Connecting to log server...");
    Serial.println(updateCounter);
    //x=x+1; //page upload counter
    // lcd.setCursor(0,3);
    // lcd.print("Data send           ");
    //delay(1000);
    lcd.setCursor(13, 3);
    //lcd.print("                    ");
    lcd.print("ok        ");
    lcd.setCursor(16, 3);
    lcd.print(updateCounter);
    if (client.connected()) {
      Serial.println("Connecting to suscesfull...");
      //Serial.println();
      failedCounter = 0;
    } else {
      failedCounter++;
      Serial.println("Connection to failed (" + String(failedCounter, DEC) + ")");
      Serial.println("connection failed");
      Serial.println();
      lcd.setCursor(13, 3);
      lcd.print("fout");
      lcd.setCursor(16, 3);
      lcd.print(String(failedCounter, DEC));
    }
  } else {
    failedCounter++;
    Serial.println("Connection to ThingSpeak Failed (" + String(failedCounter, DEC) + ")");
    Serial.println();
    lastConnectionTime = millis();
  }
  while (client.connected() && !client.available()) delay(1); //waits for data
  while (client.connected() || client.available()) { //connected or data available
    char cl = client.read(); //gets byte from ethernet buffer
    //readString += c; //places captured byte in readString
    //Serial.print(c); //print raw data
  }
  client.stop(); //stop client
  readString = ""; //clear readString variable
}

////////////////////////////////////////////////////

void startEthernet() {
  client.stop();
  Serial.println("Connecting Arduino to network...");
  Serial.println();
  delay(1000);
  // Connect to network amd obtain an IP address using DHCP
  if (Ethernet.begin(mac) == 0) {
    Serial.println("DHCP Failed, reset Arduino to try again");
    Serial.println();
  } else {
    Ethernet.begin(mac, ip, gateway, gateway, subnet);
    server.begin();
    Serial.println("Arduino connected to network using DHCP");
    Serial.println();
  }
  delay(1000);
}

////////////////////////////////////////////////////

void decodeTelegram() {
  long tl = 0;
  long tld = 0;
  if (altSerial.available()) {
    //input = altSerial.read();
    c = altSerial.read();
    // --- 7 bits instelling ---
    c &= ~(1 << 7);
    char inChar = (char) c;
    //Serial.print(c);
    input = c;
    //Serial.println(input);
    //char inChar = (char)input;
    // Fill buffer up to and including a new line (\n)
    buffer[bufpos] = input & 127;
    bufpos++;
    if (input == '\n') { // We received a new line (data up to \n)
      if (sscanf(buffer, "1-0:1.8.1(%ld.%ld", & tl, & tld) == 2) {
        tl *= 1000;
        tl += tld;
        mEVLT = tl;
      }
      // 1-0:1.8.2 = Elektra verbruik hoog tarief (DSMR v4.0)
      if (sscanf(buffer, "1-0:1.8.2(%ld.%ld", & tl, & tld) == 2) {
        tl *= 1000;
        tl += tld;
        mEVHT = tl;
      }
      // 1-0:1.7.0 = Electricity consumption actual usage (DSMR v4.0)
      if (sscanf(buffer, "1-0:1.7.0(%ld.%ld", & tl, & tld) == 2) {
        mEAV = tl + tld;
      }
      // 0-1:24.2.1 = Gas (DSMR v4.0) on Kaifa MA105 meter
      //if (strncmp(buffer, "0-1:24.2.0", strlen("0-1:24.2.1")) == 0) {
      if (sscanf(strrchr(buffer, '(') + 1, "%d.%d", & tl, & tld) == 2) {
        mG = (tl * 1000) + tld;
      }
      //}
      // Empty buffer again (whole array)
      for (int i = 0; i < 75; i++) {
        buffer[i] = 0;
      }
      bufpos = 0;
    }
    if (input == '!') { //uitroepteken geeft einde van telegram aan, dus we gaan data versturen
      // if (millis() - lastConnectionTime > postingInterval) {
      // httpRequest();
      updateThingSpeak("field1=0");
      // httpRequest();
      mEVLT = 0;
      mEVHT = 0;
      mEAV = 0;
      mG = 0;
      client.stop();
      //}
    } //Einde vraagteken detectie
    //Serial.print(c);
  } //Einde 'if AltSerial.available'
} //Einde 'decodeTelegram()' functie

, 👍0

Обсуждение

Я уже сталкивался с неожиданным зависанием микроконтроллера ATmega, но в моем случае это было из-за колебаний в источнике питания. Поскольку у вас есть полная плата Arduino, на ней должны быть необходимые конденсаторы для фильтрации. Хотя недостаточная мощность все еще может быть проблемой даже с конденсаторами. Так что, если вы не питаете его от недостаточных батарей, это, вероятно, ошибка в вашем коде. Вы можете добавить больше журналов, а последнее зарегистрированное сообщение будет подсказкой; это требует постоянного прослушивания последовательного порта другой машиной., @youen

может быть проблема в потребляемой мощности катушки реле, @Juraj

Arduino питается от адаптера на 1 ампер от сети., @user1595774


2 ответа


1

Пожалуйста, ознакомьтесь с некоторыми комментариями, которые могут вам помочь.

Почему вы используете программный последовательный порт?
У этой ATmega 4 реальных последовательных порта!

delay() опасен во время выполнения основного цикла. Попробуйте использовать его только в настройках.

Каждый раз, когда вы вызываете задачу, убедитесь, что вы не останетесь там навсегда.
Выходите из задачи, если что-то кажется слишком долгим. Используйте millis() для отметки разницы во времени, чтобы измерить затраченное время.
При необходимости распечатайте его в опции компилятора отладки.
Печать в случае сбоя не тратит время MCU, если ошибок нет.

,

Я не вижу никаких проблем с использованием delay() в циклах программы; если вы собираетесь заявить, что это опасно, пожалуйста, объясните, почему. SoftSerial удобен всякий раз, когда вы используете последовательную связь на последовательных выводах, отличных от стандартных, и для согласованности в программировании я обычно использую его, чтобы мой код был очень четким в именовании выводов. millis() великолепен, однако вы должны помнить, что добавление каких-либо операторов отладки, таких как lcd.write() или Serial.write(), обязательно приведет к смещению времени. При всем при этом могу ли я спросить, в чем заключается ваш главный смысл в этом ответе, пожалуйста?, @SDsolar

Мои рекомендации только для того, чтобы помочь вам найти две проблемы. Процессор зависает из-за повреждения буфера оперативной памяти. Последовательное оборудование сокращает время обработки последовательных данных, а задержка или длинный цикл означают, что системные службы для Ethernet и последовательных задач будут остановлены., @Rui Rfm Margarido

Я не уверен, почему требуется softserial, возможно, потому что сообщение от smartpowermeter инвертировано. Я постараюсь добавить защиту от длины буфера и сообщу вам о результате., @user1595774


1
buffer[bufpos] = input & 127;
bufpos++;

Никогда не используйте буфер без защиты длины.
Отметить, если достигнут максимальный буфер, принудительно никогда не записывать после последней позиции.

Вот так:

if ( bufpos > MaxBuff ) 
{
    Serial.print("errornumber");
    bufpos = MaxBuff;
    // И заставить \n закончить сообщение об ошибке
}
,