- ptp_hw: ptp_hw_adj_freq() (ETH_MAC_ESP_CMD_ADJ_PTP_TIME, absolute ppb
vs nominal, clamped +-500 ppm, retried while the addend update is
busy) and ptp_hw_step() (read + write time).
- ptp_clock: first Sync after a GM is selected seeds the integral with
the measured rate and steps the clock; then linuxptp-style PI (kp/ki
from the Sync interval: 0.7/0.3 at 1 s). Re-step above 1 ms. Locked
after 8 Syncs with |offset| < 1 us, unlocked after 3 above; GM change
or loss unlocks and keeps the frequency (holdover).
- Verified vs ptp4l: stepped to GM time, locked after ~19 s; locked
offset within +-510 ns (mostly < 300 ns), correction +39.9 ppm
(crystal -39.8 ppm), path delay ~10.2 us.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ptp_clock.c replaces the 3.1 logger: UDP/IPv4 multicast, E2E.
Announce: IEEE 1588 dataset comparison picks the best GM, dropped after
announceReceiptTimeout x its announce interval. Sync/Follow_Up (two-step
and one-step) with HW t2 and correctionField. Delay_Req sent as a raw
frame (DSCP ptp.dscp, TTL 1, clockIdentity = EUI-64 from MAC) with HW
TX timestamp t3, randomised at the GM's Delay_Resp interval; Delay_Resp
matched on requestingPortIdentity + seq.
- Path delay corrected for offset drift between t2 and t3 (rate from
consecutive Syncs) so it is right before the clock is syntonised.
- ptp_hw: ptp_hw_send_event() builds Eth/IPv4/UDP 319 and returns the HW
TX timestamp.
- Verified vs ptp4l (i210 GM, HP 2530 non-PTP switch, PC 1G / board 100M):
GM selected, path delay settles at ~10.3 us and stays flat, rate
-39.8 +-0.4 ppm, offset drifts at -40 us/s (no servo yet).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONFIG_ETH_TRANSMIT_MUTEX was off, so LLDP (esp_timer task) could hit the
EMAC TX descriptors at the same time as lwIP. PTP adds a third sender.
Verified: OTA-installed build passes its self-test, web/status fine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_ptp/ptp_hw.c: start the EMAC IEEE 1588 clock (IDF
ETH_MAC_ESP_CMD_PTP_ENABLE) and additionally enable snapshots for
PTP over UDP/IPv4 (IDF only enables PTP over Ethernet/L2). An RX hook
on the driver's info input path records {type, seq, sourcePortId, HW
timestamp} of port-319 PTPv2 event messages, then hands every frame to
lwIP unchanged.
- aes67_ptp.c: temporary 3.1 logger joins 224.0.1.129:319/320 and pairs
Sync HW RX timestamps with Follow_Up origin timestamps.
- Checked against linuxptp ptp4l (-4 -E -H, Intel igb) as GM: every Sync
has a HW timestamp; HW Sync intervals track the GM intervals with a
constant -39.8 us/s (+-0.3 us), i.e. local clock -39.8 ppm vs GM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test on board (ESP32-P4 rev v1.3):
- Rejected with 400 and nothing booted differently: garbage, wrong
project, min chip rev v3.0, truncated image.
- A (USB, ota_0) -> B via /api/ota: self-test passed, confirmed.
- Manual rollback B -> A; A -> B again.
- B -> C (forced self-test failure): C boots on trial in ota_0, fails,
rolls back to B in ota_1.
- D installed over the network; a later truncated upload hides
'previous' and clears can_rollback.
Docs: network flash command, serial-reset caveat, rollback test build,
OTA details in aes67-core-base.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /api/ota listed a half-written upload or a rolled-back image as
'previous'. Only report it when esp_ota_check_rollback_is_possible().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds an image whose self-test always fails, in its own build directory
(build-*/ ignored), for the step 2b rollback test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_ota: GET /api/ota (version, project, build_date, idf, running,
previous, previous_version, pending_verify, can_rollback), POST
/api/ota streamed into the inactive slot (4 KiB chunks), POST
/api/ota/confirm and /api/ota/rollback.
- Rejected before any flash write: bad magic, wrong chip, wrong
project_name, chip revision outside the image's min/max range.
esp_ota_end verifies the whole image.
- Self-test on a pending image: IP address plus HTTP 200 from our own
/api/status within 60 s marks it valid; otherwise mark invalid and
reboot into the previous slot. A crash before that is rolled back by
the bootloader.
- CONFIG_AES67_OTA_SELFTEST_FORCE_FAIL (test builds only) forces a
failed self-test.
- aes67_web: web_reboot_later() shared by /api/reboot and OTA.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PROJECT_VER was only read at CMake configure time, so builds kept the
previous commit's version. CMake now re-runs when .git/logs/HEAD or
.git/index change. Needed for OTA tests (step 2b), which rely on the
version changing between builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_net: esp_netif_set_hostname() before the interface starts, and
in the net apply callback (takes effect at the next lease renewal).
- Verified: the router's DNS (lan.apointless.space) resolves p4-aes67
to 192.168.192.244.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_net/lldp.c: IEEE 802.1AB LLDPDU sent as a raw frame with
esp_eth_transmit every 30 s (TTL 120 s) and immediately on a new IP
or hostname change. TLVs: chassis/port ID (MAC), port description,
system name (net.hostname), system description (project + version),
capabilities station-only, IPv4 management address.
- Docs: LLDP in the core Network section; step 2a ticked.
- Verified on an HP 2530-8G-PoE+ (show lldp info remote-device 2): all
fields shown, management address 192.168.192.244.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_net: espressif/mdns 1.13.1 (managed component, pinned in
dependencies.lock). Advertises <net.hostname>.local and the web UI as
_http._tcp port 80. net apply callback renames live.
- First config value actually applied: net.hostname.
- Docs: mDNS in the core Network section; build order gets step 2a
(mDNS, then LLDP).
- Verified on board: p4-aes67.local resolves via raw mDNS and getent,
http://p4-aes67.local/ answers, service shows in _http._tcp browse;
live rename to p4-rename and back works (after ~1 s probing).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_web/cfg.c: cfg_register(group, defaults, validate, apply),
cfg_override_defaults() for project defaults, cfg_get(). Stored values
are loaded on register and merged over defaults (known keys, matching
types). NVS keeps only keys that differ from defaults, one JSON string
per group, so new firmware defaults still reach untouched settings.
- POST /api/config: type check against defaults, per-group validation,
nothing stored unless every group passes; 400 "group.key: reason".
Unknown keys/groups are ignored. Returns the new config.
- Core groups registered by their components with the UI's ranges:
ptp (aes67_ptp), aes67 (aes67_tx, incl. multicast range and mono_sum
=> 1 channel), net/inet (aes67_net), log (aes67_syslog).
- Project: PROJECT.def overrides (hostname p4-aes67, name P4 AES67) and
the "source" group in main/project_cfg.c.
- Config is stored only; nothing is applied live yet.
- Verified on board: defaults, valid save, 8 rejected values (nothing
stored), persistence across reboot, restore to defaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_web: httpd (wildcard URI matching), serves web/index.html embedded
via EMBED_TXTFILES, status registry (status_register) and route registry
(web_register_uri), GET /api/status, POST /api/reboot (restarts 0.5 s
after the response).
- Status fields come from each core component: aes67_net (ip, aes67_ip,
mac, link), aes67_health (fw, uptime_s, heap_free, psram_free),
aes67_tx stub (tx_packets, underruns = 0). No ptp object yet, so the UI
shows "no data".
- Verified on board: / serves index.html byte-identical (27601 B),
/api/status returns live values, reboot via API returns in ~12 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aes67_board: Waveshare ESP32-P4-ETH pin profile and EMAC + IP101 driver
install (PHY addr 1, reset GPIO51, ref clock in on GPIO50).
- aes67_net: Ethernet netif with DHCP client; logs link up/down (speed,
duplex, MAC) and the DHCP lease.
- Verified on board: 100 Mbps full duplex, DHCP lease in ~8 s, ping
0% loss, ~0.2 ms RTT.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Board checked with esptool: ESP32-P4 rev v1.3 (engineering silicon),
32 MB GigaDevice flash. Rev <3 support and 32 MB set in sdkconfig.defaults.
- OTA partition layout: nvs, otadata, phy_init, ota_0/ota_1 (6 MB each,
below 16 MB), coredump; no factory app. Partition table at 0x10000 so
the bootloader can grow.
- App rollback enabled in the bootloader from the start.
- PROJECT_VER from git describe --tags --always --dirty.
- Empty stubs for the nine aes67_* core components; main logs version
and chip revision.
- Built with ESP-IDF v5.5.5; not yet flashed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>