Arduino365 important update

By:
Posted: 30th June 2019
Category: Arduino
Comments: 0

Update (14/07/2019): The reason for the instability and crash of the web server was the power supply providing less voltage somehow... Mystery solved (he says)! Still the extra code modifications are staying.

So after 56 days my Arduino Uno with the Ethernet Shield has decided to stop responding to GET requests (pings were working just fine). To me it seems like the web server has crashed for some reason (buggy code?) or maybe there has been some memory issue (my poor coding...) - I have no idea. The worst part about is that the arduino did not reboot which in this case makes it not good for a 24/7 project. I need to do better! So as a result I have decided to address these potential unknown issues with additional checks that will be carried out periodically (I utilise "Threads" in my sketch). One will monitor the amount of free memory (and if it is below a certain preconfigured level => reset the board!) and the second one will verify that port (80) is still listening for connections (and if not => reset the board). I am fully aware that the second method is not bullet proof because it will provide a false positive when all 4 sockets are being used - I can live with that downside + there are potentially some workarounds for that...

To monitor free memory I have used this library written by Sudar Muthu. It maybe is dated but it gets the job done. There are also few extra changes to the code: The output of the uptime includes the free memory figure + I can reset the board by sending a "hard" command. And when I say "hard" I mean an actual digital pin is connected to the RESET of the Ethernet Shield. Put it to "LOW" and it resets - old school hack (seems to be doing a decent job). So we will see now if those two extra "threads" running every 50 and 115 seconds will help with the stability of the project.


I am fully aware that this is not a fascinating blog (no robots, moving parts etc) for most people and that is why I am not going to bore you any further and just post the code below:

#include <Thread.h>
#include <ThreadController.h>

// Watchdog library !
#include <avr/wdt.h>

#include <SPI.h>
#include <Ethernet.h>

// Latest add on to monitor the board ! 26/06/2019
#include <MemoryFree.h>

int resetPin = 9;
boolean reading
= false;

byte mac
[] = {
0x5D, 0xAD, 0xB1, 0x21, 0x2A, 0x3E }; //ethernet shield mac address

byte ip
[] = {
192, 168, 7, 150 }; // arduino IP in lan

byte gateway
[] = {
192, 168, 7, 1 }; // internet access via router

byte subnet
[] = {
255, 255, 255, 0 }; //subnet mask

// (port 80 is default for HTTP):
EthernetServer server
(80);

// ThreadController that will controll all threads
ThreadController controll
= ThreadController();

// watchdog Thread
Thread
* myWatchdogThread = new Thread();

// Thread to check how much free memory there is !
Thread
* myfreeMemoryThread = new Thread();

// Thread to check if the Web Server is still running !!!
Thread
* myWebServerStatusThread = new Thread();

// callback for watchdog reset Thread...
void watchdogReset()
{
wdt_reset
(); // reset the WDT timer
}

// Hard and soft Arduino reset !
void resetArduino()
{
digitalWrite
(resetPin, LOW); // hit the reset pin hard !
// Have this as a measure just in case somehow Arduino
// manages to get to this point... or something does not work
// as expected for some reason... cable is out ?
wdt_enable
(WDTO_15MS);
}

// callback for myfreeMemoryThread...
void howMuchFreeMemory()
{

int freeMem = getFreeMemory();
if (freeMem < 1100)
{
resetArduino
();
}
}


// callback for myWebServerStatusThread
void isWebServerListening()
{
if (!server)
{
resetArduino
();
}
}

// When watchdog kicks it will run whatever is in here + do a normal watchdog reset
// just to be on the safe side of things... That is if it will even get there!
ISR
(WDT_vect)
{
digitalWrite
(resetPin, LOW); // hit the reset pin hard !
// Have this as a measure just in case somehow Arduino
// manages to get to this point... or something does not work
// as expected for some reason... cable is out ?
wdt_enable
(WDTO_15MS);
}

