Skip to content

Bed Level Map

Daniel Heinen edited this page May 9, 2026 · 1 revision

Bed Level Map

The Bed Level Map tool reads the printer's bilinear compensation grid and renders it as a heatmap, so you can see at a glance which corners are high or low and confirm an auto-leveling cycle improved the bed flatness.

What it shows

The AnkerMake M5 firmware stores a 7×7 (49-point) bilinear bed compensation grid. Each cell is a Z offset in millimeters, applied during printing to compensate for tilt and warp.

ankerctl queries the grid by sending the Marlin command M420 V, parses the BL-Grid-* lines from the response, and returns:

{
  "grid": [[...], [...], ...],
  "min": -0.18,
  "max":  0.21,
  "rows": 7,
  "cols": 7
}

The web UI renders this as a heatmap: blue = low (negative Z offset), red = high (positive Z offset). You can also keep "before" and "after" snapshots in localStorage to compare the impact of an autolevel cycle.

Where to find it

Setup → Tools → Bed Level Map

Buttons:

  • Read from printer — sends M420 V, parses the response, renders the heatmap (~15 seconds)
  • Save snapshot — persists the current grid to localStorage as "before" (or "after" if "before" already exists)
  • Compare — side-by-side heatmaps of before vs. after
  • Load last — load the most recent saved grid from disk (bed_leveling/YYYYMMDD_HHMMSS.bed) without contacting the printer

How to use

  1. Heat up the bed and nozzle (the firmware needs warm temps for accurate probing — M104 S60, M140 S60)
  2. Run G29 once via the Home page Autolevel button or the touchscreen
  3. Open Setup → Tools → Bed Level Map
  4. Click Read from printer — wait ~15 seconds for the response
  5. Inspect the heatmap; click Save snapshot to record this state
  6. Make a physical adjustment (knob turn, screw twist, mesh re-cal)
  7. Re-run G29 and Read from printer again
  8. Compare to see whether the change improved or worsened flatness

Important Do not call Read from printer during an active print. The M420 V command sends ~320 bytes back to the printer and triggers a brief pause that can corrupt the live print. The button is disabled while a print is active, but the underlying API endpoint (GET /api/printer/bed-leveling) accepts the call regardless — be careful when scripting.

API endpoints

Method Path Description
GET /api/printer/bed-leveling Sends M420 V, parses the response, returns the grid (~15 s)
GET /api/printer/bed-leveling/last Returns the most recently saved grid from disk
POST /api/printer/autolevel Starts G29 (returns immediately; track progress via ct=1007)

Live progress while G29 is running:

  • MQTT ct=1007 value field = current probe index (50 total: 1 center + 7×7)
  • The web UI polls and shows 42/50 progress next to the Autolevel button

On-disk storage

Each successful M420 V response is saved to ANKERCTL_LOG_DIR/bed_leveling/YYYYMMDD_HHMMSS.bed. The format is plain JSON (the same shape the API returns), so you can diff snapshots manually or visualize them externally:

ls /logs/bed_leveling/
# 20260501_093215.bed  20260507_142003.bed  20260509_181122.bed

cat /logs/bed_leveling/20260509_181122.bed | jq '.grid[0]'

GET /api/printer/bed-leveling/last returns the most recent .bed file contents.

Reading the heatmap

Color Meaning
Deep blue Bed is low here — printer adds Z to compensate
Cyan Slightly low
White / pale Near zero offset (ideal)
Yellow Slightly high
Deep red Bed is high here — printer reduces Z to compensate

A perfectly flat bed shows a uniform pale-yellow / white heatmap with min and max values both close to 0. Real-world beds typically span ±0.1 mm to ±0.3 mm.

If you see a pronounced gradient (blue on one side, red on the other), the bed is tilted — adjust the leveling screws under that corner.

If you see a dome or bowl (low or high in the center, opposite at the corners), the bed is warped — bilinear compensation handles it as long as the deflection is below ~0.5 mm. Beyond that, mechanical correction (a glass/PEI replacement, leveling screws) is needed.

Performance notes

  • M420 V returns ~320 bytes of data, which exceeds a single MQTT packet — the response is collected via mqtt_gcode_dump() over a window
  • The default collect window is 3 seconds; very busy MQTT sessions may need 5 s (set in code; not currently exposed as a config flag)
  • The full read takes ~15 seconds end-to-end because the firmware itself takes time to format the grid

Limitations

  • The grid is read-only through this tool — it cannot be edited or restored
  • Anker has stripped the EEPROM dump command (M503 returns only ok), so per-cell mesh values can be inspected but not exported in firmware-native format
  • The autolevel sequence (G29) cannot be customized — it always probes the 49 grid points

Troubleshooting

Read times out or returns empty

  • Confirm MQTT is connected (navbar dot is green)
  • Try Read from printer again — Anker firmware occasionally drops a single response
  • Bump the collect window in cli/mqtt.py (mqtt_gcode_dump(..., collect_window=5.0)) if it consistently fails

Grid is all zeros

  • The printer has never been auto-leveled — run G29 first
  • The bed is genuinely flat (uncommon but possible)

Grid changes between reads without re-leveling

  • The compensation grid is calculated at print time from the bed sensor — reading it back should be deterministic
  • Small variation (≤0.01 mm) is normal — the firmware quantizes Z offsets to 0.005 mm increments

"Load last" shows no data

ANKERCTL_LOG_DIR is unset (no on-disk persistence) or no successful read has happened yet. Set the env var and read at least once.

Clone this wiki locally