ESP32 Web Server periodic updating problem - html

I am trying to publish some data I get from a embedded platform using UART transmission on to a web server using ESP32. This is the code I am using, I am using Arduino IDE. I am not quite familiar with HTML and have seen couple of examples where HTML is being used to update the server values by sending requests.
#include <WiFi.h>
#include "ESPAsyncWebServer.h"
AsyncWebServer server1(81);
const char* ssid ="mywifi";
const char* password ="mywifipass";
char c;
String readuart() {
if (Serial.available() > 0) {
c = Serial.read();}
return String(c);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
Serial.println(WiFi.localIP());
server1.on("/test_data", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", readuart().c_str());
});
server1.begin();
delay(100);
}
void loop() {
}
Following part of the code is publishing the first string I get but it stops there.
server1.on("/test_data", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", readuart().c_str());
});
I want to update this one string periodically. How do I achieve that? Any help will be much appreciated.

It sounds like you want the page to reload itself every x seconds. You can change your response string to actual html and use the meta refresh tag. This happens on the client side (in the web browser).
server1.on("/test_data", HTTP_GET, [](AsyncWebServerRequest *request){
String html = "<html><head><meta http-equiv=\"refresh\" content=\"30\"></head><body>"
+ readuart() + "</body></html>";
request->send(200, "text/html", html.c_str());
});
Change the 30 to whatever timeout you want.
Another option would be to use client side JS.

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.

Multiple HTTP GETs in one go on ESP8266

I can't figure out how to send the data in one go. I am asking because sending it simultaneously delays a bit.
I did see an other topic of someone posting a piece of code, but that did not work.
This is a bit of the code:
void handleRoot() {
String s = MAIN_page; // Read HTML contents
server.send(200, "text/html", s); // Send web page
}
void handleFORESTTEMPERATURE() {
String ForrestTemperatureDev = String(ForestTemperature, 1);
server.send(200, "text/plane", ForestTemperatureDev); // Send ADC value only to client ajax request
}
void handleFORESTPRESSURE() {
String ForrestPressureDev = String(ForestPressure);
server.send(200, "text/plane", ForrestPressureDev); // Send ADC value only to client ajax request
}
void handleFORESTHUMIDITY() {
String ForrestHumidityDev = String(ForestHumidity);
server.send(200, "text/plane", ForestHumidityDev); // Send ADC value only to client ajax request
}
server.on("/readFORESTPRESSURE", handleFORESTPRESSURE);
server.on("/readFORESTTEMPERATURE", handleFORESTTEMPERATURE);
server.on("/readFORESTHUMIDITY", handleFORESTHUMIDITY);
server.on("/readFORESTWEATHERSTATUS", handleFORESTWEATHERSTATUS);
The following code would send all three values separated with a ; when \readALL is requested.
The three values can be split on the ; in your receiving application; using JSON would be just a little bit more work.
Note1: Untested, I just typed it in, but you get the idea.
Note2: It's text/plain, not text/plane.
void handleRoot() {
String s = MAIN_page; //Read HTML contents
server.send(200, "text/html", s); //Send web page
}
void handleFORESTTEMPERATURE() {
String ForrestTemperatureDev = String(ForestTemperature, 1);
server.send(200, "text/plain", ForestTemperatureDev); //Send ADC value only to client ajax request
}
void handleFORESTPRESSURE() {
String ForrestPressureDev = String(ForestPressure);
server.send(200, "text/plain", ForrestPressureDev); //Send ADC value only to client ajax request
}
void handleFORESTHUMIDITY() {
String ForrestHumidityDev = String(ForestHumidity);
server.send(200, "text/plain", ForestHumidityDev); //Send ADC value only to client ajax request
}
void handleALL() {
String AllDev = String(ForestTemperature, 1) + ";" + String(ForestPressure) + ";" + String(ForestHumidity);
server.send(200, "text/plain", AllDev); //Send ADC values only to client ajax request
}
server.on("/readFORESTPRESSURE", handleFORESTPRESSURE);
server.on("/readFORESTTEMPERATURE", handleFORESTTEMPERATURE);
server.on("/readFORESTHUMIDITY", handleFORESTHUMIDITY);
server.on("/readFORESTWEATHERSTATUS", handleFORESTWEATHERSTATUS);
server.on("/readALL", handleALL);

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.