Atmega328P+ENC28J60 freezes after 12 hours operation

Viewed 649

I am quite new to the ethernet world. So please bear with me if I do ask stupid questions.

I designed a PCB using Atmega328P+ENC28J60 chip (schematic attached in the images below - sch1, sch2). The function of this board is basically sending GET requests to the server and retrieve a set of json data to turn on output pins, so based on my understanding my board acts as a client only right? The code is attached below:

#include <EEPROM.h>
#include <ArduinoJson.h>
#include <EthernetENC.h>

#define OUT0    2
#define OUT1    A3
#define OUT2    A2
#define OUT3    A1
#define OUT4    A0
#define OUT5    9
#define OUT6    8
#define OUT7    7
#define OUT8    6
#define OUT9    5
#define CS      10

// mac: 46 57 5a 6b 48 51
#define HOSTNAME  "autolighting.afa-sports.com"

#define ID_SIZE   6

static byte mac[ID_SIZE];
static char macBuffer[ID_SIZE*2+1];
const byte output[] PROGMEM = {OUT0, OUT1, OUT2, OUT3, OUT4, OUT5, OUT6, OUT7, OUT8, OUT9};
EthernetClient client;

void clientRead() {
  StaticJsonDocument<40> filter;
  StaticJsonDocument<120> doc;
  filter.clear();
  doc.clear();
  filter["data"]["relay_actions"] = true;

  client.find("\r\n\r\n");
  deserializeJson(doc, client, DeserializationOption::Filter(filter));
  client.flush();
  delay(50);

  if (!doc["data"]["relay_actions"].isNull()) {
    for (byte i = 0; i < 10; i++) {
//      Serial.print(doc["data"]["relay_actions"][i].as<bool>());
      digitalWrite(pgm_read_byte_near(&output[i]), doc["data"]["relay_actions"][i].as<bool>());
    }
//    Serial.println();
  }
  
  filter.clear();
  doc.clear();
}

void sendReq() {
  client.println(F("GET /api/iot/master-controller/get-command HTTP/1.1"));
  client.println(F("Host: autolighting.afa-sports.com"));
//  client.println(F("DEVICE-ID: 46575a6b4851"));
  client.print(F("DEVICE-ID: "));
  client.println(macBuffer);
  client.println(F("Connection: close"));
  client.println();
}

void setup() {
  Serial.begin(115200);

  for (byte i = 0; i < 10; i++) {
    pinMode(pgm_read_byte_near(&output[i]), OUTPUT);
    digitalWrite(pgm_read_byte_near(&output[i]), LOW);
  }
  
  for (uint8_t i = 0; i < ID_SIZE; i++) {
    byte charByte = EEPROM.read(i);
    if (charByte != 0) {
      char temp[2];
      mac[i] = charByte;
      itoa(mac[i], temp, 16);
      strcat(macBuffer, strlwr(temp));    // REMOVE strlwr IN RELEASE VERSION
      free(temp);
      delay(10);
    }
  }
  strcat(macBuffer, '\0');

  Ethernet.init(CS);
  while (!Ethernet.begin(mac));
  client.setTimeout(5000);
  delay(1000);
}

void loop() {
  while (!Ethernet.begin(mac));   // init fail

  delay(1000);
  if (client.connect(HOSTNAME, 80)) {
    sendReq();
    clientRead();
    client.stop();
  }
  delay(3000);
}

Due to the high SRAM consumption and I might have other things (not sure what yet) to add in to the board in the future, I tried to minimize the dynamic memory by changing this (in the uipethernet-conf.h file):

#define UIP_SOCKET_NUMPACKETS    5
#define UIP_CONF_MAX_CONNECTIONS 4
#define UIP_CONF_UDP_CONNS       4

to this:

#define UIP_SOCKET_NUMPACKETS    2
#define UIP_CONF_MAX_CONNECTIONS 2
#define UIP_CONF_UDP_CONNS       1

I'm wondering will this affect the system performance? Btw, I've also set the timeout to 5s

After 13 hours of smooth operation, the board freezes and only became normal when i hard-reset the board. Currently, I'm connecting my board to a wireless extender, because I am not sitting right next to the wifi router. It looks like a memory leakage issue to me, but does memory leakage issue still exists in the latest ArduinoJson and EthernetENC/UIPEthernet library?

P/S: I was using UIPEthernet.h previously, but someone guided me to try out the EthernetENC library, the memory consumption definitely gone down a little bit, but the freezing problem still persist

Feel free to point out any mistakes I make, still in the learning adventure. =) Your help is greatly appreciated. Thank you very much.

Library version:

  • ArduinoJson 6.17.2
  • EthernetENC 2.0.0
  • UIPEthernet 2.0.9

Schematics: sch1 sch2

3 Answers

don't have these hardware, only analyses based on your code and Arduino references.

from your code

    while (!Ethernet.begin(mac));   // init fail

which "may" cause freeze if :

  1. Ethernet.begin always return false
  2. is Ethernet.begin "ok" to call multiple time ?

These are some reference which may help :

Arduino DHCP failed to configure

https://electronics.stackexchange.com/questions/67045/ethernet-begin-only-works-with-sd-card-removed-why

according to the wiki :

https://github.com/jandrassy/EthernetENC/wiki/Examples

You should use :

  1. Ethernet.maintain();

  2. client.available();

  3. write your own delay function :

    void mDelay(unsigned long milliseconds) {
      const unsigned d = 1;
      while (milliseconds > d) {
        Ethernet.maintain();
        delay(d);
        milliseconds -= d;
      }
      Ethernet.maintain();
      delay(milliseconds);
    }

note: for arduino or electronics question, may be https://electronics.stackexchange.com/ is a more suitable site.

  1. not sure about if the hardware can handle, you may try to "burst test" :
void loop() {
    unsigned long currentMillis = millis();

    Serial.print("Time: ");
    Serial.println(currentMillis);

    // burst test
    if (client.connect(HOSTNAME, 80)) {
        sendReq();
        Serial.print(", After sendReq(); ");

        clientRead();
        Serial.print(", After client.clientRead(); ");

        client.stop();
        Serial.print(", After client.stop(); ");
    }
    mdelay( 500 );
    Serial.println( ', mdelay(500); ' );
    // mdelay will call Ethernet.maintain();
    // Serial.print("After Ethernet.maintain(); ");
    // use Serial.println to check where it freeze ?
}
  1. based on your code, may be these can help to reduce memory problem :

move these two outside of void clientRead() as these 2 used every 4 seconds :

4 sec * 60 = 240 times / minutes

240 * 24 = 5760 times / day

  StaticJsonDocument<40> filter;
  StaticJsonDocument<120> doc;

void clientRead() {
    ...

note: all the code are illustration, not tested. you may need to modify to make it work.

note: please check with your hardware if it can handle, or will it burn when run such test ?

lots of thanks to @ocrdu and @Flash Ang for the suggestions, the code below is the working code and my PCB has been running continuously for 58 hours till now:

#include <EEPROM.h>
#include <ArduinoJson.h>
//#include <UIPEthernet.h>
#include <EthernetENC.h>

#define OUT0    2
#define OUT1    A3
#define OUT2    A2
#define OUT3    A1
#define OUT4    A0
#define OUT5    9
#define OUT6    8
#define OUT7    7
#define OUT8    6
#define OUT9    5
#define CS      10

// host name: "autolighting.afa-sports.com
// resource: "/api/iot/master-controller/get-command"
// mac: 46 57 5a 6b 48 51
// mac (char): FWZkHQ
#define HOSTNAME  "autolighting.afa-sports.com"

#define ID_SIZE   6

static byte mac[ID_SIZE];
static char macBuffer[ID_SIZE*2+1];
const byte output[] PROGMEM = {OUT0, OUT1, OUT2, OUT3, OUT4, OUT5, OUT6, OUT7, OUT8, OUT9};
EthernetClient client;

void clientRead() {
  StaticJsonDocument<40> filter;
  StaticJsonDocument<120> doc;
  filter.clear();
  doc.clear();
  filter["data"]["relay_actions"] = true;

  client.find("\r\n\r\n");
  deserializeJson(doc, client, DeserializationOption::Filter(filter));
  client.flush();
  delay(50);

  if (!doc["data"]["relay_actions"].isNull()) {
    for (byte i = 0; i < 10; i++) {
//      Serial.print(doc["data"]["relay_actions"][i].as<bool>());
      digitalWrite(pgm_read_byte_near(&output[i]), doc["data"]["relay_actions"][i].as<bool>());
    }
//    Serial.println();
  }
  
  filter.clear();
  doc.clear();
}

void sendReq() {
  client.println(F("GET /api/iot/master-controller/get-command HTTP/1.1"));
  client.println(F("Host: autolighting.afa-sports.com"));
//  client.println(F("DEVICE-ID: 46575a6b4851"));
  client.print(F("DEVICE-ID: "));
  client.println(macBuffer);
  client.println(F("Connection: close"));
  client.println();
}

void setup() {
  for (byte i = 0; i < 10; i++) {
    pinMode(pgm_read_byte_near(&output[i]), OUTPUT);
    digitalWrite(pgm_read_byte_near(&output[i]), LOW);
  }
  
  for (uint8_t i = 0; i < ID_SIZE; i++) {
    byte charByte = EEPROM.read(i);
    if (charByte != 0) {
      char temp[2];
      mac[i] = charByte;
      itoa(mac[i], temp, 16);
      strcat(macBuffer, strlwr(temp));    // REMOVE strlwr IN RELEASE VERSION
      free(temp);
      delay(10);
    }
  }
  strcat(macBuffer, '\0');

  Ethernet.init(CS);
  while (!Ethernet.begin(mac));
  client.setTimeout(5000);
  delay(1000);
}

void loop() {
  Ethernet.maintain();
  
  while (!Ethernet.begin(mac)) //Serial.println(F("IF"));   // init fail

//  Serial.println(F("IS"));     // init success
  delay(1000);
  if (client.connect(HOSTNAME, 80)) {
    sendReq();
    clientRead();
    client.stop();
  }
  Ethernet.maintain();
  delay(3000);
}

//int freeRam () {    // check remaining RAM space
//  extern int __heap_start, *__brkval;
//  int v;
//  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
//}

This time to work smart, I used the default ip address to my router and monitor whether my PCB is still connected to the router by looking at the DHCP client list, and remove all the serial commands in the code. I'm afraid for my past experiences on the disconnection issues, it might be just a short temporary disconnect from the router, instead of a permanent disconnection.

I'm sorry if there is any lack of testing in this program. Thank you for the time spent to provide me all the useful suggestions =)

Related