lwIP dan ESP32 – mDNS

Pada contoh project kali ini, kita akan membuat sensor UDP service pada local network. ESP32 terhubung ke Wi-Fi network dan mengabarkan service melalui multicast DNS (mDNS).

Ketika client terhubung dan meminta data, akan dijawab dengan UDP datagram.

Untuk sensor, kita gunakan DHT11 yang dihubungkan ke GPIO17.

Buat project baru, lalu buka platformio.ini (perhatikan untuk lib_extra_dirs, gunakan path yang sesuai dengan struktur project Anda).

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = espidf
monitor_speed = 115200
lib_extra_dirs = ../esp-idf-lib/components
build_flags =
-DWIFI_SSID=${sysenv.WIFI_SSID}
-DWIFI_PASS=${sysenv.WIFI_PASS}

Kita akan gunakan module wifi connect yang telah dipelajari pada modul sebelumnya, untuk mudahnya, Anda dapat download di https://drive.google.com/drive/folders/1qbx49wr2rJVdL-vsLHFSH5TNS28Xy-oK?usp=sharing, simpan di direktory lib.

Code

Setelah persiapan diatas, kita bisa mulai melakukan coding untuk aplikasi. Buka main.c, tambahkan kode dibawah:

//import library
#include "wifi_connect.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "dht.h"
#include "hal/gpio_types.h"
#include "mdns.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#define DHT11_PIN GPIO_NUM_17
#define SVC_PORT 1111
static const char *TAG = "sensor_app";
static int16_t temperature;
static int16_t humidity;

//start mdns
static void start_mdns(void)
{
    mdns_init();
    mdns_hostname_set("esp32_sensor");
    mdns_instance_name_set("esp32 with dht11");
    mdns_txt_item_t serviceTxtData[4] = {
        {"temperature", "y"},
        {"humidity", "y"},
        {"pressure", "n"},
        {"light", "n"},
    };
    mdns_service_add("ESP32-Sensor", "_sensor", "_udp", SVC_PORT, serviceTxtData, 4);
}

