lasselukkari / awot Goto Github PK
View Code? Open in Web Editor NEWArduino web server library.
License: MIT License
Arduino web server library.
License: MIT License
Hi all,
the aWOT library does not compile correctly on Ubuntu, because of line 24 in aWOT.cpp
:
#include <arduino.h>
I think it has to be <Arduino.h>
. But this line is not necessary, because the Arduino header is also included by aWOT.h
.
Without line 24 in aWOT.cpp
it works very well.
I use:
Greets
Kalle
Hi,
Another question for you. On page request randomly it seems, the library returns :
400 Bad Request
If you simply refresh the browser after a short wait it works fine. There seems to be no reason to this that I can tell. It just works sometimes and not others. There is no need to reset MCU or change anything, just wait a while and the requested resource will load.
I have tried increasing RAM buffers in the header file, but that didn't really help.
Do you have any other troubleshooting steps, or ideas what might cause this?
Thanks,
Sokkou
The compatibility list does not include the Portenta H7. In the source code I could find some conditional logic dependent on the microcontroller. The logic distinguishes between low (LOW_MEMORY_MCU
) and high memory MCUs. The Portenta H7 is a high memory MCU means this should not be a problem. The Ethernet part of the Portenta H7 should be handled by the arduino/mbed-os (corresponding PR) and arduino/ArduinoCore-mbed and the Machine Control Board part arduino-libraries/Arduino_MachineControl. Means adding #include <PortentaEthernet.h>
to the example sketch should be enough to make the example work.
Nevertheless I was not able to successfully run the patched example sketch with the Portenta Machine Control Board so far. Can you think of something which needs to be patched to get the example working?
Hi, in api 1.x there is a function of Request.body() which is used in example here. But .body() function was removed by this commit "in favor of functions inherited from Stream".
Can you please create example or update docs to reflect stream api? I am struggling to find solution without the docs.
Thanks for your great work! :)
I am getting the following error when trying to implement AWOT in a class
src/WebServerAccessPoint.cpp: In member function 'void WebServerAccessPoint::start_server()':
src/WebServerAccessPoint.cpp:42:36: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say '&WebServerAccessPoint::serve_html_file' [-fpermissive]
web_server_app.get("/", &this->serve_html_file);
^~~~~~~~~~~~~~~
src/WebServerAccessPoint.cpp:42:51: error: no matching function for call to 'awot::Application::get(const char [2], void (WebServerAccessPoint::*)(awot::Request&, awot::Response&))'
web_server_app.get("/", &this->serve_html_file);
^
In file included from include/WebServerAccessPoint.h:8,
from src/WebServerAccessPoint.cpp:2:
.pio/libdeps/esp32dev/aWOT/src/aWOT.h:300:8: note: candidate: 'void awot::Application::get(const char*, void (*)(awot::Request&, awot::Response&))'
void get(const char* path, Router::Middleware* middleware);
^~~
the function has return type void
, why shouldnt this work ?
Hi, I am trying to send a complete response and close it before a reboot.
Unfortunately, the client never see the response coming. The ESP32 close the connection before flushing the full response out.
What I tried:
// res is of type aWot::Response
res.set("Connection", "close");
res.flush();
res.status(200);
res.end();
serveClient(...); // serving the client as for the examples
server.flush();
reboot();
In a similar code using the WebServer class, I never had this kind of issues.
What am I missing?
TIA
Great work -- one feature suggestion is to allow for remote updating, similar to the ESP8266HTTPUpdateServer module. github link
https://awot.net/en/1x/api.html#ress is missing reference to .print(), but the capability is used in at least one example.
Using hello world example:
Output in browser if app.get("", &indexCmd); is used to route
Output in browser if app.use(&indexCmd); is used to route
HTTP/1.0 404 Not Found Content-Type: text/html
I want to deal with the routing myself as I will serve files from SD card as well as using Ajax
I can't set the parameters to change on the web from your library, can you show me, thank you very much.
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#include <Update.h>
#endif
#include <aWOT.h>
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
WiFiServer server(80);
Application app;
char contentType[100] {};
bool shouldRestart = false;
void index(Request &req, Response &res) {
res.set("Content-Type", "text/html");
res.println("");
res.println("");
res.println("
void updateup(Request &req, Response &res) {
char * type = req.get("Content-Type");
Serial.println(type);
// this part, What do i have to do to get the value from web, thank you very much .
}
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP());
app.header("Content-Type", contentType, 100);
app.get("/", &index);
app.post("/update", &updateup);
server.begin();
}
void loop() {
WiFiClient client = server.available();
if (client.connected()) {
app.process(&client);
client.stop();
}
}
There's a guide for using this library with reactJS. NextJS is a wrapper around react and I was wondering if this can be used with nextJS applications.
Hello Lasse,
I am using your Lib for the first time, everything is going great but I noticed that the automatic generation of the scripts creates a different file than in the tutorial:
import logo from './logo.svg';
import './App.css';
function App() { <-------------------------------------------------------------
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
import React, { Component } from 'react'; <-------------------------------------------------------------
import logo from './logo.svg';
import './App.css';
class App extends Component { <-------------------------------------------------------------
render() { <-------------------------------------------------------------
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}
export default App;
The automatically generated script caused errors, but if I use the script from the tutorial, everything works fine 👍
I am using windows 10 and the following versions:
PS J:\esp\eth-test> node -v
v14.15.3
PS J:\esp\eth-test> npm -v
6.14.9
Best regards,
Armin
The generated header file of the static file are taking a lot of place. I am wondering if I could use them via a sd card with something like the sd card library to serve my static files.
Hi,
I've been using aWOT in a new project and development is going well. Thanks for your work developing aWOT.
I would like to have access to the Host
header value of HTTP requests.
I see that there is the following method to configure the aWOT application to put a header value into a buffer as a request is processed.
void header(const char* name, char* buffer, int bufferLength);
The issue I'm having with this is that as requests come in, they new request's host value clobbers the previous request's host value in the buffer. This works ok in a single threaded environment but it seems like it would be much safer if the aWOT request object would hold the header values for the particular request.
Would it be possible to add something like the following to the aWOT application instead?
void header(const char* name, int bufferLength);
This would tell the aWOT application to dynamically allocate a buffer of the specified length as part of each request object and then store the header value in buffer. The middle wear could then get the header values from the aWOT request object instead of having a separate, unconnected buffer holding the value.
Hello,
I was recently evaluating your library to replace our custom Arduino HTTP Server implementation. Of all the libraries examined so far, yours is definitely the most polished, and certainly has better routing capabilities than the nested if statements ours uses. Please do not take my suggestions as thinking your library is "bad."
In the process of evaluating your library, I noticed that it uses 1398 bytes of RAM. I believe this is because, despite importing "Arduino.h", the library uses raw C strings for everything. Those strings are always stored in RAM, just sitting there doing nothing but taking up space.
This limits what devices the library can be run on, and/or the amount of data processing that is available when using this library.
#include <WString.h>
and then use F(<HttpStatus_reasonPhrase>)
. An alternate approach for some code is to use this repository, with sleight modifications.
Hello, i can't find info on this topic...
Is it possibile to use aWOT library as a websocket server?
If yes, can you provider an example please?
Thank you very much again.
We are using ethernet on esp8266 deployed on congested network, we often see the esp8266 crashing and upon debugging it points to awot while statements of the library, so we decided adding yield on every while statements with the client.available() and it fixed the frequent crash.
We have realized the solution since slow network could make the response wait for too long and since using a while(client.available) without yield inside would potentially trigger wdt resets.
SETUP:
The build I have utilizes a W5500 wiznet 850io board with an ESP32-Wrover-E. I am programming currently with the latest ESP-IDF plugin for Arduino.
PLAN:
I was able to use the Wifi, Ethernet, Webserver, and SPI libraries to connect an HTML based site either with, without, or concurrently with Ethernet and WiFi by using a processor function of sorts on the client class. I am using HTTP protocols to gain user inputs/outputs from the website locally. I would like to use the file system uploader plugin for ESP32 with littleFS or FatFS to serve the webpages and to store the values in-between other serial communication I am parsing.
SITUATION:
This led me to the asynchronous library where the server.on() functions make manipulating the HTTP/HTML items easy. My issue is that when I tried to utilize the W5500 with the asynchronous library, it wouldn't open the server. I assume this is related to the issues with W5500 bypassing its internal TCP/IP engine and having to run in MAC RAW mode to function.
QUESTION:
My question is then, does the AWOT library contain functionality that would allow me to use a file system with functionality similar to the asynchronous webserver functions that I could then parse and send elsewhere over serial communication, while still being able to use either Ethernet or WiFi connection--I don't need both simultaneously?
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);
Okay, now this is a strange one and the use case is probably so niche it's not worth worrying about. However, I have permission to share the code, and this might be useful, at least as an application note or example.
A neat trick I have found is to gzip data before storing it, then rely on the browser itself to decode the data on the other end. This works since, as long as the right header is set, all modern browsers handle it. Which is how I am able to serve an entire 150kiB SPA from a microcontroller without any additional storage.
How to generate an embed of any binary file:
gzip --keep <file_name>
xxd -i <file_name>.gz > embeds.h
Use a regex to adjust xxd's output to "const unsigned char <file_name>[] __attribute__((section(\".fin9\"))) = ..."
Code currently used:
bool is_gzip(const uint_farptr_t data, size_t length) {
if (length < 2) {
return false;
}
byte header[2] = {pgm_read_byte_far( data + 0), pgm_read_byte_far( data + 1)};
if (header[0] == 0x1F && header[1] == 0x8B) {
return true;
}
return false;
}
if (is_gzip_P(raw_bytes, length)) {
res.set("Content-Encoding", "gzip");
}
...
Adjust Response::setDefaults
to add a check if the data to be sent is gzipped or not. If it is, set the appropriate header.
As I said, this is certainly something that does not have to live in the server, but it allows for some crazy things.
In file included from src/main.cpp:2:0:
lib/aWOT/src/aWOT.h:68:0: warning: "pgm_read_byte" redefined
#define pgm_read_byte(ptr) (unsigned char)(*ptr)
In file included from /home/am/.platformio/packages/framework-arduino-sam/cores/arduino/Arduino.h:31:0,
from src/main.cpp:1:
/home/am/.platformio/packages/framework-arduino-sam/cores/arduino/avr/pgmspace.h:102:0: note: this is the location of the previous definition
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
Hello! Great job with these lib.
Please, could you explain with some example how to implement SSE with aWOT? It is possible?
Thanks in advance and bets regards
How to build basic auth with aWOT?
I found this library really helpful when dealing Ethernet and this is similar to express, and I'm heavy nodejs express user that why I'm able to understand this library easily.
How can can I help by sending some little support?
Like buy me a coffee button to the documentation page.
Hello,
i'm using your library in my project and i like it very much, thank you for your work!
I'm building a project with an angular client flashed with your aWOT-scripts and it works very amazing.
I have a static webserver at root that serve the angular client, and a Json server listening at /api:
app.get("/api", &readAPI);
app.use(staticFiles());
I need to create a rewrite for every request that isn't /api that point to index.html of the static files, like i can do with nginx rewrites:
location / {
try_files $uri $uri/ /index.html =404;
}
example, i want that http://myip/edit-config rewrite to http://myip/.
Can you hep please?
Thank you and sorry for my english!
Sometimes I notice that request processing simply wont work, but then all of a sudden will process all of the requests quickly one after another. It's as if the web server doesn't see any requests, and they're queued up somewhere and then all of a sudden they come through. This can even happen with a single request. I'm judging this based on when my endpoint handler gets called. At the top of the handler i write something to the console (with ESP_LOGI(TAG, "...")
). I'll see my browser make the network request for a single HTTP request and it will just sit there without a response for upwards of ~30 seconds before I see my log entry and the request is serviced. The endpoint handler itself seems to be sufficiently fast though - it's just getting to that that seems to take a while. The weird part is that nothing seems to be frozen or halted anywhere - because I see other stuff going on. The other weird thing is that this seems to be intermittent - it doesn't always act this way, but it's consistent enough to be a PITA.
Here's my processing code:
if (this->wifiServer.hasClient()) {
for (int i = 0; i < this->clients.size(); i++) {
if (!this->clients[i].connected()) {
this->clients[i] = this->wifiServer.available();
this->timeToLive[i] = CLOCK.now() + LED_WEBSERVER_MAX_IDLE;
break;
}
}
}
for (int i = 0; i < this->clients.size(); i++) {
if (this->clients[i].available()) {
this->webServer.process(&this->clients[i]);
this->timeToLive[i] = CLOCK.now() + LED_WEBSERVER_MAX_IDLE;
} else if (this->timeToLive[i] && this->timeToLive[i] < CLOCK.now()) {
this->clients[i].stop();
this->timeToLive[i] = 0;
}
}
and some other relevant settings:
#define LED_WEBSERVER_MAX_CLIENTS 16
#define LED_WEBSERVER_MAX_IDLE 5000
#define LED_WEBSERVER_HTTP_BUFFER_SIZE 4096
and an extremely simple example endpoint call that exhibits this behavior:
webServer.post("/api/ping",
[](Request &req, Response &res) {
ESP_LOGI(TAG, "API Ping");
JSON_RESPONSE_OK();
});
Hi,
First thanks for sharing your excellent library. I had spent quite a while looking for something like this.
However, it appears that there may be an issue with trying to pass more than one query parameter? or perhaps I am doing something wrong. (that is always very possible... )
Example code
app.get("/c", &correctionGetRequest);
.
.
.
void correctionGetRequest(Request &req, Response &res) {
char * correctionValue = req.query("VAL");
char * chanNumberParam = req.query("ID");
int chanNumber = atoi(chanNumberParam);
res.success("text/html");
res.print("chanNumber: ");
res.print(chanNumber);
res.print("<br/>");
res.print("correctionValue:");
res.print(correctionValue);
res.print("<br/>");
It seems like when you look at the values they both equal value passed for "ID" unless you don't pass that value at all, then the other parameter works, examples :
/c?ID=1&VAL=2.63"
chanNumber: 1<br/>correctionValue:1<br/>
> curl "http://192.168.1.147/c?ID=2&VAL=2.63"
chanNumber: 2<br/>correctionValue:2<br/>
> curl "http://192.168.1.147/c?ID=3&VAL=2.63"
chanNumber: 3<br/>correctionValue:3<br/>
> curl "http://192.168.1.147/c?VAL=2.63"
chanNumber: 0<br/>correctionValue:2.63<br/>
> curl "http://192.168.1.147/c?VAL=2.63&ID=1"
chanNumber: 1<br/>correctionValue:1<br/>
> curl "http://192.168.1.147/c?VAL=2.63&ID=2"
chanNumber: 2<br/>correctionValue:2<br/>
Any suggestions?
I realized that while Request and Response are pretty specific terms, Application is certainly somewhat ambiguous, and it is entirely possible to have name collisions causing issues.
This can be a breaking change, but I have a solution to that problem. However, I do not want to make a pull request until we have handled the current ones which add additional functions.
Wrap the entire library in namespace awot {...}
, and then have at the end:
#ifndef ENABLE_AWOT_NAMESPACE
using namespace awot;
#endif
This allows someone to simply add build_flags = -D ENABLE_AWOT_NAMESPACE=True
to their platformio.ini file to enable namespacing.
My webapp that I created with create-react-app is too big (12 mb)
Hi LAsse,
Thanks one more time for this library!
I am having an unexpected behaviour with the parametrized function request::query to parse the data sent from a POST form, is there any exemple on this? Shorly, I can´t get the function to find any of the parameters in the url.
Here is the part of my code, with plenty of Serial.prints to figure out what´s going on:
void EthWebManager::handle_save(Request &req, Response &res)
{
String page = FPSTR(HTTP_BEGIN);
page.replace("{pageTitle}","Settings received");
page += FPSTR(SETTINGS_RECEIVED_BODY);
page += FPSTR(FOOTER);
page += FPSTR(REDIRECT_SCRIPT);
page.replace("{timeoutS}","12");
page.replace("{timeoutms}","12000");
page.replace("{target}","/");
page += FPSTR(HTML_END);
res.set("Content-Type", "text/html");
res.print(page);
Serial.println(req.readString());
for (unsigned int i = 0; i < _paramsCount; i++)
{
Serial.print("Looking for par: "); Serial.print(_params[i]->getName()); Serial.print(", old val: "); Serial.println(_params[i]->_value);
Serial.println(req.query(_params[i]->getName(), _params[i]->_value, _params[i]->getValueLength()));
Serial.print("New val: "); Serial.println(_params[i]->_value);
}
}
Here is the corresponding serial output:
SensorReleaseTime=10&Freelights=%2300ff00&Busylights=%23ff0000&PulseDuration=15&BusyPulseRange=120&Freepulserange=40&EthIP=192.168.5.240&EthGateway=192.168.5.1&EthSubnet=255.255.255.0&EthDNS=8.8.8.8&Pod1Ch1MAC=4C%3A75%3A25%3AC3%3A06%3A18&Pod1Ch2MAC=4C%3A75%3A25%3AC1%3AB1%3A08&Pod2Ch1MAC=AC%3A0B%3AFB%3A6E%3AF3%3A1C&Pod2Ch2MAC=4C%3A75%3A25%3AC1%3A61%3AF8&Pod3Ch1MAC=4C%3A75%3A25%3AC4%3A1D%3A88&Pod3Ch2MAC=4C%3A75%3A25%3AC1%3A2A%3A48&Usr=user&Pas=password&submit=
Looking for par: SensorReleaseTime, old val: 10
0
New val:
Looking for par: Freelights, old val: #00ff00
0
New val:
Looking for par: Busylights, old val: #ff0000
0
New val:
Looking for par: PulseDuration, old val: 15
0
New val:
Looking for par: BusyPulseRange, old val: 120
0
New val:
Looking for par: Freepulserange, old val: 40
0
New val:
Looking for par: EthIP, old val: 192.168.5.240
0
New val:
Looking for par: EthGateway, old val: 192.168.5.1
0
New val:
Looking for par: EthSubnet, old val: 255.255.255.0
0
New val:
Looking for par: EthDNS, old val: 8.8.8.8
0
New val:
Looking for par: Pod1Ch1MAC, old val: 4C:75:25:C3:06:18
0
New val:
Looking for par: Pod1Ch2MAC, old val: 4C:75:25:C1:B1:08
0
New val:
Looking for par: Pod2Ch1MAC, old val: AC:0B:FB:6E:F3:1C
0
New val:
Looking for par: Pod2Ch2MAC, old val: 4C:75:25:C1:61:F8
0
New val:
Looking for par: Pod3Ch1MAC, old val: 4C:75:25:C4:1D:88
0
New val:
Looking for par: Pod3Ch2MAC, old val: 4C:75:25:C1:2A:48
0
New val:
Looking for par: Usr, old val: user
0
New val:
Looking for par: Pas, old val: password
0
New val:
You can see the complete query and the names I am asking the function to look for.
Digging deeper I had a look at the declaration of your query function, I can´t really understand how it works, but looks like it is never entering the while loop. I have modified it to add a serial output:
bool Request::query(const char *name, char *buffer, int bufferLength) {
memset(buffer, 0, bufferLength);
char *position = m_query;
int nameLength = strlen(name);
while ((position = strstr(position, name))) {
Serial.println("QueryWhileLooping");
char previous = *(position - 1);
if ((previous == '\0' || previous == '&') &&
*(position + nameLength) == '=') {
position = position + nameLength + 1;
while (*position && *position != '&' && --bufferLength) {
*buffer++ = *position++;
}
return bufferLength > 0;
}
position++;
}
Serial.println("Query exited while loop");
return false;
}
And it seems to confirm my hypotesis....
SensorReleaseTime=10&Freelights=%2300ff00&Busylights=%23ff0000&PulseDuration=15&BusyPulseRange=120&Freepulserange=40&EthIP=192.168.5.240&EthGateway=192.168.5.1&EthSubnet=255.255.255.0&EthDNS=8.8.8.8&Pod1Ch1MAC=4C%3A75%3A25%3AC3%3A06%3A18&Pod1Ch2MAC=4C%3A75%3A25%3AC1%3AB1%3A08&Pod2Ch1MAC=AC%3A0B%3AFB%3A6E%3AF3%3A1C&Pod2Ch2MAC=4C%3A75%3A25%3AC1%3A61%3AF8&Pod3Ch1MAC=4C%3A75%3A25%3AC4%3A1D%3A88&Pod3Ch2MAC=4C%3A75%3A25%3AC1%3A2A%3A48&Usr=user&Pas=password&submit=
Looking for par: SensorReleaseTime, old val: 10
Query exited while loop
0
New val:
Looking for par: Freelights, old val: #00ff00
Query exited while loop
0
New val:
Looking for par: Busylights, old val: #ff0000
Query exited while loop
0
New val:
Looking for par: PulseDuration, old val: 15
Query exited while loop
0
New val:
Looking for par: BusyPulseRange, old val: 120
Query exited while loop
0
New val:
Looking for par: Freepulserange, old val: 40
Query exited while loop
0
New val:
Looking for par: EthIP, old val: 192.168.5.240
Query exited while loop
0
New val:
Looking for par: EthGateway, old val: 192.168.5.1
Query exited while loop
0
New val:
Looking for par: EthSubnet, old val: 255.255.255.0
Query exited while loop
0
New val:
Looking for par: EthDNS, old val: 8.8.8.8
Query exited while loop
0
New val:
Looking for par: Pod1Ch1MAC, old val: 4C:75:25:C3:06:18
Query exited while loop
0
New val:
Looking for par: Pod1Ch2MAC, old val: 4C:75:25:C1:B1:08
Query exited while loop
0
New val:
Looking for par: Pod2Ch1MAC, old val: AC:0B:FB:6E:F3:1C
Query exited while loop
0
New val:
Looking for par: Pod2Ch2MAC, old val: 4C:75:25:C1:61:F8
Query exited while loop
0
New val:
Looking for par: Pod3Ch1MAC, old val: 4C:75:25:C4:1D:88
Query exited while loop
0
New val:
Looking for par: Pod3Ch2MAC, old val: 4C:75:25:C1:2A:48
Query exited while loop
0
New val:
Looking for par: Usr, old val: user
Query exited while loop
0
New val:
Looking for par: Pas, old val: password
Query exited while loop
0
New val:
Now manually BusyPulseRange: Query exited while loop
0
Do somebody ever tried to build a library over this server?
Since "ESPAsyncWiFiManager" is not working over Ethernet, I am trying to build my library for a Webmanager which works over Ethernet, but I can´t figure out how to pass member functions of my EthWebManager class as middlewares.
I am trying with std::bind, here is a code snippet:
void EthWebManager::auth(Request &req, Response &res) {
char * authHeader = req.get("Authorization");
if (!str_iseq(authHeader, encoded_login1.c_str()) && !str_iseq(authHeader, encoded_login2.c_str())) {
res.set("WWW-Authenticate", "Basic realm="Secret Area"");
res.sendStatus(401);
res.end();
DEBUG_WM(F("Unauthenticated"));
}
}
void EthWebManager::handleRoot(Request &req, Response &res)
{
String page = FPSTR(WFM_HTTP_HEAD);
page.replace("{v}", "Home");
page += FPSTR(HTTP_SCRIPT);
page += FPSTR(HTTP_STYLE);
page += FPSTR(HTTP_HEAD_END);
page += F("
void EthWebManager::startEthManager()
{
server->begin();
delay(500);
mainapp.header("Authorization", authBuffer, 200);
mainapp.use(std::bind(&EthWebManager::auth, this, std::placeholders::_1, std::placeholders::_2));
auto callable = std::bind(&EthWebManager::handleRoot, this, std::placeholders::_1, std::placeholders::_2);
mainapp.get("/", callable );
}
But I keep getting these errors:
lib\EthWebManager\src\EthWebManager.cpp: In member function 'void EthWebManager::startEthManager()':
lib\EthWebManager\src\EthWebManager.cpp:361:98: error: no matching function for call to 'awot::Application::use(std::_Bind_helper<false, void (EthWebManager::)(awot::Request&, awot::Response&), EthWebManager, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type)'
mainapp.use(std::bind(&EthWebManager::auth, this, std::placeholders::_1, std::placeholders::_2));
^
In file included from lib\EthWebManager\src\EthWebManager.h:22:0,
from lib\EthWebManager\src\EthWebManager.cpp:14:
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:322:8: note: candidate: void awot::Application::use(const char*, awot::Router*)
void use(const char* path, Router* router);
^
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:322:8: note: candidate expects 2 arguments, 1 provided
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:323:8: note: candidate: void awot::Application::use(awot::Router*)
void use(Router* router);
^
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:323:8: note: no known conversion for argument 1 from 'std::_Bind_helper<false, void (EthWebManager::)(awot::Request&, awot::Response&), EthWebManager, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::_Mem_fn<void (EthWebManager::)(awot::Request&, awot::Response&)>(EthWebManager, std::_Placeholder<1>, std::_Placeholder<2>)>}' to 'awot::Router*'
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:324:8: note: candidate: void awot::Application::use(const char*, void ()(awot::Request&, awot::Response&))
void use(const char path, Router::Middleware* middleware);
^
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:324:8: note: candidate expects 2 arguments, 1 provided
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:325:8: note: candidate: void awot::Application::use(void ()(awot::Request&, awot::Response&))
void use(Router::Middleware middleware);
^
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:325:8: note: no known conversion for argument 1 from 'std::_Bind_helper<false, void (EthWebManager::)(awot::Request&, awot::Response&), EthWebManager, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::_Mem_fn<void (EthWebManager::)(awot::Request&, awot::Response&)>(EthWebManager, std::_Placeholder<1>, std::_Placeholder<2>)>}' to 'void ()(awot::Request&, awot::Response&)'
lib\EthWebManager\src\EthWebManager.cpp:363:29: error: no matching function for call to 'awot::Application::get(const char [2], std::_Bind<std::_Mem_fn<void (EthWebManager::)(awot::Request&, awot::Response&)>(EthWebManager*, std::_Placeholder<1>, std::_Placeholder<2>)>&)'
mainapp.get("/", callable ); /*
^
In file included from lib\EthWebManager\src\EthWebManager.h:22:0,
from lib\EthWebManager\src\EthWebManager.cpp:14:
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:300:8: note: candidate: void awot::Application::get(const char*, void ()(awot::Request&, awot::Response&))
void get(const char path, Router::Middleware* middleware);
^
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:300:8: note: no known conversion for argument 2 from 'std::_Bind<std::_Mem_fn<void (EthWebManager::)(awot::Request&, awot::Response&)>(EthWebManager, std::_Placeholder<1>, std::_Placeholder<2>)>' to 'void ()(awot::Request&, awot::Response&)'
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:301:8: note: candidate: void awot::Application::get(void ()(awot::Request&, awot::Response&))
void get(Router::Middleware* middleware);
^
.pio\libdeps\m5stack-core-esp32\aWOT\src/aWOT.h:301:8: note: candidate expects 1 argument, 2 provided
*** [.pio\build\m5stack-core-esp32\lib4c6\EthWebManager\EthWebManager.cpp.o] Error 1
Hello,
I tried this library on a Arduino Mega, I used your Ethernet Example, and it increased 15% on my current sketch, is that normal?
That's too much.
Size before Library: 74%
Size after Library; 89%
Hello,
I was recently evaluating your library to replace our custom Arduino HTTP Server implementation. Of all the libraries examined so far, yours is definitely the most polished, and certainly has better routing capabilities than the nested if statements ours uses. Please do not take my suggestions as thinking your library is "bad."
We would like to be able to serve custom fallback pages, and I can not find a method to do so. Does this ability exist? If this is not possible, it would be a much appreciated feature.
These are the three use cases we currently have, and why I believe it is important:
I believe replacing/appending return m_response.sendStatus(404);
in "Application::m_process()" with a check for a custom "Router::Middleware" would easily enable this function.
Followed the tutorial on how to serve static file. npm start
(dev mode) runs OK, but after run
build
and dist
, I get this error: SyntaxError: missing } after function body2.e33b5351.chunk.js:1:5878 ........
In file included from .pio\libdeps\nodemcuv2\aWOT\src\aWOT.cpp:23:0:
.pio\libdeps\nodemcuv2\aWOT\src\aWOT.cpp: In member function 'void Response::set(const char*, const char*)':
.pio\libdeps\nodemcuv2\aWOT\src\aWOT.h:72:52: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
#define SIZE(array) (sizeof(array) / sizeof(*array))
^
.pio\libdeps\nodemcuv2\aWOT\src\aWOT.cpp:104:25: note: in expansion of macro 'SIZE'
if (m_headersCount >= SIZE(m_headers)) {
I hope it can be fixed, thanks!
Hello, thanks for your lib.
I use it with ESP32-S3 and the ethernet server fix you provided
When using WiFi, all works good,
when using ethernet,
I get net::ERR_CONNECTION_REFUSED on (random) file, for instance when the client try :
GET http://192.168.0.81/medium.gif net::ERR_CONNECTION_REFUSED
On the server side (ESP) I get no trace, that medium.gif has been requested.
Any help appreciated.
BR
Hello,
I was looking at pull #125 (I added a note there), and noticed a potential optimization. If myApp.process(client)
is called with a closed client, the urlBuffer is allocated, then the writeBuffer is allocated, then the check if client is closed is called.
This means that the heap pointer jumps quite a bit in normal operation.
It might be better to just copy and paste this code as first thing in all the versions of Application::process(...)
if (!client) {
return;
}
PS: I am changing employers, so am no longer getting paid to ask for nice features / make suggestions. I just like the library and your work on it. It also means I lost access to the $300 Arduino based PLC I was using to test this code with.
Hi, when I try to add the success response, it keep saying that class response has no member named succes.... This piece of code:
void statusCmd(Request &req, Response &res) {
res.success("application/json");
String currentStatus;
..........
Any idea?
Arduino ide for esp8266
Is there a possibility to operate the awot web server based on https with WiFi and Ethernet ?
Thank you for some advice,
Armin
Using the default Ethernet
example on Teensy 4.1 with the NativeEthernet
library, aWOT will only correctly respond to requests if I do:
void loop(){
EthernetClient client = server.available();
if (client.connected()) {
while (client.available()) {
client.read();
}
app.process(&client);
client.stop();
}
}
If I don't read() all available bytes first, I get an error 400.
Thoughts?
Hello,
I found this library very useful for working with ethernet and WiFi at the same time.
I have been able to serve files from SD (using ESP32+ SDcard reader) to the browser using one of your examples, but due to my lack of knowledge about how library works and data flow, I don't understand how to send files from browser to be stored in the SD (or SPIFFS etc..).
I think it can be done in a similar way as OTA update, but I can't get it :(
Is there any example?
Thank you in advance.
/Davide
I'm just trying to write the simplest ping command.
Using nodeJS and axios to send data to the board:
setInterval(async () => {
console.log("ping");
let res = await axios.get('http://192.168.0.150/ping');
console.log("RES:", res);
}, 3000);
And here is my arduino code (uno wifi rev 2)
#include <aWOT.h>
char ssid[] = "";
char pass[] = "";
int status = WL_IDLE_STATUS;
WiFiServer server(80);
Application app;
void setup() {
Serial.begin(9600);
connect_WiFi();
printWifiStatus();
app.get("/ping", &ping);
server.begin();
}
void loop() {
WiFiClient client = server.available();
if (client.connected()) {
app.process(&client);
}
}
void ping(Request &req, Response &res) {
Serial.println("pong");
res.sendStatus(200);
}
void connect_WiFi() {
// attempt to connect to Wifi network:
while (status != WL_CONNECTED) {
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 5 seconds for connection:
delay(5000);
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
Board connects to wifi, and for each ping sent it logs "pong" to serial out. But in nodeJS no response is ever received.
I've tried everything I can think of
res.status(200);
res.print("sent");
I've tried using res.end();
and res.flush();
in any order, I've tried setting any and all headers with no success.
I've tried sending both get
and post
using xhr from the browser as well. Ultimately I need this to work with a nodeJS backed but I'm open to any library that isn't axios
if that's the issue...
All examples on https://awot.net make it look like it just works. What am I doing wrong?
Compiler exits in this way:
cannot declare variable 'server' to be of abstract type 'EthernetServer'
Last library release installed, arduino IDE 1.8.12, ESP32 1.0.6 eith ethernet library and w5500 board.
Thanks :)
Hi! Consider this made up example:
void setupWebApp() {
webApp->use(&requestStartMiddleware);
webApp->get("/ping", &pingHandler);
webApp->get("/gpio/inputs", &getInputStatesHandler);
webApp->use(&safetyMiddleware);
webApp->post("/gpio/outputs/:pin", &setOutputStateHandler);
webApp->use(&requestEndMiddleware);
}
requestStartMiddleware
runs on all routes/ping
is hit, safetyMiddleware
should be skipped/gpio/outputs/:pin
should only be allowed when safetyMiddleware
check succeedsrequestEndMiddleware
runs at the end of every routeMy goal is to not have to duplicate anything, like I don't want to do the safety check in all relevant handlers nor I don't want to do path checking in the safetyMiddleware
.
So I tried using two routers:
void setupWebApp() {
safeRouter->get("/ping", &pingHandler);
safeRouter->get("/gpio/inputs", &getInputStatesHandler);
webApp->use(&safeRouter);
unsafeRouter->use(&safetyMiddleware);
unsafeRouter->post("/gpio/outputs/:pin", &setOutputStateHandler);
webApp->use(&unsafeRouter);
}
but I quickly realized if I don't use separate path for routers, they are executed both, so after my pingHandler
the safetyMiddleware
is still being executed.
Is there any nice way to achieve this? Thanks!
First - I really love the aWOT framework. It's been incredibly useful for creating an API-driven service on my ESP8266.
I recently updated from 2.x to 3.x and was surprised with some of the major changes that were not documented in any sort of release notes. The online documentation is accurate for 3.x, but makes no reference on changes required to upgrade from an older 2.x installation.
In the future it would be helpful if there was a migration guide for breaking changes.
Both version 1 and version 3 end up having this error on an Arduino MKR WiFi 1010. This error is very intermittent.
Not sure this is because of HTTP 1.1. But the data is retrieved after the curl call times out.
Hello,
I was recently evaluating your library to replace our custom Arduino HTTP Server implementation. Of all the libraries examined so far, yours is definitely the most polished, and certainly has better routing capabilities than the nested if statements ours uses. Please do not take my suggestions as thinking your library is "bad."
This is a very low priority issue, but may help others as well.
The AVR platform uses 16 bit pointers. To get around this issue, on larger microcontrollers the memory is paged. When using PROGMEM, or __attribute__((section(".fini7")))
, the resulting pointer is only valid within that page of memory! "avr/pgmspace.h" provides a handy pgm_get_far_address
macro to convert the pointer to a 32 bit pointer, which solves that issue. However, it requires a different function than the usual pgm_read_byte
.
We would be extremely appreciative if you would add an overload of Response::writeP(const uint_farptr_t data, size_t length)
that uses pgm_read_byte_far
.
If you are interested, I should be able to submit a pull request to resolve this issue.
I've implemented a RESTful API server. At the moment I'm running the server in a superloop. I'm calling code similar to the one from https://github.com/lasselukkari/aWOT/blob/master/examples/Ethernet/Ethernet.ino#L43-L49 periodically. I observed that dependent on the call periodicity of this logic requests are not handled properly. In my particular case (Arduino Due + my application logic) responses are delivered properly if the logic is called every 430 microseconds (approx. 2325 Hz). If the logic is called every 3-4 milliseconds (33.3-250 Hz) my client does not get a response. I'm thinking about migrating the logic into RTOS tasks. Would be great if you could provide a rule of thumb w.r.t. how regularly logic needs to be executed. That would help to assign task priorities/define task trigger periodicity.
EDIT: The requests seem to be buffered. This means one has to consider call periodicity w.r.t. application specific requirements only like e.g. accetable responsiveness for HMI related endpoints which is often around 250ms (4Hz).
Hi, I really like your lib, good work there!
Any ideas how to add ESPAsyncWebServer? It should act like normal normal WiFiServer but you dont need to wait in loop() for new requests. Other libraries were easily ported onto that, but I am struggling with this one. Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.