Sending UDP packet with ESP32 - arduino-ide

I'm trying to send a UDP packet to my Milights (wifi LED bulbs via bridge) to controll them. The packets do not show up in Wireshark though.
When I change the destination ip of the UDP packet to the one of my PC then it works. I can find the packet in Wireshark then.
/*
* This sketch sends random data over UDP on a ESP32 device
*
*/
#include <WiFi.h>
#include <WiFiUdp.h>
// WiFi network name and password:
const char * networkName = "********";
const char * networkPswd = "********";
//IP address to send UDP data to:
// either use the ip address of the server or
// a network broadcast address
const char * udpAddress = "********";
const int udpPort = ********;
//Are we currently connected?
boolean connected = false;
//The udp library class
WiFiUDP udp;
byte command[27] = {0x20, 0x00, 0x00, 0x00, 0x16, 0x02, 0x62, 0x3A, 0xD5, 0xED, 0xA3, 0x01, 0xAE, 0x08, 0x2D, 0x46, 0x61, 0x41, 0xA7, 0xF6, 0xDC, 0xAF, 0xD3, 0xE6, 0x00, 0x00, 0x1E};
void setup(){
// Initilize hardware serial:
Serial.begin(115200);
//Connect to the WiFi network
connectToWiFi(networkName, networkPswd);
}
void loop(){
//only send data when connected
if(connected){
//Send a packet
udp.beginPacket(udpAddress,udpPort);
udp.write(command, 27);
udp.endPacket();
}
//Wait for 1 second
delay(1000);
}
void connectToWiFi(const char * ssid, const char * pwd){
Serial.println("Connecting to WiFi network: " + String(ssid));
// delete old config
WiFi.disconnect(true);
//register event handler
WiFi.onEvent(WiFiEvent);
//Initiate connection
WiFi.begin(ssid, pwd);
Serial.println("Waiting for WIFI connection...");
}
//wifi event handler
void WiFiEvent(WiFiEvent_t event){
switch(event) {
case SYSTEM_EVENT_STA_GOT_IP:
//When connected set
Serial.print("WiFi connected! IP address: ");
Serial.println(WiFi.localIP());
//initializes the UDP state
//This initializes the transfer buffer
udp.begin(WiFi.localIP(),udpPort);
connected = true;
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
connected = false;
break;
}
}
Can somebody explain to me why I CAN send messages UDP packets to my PC and not to my Wifi bridge?

Related

Wrong format of JSON data (Arduino) to .net Azure App service, getting code 415

