diff --git a/CLAUDE.md b/CLAUDE.md index bfaf6ed..ec697d5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,6 +37,7 @@ Repo: https://gitea.apointless.space/bsncubed/aes67-ESP32-P4 - [x] 0. Check chip revision and flash size; create IDF project and empty component stubs (layout in aes67-core-base.md). Set up the OTA partition table (two app slots, no factory) and PROJECT_VER from git now, so the layout never changes later. - [x] 1. Ethernet: IP101 up, DHCP, IP logged. Ping works. - [x] 2. Web server + config store (cJSON in NVS) + embedded index.html; /api/config, /api/status (stub values), /api/reboot. +- [ ] 2a. Finding the device: mDNS (hostname.local + _http._tcp), then LLDP (switch shows name + IP). - [ ] 2b. Firmware update: /api/ota upload + rollback self-test. Test: update to a new build, then deliberately flash a build that fails its self-test and confirm it rolls back. After this, flash over the network; keep USB for recovery. - [ ] 3. PTP TimeReceiver: lock to an existing GM (Riedel), fill `status.ptp`. Confirm EMAC hardware timestamps work. First check whether the installed ESP-IDF has a PTP example/component for the P4 before writing one. - [ ] 4. AES67 TX with a 1 kHz test tone, PTP-paced; /stream.sdp. Verify: import SDP on a Riedel Artist 4-wire AES67 port, and check packets/timestamps in Wireshark. diff --git a/components/aes67_net/CMakeLists.txt b/components/aes67_net/CMakeLists.txt index 4eef1e2..9f17119 100644 --- a/components/aes67_net/CMakeLists.txt +++ b/components/aes67_net/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "aes67_net.c" INCLUDE_DIRS "include" REQUIRES esp_eth esp_netif esp_event - PRIV_REQUIRES aes67_web lwip) + PRIV_REQUIRES aes67_web lwip espressif__mdns) diff --git a/components/aes67_net/aes67_net.c b/components/aes67_net/aes67_net.c index 917d575..97023c9 100644 --- a/components/aes67_net/aes67_net.c +++ b/components/aes67_net/aes67_net.c @@ -10,6 +10,7 @@ #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" +#include "mdns.h" static const char *TAG = "net"; @@ -105,6 +106,31 @@ static bool inet_validate(const cJSON *g, char *err, size_t n) check_addr(g, err, n); } +// mDNS: .local plus the web UI as _http._tcp. +static void mdns_setup(void) +{ + cJSON *net = cfg_get("net"); + const char *host = cJSON_GetObjectItemCaseSensitive(net, "hostname")->valuestring; + esp_err_t err = mdns_init(); + if (err == ESP_OK) { + mdns_hostname_set(host); + mdns_instance_name_set(host); + mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0); + ESP_LOGI(TAG, "mDNS: %s.local, _http._tcp port 80", host); + } else { + ESP_LOGE(TAG, "mDNS init failed: %s", esp_err_to_name(err)); + } + cJSON_Delete(net); +} + +static void net_apply(const cJSON *g) +{ + const char *host = cJSON_GetObjectItemCaseSensitive(g, "hostname")->valuestring; + if (mdns_hostname_set(host) == ESP_OK && mdns_instance_name_set(host) == ESP_OK) { + ESP_LOGI(TAG, "mDNS: now %s.local", host); + } +} + static void net_status(cJSON *st) { char ip[16] = "", mac_s[18] = ""; @@ -126,8 +152,8 @@ static void net_status(cJSON *st) esp_err_t aes67_net_init(esp_eth_handle_t eth) { - // Config is stored only for now; applying it (hostname, static IP, VLAN) comes later. - ESP_ERROR_CHECK(cfg_register("net", NET_DEFAULTS, net_validate, NULL)); + // Applied so far: hostname (mDNS). Static IP and VLAN come later. + ESP_ERROR_CHECK(cfg_register("net", NET_DEFAULTS, net_validate, net_apply)); ESP_ERROR_CHECK(cfg_register("inet", INET_DEFAULTS, inet_validate, NULL)); ESP_ERROR_CHECK(esp_netif_init()); @@ -143,6 +169,7 @@ esp_err_t aes67_net_init(esp_eth_handle_t eth) ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ip_event, NULL)); + mdns_setup(); status_register(net_status); return esp_eth_start(eth); } diff --git a/components/aes67_net/idf_component.yml b/components/aes67_net/idf_component.yml new file mode 100644 index 0000000..edb34be --- /dev/null +++ b/components/aes67_net/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + espressif/mdns: "^1.13.1" diff --git a/dependencies.lock b/dependencies.lock new file mode 100644 index 0000000..d432dcc --- /dev/null +++ b/dependencies.lock @@ -0,0 +1,20 @@ +dependencies: + espressif/mdns: + component_hash: b679eafd0acae2066e2645bd91d073e33ca5be515e2545cd3c4e55a3e3bce3cb + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.13.1 + idf: + source: + type: idf + version: 5.5.5 +direct_dependencies: +- espressif/mdns +manifest_hash: e5eba11c864076c14a58153c7f761bec2b064244e66ca3f76c0dc170db907329 +target: esp32p4 +version: 2.0.0 diff --git a/docs/aes67-core-base.md b/docs/aes67-core-base.md index 9757f45..b47aa34 100644 --- a/docs/aes67-core-base.md +++ b/docs/aes67-core-base.md @@ -91,6 +91,7 @@ When this device is the GM, offset/frequency/delay show "–". ## Network - Default: one untagged interface. VLAN split option: AES67 untagged + internet tagged (inet.vlan_id/pcp) using the ESP-IDF vlan_support approach (examples/network/vlan_support, ESP32-P4 supported). AES67 side has no gateway; default route and DNS are on inet. - PTP, RTP, SAP and IGMP bind to the AES67 netif. HTTP server filters on `web_on`. +- mDNS (espressif/mdns): `.local` plus the web UI as `_http._tcp` port 80. A hostname change applies live (the new name answers after ~1 s of RFC 6762 probing). - Switch port: AES67 as native/untagged VLAN, internet tagged, PoE on. IGMP snooping + querier on the AES67 VLAN. ## Syslog