Benjamin Harris пре 3 месеци
комит
341c70786e
8 измењених фајлова са 550 додато и 0 уклоњено
  1. 94 0
      CLAUDE.md
  2. 202 0
      LICENSE
  3. 105 0
      README.md
  4. 8 0
      docs/REFERENCES.md
  5. 52 0
      docs/dante-aes67-relay.js
  6. 21 0
      docs/dante-protocol-notes.md
  7. 55 0
      docs/sdp.js
  8. 13 0
      gitignore

+ 94 - 0
CLAUDE.md

@@ -0,0 +1,94 @@
+# CLAUDE.md
+
+Context for any Claude Code session working in this repo.
+
+## What this is
+
+An ESP-IDF firmware project putting a Dante-interoperable audio-over-IP
+endpoint on the ESP32-P4. Phase 1 is AES67 interop; native Dante protocol
+support is a phase 2 stretch goal. Full background/rationale is in
+`README.md` — read that first if you haven't.
+
+## Hard rule: licensing boundary (read this before touching Dante protocol work)
+
+This repo is Apache-2.0. It forks `components/aes67` from
+`DatanoiseTV/aes67-esp32p4` (also Apache-2.0). That's fine to build on
+directly.
+
+`reference/inferno` (teodly/inferno) is **GPL-3.0-or-later / AGPL-3.0-or-later**
+and is present locally **for reading only** — it is not part of this repo and
+must never be copied into it.
+
+- Never copy, translate, or closely paraphrase code from `reference/inferno`
+  into any file in this repo.
+- It's fine to read `inferno_aoip` to understand *what* Dante devices do on
+  the wire (message shapes, mDNS service records, control flow) — but the
+  resulting implementation here must be written independently, from that
+  understanding, not from their source.
+- Write protocol understanding as plain-English behavioural notes in
+  `docs/dante-protocol-notes.md` (what the protocol does), not as
+  annotated/translated code (how their code does it).
+- If a task would involve porting or closely mirroring a specific
+  `inferno_aoip` file, stop and flag it instead of proceeding — don't make
+  that judgment call silently.
+
+## Repo layout
+
+```
+components/aes67/   AES67 engine, forked from aes67-esp32p4 (PTP, RTP, I2S, SAP/SDP, mDNS)
+main/                App entry point, board/pin config
+docs/                Design notes, incl. dante-protocol-notes.md
+reference/           Local clones of both source repos — read-only, gitignored
+  aes67-esp32p4/     Fork basis
+  inferno/           Dante protocol reference — see licensing rule above
+```
+
+## Build & flash
+
+Requires ESP-IDF v5.5+.
+
+```bash
+idf.py set-target esp32p4
+idf.py build
+idf.py flash monitor
+```
+
+## Target hardware (confirm before relying on this)
+
+Reference pinout is DatanoiseTV's ESP32-P4-NANO board config — Ethernet via
+IP101 GRI PHY (RMII), I2S out via APLL-clocked codec (ES8311 onboard), full
+pin table in README.md. If the actual target board differs, update
+`main/main.c` pin config and the README table together.
+
+## Known hard problems
+
+- **PTPv1**: Dante needs it; the forked AES67 stack's PTP code
+  (`components/aes67/third_party/ptpd`, `aes67_ptp.c`, `aes67_hw_timer.c`)
+  targets PTPv2 against the P4's hardware timestamp path. Scope of adding v1
+  isn't assessed yet.
+- **Dante's control protocol** (routing/subscription — Inferno calls this
+  ARC/CMC) is undocumented and proprietary. The only way to understand it is
+  reading Inferno's protocol handling, read-only, per the licensing rule
+  above — there's no spec to implement against.
+- **No multi-process model on FreeRTOS/ESP-IDF**: Inferno's Linux design
+  relies on its PTP daemon (Statime) running as a separate OS process
+  communicating over a socket/clock device. There's no equivalent process
+  boundary here — any PTPv1 work has to live inside the single firmware
+  image, most likely inside the existing `aes67_ptp` task structure rather
+  than as a separate cooperating component.
+
+## Conventions
+
+Match the existing `aes67-esp67p4` component style:
+- `-O2`, hot-path conversion/ISR functions placed in IRAM
+- Tasks pinned to core 0 for cache coherency
+- ISR-driven DMA playback where possible, avoid task-scheduling on the audio
+  hot path
+- Quiet logging by default (errors + first PTP lock only)
+
+## Useful links
+
+- Foundation: https://github.com/DatanoiseTV/aes67-esp32p4
+- Dante protocol reference (read-only): https://github.com/teodly/inferno
+- AES67 Linux daemon (what the P4 component patterns itself on):
+  https://github.com/bondagit/aes67-linux-daemon

+ 202 - 0
LICENSE

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 105 - 0
README.md

