Atmega328P + ENC28J60은 12 시간 작동 후 동결

Nov 18 2020

저는 이더넷 세계에 익숙하지 않습니다. 그러니 제가 어리석은 질문을하면 참아주세요.

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

이것이 시스템 성능에 영향을 미칠지 궁금합니다. Btw, 시간 제한도 5 초로 설정했습니다.

13 시간의 원활한 작동 후 보드가 정지되고 보드를 하드 리셋 할 때만 정상이되었습니다. 현재 Wi-Fi 라우터 바로 옆에 앉아 있지 않기 때문에 보드를 무선 확장기에 연결하고 있습니다. 나에게 메모리 누출 문제처럼 보이지만 최신 ArduinoJson 및 EthernetENC / UIPEthernet 라이브러리에 메모리 누출 문제가 여전히 존재합니까?

P / S : 이전에 UIPEthernet.h를 사용하고 있었지만 누군가가 EthernetENC 라이브러리를 사용해 보도록 안내해주었습니다. 메모리 소비는 확실히 약간 줄었지만 여전히 정지 문제가 지속됩니다.

학습 모험 중에 내가 저지른 실수를 자유롭게 지적하십시오. =) 귀하의 도움에 감사드립니다. 대단히 감사합니다.

라이브러리 버전 :

  • ArduinoJson 6.17.2
  • 이더넷 ENC 2.0.0
  • UIPEthernet 2.0.9

회로도 :

답변

FlashAng Nov 19 2020 at 11:57

이러한 하드웨어가 없으며 코드 및 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. Ethernet.maintain ();

  2. client.available ();

  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/ 더 적합한 사이트입니다.

FlashAng Nov 20 2020 at 10:56
  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. 코드에 따라 메모리 문제를 줄이는 데 도움이 될 수 있습니다.

이 두 가지는 4 초마다 사용되므로 void clientRead () 외부로 이동합니다.

4 초 * 60 = 240 회 / 분

240 * 24 = 5760 회 / 일

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

void clientRead() {
    ...

참고 : 모든 코드는 그림이며 테스트되지 않았습니다. 작동하도록 수정해야 할 수도 있습니다.

참고 : 하드웨어가 처리 할 수 ​​있는지 확인하십시오. 아니면 그러한 테스트를 실행할 때 타겠습니까?

NelsonLim Nov 26 2020 at 17:53

제안에 대한 @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가 여전히 라우터에 연결되어 있는지 모니터링하고 코드의 모든 직렬 명령을 제거했습니다. 연결 해제 문제에 대한 과거의 경험이 두렵습니다. 영구 연결 해제 대신 라우터에서 일시적으로 일시적으로 연결 해제되는 것일 수 있습니다.

이 프로그램에서 테스트가 부족하면 죄송합니다. 나에게 모든 유용한 제안을 제공하는 데 시간을 보내 주셔서 감사합니다 =)