簡體   English   中英

Atmega328P+ENC28J60 運行 12 小時后死機

[英]Atmega328P+ENC28J60 freezes after 12 hours operation

我對以太網世界很陌生。 因此,如果我確實問了一些愚蠢的問題,請耐心等待。

我使用 Atmega328P+ENC28J60 芯片設計了一個 PCB(下圖中附有示意圖 - sch1、sch2)。 這塊板子的功能基本上是向服務器發送GET請求並檢索一組json數據以打開輸出引腳,所以根據我的理解,我的板子只充當客戶端對嗎? 代碼附在下面:

#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);
}

由於 SRAM 消耗很高,而且我將來可能還有其他東西(不確定是什么)要添加到板上,我嘗試通過更改它(在 uipethernet-conf.h 文件中)來最小化動態內存:

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

對此:

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

我想知道這會影響系統性能嗎? 順便說一句,我還將超時設置為 5 秒

經過 13 小時的平穩運行,板卡死機,只有在我硬重置板時才變得正常。 目前,我正在將我的電路板連接到無線擴展器,因為我沒有坐在 wifi 路由器旁邊。 對我來說這看起來像是內存泄漏問題,但是最新的 ArduinoJson 和 EthernetENC/UIPEthernet 庫中是否仍然存在內存泄漏問題?

P/S:我之前用的是UIPEthernet.h,后來有人指導我嘗試了EthernetENC庫,內存消耗確實下降了一點,但卡頓問題仍然存在

隨時指出我犯的任何錯誤,仍在學習冒險中。 =) 非常感謝您的幫助。 非常感謝。

庫版本:

  • ArduinoJson 6.17.2
  • 以太網ENC 2.0.0
  • UIPE以太網2.0.9

示意圖: sch1 sch2

沒有這些硬件,只能根據您的代碼和 Arduino 參考進行分析。

從你的代碼

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

如果出現以下情況,“可能”會導致凍結:

  1. Ethernet.begin 總是返回 false
  2. Ethernet.begin“可以”多次調用嗎?

這些是一些可能有幫助的參考:

Arduino DHCP 配置失敗

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

根據維基:

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

你應該使用:

  1. 以太網.maintain();

  2. 客戶端可用();

  3. 編寫自己的延遲函數:

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

注意:對於 arduino 或電子產品問題,可能是https://electronics.stackexchange.com/是一個更合適的站點。

  1. 不確定硬件是否可以處理,您可以嘗試“突發測試”:
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. 根據您的代碼,這些可能有助於減少內存問題:

將這兩個移到 void clientRead() 之外,因為這兩個每 4 秒使用一次:

4 秒 * 60 = 240 次/分鍾

240 * 24 = 5760 次/天

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

void clientRead() {
    ...

注意:所有代碼都是插圖,未經測試。 您可能需要修改以使其正常工作。

注意:請檢查您的硬件是否可以處理,或者在運行此類測試時會燃燒嗎?

非常感謝@ocrdu 和@Flash Ang 的建議,下面的代碼是工作代碼,我的 PCB 至今已連續運行 58 小時:

#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);
//}

這次為了聰明地工作,我使用路由器的默認IP地址,並通過查看DHCP客戶端列表來監控我的PCB是否仍然連接到路由器,並刪除代碼中的所有串行命令。 我擔心我過去在斷開連接問題上的經驗,它可能只是與路由器的短暫臨時斷開連接,而不是永久斷開連接。

如果此程序中缺少測試,我很抱歉。 感謝您花時間為我提供所有有用的建議 =)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM