diff --git a/examples/protocols/mdns/Makefile b/examples/protocols/mdns/Makefile new file mode 100644 index 00000000..0353c51c --- /dev/null +++ b/examples/protocols/mdns/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := mdns-test + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/mdns/README.md b/examples/protocols/mdns/README.md new file mode 100644 index 00000000..8cc56d83 --- /dev/null +++ b/examples/protocols/mdns/README.md @@ -0,0 +1,21 @@ +# mDNS example + +Shows how to use mDNS to advertise lookup services and hosts + +## Example workflow + +- mDNS is initialized with host name and instance name defined through `make menuconfig` and `_http._tcp` service is added to be advertised +- WiFi STA is started and trying to connect to the access point defined through `make menuconfig` +- The system event handler is used to pass the network events to mDNS so the service is aware when the interface comes up or down +- GPIO0 (BOOT Button) is initialized as pulled-up input that can be monitored for button press +- Example task is started to check if the button is pressed so it can execute the mDNS queries defined + +## Running the example + +- Run `make menuconfig` to configure the access point's SSID and Password and the default device mDNS host name and instance name +- Run `make flash monitor` to build and upload the example to your board and connect to it's serial terminal +- Wait for WiFi to connec to your access point +- You can now ping the device at `[hostname].local` and browse for `_http._tcp` on the same network to find the advertised service +- Pressing the BOOT button will start quring the local network for the predefined in `check_button` hosts and services + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/mdns/main/Kconfig.projbuild b/examples/protocols/mdns/main/Kconfig.projbuild new file mode 100644 index 00000000..3122e030 --- /dev/null +++ b/examples/protocols/mdns/main/Kconfig.projbuild @@ -0,0 +1,29 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +config MDNS_HOSTNAME + string "mDNS Hostname" + default "esp32-mdns" + help + mDNS Hostname for example to use + +config MDNS_INSTANCE + string "mDNS Instance Name" + default "ESP32 with mDNS" + help + mDNS Instance Name for example to use + +endmenu \ No newline at end of file diff --git a/examples/protocols/mdns/main/component.mk b/examples/protocols/mdns/main/component.mk new file mode 100644 index 00000000..a98f634e --- /dev/null +++ b/examples/protocols/mdns/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/protocols/mdns/main/mdns_example_main.c b/examples/protocols/mdns/main/mdns_example_main.c new file mode 100755 index 00000000..1cfcf3e2 --- /dev/null +++ b/examples/protocols/mdns/main/mdns_example_main.c @@ -0,0 +1,244 @@ +/* MDNS-SD Query and advertise Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "mdns.h" +#include "driver/gpio.h" +#include +#include + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define EXAMPLE_MDNS_HOSTNAME CONFIG_MDNS_HOSTNAME +#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int IP4_CONNECTED_BIT = BIT0; +const int IP6_CONNECTED_BIT = BIT1; + +static const char *TAG = "mdns-test"; +static bool auto_reconnect = true; + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_CONNECTED: + /* enable ipv6 */ + tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, IP4_CONNECTED_BIT); + break; + case SYSTEM_EVENT_AP_STA_GOT_IP6: + xEventGroupSetBits(wifi_event_group, IP6_CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + if (auto_reconnect) { + esp_wifi_connect(); + } + xEventGroupClearBits(wifi_event_group, IP4_CONNECTED_BIT | IP6_CONNECTED_BIT); + break; + default: + break; + } + mdns_handle_system_event(ctx, event); + return ESP_OK; +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static void initialise_mdns(void) +{ + //initialize mDNS + ESP_ERROR_CHECK( mdns_init() ); + //set mDNS hostname (required if you want to advertise services) + ESP_ERROR_CHECK( mdns_hostname_set(EXAMPLE_MDNS_HOSTNAME) ); + //set default mDNS instance name + ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) ); + + //structure with TXT records + mdns_txt_item_t serviceTxtData[3] = { + {"board","esp32"}, + {"u","user"}, + {"p","password"} + }; + + //initialize service + ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) ); + //add another TXT item + ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") ); + //change TXT item value + ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "u", "admin") ); +} + +static const char * if_str[] = {"STA", "AP", "ETH", "MAX"}; +static const char * ip_protocol_str[] = {"V4", "V6", "MAX"}; + +static void mdns_print_results(mdns_result_t * results){ + mdns_result_t * r = results; + mdns_ip_addr_t * a = NULL; + int i = 1, t; + while(r){ + printf("%d: Interface: %s, Type: %s\n", i++, if_str[r->tcpip_if], ip_protocol_str[r->ip_protocol]); + if(r->instance_name){ + printf(" PTR : %s\n", r->instance_name); + } + if(r->hostname){ + printf(" SRV : %s.local:%u\n", r->hostname, r->port); + } + if(r->txt_count){ + printf(" TXT : [%u] ", r->txt_count); + for(t=0; ttxt_count; t++){ + printf("%s=%s; ", r->txt[t].key, r->txt[t].value); + } + printf("\n"); + } + a = r->addr; + while(a){ + if(a->addr.type == MDNS_IP_PROTOCOL_V6){ + printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + r = r->next; + } + +} + +static void query_mdns_service(const char * service_name, const char * proto) +{ + ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto); + + mdns_result_t * results = NULL; + esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results); + if(err){ + ESP_LOGE(TAG, "Query Failed"); + return; + } + if(!results){ + ESP_LOGW(TAG, "No results found!"); + return; + } + + mdns_print_results(results); + mdns_query_results_free(results); +} + +static void query_mdns_host(const char * host_name) +{ + ESP_LOGI(TAG, "Query A: %s.local", host_name); + + struct ip4_addr addr; + addr.addr = 0; + + esp_err_t err = mdns_query_a(host_name, 2000, &addr); + if(err){ + if(err == ESP_ERR_NOT_FOUND){ + ESP_LOGW(TAG, "Host was not found!"); + return; + } + ESP_LOGE(TAG, "Query Failed"); + return; + } + + ESP_LOGI(TAG, IPSTR, IP2STR(&addr)); +} + +static void initialise_button(void) +{ + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.pin_bit_mask = 1; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = 1; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); +} + +static void check_button(void) +{ + static bool old_level = true; + bool new_level = gpio_get_level(GPIO_NUM_0); + if (!new_level && old_level) { + query_mdns_host("esp32"); + query_mdns_service("_arduino", "_tcp"); + query_mdns_service("_http", "_tcp"); + query_mdns_service("_printer", "_tcp"); + query_mdns_service("_ipp", "_tcp"); + query_mdns_service("_afpovertcp", "_tcp"); + query_mdns_service("_smb", "_tcp"); + query_mdns_service("_ftp", "_tcp"); + query_mdns_service("_nfs", "_tcp"); + } + old_level = new_level; +} + +static void mdns_example_task(void *pvParameters) +{ + /* Wait for the callback to set the CONNECTED_BIT in the event group. */ + xEventGroupWaitBits(wifi_event_group, IP4_CONNECTED_BIT | IP6_CONNECTED_BIT, + false, true, portMAX_DELAY); + while(1) { + check_button(); + vTaskDelay(50 / portTICK_PERIOD_MS); + } +} + +void app_main() +{ + ESP_ERROR_CHECK( nvs_flash_init() ); + initialise_mdns(); + initialise_wifi(); + initialise_button(); + xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL); +}