|
ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
|
Work that is not finished. Organized by status rather than by theme, so what is actually in flight is visible without reading the whole file.
| bucket | meaning |
|---|---|
| Now | in flight, or blocking something that is |
| Next | committed and scoped, not started |
| Later | wanted, not yet scoped |
| Partial | a working piece is in the tree, with an explicit Remaining note |
| Decision records | settled questions, kept so they are not re-litigated |
Item states
- [ ] not started.- [~] partial - shipped in part; the item states what exists and what remains. These are the easiest to lose track of, because from outside they look finished.- [x] does not appear in this file. Completed work lives in DELIVERED.md.Sizing is S / M / L / XL, carried in the item's tag alongside the standard or vendor it implements, e.g. (L, Siemens S7-1200/1500).
Where things live. This file is the plan. DELIVERED.md is what was built; FEATURES.md is what the library does today; CHANGELOG.md is when each change landed; ARCHITECTURE.md holds design, including the multi-vendor and architecture-agnostic work this roadmap schedules. Bugs and known limitations are in TODO.md and KNOWN_LIMITATIONS.md.
Every item must keep the core guarantees: no heap after begin(), fixed-size buffers, a host-testable core where possible, and a PC_ENABLE_* flag (default off) so it costs nothing when unused.
In flight. Each of these is either being worked or blocks something that is.
CanFrame and BerEnc can collide with user code. Validate candidates against the pre-rename tree so foreign names (PCB, PCA9685, the PCR_* silicon registers) are never touched.PROTOCORE_<FILE>_H (M, naming law) - 85 of 373 headers still carry another form. PROTOCORE_ rather than PC_ because a guard has to be unique in someone else's build, where PC_* is a plausible name for their own. Two headers exceed the 31-character limit and take the documented word-elision exception in SYMBOLS.md. Also fix the 18 macros over the same limit. Measure with ci_tooling/check/check_symbols.py, then re-run --baseline so the gate ratchets down.ci_tooling/check/check_symbols.py** (M, CI guard) - enforce the naming law mechanically (prefix and casing per symbol kind, macro scope and length, guard form, absence of namespace). Must also check TYPE-vs-FUNCTION collisions: normalizing types to snake_case collapsed PCConnCounters onto pc_conn_counters() and only a link error caught it.static_assert, never heap (banned after begin()). Secrets then sit low in the stack, behind frames an attacker must smash first - the same reasoning as the crypto scratch region.pc_octet_ptr and its accessors** (L, TI C2000 enabler) - pin lane-0 (invariant across targets, NOT word endianness), the native-unwrap boundary for DMA/MAC handoff, and the rule that sizeof is no longer a wire length.Committed and scoped, not started.
arduino-esp32 dependency from the core (L, vendor decoupling) so the ESP-IDF component stops requiring it. This is the vendor leak that blocks a platform-neutral build.examples/ per platform** (L, 8 platforms) - Arduino carries the full L1-L7 catalog as the teaching surface; every other platform gets a curated 4-6. Do NOT replicate the catalog: 8 platforms x 35 examples is an unmaintainable CI matrix.begin(), bounded ISR paths, and the octet abstraction compiling away. Measured, not asserted.posix/ examples as a CI gate** (S, hardware-free regression) - the only platform that runs without hardware, so the natural regression net for the other seven.doc_region (S, dedupe) - feature_budget, gen_api_flow, gen_examples, gen_feature_tables, gen_flag_deps, and gen_readme_sections still hand-roll the marked-region read/replace/--check logic that ci_tooling/lib/doc_region.py now owns. They use the correct prettier-ignore mechanism, so they are duplicated rather than wrong; the gate is that every --check stays green and prettier stays clean.Wanted, not yet scoped.
Grouped by the area each belongs to.
services/s7comm, S7-300/400) already shipped, over the same COTP/TPKT transport (services/cotp): the S7CommPlus PDU framing + the integrity/session handshake (protocol versioning + the anti-replay integrity id) and read/write of tag values by symbolic access. Harder than classic S7comm (session keying); zero-heap codec first, then the session state machine.services/enip + the PROFINET roadmap item - map the robot's assembly objects), the FANUC Robot Ethernet / Stream Motion UDP position stream, and the KAREL / R-30iB web + socket messaging (SNPX / the RPC the robot's PC interface uses). Start with a zero-heap codec byte-checked against a public reference, then HW-verify against a real controller. Note: "Run MyRobot" (above) is the Siemens SINUMERIK route to running a robot from a CNC - a distinct path we also want. PARTIAL - the Stream Motion leg has shipped: services/fanuc_j519 (PC_ENABLE_FANUC_J519), a zero-heap symmetric codec for the J519 UDP protocol (all six packets, the 9-axis binary32 pose / joint / motor-current blocks, the 20-entry threshold tables), byte-checked at every field offset against the public Wireshark dissector fanuc-stream-motion/packet-fanuc-stream-motion-j519 and host-tested (native_fanuc_j519, 13 cases). Still open on this entry: the PROFINET / EtherNet-IP assembly-object mapping over the shipped services/enip, the KAREL / SNPX socket messaging, and HW verification of the J519 codec against a real controller (the codec has never been run against physical hardware - only against the documented wire format).services/upload_service) and the existing DNC drip-feed (services/dnc) for controllers with no network stack. The unifying goal behind the machine-tool protocol set.src/protocore_config.h PC_ENABLE_* defaults so only the core request/response path is on out of the box; every other service (WebSocket, SSE, auth, file serving, WebDAV, the L6/L7 protocols, the industrial/IoT buses, crypto, storage, ...) defaults off and is turned on per build via its own flag. Bare-bones by default shrinks the baseline flash/RAM and makes each feature a conscious opt-in. Update the examples that rely on a default-on feature to set its flag explicitly, and re-check the configurator + FEATURES grid.ci_tooling/generate/example_footprints.py) to emit a "cost of enabling service X alone" delta against the minimal build and land it as a table in docs/FOOTPRINTS.md.src/server/ of single-purpose files (L) - carve the monolith into cohesive translation units (e.g. server/lifecycle for listen/begin/restart, server/routing for route matching + dispatch, server/response for the send/send_template/send_chunked builders, server/file_serving, server/webdav, server/auth, and the server/http2 / server/http3 seams), each with a clear internal header. Pure move-and-split - no behavior change - so the native + dual-core builds and the full test suite stay green throughout; keep the owned-context / no-stdlib / single-owner rules. Makes the core easier to debug, extend, and maintain.services/safety_scl): a CRC-signature helper, a monitoring-counter state machine, and a watchdog/timeout guard, then have each profile's codec compose them. Pure + host-tested against per-profile vectors. PARTIAL - services/safety_scl (PC_ENABLE_SAFETY_SCL) lands two of the three primitives: the monitoring-counter state machine and the receive watchdog, plus the fail-safe state machine that combines them with a caller-supplied signature verdict. Lost / duplicated / inserted / reordered frames all reduce to one comparison against the expected counter, and each has its own test case. Fail-safe latches until an explicit pc_scl_reset: a safety layer that silently reheals lets an intermittent fault present as a working link, so a subsequent good frame must not revive it (asserted). Pure, explicit now, wrap-safe watchdog. Host-tested (native_safety_scl, 16 cases). Still open: the CRC-signature helper. The engine half of it now exists - shared_primitives/crc.h (see Maintenance) is the parameterized width / poly / init / reflect / xorout engine this entry asked for, so "no engine to configure" is no longer the blocker. What remains is genuinely profile-specific: each profile defines not just its polynomial, width and seed but the input ordering - which fields, in which order, feed the register - and those constants live in paid standards. A generic pc_scl_signature() would still be guessing that ordering, and a wrong safety CRC is worse than none because it looks authoritative while failing to detect the corruption it exists to catch. So the caller still passes its profile's verdict in as signature_ok, and the helper lands per profile (PROFIsafe CRC2, CIP Safety CRC-S1/S3/S5, FSoE per-slot CRC) as each profile's codec is written against its spec - each one configuring the shared engine rather than hand-rolling a loop.PC_ENABLE_PROFISAFE / services/profisafe: the Safety-PDU (F-I/O data + status/control byte + consecutive number + CRC2 signature), the F-Parameters (F_Dest_Add / F_WD_Time ...) with their CRC1, and the host/device watchdog timing. Pure codec; host-tested and interop-checked against a PROFIsafe reference (e.g. a Siemens F-host / codesys-safety).PC_ENABLE_CIP_SAFETY / services/pc_cip_safety: the safety I/O message (Base + Extended format: Mode Byte, Actual + Complemented data, Time Stamp, CRC-S1/S3/S5), the Time Coordination exchange, and the safety connection (SafetyOpen / SafetyClose) producer/consumer time expectation. Pure codec; interop vs an ODVA CIP Safety originator.PC_ENABLE_FSOE / services/fsoe: the FSoE frame (Command + SafeData + a CRC per data slot + Connection-ID / sequence), the FSoE connection state machine (Reset -> Session -> Connection -> Parameter -> Data), and the watchdog. Gated on an EtherCAT (mailbox) base transport first - EtherCAT is noted but not yet shipped (see the field-device roadmap above); FSoE itself is black-channel so it host-tests standalone against ETG.5100 vectors.PC_ENABLE_IOLINK_SAFETY / services/iolink_safety: the OSSDe signal handling plus the safety-PDU over IO-Link process data / ISDU (CRC + counter + watchdog, FS-Master / FS-Device roles). Pure codec; host-tested against the IO-Link Safety test spec vectors.services/enip. PC_ENABLE_CIP_SECURITY / services/pc_cip_security; interop vs an ODVA CIP Security stack.PC_ENABLE_PROFINET_SECURITY / extend services/profinet: the SecurityControl + integrity-MAC frame wrapping and the certificate-based identity, reusing the library's crypto (HMAC / AES-GCM, X.509). Interop vs a PROFINET Security controller.services/hart command codec (FieldComm Group). Scope the realistic slice: the WirelessHART command set (network-manager / device commands) + the network management data (superframe / link / route reports) as a pure codec over HART; the full TSMP-style TDMA MAC + channel-hopping schedule is radio-firmware territory (like the other 802.15.4 radios) and is called out as HW-gated. PC_ENABLE_WIRELESSHART / services/wirelesshart.PC_ENABLE_COEXISTENCE / services/coexistence: the reporting data model + the channel-plan policy engine (pure, host-tested); the actual RF scan feeds it from the radio driver.PC_ENABLE_FAST_ROAM; PSK or WPA2/3-Enterprise FT), expose the handover timing, and keep the safety flows pinned across the transition. Measure the handover on the S3 rig against the 50 ms budget.PC_ENABLE_RRM.PC_ENABLE_BTM.services/roaming.build_opt.h shrink hacks (S) - once the tiered defaults land, revisit the examples that manually dial tunables down (MeshCache and any others carrying a "sized down to fit
classic-ESP32 DRAM" note) and drop the override where the CLASSIC-tier default already fits, so the example shows the real intended configuration on each board instead of the lowest-common-denominator one.dram0_0_seg link check (the guard that caught MeshCache) and add an S3/PSRAM build of the same heavy examples asserting the larger-tier defaults actually took effect (link succeeds and uses the bigger sizes), so a future change can't silently re-flatten the profiles or regress the classic-board ceiling.Each item has a working piece in the tree and an explicit Remaining note.
PC_ENABLE_MTCONNECT (services/mtconnect): the MTConnect agent response documents over the existing HTTP stack - an incremental MTConnectStreams builder (the current / sample response: header with instanceId + nextSequence, then per-DataItem <Samples> / <Events> / <Condition> observations) and the MTConnectError document, XML-escaped into a caller buffer; host-tested (native_mtconnect). Remaining: the probe MTConnectDevices doc, MTConnectAssets, the fixed-capacity Devices->Components->DataItems model + the circular sample buffer keyed by instanceId + sequence (the from/count/interval long-poll semantics). Pairs with gpio_map / dashboard as DataItems.PC_ENABLE_ENIP (services/enip): the encapsulation layer (TCP/UDP 44818) - pc_eip_build / pc_eip_parse (the 24-octet header), pc_eip_build_register_session, and pc_eip_build_send_rr_data / pc_eip_parse_send_rr_data (wrap/unwrap a CIP message via the Common Packet Format). PC_ENABLE_CIP (services/cip): the CIP message inside it - pc_cip_build_epath (class/instance/attribute logical segments), pc_cip_build_get_attr_single / pc_cip_build_request, and pc_cip_parse_response. Together they form a working CIP read path (wrap the CIP request with pc_eip_build_send_rr_data); both verified against the Wireshark ENIP/CIP dissectors, host-tested. Remaining: the broader CIP object dictionary + implicit/IO messaging. PROFINET (RT, raw L2 frames) and EtherCAT rely on raw L2, which the ESP32 does provide (see Low-level networking above - esp_eth_transmit / esp_eth_update_input_path), and the IRT/isochronous cyclic deadline is met by the high-priority preempting-ISR/task model in a single-feature build (this protocol + base web server only). So the full cyclic RT/IRT stack is on the table, not just a discovery subset; PROFINET DCP is the easy first milestone, EtherCAT/IRT the hard one. Each: fixed BSS object/process-image model, no heap.PC_ENABLE_CANOPEN (services/canopen); PROFIBUS-DP is PC_ENABLE_PROFIBUS (services/profibus); DeviceNet (CIP-over-CAN) is PC_ENABLE_DEVICENET; Modbus TCP + RTU ship as modbus (PC_ENABLE_MODBUS / _MODBUS_RTU). All host-tested behind their own build flags. Remaining: the per-bus serial/CAN PHY drivers + HW-over-RS-485/CAN verify (tracked per protocol above).PC_ENABLE_MODBUS_RTU adds pc_modbus_rtu_process_adu(): the [unit-id][PDU][CRC-16/Modbus] serial frame around the existing data model + function-code dispatch - a CRC mismatch or a non-matching unit address is dropped silently, a broadcast (address 0) executes with no reply. Host-tested (test_modbus, 5 RTU cases incl. the 0xCDC5 CRC vector + read round-trip + bad-CRC/wrong-addr/broadcast). The pure codec is fed a complete frame; a UART/RS-485 driver (3.5-char inter-frame idle) + HW-over-RS-485 verify are the remaining transport step. (Modbus ASCII is a lower-priority follow-on.)PC_ENABLE_PROFINET (services/profinet) - the DCP frame codec (10-octet header + option/suboption blocks with even-padding, built by pc_pn_dcp_header / _block, walked by pc_pn_dcp_walk) for Identify + Set (assign NameOfStation / IP) over raw L2 (ethertype 0x8892); host-tested (native_profinet). PROFIBUS-DP: PC_ENABLE_PROFIBUS (services/profibus) - the FDL telegram codec - SD1 (no-data) and SD2 (variable-data: SD2 LE LEr SD2 DA SA FC data FCS ED, arithmetic-sum FCS) that a DP master exchanges with slaves over RS-485; host-tested (native_profibus). Remaining: the PROFINET IO cyclic data exchange + GSDML, and the DP-V0 master state machine + GSD slave model. Fixed BSS process image, no heap.PC_ENABLE_DNP3 (services\dnp3): a zero-heap builder + CRC-validating parser for the data-link layer - pc_dnp3_build_frame emits the 0x0564 LEN CTRL DEST SRC CRC header block plus the CRC'd 16-octet user-data blocks, and pc_dnp3_parse_frame validates the header and every block CRC (CRC-16/DNP, verified against the canonical 0xEA82 check value) and de-blocks the user data. The remaining layers (transport-function segmentation, the application objects with groups/variations, Class 0/1/2/3 polling, unsolicited responses, time-sync) layer on the de-blocked user data; fixed BSS point database, no heap. Optional Secure Authentication (IEEE 1815 SAv5) later.PC_ENABLE_HART (services/hart): the HART command-frame codec - pc_hart_build / _parse with the longitudinal XOR checksum and short (polling) + long (unique-ID) addressing - and the 8-octet HART-IP message header (version / type / id / status / seq / length) for the front-end-free UDP/TCP 5094 path; host-tested against hand-verified vectors (native_hart, command-0 checksum 0x82). Remaining: the command set (universal + common-practice) + device-variable/status model + burst mode, and the FSK modem (over UART) / 4-20 mA-off-the-ADC transports. Fixed BSS, no heap.PC_ENABLE_CCLINK (services/cclink): the CC-Link cyclic frame - pc_cclink_build / _parse for [station][command][RX/RY bit data][RWr/RWw word data][sum checksum] that a Mitsubishi CC-Link master exchanges with remote stations over RS-485, plus the bit/word process-image accessors (get_bit / set_bit / get_word); host-tested (native_cclink). Remaining: the master poll/refresh state machine + the RS-485 timing, and CC-Link IE Field (Gbit PHY-gated). Fixed BSS station model, no heap.services/profibus FDL telegrams) over the MBP (Manchester Bus Powered, IEC 61158-2) physical layer for hazardous areas. The FDL/DP telegram layer is done; remaining: the PA device profiles (transmitters/valves) + the MBP physical layer (hardware-gated - couples to a DP segment via a segment coupler in practice).PC_ENABLE_CANOPEN (services/canopen): a zero-heap CiA 301 message codec over the shared CAN frame (shared_primitives/can.h) - NMT, SYNC, heartbeat, EMCY, PDO, and expedited SDO read/write/abort plus the COB-ID classifier; host-tested (native_canopen, 17 cases). Remaining: a first-class object dictionary + node-guarding, the DS401 generic-I/O device profile, and the ESP32 TWAI/CAN transport binding. Fixed BSS, no heap.PC_ENABLE_IOLINK (services/iolink): the SDCI data-link message codec - the MC / CKT / CKS control octets and the SDCI checksum (seed 0x52 + the 8->6 compression of IO-Link spec A.1.6), against a hand-computed known-answer vector; host-tested (native_iolink). Remaining: the on-request ISDU parameter service, the IODD-described device model + state machine, and the 3-wire physical layer (hardware-gated). Fixed BSS, no heap.PC_ENABLE_POWERLINK (services/powerlink): the EPL basic frames - pc_epl_build / _parse (and the SoC / PReq / PRes convenience builders) for [messageType][dest][source][payload], the four cyclic message types (SoC start-of-cycle, PReq poll-request, PRes poll-response with process data, SoA start-of-async) that make the isochronous managed-node cycle, over raw L2 (ethertype 0x88AB, on the shipped services/fieldbus/rawl2); host-tested (native_powerlink). Remaining: the MN slot-schedule state machine + async SDO + object dictionary, and the isochronous timing (the preempting-task model). Fixed BSS, no heap.PC_ENABLE_SERCOS (services/sercos): the cyclic MDT/AT telegram codec - pc_sercos_build / _parse for [type][phase][cycle][cyclic data] (the master's setpoint telegram + the drive's actual-value telegram) over raw L2 (ethertype 0x88CD, on the shipped services/fieldbus/rawl2) - and the 16-bit IDN encode/decode (S/P bit + parameter-set + data-block, the "S-0-0100" drive-parameter addressing); host-tested (native_sercos). Remaining: the hard-real-time slot schedule + the IDN service-channel transfer state machine, and the isochronous timing (preempting-task model). Fixed BSS, no heap.PC_ENABLE_DEVICENET (services/devicenet): the CAN link adaptation for CIP-over-CAN - the 4-group 11-bit CAN id, the explicit-message header octet, single-frame explicit messages, and the fragmentation reassembler (the CIP body itself is built with the shared cip codec); host-tested (native_devicenet). Remaining: the predefined master/slave connection set, I/O (implicit) messaging, and the ESP32 TWAI/CAN transport. Fixed BSS, no heap; pairs with the EtherNet/IP CIP work.PC_ENABLE_LONWORKS (services/lonworks): the LonTalk network-variable PDU - pc_lon_build_nv / _parse_nv for [msg-code][14-bit selector][value] (an NV update / poll), over LON/IP (IEC 14908-4, over UDP, so no Neuron/transceiver is needed - the host-reachable path) - plus the common SNVT scalar encodings (SNVT_temp 0.01 K fixed-point, SNVT_switch level + state); host-tested (native_lonworks). Remaining: the fuller SNVT type table + the NV binding/address table. Fixed BSS NV table, no heap.PC_ENABLE_MBPLUS (services/mbplus): the Modbus Plus HDLC frame codec - pc_mbplus_build / _parse for [7E][addr][ctrl][payload][CRC-16/X-25][7E] and pc_mbplus_next_token (the next station in the logical ring) - reusing the shipped Modbus PDU model for the payload; host-tested (native_mbplus, CRC check value 0x906E). Remaining: the full path/routing model + the token-rotation timing, and the custom 1 Mbit/s bus (hardware-gated).PC_ENABLE_INTERBUS (services/interbus): the summation frame - pc_interbus_build / _parse assemble the single rotating frame (loopback word + each device's 16-bit process-image slice + CRC-16/CCITT FCS) from a list of per-device word slices and disassemble a received frame back into them; host-tested (native_interbus, FCS check value 0x29B1). Remaining: the PCP parameter channel + the physical ring shift-register clocking (hardware-gated). Fixed BSS, no heap.PC_ENABLE_AMQP (services\amqp): pc_amqp_protocol_header (the "AMQP" 0 0 9 1 preamble), pc_amqp_build_frame / pc_amqp_parse_frame (type + channel + size + payload + the 0xCE frame-end), pc_amqp_build_method / pc_amqp_parse_method (a METHOD frame's class-id / method-id / arguments), and pc_amqp_build_heartbeat; host-tested. Remaining: the 0-9-1 method-argument field encoding (the connection/channel/exchange/queue/basic classes) for RabbitMQ interop, and the AMQP 1.0 type system + framing (open / begin / attach / transfer / disposition) for broker links. Zero-heap: fixed link/session state, the payload streamed through the client transport, one build flag. Pairs with the MQTT / webhook outbound integrations.PC_ENABLE_DF1 (services\df1): a zero-heap framing + DLE byte-stuffing + BCC/CRC codec for the serial link layer (pub. 1770-6.5.16) - pc_df1_build_frame wraps application data in DLE STX ... DLE ETX (doubled-DLE escape) with a BCC (2's complement of the data sum) or CRC-16/ARC (over the data + ETX, low byte first), and pc_df1_parse_frame validates and un-stuffs; vectors verified against the manual (BCC 0x20->0xE0, CRC 0xBB3D). Remaining: the PCCC command set (PLC-5 / SLC-500 data-table read/write) inside the application data, the half-duplex master/slave framing, and DH+ (Data Highway Plus token LAN, physical-layer-gated). Fixed BSS, no heap.PC_ENABLE_MELSEC (services\melsec): pc_melsec_build_read emits the binary 3E batch-read (word) frame (little-endian fields, subheader 0x5000, command 0x0401, the device code + 24-bit head device + point count) and pc_melsec_parse_response validates the 0xD000 response and reports the end code + data; layout + device codes verified against a third-party MC impl, host-tested. Completes the major-vendor PLC read set (Omron FINS / Host Link, AB DF1, Siemens S7comm). Remaining: MC batch write + the 1E/4E + ASCII frame variants, and MELSECNET/H / **/10** (the cyclic control network, PHY/timing-gated). Fixed BSS device model, no heap.PC_ENABLE_FINS (services\fins): a zero-heap command/response builder + parser - pc_fins_build_command / pc_fins_build_memory_area_read emit the 10-octet routing header + MRC/SRC command code + parameters, and pc_fins_parse_command / pc_fins_parse_response read them back (the response MRES/SRES end code included), over the shipped UDP transport. Header layout verified against the FINS spec; pure, host-tested. Remaining: the full command set (memory-area write / run-stop / clock) and the FINS/TCP framing + the FINS/Hostlink-gateway addressing model. Fixed BSS device model, no heap.PC_ENABLE_HOSTLINK (services\hostlink): a zero-heap ASCII frame builder + FCS-validating parser for Omron's serial C-mode protocol - pc_hostlink_build emits @UU + header code + text + FCS + *CR (FCS = the 8-bit XOR from @ through the text, verified against the @00RD00000010 -> 57 vector), and pc_hostlink_parse / pc_hostlink_end_code validate and split a frame. Pairs with the FINS work (Host Link is the serial sibling); pure, host-tested. Remaining: the per-command text encoders (RD/WD/... of the DM/CIO areas) and the UART transport. Fixed BSS, no heap.PC_ENABLE_SNP (services/snp): the SNP master-slave serial frame - pc_snp_build / _parse for [control][length][data][arithmetic-sum BCC] with the control-byte constants, for register read/write on a GE Fanuc Series 90 (90-30/90-70) PLC over RS-485; host-tested (native_snp). Remaining: the SNP-X session setup + the per-command register-access encoders, and the UART transport. Fixed BSS, no heap.PC_ENABLE_DIRECTNET (services/directnet): the DirectNET master-slave serial frames - pc_dnet_header (SOH + ASCII-hex slave/type/address/blocks + ETB + LRC) and pc_dnet_data / _data_parse (STX + data + ETX + LRC) - for V-memory read/write on an AutomationDirect DirectLOGIC PLC, with the longitudinal-XOR LRC and control-byte constants; host-tested (native_directnet). Remaining: the ACK/NAK handshake sequencing + the V-memory address map, and the UART transport. Fixed BSS, no heap.PC_ENABLE_IEC60870 (services/iec60870): the tele-control codec - the -104 APCI (I/S/U frames), the ASDU header + 3-octet IOA, and the -101 FT1.2 fixed/variable link frames (sum checksum); host-tested (native_iec60870). Remaining: the fuller ASDU information-object type set (double points, measured values, commands) + the k/w sequence flow-control state machine over the shipped TCP transport. Fixed BSS point database, no heap.PC_ENABLE_GOOSE (services/goose) - the raw-L2 multicast event publish for fast trips (the BER-encoded IECGoosePdu wrapped in the 8-octet GOOSE header + Ethernet frame, ethertype 0x88B8, on the shipped raw-L2 codec; host-tested native_goose). MMS: PC_ENABLE_MMS (services/mms) - the ACSI client/server core, the BER confirmed-request/response Read PDUs (invokeID + read service + a named Data-Object ObjectName) build + parse, over ISO-on-TCP (TPKT + COTP via the shipped services/cotp) on port 102; host-tested (native_mms). Remaining: GOOSE subscribe/decode + the fast-retransmit timer, the fuller MMS service set (Write, GetNameList, reports) + the SCL-driven object model. Fixed BSS, no heap.PC_ENABLE_C37118 (services\c37118): a zero-heap builder + CRC-validating parser for the PMU synchrophasor wire frame (SYNC FRAMESIZE IDCODE SOC FRACSEC DATA CHK, CHK = CRC-CCITT) - pc_c37118_build_frame frames any payload, pc_c37118_build_command handles the fixed Command frame, and pc_c37118_parse_frame validates the CRC and reports the frame type / id / timestamp / payload, with pc_c37118_parse_command. CRC verified against the canonical CRC-CCITT-FALSE check value; pure, host-tested. The fixed phasor configuration / data model (encoding the CFG-2 channel layout and the matching data frame, over TCP/UDP 4712/4713, streamed via the chunked / UDP cast path) remains to be layered on top. Pairs with the telemetry-math service for on-device PMU analytics.PC_ENABLE_SEP2 (services/sep2): the core 2030.5 XML resource documents in the urn:ieee:std:2030.5:ns namespace - DeviceCapability (the root function-set links), EndDevice (sFDI/lFDI registration), and DERControl (a DER dispatch/curtailment event: interval + opModFixedW setpoint) - built into a caller buffer over the existing HTTP + TLS stack; host-tested (native_sep2). Remaining: the fuller function-set resource schema (metering, pricing, demand response) + the resource discovery walk + optional EXI, on a fixed BSS model.PC_ENABLE_OPENADR (services/openadr): the OpenADR 3.0 REST/JSON objects - pc_openadr_event (a demand-response signal: programID + eventName + interval payload points with start / duration / type / value) and pc_openadr_report (a VEN reading back to the VTN) - built into a caller buffer over the existing HTTP client/server + OAuth2, with JSON escaping and a no-stdlib 3-decimal value formatter; host-tested (native_openadr). Remaining: the full VEN/VTN role state machine + subscriptions, and the OpenADR 2.0 XML/EXI profile. Fixed BSS, no heap.PC_ENABLE_ICCP (services/iccp): the TASE.2 indication-point Data_Value BER structures - pc_iccp_state_q (StateQ: a discrete state + quality flags) and pc_iccp_real_q (RealQ: a scaled INTEGER value + quality), each with an optional 4-octet TimeStamp - the telemetry a control center transfers as MMS Reads (on the shipped services/mms + services/cotp, ISO-on-TCP 102); host-tested (native_iccp). Remaining: the data-set / transfer-set / bilateral-table object model on top of the MMS ACSI core. Fixed BSS object model, no heap.src/board_drivers/board_profiles/): board_profile.h selects, along three independent axes, per-variant default files, each guarded by #ifndef so a -D / build_opt.h override always wins. Axes: chip (classic_defaults.h / s3_defaults.h / c6_defaults.h / p4_defaults.h, auto-selected from CONFIG_IDF_TARGET_*, holding HW-specific switches + chip-appropriate defaults), PSRAM size (8/16/32mbpsram.h), and flash size (8/16/32mbflash.h) - PSRAM/flash are their own axes because a chip ships in several configs (set -DPC_PSRAM_MB / -DPC_FLASH_MB, auto-filled from CONFIG_SPIRAM_SIZE / CONFIG_ESPTOOLPY_FLASHSIZE_* on ESP-IDF). Precedence: user -D > PSRAM > flash > chip > classic floor. Classic ESP32 keeps its exact historical numbers (no regression); larger variants auto-scale. Piloted on the edge-cache + mesh pools; remaining: migrate more sizing knobs (TLS arena / MAX_TLS_CONNS / MFL, packet + handle buffers, connection tables) into the profiles, wire the HW-specific switches (e.g. PC_HAS_CRYPTO_HWACCEL) into the crypto paths that today check CONFIG_IDF_TARGET_* directly, and populate the flash-size axis once a flash-backed default exists.Each item below has a working piece in the tree and an explicit Remaining note. They are grouped by the area they came from.
PC_ENABLE_LORA, services/lora): the RadioHead 4-byte header codec + the SX1276 register protocol (init / send / recv / RSSI) over a portable register-access bus, host-tested against a mock chip; example LoRaGateway drives a real RFM95 and bridges frames northbound via the gateway. Remaining: verify the RF link on the module, an SX126x variant, and a bounded LoRaWAN Class A uplink/downlink codec.PC_ENABLE_NRF24, services/nrf24): the SPI command protocol (init / send / recv, pipe-addressed, static payload) over an SPI + CE bus, host-tested against a mock chip; example Nrf24Gateway bridges frames northbound via the gateway. Remaining: verify the RF link on the module (and optional dynamic-payload / auto-ack).PC_ENABLE_CC1101, services/cc1101): the CC1101 SPI header protocol (config registers, the 13 command strobes, status registers, TX/RX FIFO) - reset + apply a caller-supplied SmartRF register table + set channel + verify VERSION (pc_cc1101_init), variable-length pc_cc1101_send, TX-done poll, pc_cc1101_set_rx, and pc_cc1101_recv with the appended RSSI/LQI status + pc_cc1101_rssi_dbm decode, over a portable SPI bus; host-tested against a mock chip (native_cc1101). Bridges northbound via the gateway framework. Remaining: verify the RF link on the module (and an OOK/ASK preset variant).PC_ENABLE_ZIGBEE, services/zigbee): the byte-stuffed, CRC-16/CCITT ASH data-link (encode/decode) that carries EZSP, host-tested against the documented RST frame; example ZigbeeGateway bridges a real NCP. Remaining: the EZSP command layer (version negotiation, incomingMessageHandler) + verify against an NCP. Blocked on hardware: this is a bridge to an external EZSP/ZNP co-processor, so closing it needs a Silicon Labs EmberZNet NCP (EFR32), a Digi XBee, or a TI ZNP module - none on hand, and unlike Thread's spinel there is no open reference NCP to interop against in software (EmberZNet and Z-Stack are proprietary; zigpy/bellows are hosts, not NCPs). Note an ESP32-C6/H2 does not close this: its native 802.15.4 radio speaks Espressif's own Zigbee stack, not EZSP over a UART, so it is a different architecture rather than a drop-in NCP.PC_ENABLE_ZWAVE, services/zwave): SOF/LEN/Type/Cmd/Data frames + the XOR checksum + ACK/NAK/CAN, host-tested against the documented GetVersion frame; example ZWaveGateway bridges a real controller. Remaining: verify against a controller + inclusion / SendData sequences.PC_ENABLE_ENOCEAN, services/enocean): telegram parse/build + the CRC-8 (poly 0x07), host-tested with known-answer CRCs and malformed/resync framing; example EnOceanGateway bridges a real TCM 310 over UART. Remaining: verify against a module + EEP profile decoding.PC_ENABLE_SIGFOX, services/sigfox): the AT$SF=<hex> uplink formatter + OK/ERROR/pending response classifier, host-tested; example SigfoxUplink sends a reading from a real modem. Remaining: verify against a module + subscription.PC_ENABLE_WISUN, services/wisun): since the CoAP service ships only a server, the connector adds the CoAP client request builder (pc_wisun_build_coap: RFC 7252 header + delta-encoded Uri-Path options with extended-length + payload) plus the FAN node registry (register / find / joined-count) keyed on pc_ip, and pc_wisun_nodes_json for the web. The app sends the built PDU to a node's IPv6 address over pc_udp; pure + host-tested (native_wisun). Remaining: point it at a chosen border-router devboard and verify the mesh end to end (the devboard choice is the only external dependency; the connector code is board-agnostic).I2C / SPI / UART:
PC_ENABLE_PN532, services/pn532): normal-information-frame build/parse + ACK with the LCS / DCS checksums, host-tested against the documented GetFirmwareVersion frames; example NfcGateway reads a real PN532 over I2C. Remaining: verify against a reader + the MFRC522 variant.Built-in radio:
PC_ENABLE_BLE_GATT, services/ble_gatt): the Attribute Protocol PDUs under GATT (Bluetooth Core Vol 3 Part F) - build/parse read / write / notify / error with little-endian handles - plus a GATT characteristic-table JSON serializer for the web, pure + host-tested (native_ble_gatt). Remaining: wire it to the NimBLE / Bluedroid radio (scan + a GATT server) and an HCI-UART transport variant.PC_ENABLE_RADIO_SNIFF, services/radio_sniff): wraps each received 802.15.4 MAC frame in the Wireshark IEEE 802.15.4 TAP pseudo-header (per-frame RSSI + channel TLVs, an exact int->float32 RSSI encode) and a pcap record (new PC_DLT_IEEE802_15_4_TAP / _NOFCS link types in shared_primitives/pcap.h), so a sniffed channel opens directly in Wireshark; pure + host-tested (native_radio_sniff). Remaining: put the CC1101 / LoRa / Thread drivers into receive-only and route their frames through this into the existing forwarding sink on hardware.PC_ENABLE_LD2410, services/ld2410): the framed 256000-baud report codec (pc_ld2410_parse_report basic + engineering mode, a byte-by-byte Ld2410Stream reassembler that resyncs on noise, presence / distance helpers) and the FD FC FB FA config-command encoders, pure + host-tested (native_ld2410), with an ESP32 UART binding (example wired). Remaining: an MR60BHA 60 GHz vital-sign variant, the Infineon BGT60 SPI radar, and the analog Doppler (HB100 / RCWL-0516) presence path.PC_ENABLE_MPR121, services/mpr121) and the FDC2114/2214 capacitance-to-digital field sensor (PC_ENABLE_FDC2214, services/fdc2214): the 28-bit data combine + error flags + frequency scale + single-channel config-sequence builder, pure codec host-tested (native_fdc2214), with an ESP32 I2C binding that verifies the device id, applies the config, and reads the channel. Remaining: verify capacitance shift on a real electrode / bench.PC_ENABLE_LDC1614, services/ldc1614): the LDC1614 28-bit data combine + error flags + frequency scale (data/2^28 * fref) + single-channel config-sequence builder, pure codec host-tested (native_ldc1614), with an ESP32 I2C binding that verifies the device id, applies the config, and reads the channel. Remaining: verify eddy-current shift on a real coil / bench, and the magnetometer EM path.PC_ENABLE_VL53L0X, services/vl53l0x): the documented ranging registers - pc_vl53l0x_range_mm combines the range byte pair, pc_vl53l0x_data_ready decodes the interrupt-status byte, pc_vl53l0x_range_valid checks the device range-status field - pure codec host-tested (native_vl53l0x), with an ESP32 I2C binding that verifies the model id, starts continuous ranging, and reads the distance. Remaining: apply ST's tuning blob for best accuracy, a VL53L1X variant, and a bench range check.PC_ENABLE_SOUTHBOUND (services/southbound): the uniform seam every field-device driver plugs into so the app polls/drives any southbound device (Modbus slave, BACnet controller, raw SPI/I2C/UART sensor) through one facade - a bounded driver registry (pc_southbound_register / _find / _count / _clear) plus name-dispatched pc_southbound_read / _write / _read_block / _write_block, where the block calls are the atomic multi-point (register-matrix) path. Each driver owns its transport (a read/write vtable + ctx). Pure registry + dispatch, host-tested (native_southbound). The Modbus adapter is shipped (services/net/southbound/sb_modbus, PC_ENABLE_SOUTHBOUND && PC_ENABLE_MODBUS_MASTER): it binds the transport-agnostic Modbus TCP master codec into a SouthboundDriver, so an app reads register points by driver name through the one facade - pc_sb_modbus_init (holding FC 0x03 / input FC 0x04, unit, a rolling txid) + pc_sb_modbus_driver install the full vtable over an app-supplied request/response transaction seam (pc_sb_modbus_txn, bound to pc_client for Modbus TCP or a serial gateway): read (one register) and read_block (the atomic register matrix, a contiguous span up to 125 registers in one request), plus - for a holding-register driver - write (Write Single, FC 0x06) and write_block (Write Multiple, FC 0x10, up to 123 registers). An input-register driver is read-only (write / write_block unbound -> Sb::SB_ERR_UNSUPPORTED). A Modbus exception reply surfaces as PC_SB_MODBUS_EXCEPTION with the raw code captured, distinct from a propagated transport error. The matching master write codec is shipped too (pc_modbus_build_write_single / _build_write_multiple / _parse_write_response). Host-tested end to end against the real slave codec (pc_modbus_process_adu) over a loopback seam - native_sb_modbus (12 cases: single/block read + write round trips, input-register read-only, exception vs transport error, bounds, txid roll) and native_modbus_master (+6 write cases). The bit-access function codes are shipped too, completing the master's standard set: read coils (FC 0x01) and discrete inputs (FC 0x02) via pc_modbus_build_read_bits / _parse_read_bits_response (LSB-first bit unpacking, the requested count validated against the returned byte count), write single coil (FC 0x05, the 0xFF00/0x0000 value encoding) and write multiple coils (FC 0x0F, LSB-first bit packing), with _parse_write_response extended to the coil replies (which share the register-reply wire format). Host-tested as full master->slave round trips with read-back confirmation and a byte-exact packed-pattern check (native_modbus_master +5, now 24 cases). The atomic register operations are shipped too - Mask Write Register (FC 0x16, pc_modbus_build_mask_write + _parse_mask_write_response: set/clear individual bits via (reg AND And_Mask) OR (Or_Mask AND NOT And_Mask) without a read-modify-write race) and Read/Write Multiple Registers (FC 0x17, pc_modbus_build_read_write_multiple: a write span applied before a read span in one transaction) - both implemented on both the master and the slave codec (pc_modbus_process_adu) and host-tested end to end (native_modbus_master now 27; the round trip flagged a real off-by-one in the mask-write buffer guard, fixed). The master now speaks every standard Modbus function code (0x01-0x06, 0x0F, 0x10, 0x16, 0x17). Remaining: an example driving a real Modbus TCP server over pc_client (HW-verify).PC_ENABLE_LINK_MANAGER (services/link_manager): a table of interfaces (kind + priority + up/down) with deterministic best-link-up selection, graceful escalation to a higher-priority interface when it comes up, failover to the next best when it drops, and change detection so the app reconfigures the netif only on a real transition. Pure, host-tested (native_link_manager). The PHY init is HW-verified (2026-07-19) on a Waveshare ESP32-P4-POE-ETH (onboard IP101 RMII PHY, arduino-esp32 3.x): the shipped init_eth_physical() brought the link up (100M full-duplex + DHCP) and the PC server answered real HTTP over pure wired Ethernet. Remaining: the multi-interface bridge / packet forwarding (the v5 interface-forwarding milestone).pc_ip (RFC 4291 parse / RFC 5952 format / scope classify / CIDR) and the netif bring-up is PC_ENABLE_IPV6 (physical layer; listeners bind IPADDR_TYPE_ANY). HW-verified on an ESP32-S3: SLAAC formed link-local + ULA + a router-advertised global, and the dual-stack :80 listener served real HTTP GETs over IPv6 (curled on-link over both the global and the ULA). The client-side fallback is PC_ENABLE_HAPPY_EYEBALLS (services/happy_eyeballs): pc_he_pref scores a destination (RFC 6724 scope + family), pc_he_order sorts a candidate list and interleaves the address families (RFC 8305) so successive attempts alternate v6/v4, and pc_he_attempt_due gates the next attempt by the Connection Attempt Delay - fast IPv6, quick IPv4 fallback. Pure, host-tested (native_happy_eyeballs). The reverse-SSH tunnel to a relay SHIPPED (PC_ENABLE_SSH_CLIENT, network_drivers/presentation/ssh/connection/ssh_client): a full outbound SSH client - negotiates the whole modern suite (curve25519 / ecdh-p256 / dh-group14 / mlkem768x25519 hybrid, ed25519 / ECDSA / RSA host keys pinned by SHA-256, chacha20-poly1305 / aes256-gcm / aes256-ctr) through a role-aware packet layer, authenticates by ed25519 publickey, holds a tcpip-forward, and bridges forwarded-tcpip channels (a pool, PC_SSH_CLIENT_MAX_CHANNELS) back to a local service. HW-verified against OpenSSH 10.0 on an ESP32-S3 (handshake + auth + forward + the :80 bridge, concurrent and rapid-sequential, byte-exact responses). Remaining: VPN tunneling (the IKEv2 item below).services/ikev2, PC_ENABLE_IKEV2, host-tested native_ikev2; byte-exact vs scapy). The 28-octet header (initiator/responder SPIs, next-payload, MjVer/MnVer, exchange type, flags, message id, length) and the generic payload chain (next-payload + critical + length) for the payloads: SA (proposals -> transforms: ENCR / PRF / INTEG / D-H / ESN, with the key-length attribute), KE, Ni/Nr nonce, AUTH, IDi/IDr, CERT / CERTREQ, TSi/TSr traffic selectors, N notify, D delete, and SK encrypted-payload framing. Pure build/parse into caller buffers, host-tested with RFC vectors. Reuses crypto we already ship: ssh_curve25519 (D-H group 31 = X25519, and MODP groups via ssh_bignum), ssh_chachapoly (ChaCha20-Poly1305 per RFC 7634), pc_sha256/512 (PRF/INTEG HMAC), AES-GCM (the pc_quic_aead core) - so the primitive surface is largely done; this is mostly framing + the key-derivation (SKEYSEED / the SK_* chain). The key derivation is now shipped (RFC 7296 §2.13-2.14): pc_ike_prf_plus (prf+ over HMAC-SHA2-256, capped at 255 blocks) and pc_ike_derive_keys (SKEYSEED = prf(Ni|Nr, g^ir), then the SK_d/ai/ar/ei/er/pi/pr split by caller-supplied per-algorithm lengths), zero-heap, cross-checked against an independent Python HMAC reference incl. the >64-byte-key RFC 2104 pre-hash and an AES-GCM (key+salt) split length (test_ikev2, +5 KAT/guard cases). The SK-payload AEAD is shipped too (RFC 5282 AES-256-GCM-16): pc_ike_sk_aead_seal / pc_ike_sk_aead_open wrap the library's AES-256-GCM under the RFC 5282 nonce (4-byte salt from SK_ei/er || the 8-byte explicit IV), authenticating the header-through-SK-generic-header AAD and producing/consuming the IV | ciphertext | ICV body, cross-checked against Python cryptography's AES-256-GCM incl. tamper (tag + AAD) rejection and in-place operation (+3 cases). The Diffie-Hellman shared-secret step is shipped too (RFC 7296 §2.7): pc_ike_dh_public (our KE value) and pc_ike_dh_compute (g^ir, which feeds SKEYSEED), group 31 = curve25519 (RFC 7748 X25519) over the library's ssh_x25519, conformance-tested against RFC 7748's own §5.2 raw-X25519 and §6.1 Alice/Bob ECDH-agreement vectors (+3 cases; groups 19 P-256 / 14 MODP-2048 are a later increment). Tier-2 crypto is now complete (key schedule + SK AEAD + D-H); the remaining tier-2 work is the IKE_SA_INIT -> IKE_AUTH state machine (item 2). test_ikev2 is now 50 cases.pc_ike_auth_psk computes AUTH = prf(prf(PSK, "Key Pad for IKEv2"), RealMessage | Nonce | prf(SK_p, RestOfIDPayload)) over the streamed HMAC-SHA2-256 (RealMessage never re-buffered), cross-checked against an independent Python HMAC reference incl. wrong-key divergence. The IKE_SA_INIT message assembly is shipped too (RFC 7296 §1.2): pc_ike_sa_init_build composes the tier-1 codec into a whole HDR | SA | KE | Nonce message with the Next-Payload chain + header Length set correctly, and pc_ike_sa_init_parse reads it back into an IkeSaInitMsg (SPIs, first proposal, KE group + data, nonce) - the raw chain bytes are asserted against the RFC values and every field round-trips. The IKE_AUTH encrypted-message assembly is shipped too (RFC 7296 §3.14 / RFC 5282): pc_ike_auth_msg_build wraps a caller-built inner chain (IDi | AUTH | SAi2 | TSi | TSr) in the SK envelope - HDR | SK{ IV | AES-256-GCM(inner | Pad Length) | ICV } with the RFC 5282 salt||IV nonce and the AAD = header-through-SK-generic-header - and pc_ike_auth_msg_open verifies the ICV in constant time, decrypts in place, and strips the pad trailer, round-tripping an IDi|AUTH chain byte-exact with ciphertext + AAD tamper both rejected. The handshake's message layer is complete. ECDSA-P256 certificate auth is shipped too (RFC 7296 §2.15 / RFC 7427): pc_ike_signed_octets assembles RealMessage | Nonce | prf(SK_p, id) into caller scratch, and pc_ike_auth_sign_ecdsa_p256 / _verify_ecdsa_p256 sign / verify those octets with the library's ECDSA-P256-SHA256 - the octet assembly is cross-checked against a Python HMAC reference and the sign->verify round-trip rejects a tampered nonce and a wrong key. The IKE SA context + key derivation from a completed exchange is shipped (RFC 7296 §2.14/§2.17): an IkeSa holds the SPIs, role, negotiated IkeSuite, and IkeKeyMaterial; pc_ike_suite_keylengths maps the negotiated transforms to the SK_* lengths (AEAD -> sk_a = 0 + a 4-byte GCM salt in sk_e) and pc_ike_sa_keys_from_init computes g^ir and runs the key schedule - verified as a mutual agreement (initiator + responder derive identical SK_* from the RFC 7748 Alice/Bob key pair) and cross-checked against a Python reference. The initiator IKE_SA_INIT handshake driver is shipped too (RFC 7296 §1.2): an IkeHandshake state machine (IkeState) where pc_ike_initiator_start emits the IKE_SA_INIT request from caller-supplied ephemeral material (SPI, X25519 key pair, nonceheap_caps_calloc(MALLOC_CAP_SPIRAM) at begin) + asset offloading + COMPONENT_EMBED_TXTFILES (M); SPI DMA ping-pong buffers (M) _(placement policy shipped)_ - PC_ENABLE_PSRAM_POOL (services/psram_pool): pc_psram_place picks DRAM vs PSRAM for a buffer by size, DMA requirement, and free-heap headroom (large/cold to PSRAM, small/hot + DMA to DRAM, always leaving an internal-DRAM reserve), and pc_pingpong_* keeps the SPI DMA double-buffer bookkeeping (CPU fills one buffer while DMA drains the other; swap flips their roles). Pure, host-tested (native_psram_pool). Remaining: the heap_caps_calloc allocation glue at begin + the COMPONENT_EMBED_TXTFILES asset-offload build wiring (M).PC_ENABLE_HW_HEALTH (services/hw_health): pc_hwhealth_rail_sample tracks a rail's worst droop + sag/brownout counts (with a /health JSON serializer), pc_hwhealth_spi_result is a hysteretic clock-backoff state machine (halve on a CRC-fail streak, step up on an ok streak, clamped to a floor/ceiling), pc_hwhealth_gpio_short classifies a driven-vs-readback mismatch as a short to ground / Vcc, and pc_hwhealth_cap_leak compares a measured RC decay to expected (too fast = leak, too slow = high ESR). Pure, host-tested (native_hw_health). Remaining: the ADC / SPI / GPIO sampling glue that feeds them on real hardware (M).ci_tooling/generate/gen_configurator.py generator parses src/protocore_config.h and builds the flags as a hierarchy grouped under the file's section titles, with the hard dependency edges (#if child && !parent guards) encoded - the build-flag tree, kept from drifting by the check CI gate. Remaining: the virtual protocol-mocking toggles (swap a real driver for a mock transport at build time).python test/servers/interop.py <protocol>, drives the device against the real reference implementation, not just our own round-trip. Peers so far: HTTP (stdlib client), WebSocket (websockets), SNMP (net-snmp, pysnmp fallback), Modbus client + server (pymodbus), CoAP (aiocoap), MQTT broker (mosquitto + paho), OPC UA client + server (asyncua). Each peer says whether the device is the server (the harness probes it, --host ...) or the client (the harness serves a reference peer the device connects to), reports uniform PASS/FAIL, and exits 0/1/2. HW-verified against the board on real third-party stacks: HTTP 4/4, WebSocket 3/3, CoAP 2/2, Modbus 6/6, SNMP 3/3, MQTT (device client -> real mosquitto), and OPC UA (asyncua) 3/3 - all seven protocol families. Adding a protocol is one module in peers/ (documented in its README). Remaining: wiring it into CI containers, and a peer per new protocol as it lands.configurator/) (L) _(GUI + source-of-truth shipped)_ - a guided front end for the ~200 PC_ENABLE_* and sizing flags so a user assembles a firmware build without hand-editing build_flags. Shipped as ci_tooling/generate/gen_configurator.py -> docs/configurator.html: it parses protocore_config.h (the single source of truth, so it never drifts - a check CI gate fails on staleness) for every feature flag + tuning knob + section group + the hard #if child && !parent dependencies, and emits one self-contained page that ticks features, tunes knobs, resolves dependencies (mutual-exclusion), and copies out a platformio.ini build_flags block or a #define set (only the values that differ from the defaults). Beginner-friendly, ships to Pages. Remaining: a live per-option build-footprint estimate (flash + RAM from the FEATURES tables), advisory "this is unwise, but here you go" guardrails, and a standalone CLI backend (the emission is client-side today).network_drivers/tls (mbedTLS, floored at TLS 1.2). The version + cipher policy shipped as PC_ENABLE_TLS_POLICY (services/tls_policy): pc_tls_negotiate_version picks the version server-style, pc_tls_version_name makes it observable, pc_tls_select_cipher pins the suite allowlist by server preference, and pc_tls_is_aead classifies suites - pure + host-tested (native_tls_policy). Remaining: wire the policy into the mbedTLS conf (cert/cipher list) + session-resumption tickets.services/tls_policy (pc_tls_is_aead + the 1.3 suites 0x1301-0x1303 in the allowlist). Remaining: optional 0-RTT early data with replay safeguards, and exposing a 1.3-only profile toggle.PC_ENABLE_RAWL2 (services/rawl2): the host-testable core - build/parse Ethernet II + 802.1Q VLAN frames and the 802.3 FCS (CRC-32, check value 0xCBF43926); host-tested (native_rawl2). The raw-frame API the raw-L2 protocols build on. Remaining (device transport):PC_ENABLE_NTCIP (services/ntcip): the NTCIP object definitions on top of the shipped SNMP agent (snmp) - the 1202 (Actuated Signal Controller: maxPhases, phaseMinimumGreen, live phaseStatusGroupGreens) and 1203 (Dynamic Message Sign: dmsMaxMultiStringLength, dmsMessageMultiString) object roots under 1.3.6.1.4.1.1206.4.2, plus pc_ntcip_oid() to build a full object OID (root + instance index) to register with pc_snmp_agent_add_*; host-tested (native_ntcip). Remaining: the fuller 1202/1203 object set + 1211 (Signal Control and Prioritization). No heap.PC_ENABLE_UTMC (services/utmc): the UTMC common-database HTTP+XML message set - pc_utmc_request (a UTMCRequest for an object id), pc_utmc_response (a UTMCResponse carrying the object value + a data-quality flag + timestamp), and pc_utmc_parse_request (extract the requested id) - over the existing HTTP server, XML-escaped; host-tested (native_utmc). Remaining: the fuller object model + the DATEX-II profile. Fixed BSS, no heap.PC_ENABLE_OCIT (services/ocit): the OCIT-Outstations object message - pc_ocit_build / _parse for [msg-type][object-type][instance][data-type][value] (get / set / report of a field object) with the typed values (bool / byte / u16 / u32 / octets) and typed-value accessors - between central traffic computers and field controllers / detectors; host-tested (native_ocit). Remaining: the fuller object dictionary + the OCIT-O BTPPL transport profile. Fixed BSS device/detector model, no heap.PC_ENABLE_J2735 (services/j2735): the ASN.1 UPER (Unaligned Packed Encoding Rules) bit-level primitive codec - constrained INTEGER (offset in ceil(log2(range)) bits), BOOLEAN, raw bit fields, a bit writer/reader - and the three core V2X messages encode + decode on top: BSMcore (the safety kernel: msgCnt / id / secMark / lat / long / elev / speed / heading), SPaT (the MovementState list: per-signal-group phase + min/max end-time countdown), and MAP (the intersection geometry: id + reference point + per-lane id / ingress flag / node XY offsets). Host-tested against hand-computed bit patterns (native_j2735; the 162-bit BSMcore packs to 21 octets). Remaining: the full BSM part II optionals + the 1609.2 security envelope. The DSRC / C-V2X radio is an external module; the message layer is the deliverable.PC_ENABLE_WAVE (services/wave): the 1609.3 WSMP (WAVE Short Message Protocol) header - pc_wsmp_build / _parse (version + a P-encoded PSID + length + payload) with the PSID variable-length p-encoding (pc_wave_encode_psid / _decode_psid) - and the 1609.2 secured-message envelope header (pc_wave_1609dot2_wrap: protocolVersion + contentType); these carry the shipped J2735 messages. Host-tested (native_wave). Remaining: the full 1609.2 signature / certificate machinery (the crypto layer) + the WSA service advertisement. The DSRC / C-V2X radio is an external module; no heap.PC_ENABLE_NEMA_TS2 (services/nema_ts2): the traffic-cabinet SDLC bus frame - pc_nema_ts2_build / _parse for [address][control][frame-type][data][CRC-16/X-25] linking the controller to the MMU / BIUs / detector racks, with the common frame-type constants; host-tested (native_nema_ts2, CRC check value 0x906E). Remaining: the per-frame-type cabinet object model + the synchronous serial PHY / BIU timing (hardware-gated).PC_ENABLE_ATC (services/atc): the field-I/O map serialized as {"inputs":[...],"outputs":[...]} JSON for an ATC engine over HTTP, plus the output setter/getter. Remaining: the Linux ATC engine + video analytics stack (out of scope).PC_ENABLE_LWM2M (services\lwm2m): a zero-heap writer + cursor reader for the OMA TLV (application/vnd.oma.lwm2m+tlv) resource encoding - pc_lwm2m_tlv_write (+ typed _write_int shortest-form / _write_bool / _write_string / _write_float helpers), pc_lwm2m_tlv_read, and pc_lwm2m_tlv_value_int, handling 8-/16-bit ids and inline / 8- / 16- / 24-bit lengths and the Object-Instance / Resource / Multiple-Resource / Resource-Instance kinds; type-byte layout verified against the spec, host-tested. Built on the shipped CoAP service (coap). Remaining: the client interfaces (Bootstrap, Registration, Device Management, Information Reporting / Observe) and the standard object model (Security/0, Server/1, Device/3, Firmware Update/5, ...) on a fixed BSS model. The SenML-CBOR / SenML-JSON content formats are now shipped separately - PC_ENABLE_SENML (services\senml): pc_senml_json_build / pc_senml_cbor_build emit a SenML (RFC 8428) pack (base name/time, name, unit, one value, time per record) over the JSON / CBOR writers, integral numbers kept as integers; verified against the RFC example, host-tested. DTLS is gated on the TLS work; scope the NoSec + registration/observe core first. No heap, one flag.PC_ENABLE_PROTOBUF (services\protobuf): a zero-heap streaming writer (pc_pb_uint64 / pc_pb_sint64 / pc_pb_fixed32 / pc_pb_fixed64 / pc_pb_float / pc_pb_double / pc_pb_bytes / pc_pb_string, embedded messages via a sub-buffer + pc_pb_bytes) and a cursor reader (pc_pb_read_field over varint / ZigZag / I32 / I64 / length-delimited), host-tested against the spec vectors (08 96 01, the "testing" string, ZigZag mapping). gRPC-Web framing also shipped - PC_ENABLE_GRPC_WEB (services\grpcweb): the 5-octet [flags][len BE32] message frame (pc_grpcweb_frame_message), the 0x80 trailers frame (pc_grpcweb_frame_trailer, grpc-status / grpc-message), and pc_grpcweb_parse, wrapping the Protobuf codec over the shipped HTTP/1.1 server/client (host-tested). Full gRPC (the same framing but over HTTP/2 with application/grpc) remains gated on the HTTP/2 roadmap item above. Fixed BSS, no heap.PC_ENABLE_DDS (services/dds): the RTPS (DDSI-RTPS) message + submessage framing codec - the 20-octet header (magic / version / vendor / guidPrefix) and the typed submessages (INFO_TS, DATA, HEARTBEAT, ACKNACK, ...) with the endianness flag, built by pc_rtps_header / _submessage and walked by pc_rtps_parse; host-tested (native_dds). Remaining: the CDR serialized-payload encoding, SPDP/SEDP discovery, and the reliability/heartbeat reader-writer protocol + QoS subset (all zero-heap BSS); DDS-XRCE (the resource-constrained agent/client profile) is the more MCU-appropriate full target.PC_ENABLE_BACNET (services\bacnet): pc_bvlc_build / pc_bvlc_parse (the BACnet/IP virtual-link envelope - type 0x81, function, length) and pc_npdu_build / pc_npdu_parse (the network layer - version + NPCI control + optional DNET/DADR destination addressing + hop count, slicing the APDU); layout per ASHRAE 135 Annex J / Clause 6, host-tested. Remaining: the APDU application layer (the object model - Device / Analog-Input / Binary-Output / ... objects, properties, ReadProperty / WriteProperty / COV) and the BBMD foreign-device registration; then BACnet/SC (Secure Connect) reuses the shipped WebSocket + static-pool TLS for its BVLC-SC framing + the same APDU/object model. Fixed BSS object database, no heap.PC_ENABLE_XMPP (services/xmpp): the RFC 6120 stanza codec - correctly XML-escaped <stream:stream> / <message> / <presence> / <iq> builders and the stanza-name + attribute readers; host-tested against exact-output vectors (native_xmpp). Remaining: the streaming XML parser + the SASL/TLS handshake (TLS reuses the shipped client TLS) and the IoT XEPs (0030 disco, 0060 pub/sub, 0323 sensor data, 0325 control) on a fixed BSS roster/node model.PC_ENABLE_REDIS (services/redis_resp): a zero-heap pc_resp_encode_command() (array of bulk strings, binary-safe via explicit arg lengths - drives any command incl. SET/GET/HSET/XADD) + a cursor pc_resp_parse() reply decoder (simple / error / integer / bulk / array / nil; incomplete + malformed fail closed). Host-tested (test_redis_resp, 8 cases). Drive it over the shipped outbound client transport. Heavier candidates (MongoDB wire protocol, Postgres frontend/backend protocol) are larger and lower-priority. Fixed BSS, no heap, one flag per backend.PC_ENABLE_DSHOT (services/dshot): the DShot frame codec - pc_dshot_encode / _decode build + validate the 16-bit packet (11-bit throttle / command + telemetry-request + 4-bit nibble-xor CRC), with the special-command constants and the per-rate (150/300/600/1200) bit high-times for an RMT driver. Host-tested against hand-computed vectors (native_dshot). Remaining: the RMT pulse-train transport (the HW timing backend) and the HTTP/WS/dashboard throttle+arming control surface.pc_dshot_encode(..., bidirectional=true) (and validated by _decode). Remaining: decoding the ESC's GCR-encoded return frame (eRPM / temperature / voltage / current) off the same wire, so the server can stream live ESC telemetry (pairs with the telemetry-math + dashboard services).pc_dshot_encode, shipped) as four pulse-position symbols. Remaining: the nibble->pulse-position encoding + the RMT emission (the packet + CRC are done).pc_esc_pwm_ns() (services/dshot) maps a 0..1000 throttle to the pulse high-time for standard PWM (1-2 ms), OneShot125 (125-250 us), OneShot42 (42-84 us), and Multishot (5-25 us); host-tested (native_dshot). Remaining: driving it from the MCPWM peripheral synced to the control loop.PC_ENABLE_NTS (services/nts): the NTS-KE TLV record codec - pc_nts_ke_request builds the standard client request (Next Protocol NTPv4 + AEAD AES-SIV-CMAC-256 + End-of-Message, all critical) and pc_nts_ke_parse walks a response, surfacing each record (cookies, negotiated AEAD, server, port, error) via a callback; host-tested (native_nts). Remaining: running it over the static-pool mbedTLS client (ALPN ntske/1) + the RFC 5705 exporter key derivation (the label is exposed).services/nts also builds the NTS NTP extension fields (Unique Identifier, NTS Cookie, and the RFC 7822 4-byte-padded framing); host-tested. Remaining: the AEAD_AES_SIV_CMAC_256 (RFC 5297) authenticator protect/verify + the anti-replay unique-id echo + cookie rotation, then feed the validated offset to pc_clock.Settled questions, kept so they are not re-litigated.
Decided 2026-07-29. The name is ProtoCore and the rename is done. The library was previously DeterministicESPAsyncWebServer - literal, but a mouthful, and its "ESP" stopped being true the moment the multi-vendor work started. ProtoCore is accurate rather than merely shorter: what sits in the middle is protocol logic and nothing else, with silicon pushed out to board_drivers/ and platform shells out to examples/.
Shipped alongside it: the house prefix is pc_ / PC_ / PROTOCORE; the namespace question was settled in favor of one flat naming law (SYMBOLS.md); and every pre-ProtoCore token is gone from the tree.
The remainder of this section is kept as the decision record - the criteria applied and the candidates rejected. It is history, not an open action.
Candidates considered, not chosen:
Further candidates (short, on-theme - determinism / timing / solidity / structure):
Selection criteria when picking: short + easy to say/type; not colliding with a well-known language/framework or an active PlatformIO/Arduino library; a clean namespace/prefix; and a spare .io/.dev domain + a free GitHub org/repo. Decide alongside the STM32 backend landing so the name changes exactly when "ESP" stops being literally true, not before.