Ultrasonic sensor node ESP32 - FreeRTOS example ESP-IDF / Arduino Core

martinius96
Posts: 34
Joined: Thu Dec 13, 2018 1:39 am

Ultrasonic sensor node ESP32 - FreeRTOS example ESP-IDF / Arduino Core

Postby martinius96 » Sun Jan 30, 2022 1:34 pm

Today I would like to show you a few codes that I have created for my project Water level monitor. It is using ESP32 as sensor node that is doing measurement of water level using ultrasonic distance sensor HC-SR04, or its waterproof variant JSN-SR04T. Other sensors with Trigger / Echo signals from the US-02X, IOE-SR0X, SR0X, HC-SR0X, HY-SRF0X, DYP-ME007, Parallax PING)))™ series can also be used. After measurement datas are sent to web interface and stored in MySQL database. There is also dasboard where user can see his datas in graphs, tables etc with history up to 1 year ago...
Image
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.
Image
Image
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.
Image
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
Task B (consumer task - it is using datas)
  • 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
Image

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
  }
}
Output - HTTPS request task - with Verbose DEBUG ON, FreeRTOS - Arduino Core
Image
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");
    }  

}
Output - HTTPS request task - FreeRTOS - ESP-IDF
Image

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/

Image
Last edited by martinius96 on Thu Jun 16, 2022 9:54 pm, edited 2 times in total.

martinius96
Posts: 34
Joined: Thu Dec 13, 2018 1:39 am

Re: Ultrasonic sensor node ESP32 - FreeRTOS example ESP-IDF / Arduino Core

Postby martinius96 » Wed Apr 06, 2022 10:18 pm

Newest Arduino core 2.0.2 is NOT compatible with HTTPS example provided here (based on mbed_tls example from ESP-IDF), because it is based on ESP-IDF 4.4-beta1 and it is not working on webserver pages with TLS 1.3. So switch to 2.0.1 Arduino Core as release version or development version 2.0.3-RC1 is working too, also currently released 2.0.3 is working properly.

Bonus: ESP32 FreeRTOS sensor node with PHY Ethernet LAN8720 / TLK110 example sketch (Arduino Core)
Note: There were changed pins because of PHY Ethernet module, so Echo / Trigger pin does not match Schematics above.

HTTP connection - ESP32 + LAN8720

Code: Select all

/*|----------------------------------------------------------------------------|*/
/*|Project: Ultrasonic water level monitor - HC-SR04 / JSN-SR04T / HY-SRF05... |*/
/*|ESP32 (DevKit, Generic) + PHY Ethernet LAN8720 / TLK110, RMII interface     |*/
/*|Author: Martin Chlebovec (martinius96)                                      |*/
/*|E-mail: martinius96@gmail.com                                               |*/
/*|Buy me a coffee at: paypal.me/chlebovec                                     |*/
/*|Project info: https://martinius96.github.io/hladinomer-studna-scripty/en    |*/
/*|Test web interface: http://arduino.clanweb.eu/studna_s_prekladom/?lang=en   |*/
/*|Revision: 8. September 2022                                                 |*/
/*|----------------------------------------------------------------------------|*/

#include <ETH.h>
#include <NewPingESP8266.h>

const char* host = "arduino.clanweb.eu"; //webhost
String url = "/studna_s_prekladom/data.php"; //URL address to target PHP file

boolean eth_state = false;
#define pinTrigger    4 //CONNECT TO TRIGGER PIN OF ULTRASONIC SENSOR
#define pinEcho       5 //CONNECT TO ECHO PIN OF ULTRASONIC SENSOR
#define maxVzdialenost 450
NewPingESP8266 sonar(pinTrigger, pinEcho, maxVzdialenost);

/*
     ETH_CLOCK_GPIO0_IN   - default: external clock from crystal oscillator
     ETH_CLOCK_GPIO0_OUT  - 50MHz clock from internal APLL output on GPIO0 - possibly an inverter is needed for LAN8720
     ETH_CLOCK_GPIO16_OUT - 50MHz clock from internal APLL output on GPIO16 - possibly an inverter is needed for LAN8720
     ETH_CLOCK_GPIO17_OUT - 50MHz clock from internal APLL inverted output on GPIO17 - tested with LAN8720
*/
#ifdef ETH_CLK_MODE
#undef ETH_CLK_MODE
#endif
#define ETH_CLK_MODE    ETH_CLOCK_GPIO17_OUT

// Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
#define ETH_POWER_PIN   -1

// Type of the Ethernet PHY (LAN8720 or TLK110)
#define ETH_TYPE        ETH_PHY_LAN8720

// I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
#define ETH_ADDR        1

// Pin# of the I²C clock signal for the Ethernet PHY, DONT USE THIS PIN FOR ultrasonic sensor in this sketch
#define ETH_MDC_PIN     23

// Pin# of the I²C IO signal for the Ethernet PHY
#define ETH_MDIO_PIN    18

TaskHandle_t Task1; //ULTRASONIC MEASUREMENT
TaskHandle_t Task2; //PHY ETHERNET HTTP SOCKET
QueueHandle_t  q = NULL;


WiFiClient client;
static void Task1code( void * parameter);
static void Task2code( void * parameter);
void WiFiEvent(WiFiEvent_t event);

void setup() {
  Serial.begin(115200);
  WiFi.onEvent(WiFiEvent);
  ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE);
  delay(5000);
  q = xQueueCreate(20, sizeof(int));
  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"));
    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("HTTP Socket task started"));
  } else {
    Serial.println(F("Queue creation failed"));
  }
}

void loop() {
  yield();
}

static void Task1code( void * parameter) {
  if (q == NULL) {
    Serial.println(F("Queue in 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....
    String data = "hodnota=" + String(distance) + "&token=123456789";
    client.stop();
    while (eth_state != true) {
      vTaskDelay(1 / portTICK_PERIOD_MS);
    }
    if (client.connect(host, 80)) {
      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"));
    }
    yield();
    client.stop();
  }
}

void WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case ARDUINO_EVENT_ETH_START:
      eth_state = false;
      Serial.println("ETH Started");
      //set eth hostname here
      ETH.setHostname("esp32-ethernet");
      break;
    case ARDUINO_EVENT_ETH_CONNECTED:
      eth_state = false;
      Serial.println("ETH Connected");
      break;
    case ARDUINO_EVENT_ETH_GOT_IP:
      eth_state = true;
      Serial.print("ETH MAC: ");
      Serial.print(ETH.macAddress());
      Serial.print(", IPv4: ");
      Serial.print(ETH.localIP());
      if (ETH.fullDuplex()) {
        Serial.print(", FULL_DUPLEX");
      }
      Serial.print(", ");
      Serial.print(ETH.linkSpeed());
      Serial.println("Mbps");
      break;
    case ARDUINO_EVENT_ETH_DISCONNECTED:
      eth_state = false;
      Serial.println("ETH Disconnected");
      break;
    case ARDUINO_EVENT_ETH_STOP:
      eth_state = false;
      Serial.println("ETH Stopped");
      break;
    default:
      break;
  }
}

HTTPS connection using WiFiClientSecure library - ESP32 + LAN8720

Code: Select all

/*|----------------------------------------------------------------------------|*/
/*|Project: Ultrasonic water level monitor - HC-SR04 / JSN-SR04T / HY-SRF05... |*/
/*|ESP32 (DevKit, Generic) + PHY Ethernet LAN8720 / TLK110, RMII interface     |*/
/*|Author: Martin Chlebovec (martinius96)                                      |*/
/*|E-mail: martinius96@gmail.com                                               |*/
/*|Buy me a coffee at: paypal.me/chlebovec                                     |*/
/*|Project info: https://martinius96.github.io/hladinomer-studna-scripty/en    |*/
/*|Test web interface: https://hladinomer.000webhostapp.com                    |*/
/*|Revision: 8. September 2022                                                 |*/
/*|----------------------------------------------------------------------------|*/

#include <WiFiClientSecure.h>
#include <ETH.h>
#include <NewPingESP8266.h>

const char* host = "hladinomer.000webhostapp.com"; //webhost
String url = "/data.php"; //URL address to PHP file

boolean eth_state = false;
#define pinTrigger    4
#define pinEcho       5 //CHANGED FROM D23 !!!!
#define maxVzdialenost 450
NewPingESP8266 sonar(pinTrigger, pinEcho, maxVzdialenost);

