FEP 004 - FujiNetWIFI/fujinet-firmware GitHub Wiki
The FujiNet Protocol
Name | Value |
---|---|
FEP ID | 004 |
Title | FujiNet Protocol |
Author | FozzTexx |
Status | Draft |
Type | Informational |
Created | 2025-Oct-6 |
Version | 1.0 |
Motivation
As FujiNet expands beyond its original Atari and Apple implementations, maintaining consistency and interoperability across platforms has become increasingly difficult. Each platform currently implements the FujiNet protocol in slightly different ways, often resulting in duplicated codebases and redundant logic for handling the same commands. This fragmentation means that when a bug is fixed or a new feature is added on one platform, the change rarely propagates automatically to the others β every update must be manually ported, tested, and maintained separately. Over time, this has led to divergence between platforms and made it harder to ensure consistent behavior across all FujiNet devices.
The core reason for this duplication lies in how packets are structured and parsed. The existing protocol uses fixed headers and context-dependent payloads, requiring each command handler to read its own data in a custom way. This design forces per-platform command handling logic, making it difficult to share code, test features uniformly, or extend the protocol cleanly.
With the move toward a universal FujiNet architecture β modeled on the RS-232 version β this limitation becomes more apparent. Future FujiNet devices will use a Raspberry Pi RP2040 or RP2350 as the physical bus interface, with an ESP32 or host computer (Linux/Windows/macOS) managing device emulation and network communication. In this architecture, the bus interface and the device logic are no longer necessarily on the same processor, so serialization and deserialization efficiency are critical.
By making packets self-describing, each packet will indicate the sizes of its fields so that they can be fully decoded immediately upon receipt, without requiring custom parsing logic for each command.
- Generic decoding: Packets can be parsed into C/C++ structures or classes immediately upon receipt.
- Modular command handling: Common handling can be implemented once in a base class, while device-specific logic can extend it in subclasses.
- Code reuse: Shared parsing, debugging, and validation routines can be reused across all platforms.
- Protocol introspection: Tools can decode and inspect packets without needing hardcoded command definitions.
- Forward compatibility: Future extensions can introduce new fields or data types without breaking existing implementations.
Another important consideration in designing the new protocol is legacy compatibility. While the primary architecture uses a relatively powerful ARM processor (RP2040 or RP2350) to interface with the bus, the protocol is intended to be implementable directly on older, resource-constrained systems such as the 6502, 8080, or other 8 bit processor. This ensures that FujiNet remains accessible for lightweight implementations or situations where an ARM processor is not required, while still supporting more advanced features on modern hardware.
This approach moves FujiNet from a collection of per-platform implementations toward a unified, modular, and maintainable ecosystem, where new devices and hosts can be supported with minimal reimplementation.
Proposed Protocol
The new FujiNet protocol consists of two main parts:
- Packet Structure
- Packet Synchronization
1. Packet Structure
Each packet consists of a fixed-length header, a variable-length field section, and an optional binary blob. The length of the binary blob does not need to be separately encoded, since the packet has an overall length and the header specifies how many fixed-size fields are present.
Packet Header
Field | Size (bytes) | Description |
---|---|---|
Target Device | 1 | ID of the device |
Command ID | 1 | The command to execute |
Data Length | 2 | Total length of the data payload |
Checksum | 1 | Simple XOR or sum |
Data Descriptor | 1 | Byte encoding the data fields |
Data Descriptor Byte
The data descriptor byte defines the layout of the fixed-size fields in the data payload:
- Bits 7-4 (Type Bitmask): Each bit corresponds to one of the first four fields.
1
indicates a 16-bit field,0
indicates an 8-bit field. - Bit 3 (32-bit Flag): If set, the payload consists of 32-bit fields. This flag is mutually exclusive with the type bitmask.
- Bit 2 (More Descriptors): If set, the following byte is an additional field descriptor byte. Multiple descriptor bytes may be used in a packet.
- Bits 1-0 (Field Count): Number of fixed-size fields in this descriptor (1β4).
Notes on the descriptor design:
- Field types occupy the high bits for easy manipulation with carry bits on minimalist CPUs (e.g., 6502).
- Field count is in the low bits to allow simple OR operations when constructing the byte.
- When the 32-bit flag is set, the count field still applies, but all fields are treated as 32-bit.
- The "more descriptors" bit allows chaining descriptor bytes for packets with many fields. There is no hard limit, but packets should generally remain under 512 bytes to accommodate the constraints of the target systems.
- Certain bit combinations are reserved for future use or expansion.
Endianness
All multi-byte fields in the FujiNet protocol are transmitted in little-endian byte order.
We agree that little-endian is superior to big-endian.
2. Packet Synchronization
In the existing protocol, packet boundaries were indicated using an out-of-band signal via an extra pin. This approach is not universally supported, and it does not work well with network or USB virtual serial interfaces.
The new protocol replaces this with SLIP-style encoding, which frames packets and allows reliable transmission over any byte stream. SLIP uses special values to mark packet boundaries and escape certain bytes:
Description | Hex Value | Dec Value | Oct Value | Abbreviation | Notes |
---|---|---|---|---|---|
Frame End | 0xC0 | 192 | 300 | END | Marks the start and end of a packet |
Frame Escape | 0xDB | 219 | 333 | ESC | Indicates that the next byte is a transposed value |
Transposed Frame End | 0xDC | 220 | 334 | ESC_END | Replaces a data byte of 0xC0 |
Transposed Frame Esc | 0xDD | 221 | 335 | ESC_ESC | Replaces a data byte of 0xDB |
SLIP rules:
- Packets start and end with
END
(0xC0)`. - Any occurrence of
END
(0xC0) in the packet payload is replaced withESC
(0xDB) followed byESC_END
(0xDC). - Any occurrence of
ESC
(0xDB) in the packet payload is replaced withESC
(0xDB) followed byESC_ESC
(0xDD). - SLIP encoding is applied after the packet has been fully constructed.
- SLIP decoding is applied immediately upon reception, before processing the packet.
All reply data from FujiNet devices is also SLIP-encoded, regardless of whether it uses the full FujiNet packet format or raw payloads. This guarantees consistent framing across all transports and communication directions.
Note: Encoding and decoding are separate from the packet structure itself. This allows transports that already provide start/stop signaling to use the same packet format without modification.
Open Questions and Considerations
While the main protocol design is largely defined, there are several issues that remain under consideration:
1. Returning Data to the Legacy System
When the FujiNet has data to send back to the legacy system:
- Should data be wrapped in a full FujiNet packet (with header, descriptor, and checksum) or sent as a SLIP-encoded payload only, without the additional packet structure?
- If a packet is used, what is the meaning of the Device ID and Command ID?
- Do they indicate the original command being replied to, or a special βreplyβ type?
2. Signaling Data Availability to Legacy Computers
The protocol is command/response, but legacy systems may need to be notified when data or events are waiting (such as incoming network packets, errors, or connection closures). Challenges include:
- Many older systems have at best a single-byte serial buffer, and some may not support interrupts to process incoming bytes fast enough.
- Systems with no buffer at all cannot receive any data over serial unless they are signaled in advance. On RS232, this can be handled with an out-of-band signal line (e.g., Ring Indicator), but this approach does not generalize to all buses.
- How should the protocol provide a cross-platform method for alerting legacy computers to pending data without relying on polling or hardware-specific signals?
3. Direct Communication with the RP2nnn
There may be occasions when the FujiNet needs to interact with the RP2nnn directly, such as:
- Resetting the RP2nnn to reload the initial boot program
- Uploading new firmware
Questions include:
- Should there be a special device ID in the packet header that the RP2nnn filters out?
- Potential issue: the RP2nnn would need to monitor data it normally just passes through.
- Should the RP2nnn instead expose a second virtual serial interface (e.g.,
/dev/ttyACM2
) on the ESP32 or host system to handle this kind of communication?
4. Handling Cases Without an RP2nnn
To keep the protocol generic:
- How should the FujiNet behave when no RP2nnn is involved?
- Does the FujiNet need to detect the absence of an RP2nnn, or can it safely ignore the issue?
- How can the protocol remain flexible for both simple systems (without RP2nnn) and full-featured setups?
Next Steps
The protocol design outlined here captures the current thinking and rationale for a self-describing FujiNet protocol. There are several open questions (see above) that require further discussion or experimentation. Feedback, suggestions, and alternative approaches are welcome, particularly regarding:
- Signaling pending data to legacy systems
- Direct RP2nnn communication methods
- Handling systems without an RP2nnn
- Reply packet structure and semantics