Version 1.2.0
The CFS Programmer communicates with the Mac app via Bluetooth Low Energy (BLE) using a simple text-based protocol. Commands are sent from the Mac app to the device (RX characteristic), and responses are sent from the device to the Mac app (TX characteristic via notifications).
4fafc201-1fb5-459e-8fcc-c5c9c331914b
- UUID:
1c95d5e3-d8f7-413a-bf3d-7a2e5d7be87e - Properties: Notify
- Purpose: Device sends responses and status updates
- Format: UTF-8 text strings
- UUID:
beb5483e-36e1-4688-b7f5-ea07361b26a8 - Properties: Write
- Purpose: Mac app sends commands
- Format: UTF-8 text strings
All commands and responses are case-sensitive ASCII strings.
Command:
GET_VERSION
Response:
VERSION:1.2.0
Notes:
- Version is also sent automatically when Mac app first connects
- Format:
MAJOR.MINOR.PATCH(Semantic Versioning)
Command:
READ
Immediate Response:
READY
Flow:
- Mac sends:
READ - Device responds:
READY - User places tag on reader
- Device detects tag:
UID:60ea1221(4-8 hex bytes, lowercase) - Device reads and processes
- Device sends one of:
TAG_DATA:PLA|330m|#FFFFFF|S/N:123456(success)BLANK_TAG(tag is blank/empty)ERROR:<message>(failure)
TAG_DATA Format:
TAG_DATA:<material>|<length>|#<color>|S/N:<serial>
Example:
TAG_DATA:PLA|330m|#9CFF4F|S/N:736314
Timeout:
- 30 seconds if no tag is presented
- Response:
ERROR:Timeout
Command:
WRITE:<cfs_data>
Where <cfs_data> is 48 characters of CFS data in ASCII hex format.
Example:
WRITE:ABC21120276A21010010FF5F0B0330736314000000000000000
Format Breakdown:
AB = Header (constant)
C2112 = Date code
0276 = Vendor (Creality)
A2 = Unknown/Batch
101001 = Film ID (PLA)
0FF5F0B = Color (#FF5F0B with leading 0)
0330 = Length (816m in hex)
736314 = Serial number
00000000000000 = Reserved (14 bytes)
Flow:
- Mac sends:
WRITE:<cfs_data> - Device responds:
WRITE_READY - Device waits for Tag 1
- User places Tag 1 → Device writes →
TAG1_WRITTEN - Device waits for Tag 2
- User places Tag 2 → Device writes →
TAG2_WRITTEN - Complete! Both tags have identical data
Timeout:
- 60 seconds per tag
- Response:
ERROR:Timeout
Command:
CANCEL
Response: None (device returns to idle)
Purpose:
- Abort read operation
- Abort write operation
- Clear error state
Command:
WIFI_CONFIG:<ssid>,<password>
Example:
WIFI_CONFIG:MyNetwork,MyPassword123
Response:
WIFI_OK
Notes:
- Credentials are stored in device RAM (not persistent)
- Required before OTA updates
- Device does NOT connect to WiFi immediately
- WiFi is only used when checking/installing updates
Command:
CHECK_UPDATE
Flow:
- Mac sends:
CHECK_UPDATE - Device connects to WiFi
- Device fetches:
https://api.github.com/repos/USERNAME/REPO/releases/latest - Device compares versions
- Device responds with one of:
UPDATE_AVAILABLE:1.3.0,https://github.com/.../firmware.binUP_TO_DATEERROR:WiFi not configuredERROR:WiFi connection failedERROR:GitHub API error
UPDATE_AVAILABLE Format:
UPDATE_AVAILABLE:<version>,<download_url>
Example:
UPDATE_AVAILABLE:1.3.0,https://github.com/user/repo/releases/download/fw-v1.3.0/firmware.bin
Timeout:
- 30 seconds for WiFi connection
- 10 seconds for HTTP request
Command:
OTA_UPDATE:<firmware_url>
Example:
OTA_UPDATE:https://github.com/user/repo/releases/download/fw-v1.3.0/firmware.bin
Flow:
- Mac sends:
OTA_UPDATE:<url> - Device connects to WiFi
- Device downloads .bin file (30-60 seconds)
- Device flashes firmware to OTA partition
- Device sends:
UPDATE_SUCCESS - Device reboots
- New firmware starts
- Device sends:
VERSION:1.3.0
Possible Responses:
UPDATE_SUCCESS(before reboot)ERROR:WiFi not configuredERROR:Download failedERROR:Not enough spaceERROR:Update failed
Update Time:
- ~30-60 seconds typical
- Device will disconnect/reconnect during reboot
| Response | Meaning |
|---|---|
READY |
Ready for tag operation |
WRITE_READY |
Ready to write tags |
TAG1_WRITTEN |
First tag written successfully |
TAG2_WRITTEN |
Second tag written successfully |
WIFI_OK |
WiFi credentials saved |
UP_TO_DATE |
Firmware is current |
UPDATE_SUCCESS |
OTA update completed |
| Response | Format | Example |
|---|---|---|
VERSION |
VERSION:<version> |
VERSION:1.2.0 |
UID |
UID:<hex> |
UID:60ea1221 |
TAG_DATA |
TAG_DATA:<mat>|<len>|#<col>|S/N:<ser> |
TAG_DATA:PLA|330m|#FFFFFF|S/N:123456 |
BLANK_TAG |
BLANK_TAG |
Tag is empty/blank |
UPDATE_AVAILABLE |
UPDATE_AVAILABLE:<ver>,<url> |
See above |
All errors start with ERROR: followed by description.
| Error | Cause |
|---|---|
ERROR:Auth failed - check library patch |
PN532 authentication timeout |
ERROR:Not a Creality CFS Tag |
Wrong tag type or corrupted data |
ERROR:Read failed on block X |
Failed to read MIFARE block |
ERROR:Timeout |
No tag presented within timeout |
ERROR:WiFi not configured |
Need to send WIFI_CONFIG first |
ERROR:WiFi connection failed |
Can't connect to network |
ERROR:GitHub API error |
HTTP request failed |
ERROR:Download failed |
OTA download error |
ERROR:Not enough space |
Insufficient flash for update |
ERROR:Update failed |
OTA flash operation failed |
The device operates in one of 4 states:
┌─────────────�
│ STATE_IDLE │ �─────────────────�
└──────┬──────┘ │
│ │
READ │ │
↓ │
┌─────────────────� │
│ STATE_READING │ │
│ (wait for tag) │ │
└────────┬────────┘ │
│ │
Tag detected │
│ │
Read & Parse ─────────────────┘
│
│
│
┌─────────────�
│ STATE_IDLE │
└──────┬──────┘
│
WRITE │
↓
┌─────────────────�
│ STATE_WRITING │
│ (Tag 1 of 2) │
└────────┬────────┘
│
Tag 1 written
│
↓
┌─────────────────�
│ STATE_WRITING │
│ (Tag 2 of 2) │
└────────┬────────┘
│
Tag 2 written ─────────────────�
│ │
└─────────────────────────┘
┌─────────────�
│ STATE_IDLE │
└──────┬──────┘
│
OTA_UPDATE
↓
┌─────────────────�
│ STATE_UPDATING │
│ (downloading) │
└────────┬────────┘
│
Flash & Reboot
│
↓
[Device Restarts]
Mac → Device: READ
Device → Mac: READY
[User places tag]
Device → Mac: UID:60ea1221
Device → Mac: TAG_DATA:PLA|330m|#FFFFFF|S/N:123456
Mac → Device: READ
Device → Mac: READY
[User places tag]
Device → Mac: UID:4abffc03
Device → Mac: BLANK_TAG
Mac → Device: WRITE:ABC21120276A21010010FFFFFF0330123456000000000000000
Device → Mac: WRITE_READY
[User places Tag 1]
Device → Mac: TAG1_WRITTEN
[User places Tag 2]
Device → Mac: TAG2_WRITTEN
Mac → Device: WIFI_CONFIG:MyWiFi,MyPassword
Device → Mac: WIFI_OK
Mac → Device: CHECK_UPDATE
[Device connects to WiFi and checks GitHub]
Device → Mac: UPDATE_AVAILABLE:1.3.0,https://github.com/.../fw.bin
Mac → Device: OTA_UPDATE:https://github.com/.../fw.bin
[Device downloads and flashes - 30-60 seconds]
Device → Mac: UPDATE_SUCCESS
[Device reboots]
[BLE reconnects]
Device → Mac: VERSION:1.3.0
If a command doesn't complete within the timeout:
Mac → Device: READ
Device → Mac: READY
[30 seconds pass, no tag]
Device → Mac: ERROR:Timeout
If tag authentication fails:
Mac → Device: READ
Device → Mac: READY
Device → Mac: UID:12345678
Device → Mac: ERROR:Auth failed - check library patch
If WiFi or download fails:
Mac → Device: CHECK_UPDATE
Device → Mac: ERROR:WiFi connection failed
- Maximum command length: 256 characters
- Maximum response length: 256 characters
- BLE MTU: 23 bytes (default) to 512 bytes (negotiated)
For long messages (like WRITE commands), BLE will automatically fragment.
[Mac starts scanning]
[Device advertises as "CFS-Programmer"]
[Mac connects]
[Device sends VERSION automatically]
Device → Mac: VERSION:1.2.0
[Mac sends commands as needed]
[Device responds immediately or when ready]
[User closes app or moves out of range]
[BLE connection drops]
[Device returns to advertising]
[Mac reconnects automatically]
[Device sends VERSION again]
Device → Mac: VERSION:1.2.0
No explicit rate limiting, but recommended:
- Minimum time between commands: 100ms
- Maximum commands per second: 10
- Wait for response before sending next command
Possible additions in future versions:
GET_STATUS → Returns device health info
SET_LED:<color> → Manually control LED
GET_WIFI_STATUS → Check WiFi connection
FACTORY_RESET → Reset to defaults
API version follows firmware version:
- v1.0.x: Basic read functionality
- v1.1.x: Added write support
- v1.2.x: Added OTA updates (current)
- v1.3.x: (future) Enhanced features
func sendCommand(_ command: String) {
guard let data = command.data(using: .utf8),
let rxChar = rxCharacteristic else { return }
peripheral.writeValue(data, for: rxChar, type: .withResponse)
}
func handleResponse(_ message: String) {
if message.hasPrefix("TAG_DATA:") {
let parts = message.components(separatedBy: "|")
// Parse material, length, color, serial
} else if message.hasPrefix("ERROR:") {
let error = message.replacingOccurrences(of: "ERROR:", with: "")
// Show error to user
}
}Use a BLE testing app like:
- LightBlue (iOS/macOS)
- nRF Connect (iOS/Android/Desktop)
Connect to service UUID and manually send commands to RX characteristic.
- Protocol Version: 1.2.0
- Compatible Firmware: v1.2.0+
- Compatible Mac App: v1.2.0+
- BLE Version: 4.0+