PTP: drop the "Preferred TimeTransmitter" role
It behaved exactly like auto (clock class 248); the only difference was a priority1 suggestion in the page. Roles are now slave|auto; to prefer this device, use auto with a low priority1. A stored "master" is converted to auto at boot (new cfg_set_string, like cfg_set_number). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
#include "aes67_ptp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aes67_cfg.h"
|
||||
#include "aes67_web.h"
|
||||
#include "esp_log.h"
|
||||
#include "ptp_clock.h"
|
||||
#include "ptp_hw.h"
|
||||
|
||||
@@ -14,7 +17,7 @@ static const char PTP_DEFAULTS[] =
|
||||
static bool ptp_validate(const cJSON *g, char *err, size_t n)
|
||||
{
|
||||
static const char *const modes[] = { "multicast", "hybrid", NULL };
|
||||
static const char *const roles[] = { "slave", "auto", "master", NULL };
|
||||
static const char *const roles[] = { "slave", "auto", NULL };
|
||||
return cfg_check_enum(g, "mode", modes, err, n) &&
|
||||
cfg_check_enum(g, "role", roles, err, n) &&
|
||||
cfg_check_int(g, "domain", 0, 127, err, n) &&
|
||||
@@ -34,7 +37,20 @@ static void ptp_apply(const cJSON *g)
|
||||
|
||||
esp_err_t aes67_ptp_init(void)
|
||||
{
|
||||
return cfg_register("ptp", PTP_DEFAULTS, ptp_validate, ptp_apply);
|
||||
esp_err_t err = cfg_register("ptp", PTP_DEFAULTS, ptp_validate, ptp_apply);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
// Role "master" was dropped (it behaved like auto; a low priority1 does the same). Stored values
|
||||
// aren't re-validated, so convert it here.
|
||||
cJSON *c = cfg_get("ptp");
|
||||
bool master = !strcmp(cJSON_GetObjectItemCaseSensitive(c, "role")->valuestring, "master");
|
||||
cJSON_Delete(c);
|
||||
if (master) {
|
||||
ESP_LOGW("ptp", "stored role \"master\" is no longer offered: now \"auto\" (same behaviour)");
|
||||
cfg_set_string("ptp", "role", "auto", false);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t aes67_ptp_start(esp_eth_handle_t eth)
|
||||
|
||||
@@ -622,8 +622,8 @@ static void load_config(void)
|
||||
s.cfg_log_sync = cJSON_GetObjectItem(c, "log_sync")->valueint;
|
||||
s.cfg_log_announce = cJSON_GetObjectItem(c, "log_announce")->valueint;
|
||||
s.cfg_log_dreq = cJSON_GetObjectItem(c, "log_delay_req")->valueint;
|
||||
// Roles (docs): slave = clockClass 255, never transmits; auto/master = 248 with the configured
|
||||
// priorities (auto 250/250 by default, master e.g. p1 100).
|
||||
// Roles (docs): slave = clockClass 255, never transmits; auto = 248 with the configured priorities
|
||||
// (250/250 by default: loses to any real GM; a lower priority1 prefers this device).
|
||||
s.slave_only = strcmp(cJSON_GetObjectItem(c, "role")->valuestring, "slave") == 0;
|
||||
s.hybrid = strcmp(cJSON_GetObjectItem(c, "mode")->valuestring, "hybrid") == 0;
|
||||
s.own_class = s.slave_only ? 255 : 248;
|
||||
@@ -711,7 +711,7 @@ static void ptp_task(void *arg)
|
||||
int tos2 = s.dscp << 2;
|
||||
setsockopt(s.gen, IPPROTO_IP, IP_TOS, &tos2, sizeof(tos2));
|
||||
ESP_LOGI(TAG, "config applied: role %s, mode %s, p1 %u p2 %u, domain %u",
|
||||
s.slave_only ? "slave" : "auto/master", s.hybrid ? "hybrid" : "multicast",
|
||||
s.slave_only ? "slave" : "auto", s.hybrid ? "hybrid" : "multicast",
|
||||
s.own.p1, s.own.p2, s.domain);
|
||||
// Re-run the decision: a foreign GM worse than our new dataset is dropped; as
|
||||
// TimeTransmitter with role slave we stop.
|
||||
|
||||
@@ -295,6 +295,37 @@ esp_err_t cfg_set_number(const char *group, const char *key, double value, bool
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t cfg_set_string(const char *group, const char *key, const char *value, bool apply)
|
||||
{
|
||||
if (!s_lock) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
xSemaphoreTake(s_lock, portMAX_DELAY);
|
||||
cfg_group_t *g = find(group);
|
||||
cJSON *cur = g ? cJSON_GetObjectItemCaseSensitive(g->values, key) : NULL;
|
||||
esp_err_t err = ESP_OK;
|
||||
if (!cur || !cJSON_IsString(cur)) {
|
||||
err = ESP_ERR_NOT_FOUND;
|
||||
} else {
|
||||
cJSON_ReplaceItemInObjectCaseSensitive(g->values, key, cJSON_CreateString(value));
|
||||
nvs_handle_t h;
|
||||
if (s_nvs_ok && (err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h)) == ESP_OK) {
|
||||
err = store(h, g);
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(h);
|
||||
}
|
||||
nvs_close(h);
|
||||
}
|
||||
}
|
||||
cJSON *copy = err == ESP_OK && apply && g->apply ? cJSON_Duplicate(g->values, true) : NULL;
|
||||
xSemaphoreGive(s_lock);
|
||||
if (copy) {
|
||||
g->apply(copy);
|
||||
cJSON_Delete(copy);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ----- HTTP ----- */
|
||||
|
||||
static esp_err_t config_get(httpd_req_t *req)
|
||||
|
||||
@@ -26,6 +26,8 @@ cJSON *cfg_get(const char *group);
|
||||
// Firmware-side change of one number (e.g. aes67.session_ver): stored like a POST, no validation.
|
||||
// apply = false skips the group's apply callback (e.g. to avoid restarting the stream).
|
||||
esp_err_t cfg_set_number(const char *group, const char *key, double value, bool apply);
|
||||
// Same for a string value.
|
||||
esp_err_t cfg_set_string(const char *group, const char *key, const char *value, bool apply);
|
||||
|
||||
// Validation helpers for validate callbacks. Each returns false and fills err on failure.
|
||||
bool cfg_check_num(const cJSON *g, const char *key, double min, double max, char *err, size_t n);
|
||||
|
||||
@@ -13,12 +13,12 @@ Reference devices for UI and defaults: Riedel Bolero (PTP status), Riedel Direct
|
||||
- Status table values can be text or {cls: ok|warn|bad, text} for colour.
|
||||
- Device config is merged over defaults on load, so older firmware missing keys still renders.
|
||||
- Save bumps `aes67.session_ver` (SDP o= version). Save is blocked while any field is invalid (e.g. multicast out of range).
|
||||
- UI uses IEEE 1588-2019 terms (TimeTransmitter / TimeReceiver), like Riedel. Config/API values stay master/slave/auto.
|
||||
- UI uses IEEE 1588-2019 terms (TimeTransmitter / TimeReceiver), like Riedel. Config/API values stay slave/auto.
|
||||
- AES67 fields carry Riedel Director-style "Default / Range" hints.
|
||||
- PTP preset buttons: "Riedel SIC defaults" and "AES67 media profile defaults" fill the form (Save to apply).
|
||||
|
||||
## Config schema (core groups)
|
||||
- ptp: {mode: multicast|hybrid, role: slave|auto|master, domain, priority1, priority2, log_sync, log_announce, announce_timeout, log_delay_req, dscp} (hardware timestamps always; not configurable)
|
||||
- ptp: {mode: multicast|hybrid, role: slave|auto, domain, priority1, priority2, log_sync, log_announce, announce_timeout, log_delay_req, dscp} (hardware timestamps always; not configurable)
|
||||
- aes67: {name, enabled, discovery: manual|sap, mcast, port, ttl, dscp, channels, mono_sum, encoding: L16|L24, rate: 48000 only (all sources are 48 kHz), ptime, pt, ssrc, clk_offset, session_id, session_ver}
|
||||
- net: {hostname, vlan, web_on: both|inet|aes67, dhcp, ip, mask, gw, dns} (AES67 / untagged interface)
|
||||
- inet: {vlan_id, pcp, dhcp, ip, mask, gw, dns} (internet / tagged, only if net.vlan)
|
||||
@@ -52,7 +52,7 @@ Reference devices for UI and defaults: Riedel Bolero (PTP status), Riedel Direct
|
||||
- Roles / BMCA dataset:
|
||||
- slave (TimeReceiver only): clockClass 255, never transmits time.
|
||||
- auto (default): clockClass 248, priority1 250, priority2 250. Loses to any real GM (Riedel default 128); takes over only if none.
|
||||
- master (preferred TimeTransmitter): clockClass 248, priority1 100 (UI suggestion, editable).
|
||||
- To prefer this device as TimeTransmitter, use auto with a lower priority1 (e.g. 100). (A separate "master" role was dropped: it behaved like auto; a stored "master" is converted to auto at boot.)
|
||||
- clockAccuracy 0xFE (unknown), offsetScaledLogVariance 0xFFFF, timeSource 0xA0 (internal oscillator).
|
||||
- clockIdentity = EUI-64 from MAC (xx-xx-xx-FF-FE-xx-xx-xx).
|
||||
- Note: Riedel Bolero reports its own clock class as 228.
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ The board locks its media clock to the PTP grandmaster on the network (IEEE 1588
|
||||
| Setting | Default | Notes |
|
||||
|---|---|---|
|
||||
| Mode | multicast | **hybrid** sends the delay requests unicast (fewer multicast packets). |
|
||||
| Role | Auto | **TimeReceiver only**: never becomes grandmaster. **Auto**: becomes grandmaster only if there is none (fallback). **Preferred TimeTransmitter**: tries to be grandmaster. |
|
||||
| Role | Auto | **TimeReceiver only**: never becomes grandmaster. **Auto**: becomes grandmaster only if there is none (fallback). To make this board the preferred grandmaster, keep *Auto* and set a low *Priority 1* (e.g. 100). |
|
||||
| Domain | 0 | Must match your grandmaster. |
|
||||
| Priority 1 / 2 | 250 / 250 | Lower wins the grandmaster election. |
|
||||
| Intervals, timeout, DSCP (46) | | Presets below set them. |
|
||||
|
||||
+3
-4
@@ -105,8 +105,7 @@ e.g. curl -X POST http://p4-aes67.local/api/player/next
|
||||
<small>hybrid: Delay_Req/Resp unicast</small></label>
|
||||
<label><span>Role</span><select name="ptp.role">
|
||||
<option value="slave">TimeReceiver only</option>
|
||||
<option value="auto">Auto (TimeTransmitter if none)</option>
|
||||
<option value="master">Preferred TimeTransmitter</option></select></label>
|
||||
<option value="auto">Auto (TimeTransmitter if none)</option></select></label>
|
||||
<label><span>Domain</span><input name="ptp.domain" type="number" min="0" max="127"></label>
|
||||
<label><span>Priority 1</span><input name="ptp.priority1" type="number" min="0" max="255"></label>
|
||||
<label><span>Priority 2</span><input name="ptp.priority2" type="number" min="0" max="255"></label>
|
||||
@@ -291,7 +290,7 @@ const CORE_DEF = {
|
||||
log:{syslog:false,host:'',port:514,level:'info',facility:'16',format:'rfc5424'}
|
||||
};
|
||||
// Priority1 suggested when the role is changed in the UI (user can still edit it).
|
||||
const ROLE_P1 = {auto:250, master:100};
|
||||
const ROLE_P1 = {auto:250};
|
||||
// PTP presets. riedel = Riedel Director defaults for a SIC AES67 card.
|
||||
const PTP_PRESETS = {
|
||||
riedel: {mode:'multicast', role:'slave', domain:0, priority1:128, priority2:126,
|
||||
@@ -302,7 +301,7 @@ function ptpPreset(name) {
|
||||
for (const [k, v] of Object.entries(PTP_PRESETS[name])) f['ptp.' + k].value = v;
|
||||
toggle(); sdp(); msg.textContent = `Loaded ${name} PTP defaults – Save to apply.`;
|
||||
}
|
||||
const CLOCK_CLASS = {slave:255, auto:248, master:248};
|
||||
const CLOCK_CLASS = {slave:255, auto:248};
|
||||
|
||||
const $ = id => document.getElementById(id);
|
||||
const f = $('f'), sdpEl = $('sdp'), msg = $('msg');
|
||||
|
||||
Reference in New Issue
Block a user