I would like to ask about sending data from arduino/esp to .net App service running on Azure.
I am sending JSON to API with "list" of measurements (simple JSON, Id and Value).
Over PostMan I don't have any issue, but over Arduino I am facing issue that I receive result code 415.
Data are sent in JSON.
Arduino Code:
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
const char* ssid = "*******";
const char* password = "*******";
//Your Domain name with URL path or IP address with path
const char* serverName = "https://********.azurewebsites.net/api/Measurements/";
unsigned long lastTime = 0;
// Set timer to 5 seconds (5000)
unsigned long timerDelay = 5000;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
Serial.println("Timer set to 5 seconds (timerDelay variable), it will take 5 seconds before publishing the first reading.");
}
void loop() {
//Send an HTTP POST request every 10 minutes
if ((millis() - lastTime) > timerDelay) {
//Check WiFi connection status
if(WiFi.status()== WL_CONNECTED)
{
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
client->setInsecure();
HTTPClient https;
https.addHeader("Content-Type", "application/json"); // tried even "text/json"
//https.addHeader("Content-Length", "32");
https.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
https.addHeader("Host","******.azurewebsites.net");
https.addHeader("Cache-Control","no-cache");
String httpRequestData = "[{\"SensorId\": 1, \"Value\": 77.7}]";
if (https.begin(*client, serverName))
{
Serial.print("[HTTPS] POST...\n");
int httpCode = https.POST(httpRequestData);
if (httpCode > 0)
{
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTPS] POST... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
{
String payload = https.getString();
Serial.println(payload);
}
else
{
Serial.printf("[HTTPS] POST... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
}
else
{
Serial.printf("[HTTPS] POST... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
}
else
{
Serial.printf("[HTTPS] Unable to connect\n");
}
}
lastTime = millis();
}
}
Not receiving any error, just response code 415.
The HTTP 415 Unsupported Media Type client error response code indicates that the server refuses to accept the request because the payload
JSON text:
[{"SensorId": 1, "Value": 77.7}]
AppInsight:
PostMan successful request:
body of request in Postman:
And report from AppInsight:
App service:
Over PostMan I am able to reach azure and send data, but over Arduino I am not able to send data (I am able to get data, but not post)
Probably some beginner mistake.
Can someone give me hint?
Do I have wrong format of JSON?

Encryption on ESP-32 Async Webserver

I am currently trying to implement a save method for someone to change the Mode of an ESP-32 from Accesspoint Mode to Station mode via a Webpage on an asynchronous Webserver. The data gets put into a JSON string and sent to the ESP-32 where it gets written in a JSON file, and later read out of again and used to connect to a network with.
else {
IP = "NULL";
Netmask = "NULL";
Gateway = "NULL";
var data = {ssid:ssid, password:password, IP:IP, Netmask:Netmask, Gateway:Gateway};
console.log(data);
var xhr = new XMLHttpRequest();
var url ="/settings";
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status ==200) {
console.log(xhr.response);
}
};
xhr.open("POST", url, true);
xhr.send(JSON.stringify(data));
}
The problem with this method is that someone who is spying on the network could just grab the JSON string as it is and gain access to the network the ESP-32 is trying to connect itself with. Thats why I want to implement a lightweight way to encrypt this string before it gets sent, and to locally encrypt it, using Javascript on the webpage. The Problem is that, the moment I execute this operation, I am not online, so grabbing a script from the outside, e.g. CryptoJS is not possible. I'm stuck on this for weeks now and have no idea how to do this.
Does anyone have an idea?
The de-facto way of protecting against eavesdropping is to implement HTTPS.
ESP32 has built-in TLSv1.3 support thanks to wolfssl.
Below is an example HTTPS server taken from the official ESP-IDF examples.
You will need a server certificate to run it. You can use generate a self-signed certificate with openssl, or if you have a valid DNS name, get a CA-signed one e.g. from LetsEncrypt. Even a self-signed cert with TLS 1.2+ will protect against sniffing.
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include "esp_netif.h"
#include "esp_eth.h"
#include "protocol_examples_common.h"
#include <esp_https_server.h>
/* A simple example that demonstrates how to create GET and POST
* handlers and start an HTTPS server.
*/
static const char *TAG = "example";
/* An HTTP GET handler */
static esp_err_t root_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, "<h1>Hello Secure World!</h1>", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static const httpd_uri_t root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler
};
static httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
// Start the httpd server
ESP_LOGI(TAG, "Starting server");
httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();
extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start");
extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end");
conf.cacert_pem = cacert_pem_start;
conf.cacert_len = cacert_pem_end - cacert_pem_start;
extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start");
extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end");
conf.prvtkey_pem = prvtkey_pem_start;
conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start;
esp_err_t ret = httpd_ssl_start(&server, &conf);
if (ESP_OK != ret) {
ESP_LOGI(TAG, "Error starting server!");
return NULL;
}
// Set URI handlers
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &root);
return server;
}
static void stop_webserver(httpd_handle_t server)
{
// Stop the httpd server
httpd_ssl_stop(server);
}
static void disconnect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server) {
stop_webserver(*server);
*server = NULL;
}
}
static void connect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server == NULL) {
*server = start_webserver();
}
}
void app_main(void)
{
static httpd_handle_t server = NULL;
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* Register event handlers to start server when Wi-Fi or Ethernet is connected,
* and stop server when disconnection happens.
*/
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
}
ESP32 can even do Websocket over SSL - see wss_server example.