@@ -0,0 +1,105 @@
+# Dante / AES67 Audio-over-IP for ESP32-P4
+
+**Status: planning / architecture phase — no firmware written yet.**
+
+## Goal
+
+A low-cost, network audio endpoint on the ESP32-P4 that can interoperate with
+Dante-based AV systems. In practice "Dante-interoperable" is being pursued in
+two phases:
+
+1. **AES67 interop (primary target)** — most Dante hardware from the last
+   several years can send/receive AES67, so a solid AES67 endpoint already
+   gets us into the same audio network as Dante gear.
+2. **Native Dante protocol support (stretch goal)** — Dante's proprietary
+   discovery (mDNS records), control protocol (routing/subscription), and
+   PTPv1 clocking, for talking to older or AES67-disabled Dante devices.
+
+## Why ESP32-P4
+
+The original ESP32 (WiFi, Xtensa) can't hold AES67's ~1ms RTP packet timing
+tightly enough over WiFi to be spec-compliant. The P4 drops WiFi in favour of
+a real Ethernet MAC with hardware PTP timestamping, which is what makes
+sub-millisecond, spec-accurate AoIP achievable on this class of chip at all.
+
+## Two source projects this builds on
+
+### 1. [DatanoiseTV/aes67-esp32p4](https://github.com/DatanoiseTV/aes67-esp32p4) — foundation (Apache-2.0)
+
+This is what we fork from. It already solves the hard, P4-specific problems:
+hardware PTP timestamping via the EMAC, an I2S driver with APLL clock
+recovery, an RTP engine with an L2 hook that bypasses lwIP for latency,
+SAP/SDP, and mDNS/DNS-SD advertisement. Apache-2.0 means we can build on it
+freely, including for commercial client work.
+
+### 2. [teodly/inferno](https://github.com/teodly/inferno) — protocol reference only (GPL-3.0-or-later / AGPL-3.0-or-later)
+
+A full software Dante device implementation for Linux. **We do not vendor or
+copy code from this repo.** It's copyleft-licensed, and incorporating its
+code would obligate this whole firmware image to the same license — which
+rules out using it in closed commercial deliverables. Instead it's used as
+documentation: reading `inferno_aoip` to understand Dante's wire behaviour
+(mDNS service records, control-protocol framing, flow subscriptions) well
+enough to write an independent, clean-room implementation against the P4
+component.
+
+> **This licensing boundary is the most important constraint on this
+> project.** See `CLAUDE.md` for the hard rule any dev session (human or
+> agent) needs to follow.
+
+## Hardware target
+
+Starting point is DatanoiseTV's reference pinout for the **ESP32-P4-NANO**
+board — confirm/update against whatever board this actually gets deployed on:
+
+| Function | GPIO | Notes |
+|---|---|---|
+| Ethernet MDC | 31 | |
+| Ethernet MDIO | 52 | |
+| Ethernet REF_CLK | 50 | External 50MHz crystal |
+| Ethernet PHY_RST | 51 | |
+| I2S MCLK | 13 | 18.432MHz via APLL |
+| I2S BCLK | 36 | (onboard: 12) |
+| I2S LRCLK | 33 | (onboard: 10) |
+| I2S DOUT | 32 | (onboard: 9) |
+| I2S DIN | 11 | Codec ADC input |
+| PA Enable | 53 | Active high |
+| I2C SCL / SDA | 8 / 7 | ES8311 control |
+
+Ethernet PHY: IP101 GRI via RMII. Onboard codec: ES8311 + NS4150B amp.
+
+## Repo layout
+
+```
+components/aes67/   Forked from aes67-esp32p4 — AES67 engine (PTP, RTP, I2S, SAP/SDP, mDNS)
+main/                Application entry point, board config
+docs/                Design notes, incl. clean-room Dante protocol notes
+reference/           Local clones of both source repos, for reading only (gitignored, not part of this repo)
+```
+
+## Build
+
+Requires ESP-IDF v5.5+ with the ESP32-P4 target.
+
+```bash
+idf.py set-target esp32p4
+idf.py build
+idf.py flash monitor
+```
+
+(Not yet functional — `components/aes67` hasn't been forked in yet.)
+
+## Roadmap
+
+- [ ] Fork `aes67-esp32p4`'s `components/aes67` in as the starting point
+- [ ] Confirm pinout against actual target hardware
+- [ ] Get AES67 send/receive verified against real Dante gear in AES67 mode
+- [ ] Write clean-room Dante protocol notes (`docs/dante-protocol-notes.md`)
+- [ ] Prototype Dante mDNS advertisement/discovery
+- [ ] Prototype Dante control protocol (routing/subscriptions)
+- [ ] Investigate PTPv1 support alongside the existing PTPv2 stack
+
+## License
+
+Apache-2.0 (see `LICENSE`), matching the `aes67-esp32p4` base this project
+forks from.

+ 8 - 0
docs/REFERENCES.md

@@ -0,0 +1,8 @@
+# More Interesting References
+
+https://gist.github.com/philhartung
+https://gist.github.com/philhartung/87d336a3c432e2ce5452befcad1b945f
+
+
+## AES67 Recorder
+https://github.com/voc/aes67-recorder

+ 52 - 0
docs/dante-aes67-relay.js

@@ -0,0 +1,52 @@
+const dgram = require('dgram');
+const client = dgram.createSocket({ type: 'udp4', reuseAddr: true });
+const sdp = require('./sdp');
+
+//config
+const addr = '10.10.1.100';
+const danteMulticast = '239.255.220.221';
+const aes67Multicast = '239.69.1.122';
+const samplerate = 48000;
+const channels = 2;
+const encoding = 'L24';
+const name = 'Dante Multicast Relay';
+const sessID = Math.round(Date.now() / 1000);
+const sessVersion = sessID;
+const ptpMaster = '08-00-00-ff-fe-00-00-1f:0';
+
+//rtp specific vars
+var seqNum = 0;
+
+client.on('listening', function() {
+	client.addMembership(danteMulticast, addr);
+	client.setMulticastInterface(addr);
+});
+
+client.on('message', function(buffer, remote) {
+	//read values from buffer
+	var channelCount = buffer.readUInt8(0);
+	var timestampSeconds = buffer.readUInt32BE(1);
+	//bytes 6 and 7 seem to be always 0x00, maybe reserved bytes
+	var timestampMedia = buffer.readUInt16BE(7);
+	var pcmData = buffer.slice(9);
+
+	//calculate media timestamp for rtp
+	var timestampRTP = ((timestampSeconds * samplerate) + timestampMedia) & 0xffffffff;
+
+	//create RTP header
+	var rtpHeader = Buffer.alloc(12);
+	rtpHeader.writeUInt16BE(0x8061, 0);
+	rtpHeader.writeUInt16BE(seqNum, 2);
+	rtpHeader.writeInt32BE(timestampRTP, 4);
+	rtpHeader.writeUInt32BE(0xaf12af34, 8);
+
+	//create and send RTP packet
+	var rtpBuffer = Buffer.concat([rtpHeader, pcmData]);
+	client.send(rtpBuffer, 5004, aes67Multicast);
+
+	//increase seqnum
+	seqNum = (seqNum + 1) % 65536;
+});
+
+client.bind(4321);
+sdp.start(addr, aes67Multicast, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster);

+ 21 - 0
docs/dante-protocol-notes.md

@@ -0,0 +1,21 @@
+# Dante protocol notes (clean-room)
+
+Behavioural notes on how Dante devices behave on the wire, written from
+understanding gained by reading `reference/inferno` — not translated or
+copied from it. See the licensing rule in `CLAUDE.md` before adding to this
+file.
+
+Write what a Dante device *does*, in our own words (message purpose, timing,
+field meanings) — not how Inferno's code does it.
+
+## Discovery (mDNS)
+
+TBD
+
+## Control protocol (routing / subscriptions)
+
+TBD
+
+## Clocking (PTPv1)
+
+TBD

+ 55 - 0
docs/sdp.js

@@ -0,0 +1,55 @@
+var dgram = require('dgram');
+var socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });
+
+var constructSDPMsg = function(addr, multicastAddr, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster){
+	var sapHeader = Buffer.alloc(8);
+	var sapContentType = Buffer.from('application/sdp\0');
+	var ip = addr.split('.');
+
+	//write version/options
+	sapHeader.writeUInt8(0x20);
+
+	//write hash
+	sapHeader.writeUInt16LE(0xefef, 2);
+
+	//write ip
+	sapHeader.writeUInt8(parseInt(ip[0]), 4);
+	sapHeader.writeUInt8(parseInt(ip[1]), 5);
+	sapHeader.writeUInt8(parseInt(ip[2]), 6);
+	sapHeader.writeUInt8(parseInt(ip[3]), 7);
+
+	var sdpConfig = [
+		'v=0',
+		'o=- '+sessID+' '+sessVersion+' IN IP4 '+addr,
+		's='+name,
+		'c=IN IP4 '+multicastAddr+'/32',
+		't=0 0',
+		'a=clock-domain:PTPv2 0',
+		'm=audio 5004 RTP/AVP 96',
+		'a=rtpmap:96 '+encoding+'/'+samplerate+'/'+channels,
+		'a=sync-time:0',
+		'a=framecount:48',
+		'a=ptime:1',
+		'a=mediaclk:direct=0',
+		'a=ts-refclk:ptp=IEEE1588-2008:'+ptpMaster,
+		'a=recvonly',
+		''
+	];
+
+	var sdpBody = Buffer.from(sdpConfig.join('\r\n'));
+
+	return Buffer.concat([sapHeader, sapContentType, sdpBody]);
+}
+
+exports.start = function(addr, multicastAddr, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster){
+	sdpMSG = constructSDPMsg(addr, multicastAddr, samplerate, channels, encoding, name, sessID, sessVersion, ptpMaster);
+
+	socket.bind(9875, function(){
+		socket.setMulticastInterface(addr);
+		socket.send(sdpMSG, 9875, '239.255.255.255', function(err){});
+	});
+	
+	setInterval(function(){
+		socket.send(sdpMSG, 9875, '239.255.255.255', function(err){});
+	}, 30*1000);
+}

+ 13 - 0
gitignore

@@ -0,0 +1,13 @@
+# Local reference clones — read-only material, never part of this repo
+reference/
+
+# ESP-IDF build output
+build/
+sdkconfig
+sdkconfig.old
+managed_components/
+dependencies.lock
+
+# Editor/OS cruft
+.vscode/
+.DS_Store