[英]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庫,內存消耗確實下降了一點,但卡頓問題仍然存在
隨時指出我犯的任何錯誤,仍在學習冒險中。 =) 非常感謝您的幫助。 非常感謝。
庫版本:
沒有這些硬件,只能根據您的代碼和 Arduino 參考進行分析。
從你的代碼
while (!Ethernet.begin(mac)); // init fail
如果出現以下情況,“可能”會導致凍結:
這些是一些可能有幫助的參考:
根據維基:
https://github.com/jandrassy/EthernetENC/wiki/Examples
你應該使用:
以太網.maintain();
客戶端可用();
編寫自己的延遲函數:
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/是一個更合適的站點。
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 ?
}
將這兩個移到 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.