mdns: added example test for ci runners

This commit is contained in:
David Cermak 2018-10-12 16:45:52 +02:00 committed by bot
parent d16762a036
commit 6309643c1d
4 changed files with 241 additions and 22 deletions

View file

@ -10,12 +10,88 @@ Shows how to use mDNS to advertise lookup services and hosts
- 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
### Configure the project
- 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
```
make menuconfig
```
* Set `Default serial port` under `Serial flasher config`.
* Set `WiFi SSID` and `WiFi Password` for the board to connect to AP.
* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name`
* Disable `Resolve test services` to prevent the example from querying defined names/services on startup (cause warnings in example logs, as illustrated below)
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
- Wait for WiFi to connect to your access point
- You can now ping the device at `[board-hostname].local`, where `[board-hostname]` is a string created from preconfigured hostname (`esp32-mdns` by default) and last 3 bytes from device MAC address. Please check the serial output log for the specific board-hostname (`esp32-mdns_80FFFF` in the log below)
- You can also browse for `_http._tcp` on the same network to find the advertised service
- Pressing the BOOT button will start querying the local network for the predefined in `check_button` hosts and services
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (0) cpu_start: Starting scheduler on APP CPU.
I (276) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (276) mdns-test: mdns hostname set to: [esp32-mdns_80FFFF]
I (286) wifi: wifi driver task: 3ffc2fa4, prio:23, stack:3584, core=0
I (286) wifi: wifi firmware version: a3be639
I (286) wifi: config NVS flash: enabled
I (296) wifi: config nano formating: disabled
I (296) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (306) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (336) wifi: Init dynamic tx buffer num: 32
I (336) wifi: Init data frame dynamic rx buffer num: 32
I (336) wifi: Init management frame dynamic rx buffer num: 32
I (346) wifi: Init static rx buffer size: 1600
I (346) wifi: Init static rx buffer num: 10
I (346) wifi: Init dynamic rx buffer num: 32
I (356) mdns-test: Setting WiFi configuration SSID myssid...
I (426) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0
I (426) wifi: mode : sta (30:ae:a4:80:FF:FF)
I (426) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (1756) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1
I (2736) wifi: state: init -> auth (b0)
I (2756) wifi: state: auth -> assoc (0)
I (2766) wifi: state: assoc -> run (10)
I (2786) wifi: connected with myssid, channel 11
I (2786) wifi: pm start, type: 1
I (4786) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2
I (4786) mdns-test: Query A: tinytester.local
W (6876) mdns-test: ESP_ERR_NOT_FOUND: Host was not found!
I (6876) mdns-test: Query PTR: _tiny._tcp.local
W (9976) mdns-test: No results found!
I (21126) mdns-test: Query A: esp32.local
W (23176) mdns-test: ESP_ERR_NOT_FOUND: Host was not found!
I (23176) mdns-test: Query PTR: _arduino._tcp.local
W (26276) mdns-test: No results found!
I (26276) mdns-test: Query PTR: _http._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:80
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : switch4e4919
SRV : switch4e4919.local:80
TXT : [1] path=/config/authentication_page.htm;
A : 192.168.0.118
I (29396) mdns-test: Query PTR: _printer._tcp.local
1: Interface: STA, Type: V6
PTR : HP Color LaserJet MFP M277dw (7C2E10)
SRV : NPI7C2E10.local:515
A : 254.128.0.0
2: Interface: STA, Type: V4
PTR : HP Color LaserJet MFP M277dw (7C2E10)
```
See the README.md file in the upper level 'examples' directory for more information about examples.

View file

@ -2,28 +2,37 @@ menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
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
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
default "ESP32 with mDNS"
help
mDNS Instance Name for example to use
config RESOLVE_TEST_SERVICES
bool "Resolve test services"
default y
help
Enable resolving test services on startup.
These services are advertized and evaluated in automated tests.
When executed locally, these will not be resolved and warnings appear in the log.
Please set to false to disable initial querying to avoid warnings.
endmenu

View file