Parsing a JSON document in an HTTP response-Arduino (Unexpected response: HTTP/1.1 301 moved permanently error)

I'm using Arduino's ethernet shield with Arduino mega to parse a JSON document in a HTTP response to read some values in the JSON file.
The JSON address is: https://api.coinbase.com/v2/prices/BTC-USD/spot.
I'm getting this error message:
Connecting...
Connected!
Unexpected response: HTTP/1.1 301 Moved Permanently
Is is that the the Arduino library doesn't have SSL capability, so it can't connect to a secure server? If it is, then I would need your help to resolve this issue.
Or perhaps I need to use another port that's related to https?
Here is my code:
#include <ArduinoJson.h>
#include <Ethernet.h>
#include <SPI.h>
void setup() {
// Initialize Serial port
Serial.begin(9600);
while (!Serial) continue;
// Initialize Ethernet library
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
if (!Ethernet.begin(mac)) {
Serial.println(F("Failed to configure Ethernet"));
return;
}
delay(1000);
Serial.println(F("Connecting..."));
// Connect to HTTP server
EthernetClient client;
client.setTimeout(10000);
if (!client.connect("api.coinbase.com", 80)) {
Serial.println(F("Connection failed"));
return;
}
Serial.println(F("Connected!"));
// Send HTTP request
client.println(F("GET /v2/prices/BTC-USD/spot/response.json HTTP/1.0"));
client.println(F("Host: api.coinbase.com"));
client.println(F("Connection: close"));
if (client.println() == 0) {
Serial.println(F("Failed to send request"));
return;
}
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
// It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
if (strcmp(status + 9, "200 OK") != 0) {
Serial.print(F("Unexpected response: "));
Serial.println(status);
return;
}
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client.find(endOfHeaders)) {
Serial.println(F("Invalid response"));
return;
// the rest of the code related to parsing that I didn't paste for the sake the argument.
Thanks everybody.

Why my client does not receive data from server that is sent from interrupt function?

I have to send data to the client in the interrupt function. But apparently, the client cannot receive the data. The server did get the data sent from the client but the client cannot reply back.
When I try the communication in void loop it is a success. But once I put it in the interrupt, I cannot get the data back. The data that the server has to send is simply a number which will then trigger the led at the client side. It is really helpful if someone can help me figure it out.
This is part of the server code.
void ICACHE_FLASH_ATTR detectsMovement() {
LightValue = LightSensor.GetLightIntensity();
Serial.print("Light: ");
Serial.println(LightValue);
if (LightValue < threshold )
{
Serial.println("MOTION DETECTED!!!");
analogWrite(led, 1023);
startTimer = true;
lastTrigger = millis();
WiFiClient client = server.available();
if (client) {
if (client.connected())
{
digitalWrite(ledPin, LOW); // to show the communication only
(inverted logic)
Serial.println("");
String request = client.readStringUntil('\r'); // receives the
message from the client
Serial.print("From client: "); Serial.println(request);
client.flush();
client.println("1\r"); // sends the answer to the client
digitalWrite(ledPin, HIGH);
}
client.stop(); // terminates the connection with the client
}
}
}
void setup() {
Serial.begin(115200); // only for debug
WiFi.config(ip, gateway, subnet); // forces to use the fix IP
WiFi.begin(ssid, pass); // connects to the WiFi router
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(500);
}
pinMode(motionSensor, INPUT_PULLUP);
// Set motionSensor pin as interrupt, assign interrupt function and set RISING mode
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
server.begin(); // starts the server
pinMode(light, INPUT);
LightSensor.begin();
Serial.println("Running...");
pinMode(ledPin, OUTPUT);
pinMode(led, OUTPUT);
}
And the client code
void loop () {
client.connect(server, 80); // Connection to the server
digitalWrite(ledPin, LOW); // to show the communication only (inverted logic)
Serial.println(".");
client.println("Hello server! Are you sleeping?\r"); // sends the message to the server
String answer = client.readStringUntil('\r'); // receives the answer from the server
Serial.println("from server: " + answer);
if (answer == "1")
{
Serial.println("Success!");
analogWrite(D0, 512);
}
client.flush();4
digitalWrite(ledPin, HIGH);
delay(2000); // client will trigger the communication after two seconds
}
The client supposed to get the value '1'. I don't know what to do.

