Falcon A1C with the Creality Falcon HTTP flavour: what works, what breaks, and why
My setup: Creality Falcon A1C (5 W), firmware 1.1.2 (CV40mini), LightBurn 2.2.00-RC-5 on macOS, Custom GCode device using the “Creality Falcon HTTP” flavour over Wi-Fi.
Working: connecting and identifying the machine, state polling, jogging, Home, Z Focus (/work/probe), file upload, start and stop, and the camera added as a network camera at http://<laser>:8080/media/getCapturePhoto. Lens calibration and workspace alignment both work with it.
Three things don’t work. I pulled the laser’s HTTP replies and its firmware log (GET /work/download?file=new_log.log) to find out why. Log lines and the fixes are at the bottom.
1. “Transfer failed” on Start and Frame, even though the file arrived
The A1C replies to POST /work/upload?action=upload with:
{"errorcode":0,"payload":{"desc":"File uploaded successfully"}}
There is no payload.result in that reply. The driver’s webapi_creality_falcon_v1.json reads result with a lacuna of -1 and treats anything other than 0 as an error, so every upload is reported as failed and /work/start is never sent. The file is on the laser (GET /work/download?file=tmp.gcode returns it), and sending GET /work/start by hand runs it fine.
2. The A1C rejects inline comments, and that breaks jobs with relative moves
The flavour writes G21;Restore metric mode and G90;Restore absolute mode. The A1C’s parser logs
gc_execute_line err:1 line:G90;RESTOREABSOLUTEMODE
and throws away the whole line. The camera alignment pattern ends its G91 block with that G90. With the G90 gone, the machine stays in relative mode, the next absolute moves drive it into the Y limit switch (alarm 01001601), and the job stops at about 50 %. Falcon Design Space only writes comments on their own lines, which is why its files run.
3. Frame never stops
GET /work/preview loops until /work/stop is sent. The firmware frames once only if the file’s header contains ; Frequency: single. Falcon Design Space writes ; Frequency: cycle or single in every file. With that line added to a frame file, the A1C traces the box once and goes back to idle in about 5 seconds.
Two smaller notes. POST /work/home with a JSON body homes X and Y; it is useful after a job because the A1C parks the head in the middle of the bed, right in front of the fixed camera. Also, the camera overlay is only accurate at the autofocused height. The alignment wizard let me burn the pattern on an unfocused bed, and the resulting overlay was off by 19 mm until I redid the alignment after focusing. A note in the docs would save others the same hour.
I am running all three fixes in a small HTTP proxy between LightBurn and the laser. With them in place the A1C works end to end from LightBurn: frame, engrave, and camera placement checked on wood. I can send logs or test builds.
Firmware log excerpts (laser clock is UTC)
- Upload reply with no
result:
I (20261008:21:16:53.853) http servser: action=upload
I (20261008:21:16:53.853) http servser: Content-Type=application/octet-stream
I (20261008:21:16:53.860) http servser: Start receiving file, size=38583
I (20261008:21:16:54.124) http servser: File upload finished: /sdcard/tmp.gcode (38583 bytes)
I (20261008:21:16:54.125) http servser: HTTPD_JSON_RESP : {"errorcode":0,"payload":{"desc":"File uploaded successfully"}}
- The inline comment rejected during the alignment pattern, then the Y limit:
I (20261008:21:25:24.942) Log: [E][./main/Grbl/protocol.cpp][protocol_main_loop][1130]Sgc_execute_line err:1 line:G90;RESTOREABSOLUTEMODE
I (20261008:21:25:32.375) limits: Y-axis hardware limit bit trigger
I (20261008:21:25:32.375) device_info: STATE_ALARM >>> <13, 0>
I (20261008:21:25:32.401) device_info: ----------device_alarm--------- errorLevel:1, errorCode:01001601
I (20261008:21:25:32.761) http servser: HTTPD_JSON_RESP : {"errorcode":0,"payload":{"state":4}}
Position after the abort was <103.238,152.250,...> on a 150 mm bed, and /work/progress read 0.504686. The same file with the comment tails removed (G90 alone on its line) ran to 100 % with no alarm.
- Framing. Falcon Design Space’s file, which has
; Frequency: single in the header:
I (20261007:22:46:27.071) preview: exit PreviewTask when mode is sigle...
LightBurn’s frame file, which has no such line, cycles until stopped:
I (20261008:22:07:42.853) preview: Lvie preview file eof...
I (20261008:22:07:47.134) preview: Lvie preview file new clcye begin...
I (20261008:22:07:47.136) Log: [I][./main/user_app/preview.cpp][LivePreviewTask][277]SPreviewTask END, duto user stop preview
Falcon Design Space’s job header on this machine, for comparison:
; Falcon Design Space 1.10.7
; GRBL device profile, absolute coords
; Bounds: X24.2461 Y70.2744 to X100.0547 Y108.6744
; Feed speed: F6000.00
; Laser type: 5W Diode
; materialInfo: 0.65mm Brown leather
; Processing Mode: Plane Engraving
Fixes
1. webapi_creality_falcon_v1.json, upload_file.response.extract. The A1C never sends result on upload, so a missing value should count as success (or success should be keyed off errorcode):
{"name": "result", "path": "payload.result", "lacuna": 0},
{"name": "resultStatus", "path": "payload.result", "lacuna": "ok",
"value_map": {"0": "ok", "default": "error"}}
2. G-code output for the creality_falcon flavour. No comment on the same line as a command. Today that is G21;Restore metric mode and G90;Restore absolute mode, in the header and again after every G91 block. Put the comment on its own line or leave it out.
3. Frame jobs for this flavour. Make the first line ; Frequency: single. Checked on firmware 1.1.2: one pass, then idle.
This is how my proxy does 2 and 3 (Python, standard library):
FRAME_HEADER = "; Frequency: single"
def sanitise(text):
out = []
for line in text.splitlines():
if line.startswith(";") or not line.strip():
out.append(line); continue # comment lines are fine on their own
if ";" in line:
line = line.split(";", 1)[0].rstrip() # 'G90;Restore absolute mode' -> 'G90'
out.append(line)
fixed = "\n".join(out) + "\n"
powered = any(int(m) > 0 for m in re.findall(r"S(\d+)", fixed))
if not powered and FRAME_HEADER not in fixed: # no S above 0 anywhere = a frame job
fixed = FRAME_HEADER + "\n" + fixed
return fixed
The proxy also adds payload.result = 0 to the upload reply when it is missing, and sends POST /work/home once a started job returns to idle. The full file is about 130 lines; I can post it if it helps.