-
Notifications
You must be signed in to change notification settings - Fork 0
Discovery Protocol
FlashForge printers use UDP-based discovery protocols to announce their presence on the network. A robust client implementation must handle both modern and legacy protocols to support the full printer fleet.
| Family | Protocol Type | Ports | Packet Size | Key Identifier |
|---|---|---|---|---|
| Modern (5M / 5M Pro / AD5X) | UDP Broadcast & Multicast | 19000 (Multi), 48899 (Broad) | 276+ bytes | Serial Number (Offset 0x92) |
| Legacy (A3 / A4 Pro) | UDP Multicast | 8899 | 140 bytes | Machine Name (Offset 0x00) |
Multicast Group: 225.0.0.9
Modern printers listen on both multicast and broadcast addresses:
| Type | Address | Port |
|---|---|---|
| Multicast | 225.0.0.9 | 19000 |
| Broadcast | 255.255.255.255 | 48899 |
Probe: Send any UDP packet (payload ignored) to either address.
Response: Fixed-size binary packet (minimum 276 bytes).
Total Size: 276 bytes minimum (Big Endian)
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x00 | 132 | char[] | Machine Name (null-terminated) |
| 0x84 | 2 | uint16 | Command Port (TCP) - typically 8899 |
| 0x86 | 2 | uint16 | VID - typically 0x2B71 |
| 0x88 | 2 | uint16 | PID - 0x0024 or 0x0026 |
| 0x8A | 2 | uint16 | Reserved |
| 0x8C | 2 | uint16 | Product Type (e.g., 0x5A02) |
| 0x8E | 2 | uint16 | HTTP/Event Port - typically 8898 |
| 0x90 | 2 | uint16 | Status (0=Ready, 1=Busy, 2=Error) |
| 0x92 | 130 | char[] | Serial Number (null-terminated) |
Packets with length >= 196 bytes (0xC4) should be parsed as modern protocol.
Legacy printers listen on multicast only:
| Type | Address | Port |
|---|---|---|
| Multicast | 225.0.0.9 | 8899 |
Probe: Send any UDP packet to the multicast address.
Response: Fixed-size binary packet (140 bytes).
Total Size: 140 bytes (Big Endian)
| Offset | Size | Type | Description |
|---|---|---|---|
| 0x00 | 128 | char[] | Machine Name (null-terminated) |
| 0x80 | 2 | uint16 | Status Code |
| 0x82 | 2 | uint16 | TCP Port - typically 8899 |
| 0x84 | 2 | uint16 | Secondary Port - often 8 |
| 0x86 | 2 | uint16 | HTTP Port (Camera) - 19000 or 8080 |
| 0x88 | 4 | bytes | Unknown/Padding |
Packets with length == 140 bytes should be parsed as legacy protocol.
The legacy packet does not contain the Serial Number. To retrieve the SN for unique identification, you must:
- Connect via TCP to port 8899
- Send
~M115 - Parse the Serial Number from the response
To auto-discover any FlashForge printer:
Bind a UDP socket capable of receiving from multiple interfaces.
Send discovery probes to all known addresses:
225.0.0.9:19000 (Modern Multicast)
255.255.255.255:48899 (Modern Broadcast)
225.0.0.9:8899 (Legacy Multicast)
Parse responses based on packet length:
Packet Length >= 196 --> Modern Protocol
Packet Length == 140 --> Legacy Protocol
For legacy printers, initiate a TCP connection to retrieve the serial number.
import dgram from 'dgram';
interface DiscoveredPrinter {
family: 'Modern' | 'Legacy';
name: string;
serialNumber: string | null;
ip: string;
tcpPort: number;
apiPort: number;
}
function parseDiscoveryPacket(packet: Buffer, rinfo: { address: string }): DiscoveredPrinter | null {
if (packet.length >= 196) {
// Modern Protocol
const name = packet.toString('utf8', 0, 132).replace(/\0/g, '').trim();
const serialNumber = packet.toString('utf8', 0x92, 0x92 + 130).replace(/\0/g, '').trim();
const tcpPort = packet.readUInt16BE(0x84);
const httpPort = packet.readUInt16BE(0x8E);
return {
family: 'Modern',
name,
serialNumber,
ip: rinfo.address,
tcpPort: tcpPort || 8899,
apiPort: httpPort || 8898
};
} else if (packet.length === 140) {
// Legacy Protocol
const name = packet.toString('utf8', 0, 128).replace(/\0/g, '').trim();
const tcpPort = packet.readUInt16BE(0x82);
const cameraPort = packet.readUInt16BE(0x86);
return {
family: 'Legacy',
name,
serialNumber: null, // Requires TCP M115 query
ip: rinfo.address,
tcpPort: tcpPort || 8899,
apiPort: cameraPort || 8080
};
}
return null;
}
async function discoverPrinters(timeout: number = 5000): Promise<DiscoveredPrinter[]> {
const socket = dgram.createSocket('udp4');
const printers: Map<string, DiscoveredPrinter> = new Map();
return new Promise((resolve) => {
socket.on('message', (msg, rinfo) => {
const printer = parseDiscoveryPacket(msg, rinfo);
if (printer && !printers.has(printer.ip)) {
printers.set(printer.ip, printer);
}
});
socket.bind(() => {
socket.setBroadcast(true);
socket.addMembership('225.0.0.9');
// Send probes
const probe = Buffer.from([]);
socket.send(probe, 19000, '225.0.0.9');
socket.send(probe, 48899, '255.255.255.255');
socket.send(probe, 8899, '225.0.0.9');
// Wait for responses
setTimeout(() => {
socket.close();
resolve(Array.from(printers.values()));
}, timeout);
});
});
}| Field | Modern (276+ bytes) | Legacy (140 bytes) |
|---|---|---|
| Machine Name | Yes (offset 0x00) | Yes (offset 0x00) |
| Serial Number | Yes (offset 0x92) | No (requires TCP) |
| TCP Port | Yes (offset 0x84) | Yes (offset 0x82) |
| HTTP Port | Yes (offset 0x8E) | Camera only (offset 0x86) |
| Status | Yes (offset 0x90) | Yes (offset 0x80) |
| VID/PID | Yes (offsets 0x86, 0x88) | No |
| Product Type | Yes (offset 0x8C) | No |
- Verify the printer is powered on and connected to the network
- Check firewall settings allow UDP traffic on discovery ports
- Ensure the client is on the same network segment as the printer
- Try direct IP connection if discovery fails
A single printer may respond to multiple probe addresses. Use the IP address or serial number to deduplicate responses.
For legacy printers, always fetch the serial number via TCP ~M115 before caching or displaying printer details. The machine name alone may not be unique across multiple printers.