arduino ide esp8266 json decode from URL

I program esp8266 using the Arduino IDE. I join with the local network via WIFI. Ultimately, I want to download content JSON generated on the local server. I am using the code:
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
const char* ssid = "NeoRaf";
const char* password = "password";
const char* host = "192.168.1.8";
void setup() {
Serial.begin(115200);
delay(100);
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
//-----------------------
delay(5000);
Serial.print("connecting to ");
Serial.println(host);
// Use WiFiClient class to create TCP connections
WiFiClient client;
const int httpPort = 8095;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
// We now create a URI for the request
String url = "/api";
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print("GET " + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"ApiCode: 8273\r\n" +
"Content-Type: application/json\r\n" +
"Connection: close\r\n\r\n");
delay(500);
char c[1024];
// Read all the lines of the reply from server and print them to Serial
while(client.available()){
c[0] = client.read();
//Serial.print(c);
Serial.print(c);
}
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(c);
int data = root["lowVersion"];
Serial.println();
Serial.print(data);
Serial.println();
Serial.println("closing connection");
}
void loop()
{}
The result is this:
Connecting to NeoRaf
......
WiFi connected
IP address:
192.168.1.21
connecting to 192.168.1.8
Requesting URL: /api
HTTP/1.1 200 OK
Content-Length: 32
Content-Type: application/json
Server: Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Date: Mon, 06 Jun 2016 16:50:48 GMT
Connection: close
{"lowVersion":1,"highVersion":3}
0
closing connection
This line is JSON:
{"lowVersion":1,"highVersion":3}
So it should display 1 and displays 0.
I do not know how to get rid of the header:
HTTP/1.1 200 OK
Content-Length: 32
Content-Type: application/json
Server: Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Date: Mon, 06 Jun 2016 16:50:48 GMT
and how to read the contents of JSON ( lowVersion or highVersion)?
You need to detect when the payload starts in the HTTP response. So, when you read data from the client you can skip all HTTP header lines. Incidentally in a JSON response the first { usually signifies the start of the JSON document.
httpbin example
Requesting http://httpbin.org/get yields
{
"args": {},
"headers": {
...
},
"origin": "XXXXXXX",
"url": "http://httpbin.org/get"
}
You can print the "url" field like so:
String json = "";
boolean httpBody = false;
while (client.available()) {
String line = client.readStringUntil('\r');
if (!httpBody && line.charAt(1) == '{') {
httpBody = true;
}
if (httpBody) {
json += line;
}
}
StaticJsonBuffer<400> jsonBuffer;
Serial.println("Got data:");
Serial.println(json);
JsonObject& root = jsonBuffer.parseObject(json);
String data = root["url"];
HTTP headers always end with an empty line, so you just need to read until you find two consecutive line breaks.
Have a look at ArduinoJson's example JsonHttpClient.ino, it uses Stream::find():
// Skip HTTP headers so that we are at the beginning of the response's body
bool skipResponseHeaders() {
// HTTP headers end with an empty line
char endOfHeaders[] = "\r\n\r\n";
client.setTimeout(HTTP_TIMEOUT);
bool ok = client.find(endOfHeaders);
if (!ok) {
Serial.println("No response or invalid response!");
}
return ok;
}
First of all, for handling HTTP requests, you can use a RestClient library rather than writing all the low level requests. It saves a lot of time and is less error-prone.
For example, for a GET request, all you have to do is:
String response = "";
int statusCode = client.get("/", &response);
One good such library with SSL support is written by github user DaKaz.
You can use it for your GET request. The returned response will be without the HTTP header. The function will return the response from the server without the headers.
Now you can use the ArduinoJson Library by bblanchin for decoding the JSON object.
Details can be seen here.
Or you can do plain string manipuation to get the values though it is not a recommended route to take and is prone to errors.