Program implementations that you will see today were created in Arduino Core 2.0.1 and in framework ESP-IDF 4.3.2. In FreeRTOS and ESP-IDF I am beginner, so I can't describe so much about how to develop apps etc... Both program implementations are using FreeRTOS with 2 tasks. One task (TASK A) is for measurement and second task (TASK B) for sending datas to web interface using WiFi. There is also implemented inter-task communication via Queue FIFO buffer.
Task for sending datas is waiting for datas in FIFO buffer for portMAX_DELAY time, that is maximum time uint_32, it is similar to millis() function from Arduino Core. In that case task for HTTP / HTTPS request will wait for datas almost 50 days. Queue were created for 20 values, but.. there are never more than 1 value in Queue, because HTTP / HTTPS task will use and delete that value from Queue instantly after receiving it. Request will take few milliseconds.
For effective code i divided tasks between cores of Xtensa main CPU. Core 0 - called also as PRO_CPU is running WiFi stack, so it is effective to pin transmission-related task to that core and second task that is doing measurement to APP_CPU - Core 1. In case of Arduino IDE and Arduino Core program, you can set where loop task (main task application) will run. Standardly it is pinned to Core 1, you can set it at Tools --> Loop Task, if you have any ESP32 board selected. Tasks were implemented under ESP-IDF and Arduino Core with same logic that is described below. For measurement using ultrasonic sensors there were used libraries NewPingESP8266 in Arduino Core and ultrasonic (C library from Ruslan V.) for ESP-IDF framework implementation.
Task A (producer task - it is creating datas)
- Is is doing measurement with ultrasonic distance sensor (10x measurement, then average value)
- Averaged value (uint_32) sent to Queue
- Waiting 300 seconds
- Waiting for datas from Queue
- During waiting is is not using CPU time, so it can be more effectively used for other tasks that need to be run
- After receiving value from Queue, it will delete value in Queue
- It will connect to webserver using HTTP or HTTPS protocol and with POST request datas are stored into MySQL database using PHP
Arduino Core
For HTTP request I have used WiFiClient class from standard WiFi library. For HTTPS connection with webserver I have used WiFiClientSecure class from WiFiClientSecure library. It is also required to use Root CA certificate that signed certificate for webserver (domain). You need to put it to program in .pem format. In both source codes I have called also yield(); in each loop iteration. I have used code that was in my standard HTTPS example code for sensor node without FreeRTOS and i put WiFi related code to PRO_CPU task and measurement related code to APP_CPU task.
Source code - HTTPS request task - ESP32 - FreeRTOS - Arduino Core
Code: Select all
/*|-------------------------------------------------------------------------------|*/
/*|Project: Ultrasonic sensor node - HTTPS - FreeRTOS - HC-SR04 / JSN-SR04T |*/
/*|ESP32 (DevKit, Generic) |*/
/*|Author: Martin Chlebovec (martinius96) |*/
/*|E-mail: martinius96@gmail.com |*/
/*|Project info: https://martinius96.github.io/hladinomer-studna-scripty/en/ |*/
/*|Test web interface where data is sent: https://hladinomer.000webhostapp.com/ |*/
/*|Buy me coffee: https://paypal.me/chlebovec |*/
/*|Revision: 8. April 2022 |*/
/*|Compatible Arduino Core based on ESP-IDF 4.4 - versions 2.0.1, 2.0.3-RC1 |*/
/*|Partition scheme Default 1,2 MB APP, 827 kB (63%) flash, 38 kB (11%) RAM usage |*/
/*|-------------------------------------------------------------------------------|*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <NewPingESP8266.h>
const char * ssid = "MY_WIFI"; //WiFi SSID name
const char * password = "MY_WIFI_PASSWORD"; //WiFi password
const char* host = "hladinomer.000webhostapp.com"; //webserver address (domain)
String url = "/data.php"; //PHP file under domain host
#define pinTrigger 22
#define pinEcho 23
#define maxVzdialenost 450
NewPingESP8266 sonar(pinTrigger, pinEcho, maxVzdialenost);
TaskHandle_t Task1; //ULTRASONIC MEASUREMENT
TaskHandle_t Task2; //HTTPS POST request task
QueueHandle_t q = NULL; //Queue handler
WiFiClientSecure client; //Secured client object for HTTPS connection
static void Task1code( void * parameter);
static void Task2code( void * parameter);
//DigiCert Global Root CA in .pem format, stored in PROGMEM flash
//DST ROOT CA X3 EXAMPLE (https://i.imgur.com/fvw4huT.png)
const static char* test_root_ca PROGMEM = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \
"-----END CERTIFICATE-----\n";
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password); //connect to WiFi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println(F(""));
Serial.println(F("Wifi connected with IP:"));
Serial.println(WiFi.localIP());
client.setCACert(test_root_ca);
q = xQueueCreate(20, sizeof(int)); //create Queue of 20 INT values
if (q != NULL) {
Serial.println(F("Queue FIFO buffer is created"));
vTaskDelay(1000 / portTICK_PERIOD_MS); //wait for a second
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
Serial.println(F("Ultrasonic measurement task started - pinned to APP CPU"));
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
Serial.println(F("HTTPS POST request task started - pinned to PRO CPU"));
} else {
Serial.println(F("Queue creation failed"));
}
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid, password); //pripoj sa na wifi siet s heslom
}
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
yield();
}
static void Task1code( void * parameter) {
if (q == NULL) {
Serial.println(F("Queue in ultrasonic Measurement task is not ready"));
return;
}
while (1) {
int distance = sonar.ping_cm();
delay(50);
Serial.print(F("Test measurement: "));
Serial.print(distance);
Serial.println(F(" cm"));
if (distance > 0) {
distance = 0;
for (int i = 0; i < 10; i++) {
distance += sonar.ping_cm();
delay(50);
}
distance = distance / 10;
Serial.print(F("Distance to water level is: "));
Serial.print(distance);
Serial.println(F(" cm."));
xQueueSend(q, (void *)&distance, (TickType_t )0); //add the measurement value to Queue
for (int countdown = 300; countdown >= 0; countdown--) {
Serial.print(F("Next measurement in: "));
Serial.print(countdown);
Serial.println(F(" seconds"));
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
}
}
static void Task2code( void * parameter) {
int distance;
if (q == NULL) {
Serial.println(F("Queue in HTTP socket task is not ready"));
return;
}
while (1) {
xQueueReceive(q, &distance, portMAX_DELAY); //read measurement value from Queue and run code below, if no value, WAIT until portMAX_DELAY
//If received data in Queue, delete value in Queue and execute code below
String data = "hodnota=" + String(distance) + "&token=123456789";
client.stop(); //close all opened connections
if (client.connect(host, 443)) {
Serial.println(F("Connected to server successfully"));
client.println("POST " + url + " HTTP/1.0");
client.println("Host: " + (String)host);
client.println(F("User-Agent: ESP"));
client.println(F("Connection: close"));
client.println(F("Content-Type: application/x-www-form-urlencoded;"));
client.print(F("Content-Length: "));
client.println(data.length());
client.println();
client.println(data);
Serial.println(F("Datas were sent to server successfully"));
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
break;
}
}
String line = client.readStringUntil('\n');
} else {
Serial.println(F("Connection to webserver was NOT successful"));
}
client.stop(); //close all opened connections
}
}
ESP-IDF
For HTTP request under ESP-IDF framework I have used https_request example from protocols directory. Original example request there is for GET request only, so I need to change REQUEST type there and it was also required to add there some HTTP headers such as encoding of datas that is required for that type of request. Also there are dynamic datas (value of water level) that need to be set it request at each time when datas are sent to webserver. For HTTPS request I have used https_mbedtls example from protocols directory. There is advantage, you don't need to put there Root CA certificate manually, because example have available CA Bundle, where are all main CA authorities certificates. So example will automatically choose certificate that is required.
Source code- HTTPS request task - ESP32 - FreeRTOS - ESP-IDF
Code: Select all
/*|-----------------------------------------------------------------------------------|*/
/*|Projekt: Hladinomer - HTTPS - HC-SR04 / JSN-SR04T / HY-SRF05 |*/
/*|ESP32 (DevKit, Generic) - ESP-IDF v4.3.X |*/
/*|Autor: Martin Chlebovec (martinius96) |*/
/*|E-mail: martinius96@gmail.com |*/
/*|Info k projektu (schéma): https://martinius96.github.io/hladinomer-studna-scripty/ |*/
/*|Testovacie webove rozhranie HTTPS: https://hladinomer.000webhostapp.com/ |*/
/*|Revízia: 20. Január 2022 |*/
/*|-----------------------------------------------------------------------------------|*/
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/esp_debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#include "esp_crt_bundle.h"
#include "ultrasonic.h"
#include "driver/dac.h"
/* Constants that aren't configurable in menuconfig */
#define MAX_DISTANCE_CM 450 // 5m max
#define GPIO_TRIGGER 22
#define GPIO_ECHO 23
// Webserver
/* Constants that aren't configurable in menuconfig */
#define WEB_SERVER "hladinomer.000webhostapp.com"
#define WEB_PORT "443"
static const char *TAG = "https_request";
static const char *TAG2 = "ultrasonic_measurement";
QueueHandle_t q=NULL;
static void ultrasonic(void *pvParamters)
{
ultrasonic_sensor_t sensor = {
.trigger_pin = GPIO_TRIGGER,
.echo_pin = GPIO_ECHO
};
ultrasonic_init(&sensor);
uint32_t distance = 0;
if(q == NULL){
printf("Queue is not ready \n");
return;
}
while (true) {
uint32_t avg_distance = 0;
int index_loop = 1;
while(index_loop<=10){
esp_err_t res = ultrasonic_measure_cm(&sensor, MAX_DISTANCE_CM, &distance);
if (res != ESP_OK) {
printf("Error: ");
switch (res) {
case ESP_ERR_ULTRASONIC_PING:
printf("Cannot ping (device is in invalid state)\n");
break;
case ESP_ERR_ULTRASONIC_PING_TIMEOUT:
printf("Ping timeout (no device found)\n");
break;
case ESP_ERR_ULTRASONIC_ECHO_TIMEOUT:
printf("Echo timeout (i.e. distance too big)\n");
break;
default:
printf("%d\n", res);
}
} else {
printf("Measurement %d: %d cm\n", index_loop, distance);
avg_distance += distance;
index_loop++;
}
}
esp_err_t res = ultrasonic_measure_cm(&sensor, MAX_DISTANCE_CM, &distance);
if (res == ESP_OK) {
avg_distance = avg_distance / 10;
distance = avg_distance;
xQueueSend(q,(void *)&distance,(TickType_t )0); // add the value to the queue
}
for(int countdown = 300; countdown >= 0; countdown--) {
ESP_LOGI(TAG2, "%d... ", countdown);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
}
static void https_get_task(void *pvParameters)
{
uint32_t distance;
if(q == NULL){
printf("Queue is not ready \n");
return;
}
char buf[512];
int ret, flags, len;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_x509_crt cacert;
mbedtls_ssl_config conf;
mbedtls_net_context server_fd;
mbedtls_ssl_init(&ssl);
mbedtls_x509_crt_init(&cacert);
mbedtls_ctr_drbg_init(&ctr_drbg);
ESP_LOGI(TAG, "Seeding the random number generator");
mbedtls_ssl_config_init(&conf);
mbedtls_entropy_init(&entropy);
if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
NULL, 0)) != 0)
{
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
abort();
}
ESP_LOGI(TAG, "Attaching the certificate bundle...");
ret = esp_crt_bundle_attach(&conf);
if(ret < 0)
{
ESP_LOGE(TAG, "esp_crt_bundle_attach returned -0x%x\n\n", -ret);
abort();
}
ESP_LOGI(TAG, "Setting hostname for TLS session...");
/* Hostname set here should match CN in server certificate */
if((ret = mbedtls_ssl_set_hostname(&ssl, WEB_SERVER)) != 0)
{
ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
abort();
}
ESP_LOGI(TAG, "Setting up the SSL/TLS structure...");
if((ret = mbedtls_ssl_config_defaults(&conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
goto exit;
}
/* MBEDTLS_SSL_VERIFY_OPTIONAL is bad for security, in this example it will print
a warning if CA verification fails but it will continue to connect.
You should consider using MBEDTLS_SSL_VERIFY_REQUIRED in your own code.
*/
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_esp_enable_debug_log(&conf, CONFIG_MBEDTLS_DEBUG_LEVEL);
#endif
if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0)
{
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
goto exit;
}
while(1) {
xQueueReceive(q,&distance,portMAX_DELAY);
char REQUEST [1000];
char values [250];
sprintf(values, "hodnota=%d&token=123456789", distance);
sprintf (REQUEST, "POST /data.php HTTP/1.0\r\nHost: "WEB_SERVER"\r\nUser-Agent: ESP32\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded;\r\nContent-Length:%d\r\n\r\n%s\r\n",strlen(values),values);
mbedtls_net_init(&server_fd);
ESP_LOGI(TAG, "Connecting to %s:%s...", WEB_SERVER, WEB_PORT);
if ((ret = mbedtls_net_connect(&server_fd, WEB_SERVER,
WEB_PORT, MBEDTLS_NET_PROTO_TCP)) != 0)
{
ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret);
goto exit;
}
ESP_LOGI(TAG, "Connected.");
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
ESP_LOGI(TAG, "Performing the SSL/TLS handshake...");
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
{
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
{
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
goto exit;
}
}
ESP_LOGI(TAG, "Verifying peer X.509 certificate...");
if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0)
{
/* In real life, we probably want to close connection if ret != 0 */
ESP_LOGW(TAG, "Failed to verify peer certificate!");
bzero(buf, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
ESP_LOGW(TAG, "verification info: %s", buf);
}
else {
ESP_LOGI(TAG, "Certificate verified.");
}
ESP_LOGI(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(&ssl));
ESP_LOGI(TAG, "Writing HTTP request...");
size_t written_bytes = 0;
do {
ret = mbedtls_ssl_write(&ssl,
(unsigned char *)REQUEST + written_bytes,
strlen(REQUEST) - written_bytes);
if (ret >= 0) {
ESP_LOGI(TAG, "%d bytes written", ret);
written_bytes += ret;
} else if (ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_WANT_READ) {
ESP_LOGE(TAG, "mbedtls_ssl_write returned -0x%x", -ret);
goto exit;
}
} while(written_bytes < strlen(REQUEST));
ESP_LOGI(TAG, "Reading HTTP response...");
do
{
len = sizeof(buf) - 1;
bzero(buf, sizeof(buf));
ret = mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);
if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
continue;
if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
ret = 0;
break;
}
if(ret < 0)
{
ESP_LOGE(TAG, "mbedtls_ssl_read returned -0x%x", -ret);
break;
}
if(ret == 0)
{
ESP_LOGI(TAG, "connection closed");
break;
}
len = ret;
ESP_LOGD(TAG, "%d bytes read", len);
/* Print response directly to stdout as it is read */
for(int i = 0; i < len; i++) {
putchar(buf[i]);
}
} while(1);
mbedtls_ssl_close_notify(&ssl);
exit:
mbedtls_ssl_session_reset(&ssl);
mbedtls_net_free(&server_fd);
if(ret != 0)
{
mbedtls_strerror(ret, buf, 100);
ESP_LOGE(TAG, "Last error was: -0x%x - %s", -ret, buf);
}
putchar('\n'); // JSON output doesn't have a newline at end
static int request_count;
ESP_LOGI(TAG, "Completed %d requests", ++request_count);
ESP_LOGI(TAG, "Starting again!");
}
}
void app_main(void)
{
ESP_ERROR_CHECK( nvs_flash_init() );
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* 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());
q=xQueueCreate(20,sizeof(unsigned long));
if(q != NULL){
printf("Queue is created\n");
vTaskDelay(1000/portTICK_PERIOD_MS); //wait for a second
xTaskCreate(&ultrasonic, "ultrasonic", 2048, NULL, 5, NULL);
printf("Measurement task started\n");
xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL);
printf("HTTPS socket task started\n");
}else{
printf("Queue creation failed");
}
}
You can't use ESP-IDF program without required ultrasonic library and ESP-IDF components, make rules. So, I will put there some references to Github repository, where you can find whole source codes with other required libraries, files... ESP-IDF implementation was created under ESP-IDF 4.3.2, but should be 100% compatible with ESP-IDF 4.0 or 4.2 release version of framework.
There are two available web interfaces where are example codes sending datas.
You can see there datas in real-time.
For HTTP connectivity there is web interface hosted at: http://arduino.clanweb.eu/studna_s_prekladom/?lang=en
For HTTPS connectivity: https://hladinomer.000webhostapp.com/?lang=en
Github repository with HTTPS connectivity sensor node under ESP-IDF framework:
https://github.com/martinius96/hladinom ... ps_mbedtls
Github repository with HTTPS connectivity sensor node under Arduino Core:
https://github.com/martinius96/hladinom ... rduino.ino
There are also HTTP or HTTPS related examples if you browse that repository. You can find there implementations without FreeRTOS for standard connection or with OTA or with Deep Sleep for ULP application.
More informations about project of water level monitor you can find at its webpage if you are interested: https://martinius96.github.io/hladinome ... cripty/en/