0

In the past few months I have successfully logged the data from the power and gas meter in my house to a remote web server.

Now I try to add the ability to enable and disable my doorbell over the internet.

Although it seems to work, I experience 2 problems.

Problems

  • Arduino freezes after a few hours

  • When the meter reading is transmitted, the doorbell can not be switched.

Questions

Is there a way to give priority to control the doorbell?

and ..

is there a way to find out why the Arduino freezes after a while?

Setup:

  • Arduino Mega 2560 board
  • Ethernet shield
  • LCD display
  • Relay
  • light sensor(for backlight control)
  • serial connection to smart power/gas meter
//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
  • I already experienced an ATmega microcontroller freeze unexpectedly, but in my case it was because of fluctuations in the power source. As you have a full Arduino board here, it should have the necessary capacitors to filter things. Though insufficient power can still be an issue even with capacitors. So, unless you power it with insufficient batteries, it's probably a bug in your code. You could add more logging and last logged message is a clue ; this requires to constantly listen on the serial port with another machine. – youen Sep 9 '17 at 23:23
  • power consumption of the relay coil could be the problem – Juraj Sep 10 '17 at 6:53
  • The Arduino is powered with 1amp adapter on the power grid – user1595774 Sep 10 '17 at 7:34
0
buffer[bufpos] = input & 127;
bufpos++;

Never use a buffer without a length protection.
Flag if max buffer is reached, force to never write after last position.

Like this:

if ( bufpos > MaxBuff ) 
{
    Serial.print("errornumber");
    bufpos = MaxBuff;
    //And force \n to finish errored message
}
| improve this answer | |
0

Please see some comments that can help you.

Why do you use a software serial port?
This ATmega has 4 real serial ports!

delay() is dangerous in the main loop runtime. Try to use it only in setup.

Every time you call a task, be sure you will not stay there forever.
Get out of the task, if something seems to be too long time. Use millis() stamp time differences to measure time spent.
Print it if necessary on debug compiler option.
Printing on failure case do not spend MCU time if there are no errors.

| improve this answer | |
  • I don't see any problem using delay() in program loops; if you are going to state that it is dangerous please explain why. SoftSerial is handy whenever you are using serial communications on the non-default serial pins, and for consistency in programming I find I tend to use it just so my code is very clear in naming pins. millis() is great, however you must remember that adding any debugging statements like lcd.write() or Serial.write() will necessarily throw off the timing. All that being said, can I ask what your main point is in this answer, please? – SDsolar Sep 10 '17 at 2:56
  • 1
    My recomendations are just to help you to find the two problems. The cpu freezes because there are ram buffer corruption. Serial hardware decreases time to process serial data and, a delay or a long loop means the system services for ethernet and serial tasks will stop – Rui Rfm Margarido Sep 10 '17 at 3:00
  • Im not sure why softserial is required, maby beqause the message from the smartpowermeter is inverted . I will try to add the buffer lenght protection and let you know te result. – user1595774 Sep 10 '17 at 7:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.