How to convert HTTPS POST request to ESP8266 with API - arduino

Hey y'all
I got a Wemos D1 Mini and try to use the service <getnotify.me> to send notifications to my alexa directly from the microcontroller.
I cant get it to work i really tried everything but the api won't receive my data. Im either getting "-1" as response code or "307 - Temporary Redirect".
The API seems kinda easy and it also seems to work with http instead of https.
The developer needs the request in this format:
Curl example:
curl --header "Content-Type: application/json" --request POST --data '{"message":"your message here"}' --user nmxxxxxxxxxx:scxxxxxxxxxx https://api.getnotify.me/submit
Python example:
import requests
API_ENDPOINT = "https://api.getnotify.me/submit"
API_KEY = "nmxxxxxxxxxx"
API_SECRET = "scxxxxxxxxxx"
data = {'message':'your message here'}
r = requests.post(url = API_ENDPOINT, json=data, auth=(API_KEY, API_SECRET))
print ("Response code: %d" %(r.status_code))
If i convert the Curl to a RAW Request it looks simle as this:
Authorization: Basic hu03NGGxMzGyZfVkOnTjTjAwZjIzBFWhNS==
Host: api.getnotify.me
Content-Type: application/json
Content-Length: 31
{"message":"your message here"}
I already figured out, that the API Key which looks similar to this: "nmxxxxxxxxxx"
and the API secret: "scxxxxxxxxxx" need to be base64.
This is what i got so far:
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.begin("ssid", "wifipw");
while (WiFi.status() != WL_CONNECTED) { //Wait for the WiFI connection completion
delay(500);
Serial.println("Waiting for connection");
}
}
void loop() {
if(WiFi.status()== WL_CONNECTED){ //Check WiFi connection status
Serial.println("Connected");
HTTPClient http;
http.begin("http://api.getnotify.me/submit");
http.addHeader("Content-Type", "application/json");
http.setAuthorization("Authorization", "Basic hu03NGGxMzGyZfVkOnTjTjAwZjIzBFWhNS==");
int httpCode = http.POST("{\"message\":\"your message here\"}");
String payload = http.getString();
Serial.println(httpCode);
Serial.println(payload);
http.end();
}else{
Serial.print("Error in Wifi connection");
}
delay(30000); //Send a request every x seconds
}
In Python and via Curl it works like a charm (even with http instead of https!)
Do you have any idea how i can get this to work ? im really new to this arduino / esp8266 stuff and unfortunately there is no good examples in the webs and almost no documentation from the developer. but it should be pretty basic stuff. I tried a LOT of variations to the request like sending it all as headers or all in the POST but nothing works. what am i making wrong here?
Thank you so much for your help guys !!

From looking at https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h it seems that you use setAuthorization incorrectly. The first parameter is username, second one is password:
http.setAuthorization("replace_username", "replace_password");

Related

POST Request with WiFiNina Library on Arduino Uno Wifi Rev 2

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
}

Web2Py GET requests from Arduino (ESP8266WiFi)

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)

How to build HTTP Response with ESP8266 and Arduino

I want to toggle some LEDs with my Android device. These LEDs are connected to the digital Pins of my Arduino which is also connected to a ESP8266. Now, my ESP8266 is defined as an AccessPoint and my Tablet can send HTTP requests (e.g. http://192.168.4.1:80/?pin=11).
I found the code here http://allaboutee.com/2015/01/20/esp8266-android-application-for-arduino-pin-control/
It works fine but my question is which HTTP header fields should be used? In this code he used some (e.g. Content-Length) but there are so much more possible (Date, Server, Content-Language,...).
Are these fields optional or which of these have to be used to build the right response?
Here is the piece of code I do not understand:
void sendHTTPResponse(int connectionId, String content)
{
String httpResponse;
String httpHeader;
httpHeader = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n";
httpHeader += "Content-Length: ";
httpHeader += content.length();
httpHeader += "\r\n";
httpHeader +="Connection: close\r\n\r\n";
httpResponse = httpHeader + content + " ";
sendCIPData(connectionId,httpResponse);
}
It largely depends on the client (i.e. consumer) which fields are required and which are mandatory.
The only one that is always required is "HTTP/1.1 200 OK". Of course you need to replace that status code if you're not sending an OK message.

Code in arduino for sending http request with certificate (.py file)

I have a requirement to create a https request with certificate file(.pfx) same as that of we do in node.js like below:
var pfx = fs.readFileSync(certificatePath);
options.pfx = pfx;
options.passphrase = passphrase;
options.method = 'POST';
options.headers = {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
};
var req = https.request(options, function(res) {
Right now in ardiuno I can see only API to create HTTP request like below but there is no option to send the certificate:
WiFiSSLClient client;
if (client.connect(server, 443)) {
Serial.println("connected to server");
// Make a HTTP request:
client.println("GET /api/getData HTTP/1.1");
...........................
Edit: I have added a WIFI shield on the board so that it can support encryption
but still unable to find any solution.
Please suggest and thanks in advance!
The subject should ask how to do an https request not an http request since that is what you want.
I see you've tagged this with Arduino UNO. The uno is not powerful enough to do encryption, you need to look for a more powerful platform. This much have been answered before but I can't find another question to reference just now.

Making a http POST request using Arduino

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
}

Resources