lasselukkari / aWOT

Arduino web server library.
MIT License
283 stars 41 forks source link

Server down when use Router object #149

Closed rbnrtg closed 1 month ago

rbnrtg commented 3 months ago

Hi,

Making a REST API on an Arduino UNO and including new methods over time, it has come to a point that when making use of a Router object as the root element of my calls, after an undetermined number of calls, the server crashes. These are the REST API calls:

router.get("/ip/:ip", &change_ip_arduino); router.get("/id", &get_id_arduino); router.get("/semaphore/:rgbId/:time", &on_semaphore); router.get("/offsemaphore", &off_semaphore); router.get("/beat/:rgbId/:ncodes", &beat); router.get("/spiral/:type/:rgbId/:ncodes", &spiral); router.get("/alarm/:time", &on_alarm); router.get("/offalarm", &off_alarm); router.get("/shortlongalarm/:alarmtype/:ncodes", &short_long_alarm); router.get("/combined/:semaphoretype/:rgbId/:alarmtype/:ncodes", &combined); router.get("/relay/:type/:relayId", &on_off_relay); router.get("/watchdog/:time", &watchdog); router.get("/dhcp/:type", &add_dhcp); router.get("/dhcp", &dhcpState); router.get("/reset", &resetIP);

app.use("/box", &router);

But when the REST API calls are defined directly from the Application object, the server does not crash at any time. Why is this? Is there some kind of limitation in the Router objects?

In this way I define all the methods without Router:

app.get("/ip/:ip", &change_ip_arduino); app.get("/id", &get_id_arduino); app.get("/semaphore/:rgbId/:time", &on_semaphore); app.get("/offsemaphore", &off_semaphore); app.get("/beat/:rgbId/:ncodes", &beat); app.get("/spiral/:type/:rgbId/:ncodes", &spiral); app.get("/alarm/:time", &on_alarm); app.get("/offalarm", &off_alarm); app.get("/shortlongalarm/:alarmtype/:ncodes", &short_long_alarm); app.get("/combined/:semaphoretype/:rgbId/:alarmtype/:ncodes", &combined); app.get("/relay/:type/:relayId", &on_off_relay); app.get("/watchdog/:time", &watchdog); app.get("/dhcp/:type", &add_dhcp); app.get("/dhcp", &dhcpState); app.get("/reset", &resetIP);

lasselukkari commented 3 months ago

Does the router example code work for you?

It would really help if you could provide a minimal full example sketch of code that does not work as you expect.

Also Arduino UNO has really limited amount of ROM and RAM. Make sure you are not running out. Random crashes sound like memory issues of some sort.

rbnrtg commented 3 months ago

Yes, the Router object was working correctly, but the "combined" method of the REST API crashes the server. The method is as follows: void combined(Request &req, Response &res) {

char semaphoretype[2]; char rgb[12]; char alarmtype[2]; char ncodes[3];

req.route("semaphoretype", semaphoretype, 2); req.route("rgbId", rgb, 12); req.route("alarmtype", alarmtype, 2); req.route("ncodes", ncodes, 3);

int semaphoretypei = atoi(semaphoretype); int alarmtypei = atoi(alarmtype);

int ncodesi = atoi(ncodes); int time = 0; int secid = 0; int p_r = 0; int p_g = 0; int p_b = 0;

pixels.clear();

if(semaphoretypei!=4){ char* parameters = strtok(rgb, ":"); p_r = atoi(parameters); parameters= strtok(0, ":"); p_g = atoi(parameters); parameters = strtok(0, ":"); p_b = atoi(parameters); }else{ secid = atoi(rgb); if(secid==0){ secid = random(1,4); } }

if(alarmtypei==1){ time = 200; } else if(alarmtypei==2){ time = 1000; }

if(semaphoretypei==1){ turn_on_semaphore(p_r, p_g, p_b, time, 1, ncodesi); } else if(semaphoretypei==2){ turn_on_beat(p_r, p_g, p_b, ncodesi, 1, time); } else if(semaphoretypei==3){ turn_on_spiral_color(p_r, p_g, p_b, ncodesi, 1, time); } else if(semaphoretypei==4){ turn_on_spiral_multicolor(secid, ncodesi, 1, time); } res.print("OK"); Serial.println(freeMemory()); }

When I use the Router object, the server crashes after 10-15 executions, but when I don't use the Router object and initialize the calls from the Application object directly, this does not happen and the server does not crash.

Another interesting thing is that when the server crashes, first the called method is executed correctly (whitout return "OK") but the server crashes because the execution of the program does not return to the loop() method. This is my loop method: void loop() {

EthernetClient client = server.available(); if (client.connected()) { app.process(&client); client.stop(); } wdt_reset(); }

In case you are interested, this is the output given when compiling the program: Sketch uses 27326 bytes (84%) of program storage space. Maximum is 32256 bytes. Global variables use 1127 bytes (55%) of dynamic memory, leaving 921 bytes for local variables. Maximum is 2048 bytes.

lasselukkari commented 3 months ago

Please give me a FULL working example sketch that I can use to reproduce the problem. Try to find a minimal amount of code that demonstrates the problem you are having. Remove all unnecessary code that is not related to the aWOT server. Basically I want the a sketch that has the handlers, router and the server set up. Without this I can not help you.

rbnrtg commented 3 months ago

Hi again,

Attached is a reproducible reduced code in which the same problem occurs. The only thing is that I am using the LED on/off with the Adafruit_NeoPixel library.

reproducedProblem.zip

For the code to work, the MAC must be changed to that of your device and an undetermined number of calls must be made to the following method:

http://192.168.0.205/box/combined/1/0:255:0/1/1

lasselukkari commented 3 months ago

I will take a look at this during the weekend.

lasselukkari commented 3 months ago

Unfortunately I could not find my Ethernet shield for Arduino Uno so I had to test your code on esp8266. I commented out the parts from that combined handler that are not related to the library and just printed out the values you are reading from the path variables. I pushed the route with a load testing tool using 10000 requests without a problem.

Currently I have no reason to believe there is any problem with the library. It's most likely either your Uno running out of resources, a bug somewhere in your code or problem in your hardware setup.

What I recommend is that comment everything away from your sketch an start enabling features one by one to see where it breaks. First try out commenting all the other code away and just test the handler with the calls to the library code alone. Also comment out the library imports you have. If the sketch still does not work with just the aWOT related function calls, please send me that version of the sketch and will take another look and find the old ethernet shield from the garage.

rbnrtg commented 3 months ago

Hi @lasselukkari

The problem comes because when I make an undetermined number of calls to combined, the server crashes when the combined method is completely finished and the code does not get out of this method executed in the loop():

app.process(&client);

Since all the lines of the combined method are executed but the aWOT library reaches a point that does not allow to finish executing app.process() to jump to the client.stop() call and terminate the connection in loop() method.

In addition, I do not see much sense that if I use a Router object, this bug appears in the code and app.process() never finishes its execution, but when all calls are defined from app.get() the bug never appears and the code executes correctly.

lasselukkari commented 3 months ago

I'm really interested to fix the problem if there really is a bug in the library. Unfortunately I could not reproduce it and to move forward I would need you to give me an example sketch that demonstrates the problem without any external decencies.