/*
     ETH_CLOCK_GPIO0_IN   - default: external clock from crystal oscillator
     ETH_CLOCK_GPIO0_OUT  - 50MHz clock from internal APLL output on GPIO0 - possibly an inverter is needed for LAN8720
     ETH_CLOCK_GPIO16_OUT - 50MHz clock from internal APLL output on GPIO16 - possibly an inverter is needed for LAN8720
     ETH_CLOCK_GPIO17_OUT - 50MHz clock from internal APLL inverted output on GPIO17 - tested with LAN8720
*/
#ifdef ETH_CLK_MODE
#undef ETH_CLK_MODE
#endif
#define ETH_CLK_MODE    ETH_CLOCK_GPIO17_OUT

// Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
#define ETH_POWER_PIN   -1

// Type of the Ethernet PHY (LAN8720 or TLK110)
#define ETH_TYPE        ETH_PHY_LAN8720

// I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
#define ETH_ADDR        1

// Pin# of the I²C clock signal for the Ethernet PHY, DONT USE THIS PIN FOR ultrasonic sensor in this sketch
#define ETH_MDC_PIN     23

// Pin# of the I²C IO signal for the Ethernet PHY
#define ETH_MDIO_PIN    18

WiFiClientSecure client; //Secured client object for HTTPS connection
TaskHandle_t Task1; //ULTRASONIC MEASUREMENT
TaskHandle_t Task2; //PHY ETHERNET HTTP SOCKET
QueueHandle_t  q = NULL;

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


static void Task1code( void * parameter);
static void Task2code( void * parameter);
void WiFiEvent(WiFiEvent_t event);

void setup() {
  Serial.begin(115200);
  WiFi.onEvent(WiFiEvent);
  ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE, ETH_CLK_MODE);
  delay(5000);
  client.setCACert(test_root_ca);
  q = xQueueCreate(20, sizeof(int));
  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"));
    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 Socket task started"));
  } else {
    Serial.println(F("Queue creation failed"));
  }
}

void loop() {
  yield();
}

static void Task1code( void * parameter) {
  if (q == NULL) {
    Serial.println(F("Queue in 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....
    String data = "hodnota=" + String(distance) + "&token=123456789";
    client.stop();
    while (eth_state != true) {
      vTaskDelay(1 / portTICK_PERIOD_MS);
    }
    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"));
    }
    yield();
    client.stop();
  }
}

void WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case ARDUINO_EVENT_ETH_START:
      eth_state = false;
      Serial.println("ETH Started");
      //set eth hostname here
      ETH.setHostname("esp32-ethernet");
      break;
    case ARDUINO_EVENT_ETH_CONNECTED:
      eth_state = false;
      Serial.println("ETH Connected");
      break;
    case ARDUINO_EVENT_ETH_GOT_IP:
      eth_state = true;
      Serial.print("ETH MAC: ");
      Serial.print(ETH.macAddress());
      Serial.print(", IPv4: ");
      Serial.print(ETH.localIP());
      if (ETH.fullDuplex()) {
        Serial.print(", FULL_DUPLEX");
      }
      Serial.print(", ");
      Serial.print(ETH.linkSpeed());
      Serial.println("Mbps");
      break;
    case ARDUINO_EVENT_ETH_DISCONNECTED:
      eth_state = false;
      Serial.println("ETH Disconnected");
      break;
    case ARDUINO_EVENT_ETH_STOP:
      eth_state = false;
      Serial.println("ETH Stopped");
      break;
    default:
      break;
  }
}

Last edited by martinius96 on Thu Sep 08, 2022 10:59 am, edited 3 times in total.

martinius96
Posts: 34
Joined: Thu Dec 13, 2018 1:39 am

Re: Ultrasonic sensor node ESP32 - FreeRTOS example ESP-IDF / Arduino Core

Postby martinius96 » Wed Aug 03, 2022 12:25 pm

Today I have tried PHY Ethernet LAN7820 sketch on my hardware. I need to change sketch a bit due signals that are required for RMII interface and also for some outputs, because system was printing IP address 0.0.0.0, but milliseconds after that PHY Ethernet had IP and I was able to see it only in Verbose Debug output, so slighty delay is required and it will appear in Serial output. Also pins for ultrasonic sensor were changed too. But overall, working like charm. Edited code is available above in previous post. HTTP and HTTPS connection working properly. Tested on Arduino Core 2.0.3 (release). 8-)

Image
Image
Image

calvsg88
Posts: 10
Joined: Tue Aug 09, 2022 7:54 am

Re: Ultrasonic sensor node ESP32 - FreeRTOS example ESP-IDF / Arduino Core

Postby calvsg88 » Wed Aug 10, 2022 12:09 pm

nice project!

Who is online

Users browsing this forum: No registered users and 31 guests