//start udp server
static void start_udp_server(void)
{
    char data_buffer[64];
    struct sockaddr_in server_addr = {
    .sin_family = AF_INET,
    .sin_port = htons(SVC_PORT),
    .sin_addr = {
    .s_addr = htonl(INADDR_ANY)}};
    while (1)
    {
        int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
        if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
        {
            ESP_LOGE(TAG, "bind failed");
        }
        else
        {
            while (1)
            {
                struct sockaddr_storage client_addr;
                socklen_t socklen = sizeof(client_addr);
                int len = recvfrom(sock, data_buffer,
                sizeof(data_buffer) - 1, 0,
                (struct sockaddr *) &client_addr, &socklen);
                if (len < 0)
                {
                    ESP_LOGE(TAG, "recvfrom failed");
                    break;
                }

                data_buffer[len] = 0;
                if (!strcmp(data_buffer, "temperature"))
                {
                    sprintf(data_buffer, "%d", temperature);
                }
                else if (!strcmp(data_buffer, "humidity"))
                {
                    sprintf(data_buffer, "%d", humidity);
                }
                else
                {
                    sprintf(data_buffer, "err");
                }
                len = strlen(data_buffer);
                if (sendto(sock, data_buffer, len, 0, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0)
                {
                    ESP_LOGE(TAG, "sendto failed");
                    break;
                }
            }
        }
        if (sock != -1)
        {
            shutdown(sock, 0);
            close(sock);
        }
        vTaskDelay(1000);
    }
}

//start sensor service
static void start_sensor_service(void *arg)
{
    start_mdns();
    start_udp_server();
    vTaskDelete(NULL);
}

//wifi connected cb
static void wifi_connected_cb(void)
{
    ESP_LOGI(TAG, "wifi connected");
    xTaskCreate(start_sensor_service, "svc", 5 * configMINIMAL_STACK_SIZE, NULL, 5, NULL);
}

//wifi failed cb
static void wifi_failed_cb(void)
{
    ESP_LOGE(TAG, "wifi failed");
}

//read dht11
static void read_dht11(void *arg)
{
    while (1)
    {
        vTaskDelay(2000 / portTICK_RATE_MS);
        dht_read_data(DHT_TYPE_DHT11, DHT11_PIN, &humidity, &temperature);
        humidity /= 10;
        temperature /= 10;
    }
}

// main function
void app_main()
{
    connect_wifi_params_t p = {
        .on_connected = wifi_connected_cb,
        .on_failed = wifi_failed_cb};
    connect_wifi(p);
    xTaskCreate(read_dht11, "temp", 3 * configMINIMAL_STACK_SIZE, NULL, 5, NULL);
}

Penjelasan Code

Bagian import library

Kita include header files yang digunakan, kemudian definisikan DHT11 pin dan service port
number, SVC_PORT. Ini adalah UDP port number untuk sensor service dimana clients akan
connect dan query.

Bagian start mDNS

Pada fungsi start_mdns, kita inisialisasi library dan set host name sebagai esp32_sensor, yang akan dilis pada network sebagai esp32_sensor.local.

Kita dapat ping esp32_sensor.local dan menerima reply jika aplikasi berjalan dengan baik.

Kita juga tambahkan service dengan namam ESP32-Sensor. Pada service description, kita spesifikasikan data yang dapat di-query dari service. Karena kita menggunakan DHT11, data yang tersedia adalah temperature dan humidity.

Bagian start UDP server

Pada fungsi start_udp_server kita implementasikan service. Logikanya cukup sederhana.

Pertama kita definisikan server socket address dengan port SVC_PORT. Kemudan dalam while loop, kita create dan manage UDP socket.

Setelah UDP socket dicreate, bind ke server address. Terdapat dua two while loops.

Loop terluar untuk menjaga service up dan berjalan.

Loop kedua akan mendengarkan clients. Fungsi recvfrom menunggu request dari client. Ketika request diterima, kita periksa apakah query adalah temperature atau humidity.

Kemudian kita jawab dengan memanggil fungsi sendto.

Kita handle semua requests dan reply dalam inner loop. Meskipun demikian, jika ada function calls fail, loop akan breaks dan kita kembali ke scope outer loop.

Bagian start sensor service

start_sensor_service adalah FreeRTOS task. Akan memanggil fungsi yang telah kita implementasikan sebelumnya untuk menjalankan mDNS service danUDP server, diakhir akan melakukan self delete dari FreeRTOS task list.

Bagian wifi connected cb

wifi_connected_cb adalah callback function yang dipanggil ketika local Wi-Fi terhubung. Akan membuat FreeRTOS task start_sensor_service.

Bagian wifi fail cb

Pada wifi_failed_cb, kita print log message ke serial monitor agar kita paham bila WiFi connection failed.

Bagian read dht11

read_dht11 adalah FreeRTOS task function, akan membaca temperature dan humidity dari DHT11 dan menyimpannya pada global variables.

Bagian Main function

Pada app_main, kita akan panggil connect_wifi (dari project library) dan membuat DHT11 task.

Compile dan Upload

Buka Platformio CLI, lalu jalankan perintah berikut

(penv)$ export WIFI_SSID='\"<your_wifi_ssid>\"'
(penv)$ export WIFI_PASS='\"<your_wifi_passwd>\"'
(penv)$ pio run
(penv)$ pio run -t erase
(penv)$ pio run -t upload

Setelah aplikasi diupload, kita dapat lakukan test ping.

(penv)$ ping esp32_sensor.local
PING esp32_sensor.local (192.168.1.82) 56(84) bytes of data
.
64 bytes from espressif (192.168.1.82): icmp_seq=1 ttl=255
 time=512 ms
64 bytes from espressif (192.168.1.82): icmp_seq=2 ttl=255
 time=80.9 ms
64 bytes from espressif (192.168.1.82): icmp_seq=3 ttl=255
 time=456 ms

Kita dapat list semua mDNS services di network untuk melihat apakah sensor tersedia:

(penv)$ avahi-browse -a

+ wlp2s0 IPv4 ESP32-Sensor _sensor._udp local

Kita dapat melihat detail dari sensor service dengan perintah berikut:

(penv)$ avahi-browse _sensor._udp -rt
+ wlp2s0 IPv4 ESP32-Sensor _sensor._udp local
= wlp2s0 IPv4 ESP32-Sensor _sensor._udp local
hostname = [esp32_sensor.local]
address = [192.168.1.82]
port = [1111]
txt = ["temperature=y" "humidity=y" "pressure=n" "light=n"]

Sampai disini kita sudah belajar menggunakan mDNS, pada modul selanjutnya kita akan mempelajari SNTP.

Sharing is caring:

Leave a Comment