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

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.

Related

HTTP thingsboard issue

i tried to send data to simulate some alarms in thingsboard ,when i send data over MQTT it works but when i send data with http ,alarm dont show up in thingsboard , probably it's an issue of keeping connexion with the device, i tried to pass -H "connection :close" but i have the same issue.
i tested that with two different versions tb 3.3.3 and tb 3.4.
and when this happens with http and i tried to change to mqtt , still bugged .to resolve that i need to create another device profile and i start sending with MQTT to see the alarms
my http request is like that :
`import requests,json
headers = {
'accept': 'application/json',
'Connetion': 'close',
'X-Authorization': 'Bearer MY JWT TOKEN',
}
params = {
'scope': 'ANY',
}
json_data = {
'temperature': 90,
}
response = requests.post(
'http://localhost:8080/api/plugins/telemetry/DEVICE/deviceID/timeseries/ANY',
params=params,
headers=headers,
json=json_data,
)`
i create the alarm using device profile because i dont know how to create a rule chain and call 7 or 6 shared attribute with from different devices

How to convert HTTPS POST request to ESP8266 with API

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");

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
}

Koa SSE connection reconnecting

I have set up an SSE connection using Koa like so:
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// Sets up the HTTP header and sends a message via SSE
function writeSSE(ctx, message) {
ctx.res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'Access-Control-Allow-Origin': '*',
});
ctx.res.write(`id: 01\n`);
ctx.res.write(`data: ${message}\n\n`);
}
// Router Middleware
router.get('/stream', (ctx, next) => {
writeSSE(ctx, 'Stream reached.');
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(8080);
Where my React components starts the connection like so:
new EventSource("http://localhost:8080/stream")
The component then receives the answer sent by the writeSSE method on the backend.
But for some reason the /stream endpoint is reached every 3 seconds or so, as if the connection was being reestablished.
And my error listener on the front-end catches a CONNECTING event every time.
this.state.source.onerror = (e) => {
if (e.target.readyState == EventSource.CONNECTING) {
console.log("Connecting...");
}
};
And on the back-end, ctx.response equals { status: 404, message: 'Not Found', header: {} }.
Would anyone know the cause of this issue? Is it linked to the way I use Koa?
this is a bit too late, but I will write my experience with sse using Koa.
First of all using ctx.res directly is not much appreciated by Koa, if you still want to use it make sure to put ctx.respond = false to bypass koa response mecanism.
In my experience a stream is the best way to use SSE with Koa you can do something like :
const stream = require('stream');
const koa = require('koa');
const app = new koa();
app.use(async ctx => {
ctx.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
ctx.status = 200;
const stream = new stream.PassThrough()
ctx.body = stream; // here koa will pipe the ctx.res to stream and end the ctx.res when ever the stream ends.
let counter = 5;
const t = setInterval(() => {
stream.write(`data: hi from koa sse ${counter}`);
counter--;
if (counter === 0) {
stream.end();
clearInterval(t);
}
}, 1000);
});
Hope this help anyone will play with SSE on koa.
PS: I wrote this on hurry if there is anything wrong with code tell me and I will correct it.
I'm in the process of implementing a Koa-based server for SSE. I've been running into the same problem, and here are my thoughts / working solution:
As far as I can tell, the reason why onmessage and onerror keep getting called is because the EventSource object on the client side is emitting an error event. This is causing the connection to be disconnected, which causes the client to send another request to initialize the stream to the server. From here, the process repeats itself indefinitely.
Based on my own testing, EventSource is emitting an error due to the data that is being sent back from the server. Per the docs, a 200 response that has as Content-Type other than 'text/event-stream' will cause a failure.
In your example, you have declared your response as 'text/event-stream' and are passing a string into the ctx.res.write method. While this looks correct, and in fact works when using comparable code and Express, it seems that it doesn't work in Koa. However, if you change the 'data' you are writing to your response to a stream, such as this example here, you'll find that the connection establishes correctly.
Maybe try the following:
//require Passthrough
const PassThrough = require('stream').PassThrough;
//then, in your writeSSE function, try this:
let stream = new PassThrough();
stream.write(`data: ${message}\n\n`);
ctx.res.write(stream);
I'm not 100% sure why this change works. My best guess is that there is something about Koa's ctx object that prevents a plain string or template literal from being viewed as valid text/event-stream data, but this is entirely supposition (this begs the question as to why it works in Express, but hopefully someone more knowledgeable can answer this for both of us). From what I've seen of other snippets published online, the stream approach is the one to take in Koa.
I'm not sure what your results will be, as it looks like you may be using a different version of Koa than I am, but I'd give it a shot. I was able to get my connection established correctly making this small change.

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