void setup()
{
digitalWrite
(resetPin, HIGH); // so we don't end up in an ever lasting loop
pinMode
(resetPin, OUTPUT);
wdt_reset
(); // reset the WDT timer
wdt_disable
();
// start the Ethernet connection and the server:
Ethernet
.begin(mac, ip, gateway, gateway, subnet);
// Ethernet.begin(mac, ip);
server
.begin();

// Start the watchdog - 8 seconds !
cli
(); // disable all interrupts
MCUSR
&= ~(1<<WDRF);
// Enter Watchdog Configuration mode:
WDTCSR
= (1<<WDCE) | (1<<WDE);
// Set Watchdog settings: interrupt enable, 8 second timeout !
// To enable an interrupt and reset mode just add this " | (1<<WDE) "
WDTCSR
= (1<<WDIE) | 1<<WDP0 | 1<<WDP3 | (1<<WDE);
sei
();

myWatchdogThread
->onRun(watchdogReset);
myWatchdogThread
->setInterval(6000); // watchdog reset every 6 seconds !

myfreeMemoryThread
->onRun(howMuchFreeMemory);
myfreeMemoryThread
->setInterval(50000); // every 50 seconds.

myWebServerStatusThread
->onRun(isWebServerListening);
myWebServerStatusThread
->setInterval(115000); // every 115 seconds

// Adds the WatchDog Reset Thread !
controll
.add(myWatchdogThread);
// Adds the memory check Thread !
controll
.add(myfreeMemoryThread);
// Adds a check on the Socket Thread !
controll
.add(myWebServerStatusThread);
}

void loop(){
// run ThreadController
// this will check every thread inside ThreadController,
controll
.run();
// Rest of my code... Ethernet Web Server that returns uptime only !
// listen for incoming clients
EthernetClient client
= server.available();
if (client)
{
// send http reponse header
client
.println("HTTP/1.1 200 OK");
client
.println("Content-Type: text/html");
client
.println("Access-Control-Allow-Origin: *");
client
.println();
// process request.
processClient
(client);
}
}

void processClient(EthernetClient client)
{
// http request will end with a blank line
boolean lineIsBlank
= true;
// string buffers for receiving URL arg
char bufferUrl[16];
int urlChars = 0;

while (client.connected())
{
if (client.available())
{
char c = client.read();
if(reading && c == ' ') reading = false;
if(c == '?') reading = true; // ? in GET request was found, start reading the info
//check that we are reading, and ignore the '?' in the URL, then append the get parameter into a single string
if(reading && c != '?')
{
bufferUrl
[urlChars++] = c;
}
if (c == '\n' && lineIsBlank) break;
if (c == '\n')
{
lineIsBlank
= true;
}
else if (c != '\r')
{
lineIsBlank
= false;
}
}
}

if
(
( bufferUrl[0] == 'u' ) &&
( bufferUrl[1] == 'p' )
)
{
// First... Let me check when was the last reading done...
unsigned long currentMillis = millis();
unsigned long seconds = currentMillis / 1000;
int days = seconds / 86400;
seconds
%= 86400;
byte hours
= seconds / 3600;
seconds
%= 3600;
byte minutes
= seconds / 60;
seconds
%= 60;
client
.print(String(days) + ":" + String(hours) + ":" + String(minutes) + ":" + String(seconds) + ", Free Mem: " + getFreeMemory());
delay
(1); // give the web browser a moment to recieve
}
else if
(
// Reset the entire board via the wire reset !
( bufferUrl[0] == 'h' ) &&
( bufferUrl[1] == 'a' ) &&
( bufferUrl[2] == 'r' ) &&
( bufferUrl[3] == 'd' )
)
{
// First lets check if the device has been working for at least 5 minutes...
unsigned long currSeconds = millis() / 1000;
// 5 min : 300
// 15 min : 900
// 30 min : 1800
// 12 hours : 43200
if (currSeconds >= 300)
{
client
.flush();
client
.stop(); // close connection
delay
(10); // pause for a short moment !
digitalWrite
(resetPin, LOW); // hit the reset pin hard !
}
}

client
.flush();
client
.stop(); // close connection
}

Hey, like this? Why not share it with a buddy?