I am using the ESP8266 with Arduino IDE and have set up a server at port 200 . The IP is also defined as the same as 192.168.1.100.
ESP8266WebServer server(200);
IPAddress ip(192, 168, 1, 100); //Node static IP
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
server.on("/parseIFTTT", parseIFTTT);
void parseIFTTT() {
String message;
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
Serial.println(message);
Serial.println(server.argName(0));
Serial.println(server.arg(0));
server.send(200, "text/plain", "Success " + message);
}
is done to route the parseIFTTT request to this parseIFTTT() method.
I have done port forwarding and using the duckdns to access this server from outside.
This is my duckdns address
http://xxxxxx.duckdns.org:200/parseIFTTT
When I make a POST using a POSTMAN tool with content type as text/plain , the body contents are shown in the serial monitor as
plain
--body contents--
But when the same request is made from IFTTT the serial monitor shows nothing but plain as empty .Initially I felt the issue is with IFTTT.
But that is not the issue as when I use the WiFiWebServer example in the arduino , using the following code
String req = client.readString();
Serial.println(req);
client.flush();
I see the data from IFTTT as :
POST /parseIFTTT HTTP/1.1
Content-type: text/plain
host: xxxxxx.duckdns.org:200
content-length: 27
x-newrelic-id: XAMGV15QGwQJVllRDgQ=
x-newrelic-transaction: PxQFA1NbAQQJVwJWA1dSB0YdUFIOFQZOEgEPVA5ZBFYGXAwECFgFAFcUG0MHUwoLBAcDAxVs
Connection: close
{"value":"test data from IFTTT"}
So I believe I am doing something wrong with the server.args(). I am under the impression that server.args() should give the body contents used in the POST whether contentType is text/plain or x-www-form-urlencoded.
Am I doing something wrong or with the server.args() can't we get the body data from the POST request ?
There are a few 'gotchas' in the ESP8266WebServer's implementation. I have found the body shows up in the
server.arg("plain")
but only if the class cannot find any key value pairs. The ESP8266WebServer will look for an '=' and only if it cannot find one will it put the body in the "plain" arg.
This arg will contain the full body so you will have to parse the JSON yourself. I have found ArduinoJson to be a very easy to use library to do so.
Short example:
void handleRequest() {
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(server.arg("plain"));
root.printTo(Serial);
}
On a sidenote. If you are testing with a POSTMAN like tool, do not forget to set the 'Content-length'. ESP8266WebServer will treat your body as empty (or of a different length) if it does not correspond with this header value.
Hope this answers your question.
Thanks the code solution for " how to get the IP client using ESP8266WebServer" is :
// HTTP Request Path, IRIA Entry Form
server.on("/", [](){
// As sending the form as a response to the client
server.send(200, "text/html",login_CASA);
String addy = server.client().remoteIP().toString();
Serial.println(addy);
});
Related
I'm trying to write some code for the Arduino Uno WiFi Rev2 board. Specifically, I'd like to send some data, using a POST HTTP request, to an API Endpoint that I wrote. I've tested this endpoint with Postman, and it works fine. However, when I try POSTing my data using the WiFiNina library (https://www.arduino.cc/en/Reference/WiFiNINA), the request never makes it to my endpoint.
My Arduino sketch consists of two files. The first is my "main" file that serves as the entrypoint for my code and deals with most of my code's functionality. In this file, I setup the WiFiClient as instructed in Arduino's documentation:
#define URL "myappdomain.azurewebsites.net"
...
WiFiClient client;
int status = WL_IDLE_STATUS;
...
void setup(){
...
status = WiFi.begin(WifiSSID, WifiPassword);
while (WiFi.status() != WL_CONNECTED) { //Wait for the WiFI connection completion
delay(500);
Serial.println("Waiting for connection");
}
...
}
...
void loop(){
...
String requestBody =
"{\n \"clientReadings\": {\n \"sensorA\": [],\n \"sensorB\": []\n },\n \"deviceId\": 1,\n \"millilitersConsumed\" : 999\n}";
//send the request
postData(requestBody);
...
}
In my second file, here's the part of my code that handles this API request:
void postData(String body){
if(WiFi.status()== WL_CONNECTED){ //Check WiFi connection status
if (client.connect(URL, 80)){
client.println("POST /api/PGWC HTTP/1.1");
client.print("Host: ");
client.println(URL);
client.println("Content-type: application/json");
client.println("Accept: */*");
client.println("Cache-Control: no-cache");
client.print("Host: ");
client.println(URL);
client.println("Accept-Encoding: gzip, deflate");
client.print("Content-Length: ");
client.println(body.length());
client.println("Connection: close");
client.println();
client.println(body);
}else{
Serial.println("Error connecting to server");
}
}
}
I've structured this request based off examples and documentation I've found online for the WiFiNina library. (Here's one example: https://www.arduino.cc/en/Reference/WiFiNINAClient). The header information and the body are based off the requests I've sent through Postman, so I believe that the content of my request is accurate. I believe that I'm able to connect to the server via the "client.connect" line because I never see the error message printed to the Serial monitor, but I have seen the Serial Monitor display the contents of "Serial.println" statements that I placed prior to my "client.println" statements. However, the Azure Function App that hosts the API endpoints shows no indication that the API endpoint was hit. When I send this same data with Postman, the function app logs the connection just fine.
When I try printing the contents of the body, URL, and body.length() to the Serial monitor from within the postData function, everything appears as expected. Further, I've tried different options on the "Connection: close" line to no avail.
For reference, here's the content that Postman tells me it's sending when it successfully hits the API endpoint. I've also tried port 443. It works fine in Postman, but, again, not in the Arduino.
Host: myappdomain.azurewebsites.net:80
Content-Type: application/json
User-Agent: PostmanRuntime/7.17.1
Accept: */*
Cache-Control: no-cache
Postman-Token: [some UUID, some other UUID]
Host: myappdomain.azurewebsites.net:80
Accept-Encoding: gzip, deflate
Content-Length: 130
Connection: keep-alive
cache-control: no-cache
{
"clientReadings": {
"sensorA": [],
"sensorB": []
},
"deviceId": 1,
"millilitersConsumed" : 123
}```
(I've changed the domain for this post, so this request won't work if you try to plug it into Postman because myappdomain.azurewebsites.net is not my real domain)
EDIT: figured it out.
turns out wifi.println("Content-Length: " + body.length());
is not the same as wifi.println("Content-Length: " + String(body.length()));
the body length var needs to be wrapped as a string then it works fine
What confused me was seeing the OP write it as
wifi.print("Content-Length: ");
wifi.println(body.length());
I did not think it needed to be wrapped as a String, but I guess when concatenating strings its different.
Also it was curious I needed this header to begin with (the REST app didn't require it to work) and to have a correct body value, using something like 200 caused the connection to hang (but on the REST app setting it to something generic like 200 worked fine as well)
END EDIT ...leaving original content below for reference
I am trying to figure this out as well, so this is an extended comment rather than an answer.
When I use an app like postman (rested) it works I get 200 response, but from arduino uno wifi rev2 it fails with HTTP/1.1 422 Unprocessable Entity
{"reason":"Unprocessable Entity","error":true}
my json is just String body = "{\n \"value\": 500\n}";
and inside if (wifi.connect(serverAddress, port)) {
I call
void postData(String body) {
// send HTTP request header
wifi.println("POST /gardener/Douglas/George/water HTTP/1.1");
wifi.println("Host: " + String(serverAddress));
wifi.println("Content-Type: application/json");
wifi.println("Accept: */*");
wifi.println("Cache-Control: no-cache");
wifi.println("Accept-Encoding: gzip, deflate");
wifi.println("Accept-Language: en-us");
wifi.println("Content-Length: " + body.length()); //ANSWER: body.length() needs to be wrapped as a string, see above
wifi.println("Connection: close");
wifi.println(); // end HTTP request header
wifi.println(body);
}
fwiw a get request works fine
void getHello() {
wifi.println("GET /hello HTTP/1.1");
wifi.println("Host: " + String(serverAddress));
wifi.println("Connection: close");
wifi.println(); // end HTTP request header
}
I'm using a NodeMCU and want to log data to my local Web2Py server.
The request: "http://minion.local:8000/ardulog/default/add/6476366/45643" works fine from the browser and returns a record id.
My Arduino can connect to my server but don't get any return data error or otherwise and nothing appears in my database.
// This will send the request to the server
samptime = millis();
rpm = (samptime + 333) % 96789;
String request = "10.0.0.244:8000/ardulog/default/add/"+String(samptime)+"/"+String(rpm)+" HTTP/1.1";
Serial.println("\ntrying: ");
Serial.println("GET " + request);
Serial.println("host: minion.local");
client.println("GET " + request);
client.println("host: minion.local");
// if there are incoming bytes available
// from the server, read them and print them:
while (client.available()) {
char c = client.read();
Serial.print(c);
}
Serial.println("closing connection");
client.stop();
I've tried every variant I can think of but get only the following:
connecting to minion.local
[hostByName] request IP for: minion.local
[hostByName] Host: minion.local IP: 10.0.0.244
Requesting:
GET 10.0.0.244:8000/ardulog/default/add/112725/16269 HTTP/1.1
host: minion.local
closing connection
wait 5 sec...
Why am I not reading anything retuned from the server?
SOLVED! Though I was unsuccessful POSTing to Google Sheets, simply changing the word from GET to POST worked with Web2Py without sending any body data:
if(client.connect(host,port))
client.println("POST /ardulog/default/add/" + String(samptime)+ "/" + String(rpm) + " HTTP/1.1");
(still not receiving a result page from the server though)
I have a setup which uses the ESP8266 12E and it opens a web server at a specific port 200. I have used the port forwarding to route the incoming data to this server .And I have used the duckdns to register the IP and call the duckdns domain to trigger the ESP.
This works fine and I am able to trigger using the following
http://mydomain.duckdns.org:200/parseIFTTT
Using the postman tool, with the contentType as plain/text and the method as POST what ever contents I pass are getting parsed by parseIFTTT method in the ESP
void parseIFTTT() {
String message;
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
Serial.println(message);
server.send(200, "text/plain", "Success");
}
But when tried to integrate with IFTTT for any Facebook or gmail events, I am not able to parse the data from IFTTT.
The request goes to ESP8266 but the request data I am not able to parse.
The following is the request :
URL : http://mydomain.duckdns.org:200/parseIFTTT
Method : POST
Content Type: text/plain
Body : {{Message}}
In the body I have just added the {{Message}} only. In the serial monitor I get the op as blank
1
plain:
I found the issue after a lot of debugging.
The esp8266\hardware\esp8266\2.2.0\libraries\ESP8266WebServer\src\Parsing.cpp
in the core library for the Http server was using the 'Content-Length' and IFTTT was sending the request header with name as "content-length' and hence the content length was not retrieved and the parsed data was not retrieved.
Not sure whether its an issue with the IFTTT where they send this as a lower cased value.
Using Qt5, how to simply check if given url is available?
Not using special functions for signal slots, but simply using something like bool isUrlAvailable(QString url), are there any function like this?
Update QUrl.isValid() is incorrect answer, it is just checks if url is correctly formed.
Update 2 QUrl.host() is incorrect answer too, it is just returns host part of given url, it does not check for its availability.
Update 3 pinging host is also incorrect, because url may be available, but does not accept icmp echo (=ping)
Yes, you can do a HEAD request to a given URL.
bool urlExists (QString url_string) {
QUrl url(url_string);
QTcpSocket socket;
socket.connectToHost(url.host(), 80);
if (socket.waitForConnected()) {
socket.write("HEAD " + url.path().toUtf8() + " HTTP/1.1\r\n"
"Host: " + url.host().toUtf8() + "\r\n\r\n");
if (socket.waitForReadyRead()) {
QByteArray bytes = socket.readAll();
if (bytes.contains("200 OK")) {
return true;
}
}
}
return false;
}
This is just an example for 200 OK and you might also want to check if the status code is some other in 2XX or in 3XX (redirection) class.
So taking from pajaja + a few other SO answers + a tutorial I found
(http://www.blikoon.com/networking/http-potocol-writting-a-simple-client-using-qt-qtcpsocket-and-troubleshooting-using-telnet)
I came up with this tweaked version because the one above didn't work
bool urlExists(QUrl theurl){
QTextStream out(stdout);
QTcpSocket socket;
QByteArray buffer;
socket.connectToHost(theurl.host(), 80);
if (socket.waitForConnected()) {
//Standard http request
socket.write("GET / HTTP/1.1\r\n"
"host: " + theurl.host().toUtf8() + "\r\n\r\n");
if (socket.waitForReadyRead()) {
while(socket.bytesAvailable()){
buffer.append(socket.readAll());
int packetSize=buffer.size();
while(packetSize>0)
{
//Output server response for debugging
out << "[" << buffer.data() << "]" <<endl;
//set Url if 200, 301, or 302 response given assuming that server will redirect
if (buffer.contains("200 OK") ||
buffer.contains("302 Found") ||
buffer.contains("301 Moved")) {
return true;
}
buffer.remove(0,packetSize);
//packetSize=getPacketSize(buffer);
packetSize=buffer.size();
} //while packet size >0
} //while socket.bytesavail
} //socket wait for ready read
}//socket write
return false;
}
The QTextStream prints what is being read from the socket so you can know what conditions to add and why your http request didn't work (I used it to figure out that I needed 301 and 302). The while loops are a modified version of ratchetfreak's answer here
How to read complete data in QTcpSocket?
to make sure you get everything out of the socket. I'm testing if I need to change the "/index.html" part of the socket write but so far it seems fine.
Edit: Should just be "GET /" not "GET /index.html"
I am trying to post information to an API on a web project that I have created and hosted. I am not sure what the exact format is for the HTTP POST request. Every time I try I get HTTP 400 errors with the message that there is "an invalid verb".
Sample Code:
byte server[] = {"our IP"}
..
..
client(server, 80)
..
..
client.println("POST /Api/AddParking/3");
It connects to the IP address supplied without any problems, but all I get back in the above mentioned HTTP error code 400. I am not sure if I was supposed to include a HTTP version after my POST or and Content-Length or any other information.
The original question is already answered, but just for reference for people passing by via Google; here is a more complete example how to post data to a webserver with an Arduino:
IPAddress server(10,0,0,138);
String PostData = "someDataToPost";
if (client.connect(server, 80)) {
client.println("POST /Api/AddParking/3 HTTP/1.1");
client.println("Host: 10.0.0.138");
client.println("User-Agent: Arduino/1.0");
client.println("Connection: close");
client.print("Content-Length: ");
client.println(PostData.length());
client.println();
client.println(PostData);
}
Another option is using the HTTPClient.h (for the arduino IDE on adafruit's ESP32 feather), which handles https with no effort it seems. I'm including JSON payload also and can successfully send an IFTTT webhook.
HTTPClient http;
String url="https://<IPaddress>/testurl";
String jsondata=(<properly escaped json data here>);
http.begin(url);
http.addHeader("Content-Type", "Content-Type: application/json");
int httpResponseCode = http.POST(jsondata); //Send the actual POST request
if(httpResponseCode>0){
String response = http.getString(); //Get the response to the request
Serial.println(httpResponseCode); //Print return code
Serial.println(response); //Print request answer
} else {
Serial.print("Error on sending POST: ");
Serial.println(httpResponseCode);
http.end();
}
Sending hand-crafted HTTP packets can be a bit tricky because they are extremely picky about the format used. I highly recommend reading through the HTTP protocol if you have the time because it explains the syntax and fields required. In particular you should look at section 5 "Request".
With regards to your code, you do need to specify the HTTP version after the POST URI and I believe you also need to specify the "Host" header. On top of that you need to be sure to have a carriage-return line-feed (CRLF) at the end of each line. So, your packet should look something like:
POST /Api/AddParking/3 HTTP/1.1
Host: www.yourhost.com
Requests can be sent like that too
// Check if we are Connected.
if(WiFi.status()== WL_CONNECTED){ //Check WiFi connection status
HTTPClient http; //Declare object of class HTTPClient
http.begin("http://useotools.com/"); //Specify request destination
http.addHeader("Content-Type", "application/x-www-form-urlencoded", false, true);
int httpCode = http.POST("type=get_desire_data&"); //Send the request
Serial.println(httpCode); //Print HTTP return code
http.writeToStream(&Serial); // Print the response body
}