@ -29,11 +29,12 @@
#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;
static const char c_config_hostname[] = CONFIG_MDNS_HOSTNAME;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
@ -97,10 +98,20 @@ static void initialise_wifi(void)
static void initialise_mdns(void)
{
_Static_assert(sizeof(c_config_hostname) < CONFIG_MAIN_TASK_STACK_SIZE/2, "Configured mDNS name consumes more than half of the stack. Please select a shorter host name or extend the main stack size please.");
const size_t config_hostname_len = sizeof(c_config_hostname) - 1; // without term char
char hostname[config_hostname_len + 1 + 3*2 + 1]; // adding underscore + 3 digits + term char
uint8_t mac[6];
// adding 3 LSBs from mac addr to setup a board specific name
esp_read_mac(mac, ESP_MAC_WIFI_STA);
snprintf(hostname, sizeof(hostname), "%s_%02x%02X%02X", c_config_hostname, mac[3], mac[4], mac[5]);
//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) );
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) );
@ -191,7 +202,7 @@ static void query_mdns_host(const char * host_name)
return;
}
ESP_LOGI(TAG, IPSTR, IP2STR(&addr));
ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr));
}
static void initialise_button(void)
@ -228,6 +239,12 @@ 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);
#if CONFIG_RESOLVE_TEST_SERVICES == 1
/* Send initial queries that are started by CI tester */
query_mdns_host("tinytester");
#endif
while(1) {
check_button();
vTaskDelay(50 / portTICK_PERIOD_MS);

View file

@ -0,0 +1,117 @@
import re
import os
import sys
import socket
import time
import imp
import struct
import dpkt, dpkt.dns
from threading import Thread
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
g_run_server = True
g_done = False
def mdns_server(esp_host):
global g_run_server
global g_done
UDP_IP="0.0.0.0"
UDP_PORT=5353
MCAST_GRP = '224.0.0.251'
sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM )
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
sock.bind( (UDP_IP,UDP_PORT) )
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
# sock.sendto(dns.pack(),(MCAST_GRP,UDP_PORT))
sock.settimeout(30)
resp_dns = dpkt.dns.DNS(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
resp_dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA
resp_dns.rcode = dpkt.dns.DNS_RCODE_NOERR
arr = dpkt.dns.DNS.RR()
arr.cls = dpkt.dns.DNS_IN
arr.type = dpkt.dns.DNS_A
arr.name = u'tinytester.local'
arr.ip =socket.inet_aton('127.0.0.1')
resp_dns. an.append(arr)
sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT))
while g_run_server:
try:
m=sock.recvfrom( 1024 );
dns = dpkt.dns.DNS(m[0])
if len(dns.qd)>0 and dns.qd[0].type == dpkt.dns.DNS_A:
if dns.qd[0].name == u'tinytester.local':
print (dns.__repr__(),dns.qd[0].name)
sock.sendto(resp_dns.pack(),(MCAST_GRP,UDP_PORT))
if len(dns.an)>0 and dns.an[0].type == dpkt.dns.DNS_A:
if dns.an[0].name == esp_host + u'.local':
print("Received answer esp32-mdns query")
g_done = True
print (dns.an[0].name)
dns = dpkt.dns.DNS(b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01')
dns.qd[0].name= esp_host + u'.local'
sock.sendto(dns.pack(),(MCAST_GRP,UDP_PORT))
print("Sending esp32-mdns query")
time.sleep(0.5)
except socket.timeout:
break
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_mdns(env, extra_data):
global g_run_server
global g_done
"""
steps: |
1. join AP + init mdns example
2. get the dut host name (and IP address)
3. check the mdns name is accessible
4. check DUT output if mdns advertized host is resolved
"""
dut1 = env.get_dut("mdns-test", "examples/protocols/mdns")
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "mdns-test.bin")
bin_size = os.path.getsize(binary_file)
IDF.log_performance("mdns-test_bin_size", "{}KB".format(bin_size//1024))
IDF.check_performance("mdns-test_bin_size", bin_size//1024)
# 1. start mdns application
dut1.start_app()
# 2. get the dut host name (and IP address)
specific_host = dut1.expect(re.compile(r"mdns hostname set to: \[([^\]]+)\]"), timeout=30)
specific_host = str(specific_host[0])
dut_ip = ""
try:
dut_ip = dut1.expect(re.compile(r" sta ip: ([^,]+),"), timeout=30)
except DUT.ExpectTimeout:
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
# 3. check the mdns name is accessible
thread1 = Thread(target = mdns_server, args = (specific_host,))
thread1.start()
start = time.time()
while (time.time() - start) <= 60:
if g_done:
print("Test passed")
break
g_run_server = False
thread1.join()
if g_done == False:
raise ValueError('Test has failed: did not receive mdns answer within timeout')
# 4. check DUT output if mdns advertized host is resolved
dut1.expect(re.compile(r"mdns-test: Query A: tinytester.local resolved to: 127.0.0.1"), timeout=30)
if __name__ == '__main__':
test_examples_protocol_mdns()