Skip to content

tlf30/lumenpnp-mods

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LumenPnP Addons — ESP32 Force + Laser Z Probe

PlatformIO firmware for an ESP32 addon that plugs into OpenPnP as a USB serial GcodeDriver. It provides two Z-sensing methods on one board:

Sensor Method OpenPnP use
HX711 + load cells Contact / force G38.2 in-bed Z touch-off
VL53L4CX laser Non-contact distance M212 height read → surface Z via OpenPnP delta math

Serial is 115200 baud. Successful commands end with ok.


Installation

Everything needed to wire the hardware, verify sensors, flash firmware, and reach the diagnostics webpage.

Prerequisites

  • ESP32-WROOM-32 dev board (USB-UART bridge, e.g. CP2102 → often /dev/ttyUSB0 on Linux)
  • PlatformIO (install guide)
  • Adafruit HX711 breakout (#5974) + two 5kg 4-wire bar load cells (e.g. Amazon B09VYSHW16)
  • Adafruit VL53L4CX ToF sensor (#5425), mounted on the tool head pointing down

Clone or open this project, then use the commands below from the repo root.

Pin map

Defined in include/pins.h. On boards labeled D##, use the silkscreen labels (D16 = GPIO 16, etc.).

Function GPIO Connect to
HX711 DATA 16 (D16) Adafruit DATA
HX711 SCK 17 (D17) Adafruit SCK
Status LED 2 On-board LED (optional)
I2C SDA 21 (D21) VL53L4CX SDA
I2C SCL 22 (D22) VL53L4CX SCL
VL53 XSHUT 18 (D18) VL53 XSHUT (required)
VL53 GPIO 23 (D23) VL53 GPIO (data-ready, recommended)

HX711 + load cell wiring

Split power (important)

The Adafruit HX711 has two supply pins:

Adafruit pin Connect to Purpose
VCC ESP32 5V Load cell excitation (E+/E-)
VDD ESP32 3.3V Digital logic (DATA/SCK levels)
GND GND

Leave the VIO solder jumper on the back open (default split-supply mode).

Set the board RATE switch to L (10 SPS) for probing — less noise than 80 SPS.

Digital lines

Adafruit ESP32
DATA D16
SCK D17

Two cells in parallel (Channel A)

Wire both cells the same way into the 6-pin terminal:

Cell wire (typical) Terminal
Red E+
Black E-
Green A+
White A-

Leave B+/B- empty. Test with one cell first; add the second once M114 responds to force.

Mechanical: Fix the mounting-hole end rigidly; apply downward force on the sensing end. Do not press the rubber boot in the middle of the beam.

VL53L4CX wiring

VL53L4CX ESP32
VIN 3.3V
GND GND
SDA D21
SCL D22
XSHUT D18
GPIO D23

I2C address: 0x29 (7-bit) / 0x52 (8-bit write form used by the driver). Firmware uses Medium distance mode (VL53L4CX does not support Short mode).

STEMMA QT cables carry the same signals if you prefer a harness to the head.

Z laser mount (3D print)

Mount the VL53L4CX on the tool head so the laser points straight down, offset from the top camera along head Y (match the LaserZ head offset in OpenPnP — e.g. 30 mm ahead of the camera center).

Print 3d_models/z_laser_mount.stl and attach the VL53L4CX as shown:

Z laser mount — printed part

Z laser mount — installed on LumenPnP head

Verify wiring (test firmware)

Flash the dedicated test sketches before the full firmware when bringing up new hardware.

VL53L4CX test:

pio run -e esp32dev_vl53_test -t upload --upload-port /dev/ttyUSB0
pio device monitor --port /dev/ttyUSB0 --baud 115200

Expect VL53L4CX DETECTED! and live distance readings. Serial: r (read), x (XSHUT reset), ? (help).

HX711 test:

pio run -e esp32dev_hx711_test -t upload --upload-port /dev/ttyUSB0
pio device monitor --port /dev/ttyUSB0 --baud 115200

Expect HX711 DETECTED! and changing gram values when you press the pad. Serial: t (tare), c -2150 (cal factor), ? (help).

Build and flash production firmware

pio run -e esp32dev -t upload --upload-port /dev/ttyUSB0

If upload fails (USB busy or CP2102 quirks), close OpenPnP/Java serial users and try a slower speed in platformio.ini (upload_speed = 115200).

Serial monitor:

pio device monitor --port /dev/ttyUSB0 --baud 115200

Typical boot output:

LumenPnP-Addon starting (HX711 + VL53L4CX)
Reset reason: 1
HX711 ready.
Send M115 for info. Ready for OpenPnP GcodeDriver.
ok
Multicore: serial/probe on core 1, laser+web on core 0

If WiFi credentials are stored in flash, you will also see DHCP IP and Web server started on port 80. (Reset reason: 1 is a normal power-on reset.)

WiFi and diagnostics webpage

WiFi credentials are not hardcoded. Configure once over serial (or the web G-code console after first connect):

M710 S"YourNetwork" P"YourPassword"
Command Action
M710 Report SSID, configured flag, connection state, IP
M710 S"<ssid>" Set SSID (quote if it contains spaces)
M710 P"<password>" Set password
M710 R Reconnect with saved credentials
M710 C Clear saved WiFi from flash

Credentials are stored in NVS and survive reboot. mDNS hostname: http://lumen-probe.local (same LAN).

Web UI:

URL Purpose
/ Live diagnostics — force, laser, 10s trends, histogram, datalog, G-code tester
/settings Edit and save probe/laser settings to flash

Hard-refresh the browser (Ctrl+Shift+R) after firmware updates — the HTML is embedded in the binary.

Installation checklist

  • HX711 split power: 5V → VCC, 3.3V → VDD, GND common
  • Load cells on Channel A; force changes M114 / test firmware grams
  • Z laser mount printed and VL53 mounted on head (see Z laser mount)
  • VL53 XSHUT wired; test firmware shows live mm
  • Production firmware flashes; M115 responds
  • M710 WiFi configured; diagnostics page loads
  • OpenPnP LumenPnP-Addon driver + LaserZ head actuator configured (see Configuration below)

Configuration

How to calibrate sensors, tune laser/OpenPnP math, and wire actuators in OpenPnP.

OpenPnP architecture (two drivers)

The addon uses a second GcodeDriver on its own USB serial port. Your main GcodeDriver (Marlin on the LumenPnP motion controller) still handles all XY/Z motion. The addon driver only sends sensor commands (M212, G38.2, M600, etc.) — it never moves the machine.

Driver Port (example) Role
GcodeDriver ttyACM1 Marlin motion, vacuum, LEDs
LumenPnP-Addon ttyUSB0 HX711 force probe + VL53 laser

This matches a working LumenPnP machine.xml setup: one ESP32 serial device, one motion-controller serial device.

Add the LumenPnP-Addon driver

Machine Setup → Drivers → Add GcodeDriver — name it LumenPnP-Addon (or similar) and point it at the ESP32 port.

Setting Value Why
Communications Serial ESP32 USB-UART
Port ttyUSB0 (Linux) / your COM port Addon board
Baud 115200 Firmware default
Command confirm regex ^ok.* Every command ends with ok
Connect command M115 ; LumenPnP addon ready check Confirms firmware alive after USB reset
Connect wait 1500 ms ESP32 reboots when USB opens
Timeout 10000 ms M212 can take a few seconds
Sync initial location Off Addon has no machine coordinates
Allow unhomed motion On Sensor reads work without homing
Visual homing Off Not applicable
Units Millimeters

Leave feed rate / acceleration send-on-change disabled — this driver does not issue moves.

Virtual camera Z axis (zCamera)

Laser height probing in OpenPnP uses a virtual Z axis, not the physical nozzle Z screw.

Machine Setup → Axes → Add axis:

Field Value
Type ReferenceVirtualAxis
Name zCamera (or similar)
Axis type Z

The Top down-looking camera and the LaserZ actuator both bind to this virtual Z axis (axis-Z-idzCamera). OpenPnP tracks a separate “camera/laser height” coordinate that does not command Marlin during a laser probe read.

Set the Top camera Default Z to your normal focus height (e.g. 7.18 mm in a calibrated LumenPnP config). That value becomes LzRef in the addon firmware — the virtual Z OpenPnP reports for LaserZ at the teach pose.

LaserZ actuator (on the Head)

Create the laser actuator under the Head, not under machine-level actuators.

Machine Setup → Heads → H1 → Actuators → Add

Field Value
Name LaserZ
Driver LumenPnP-Addon
Value type Double
Axis Z zCamera (virtual axis)
Coordinated before actuate Wait for stillstand
Coordinated before read Wait for stillstand
Head offsets Set Y to the laser offset from the camera center in head coordinates (e.g. 30.0 mm if the VL53 is 30 mm ahead of the top camera on the Y axis). X/Z as measured.

Then set the head’s Z probe actuator:

Machine Setup → Heads → H1 → General → Z probe actuator: LaserZ

OpenPnP will use LaserZ for board/feeder Z probing scripts instead of driving the nozzle into the pad.

Driver mappings for LaserZ

On the LumenPnP-Addon driver, under Actuator read for LaserZ:

Mapping G-code / regex
Actuator read command M212
Actuator read regex (?i)Laser:(?<Value>[-\d.]+)

Example response (delta mode, K taught):

Laser:0.125
; raw 42 refined 41 corrected 41 samples 9/9 sigma 0.15 peak R12 centroid 12.34
ok

If K is not calibrated, M212 returns Laser:-1 with an error comment.

How OpenPnP uses the laser reading

With delta mode on (firmware default), M212 returns a delta, not raw standoff mm:

capturedZ = actuatorLocationZ + M212_reading

where:

  • actuatorLocationZ — current zCamera virtual Z in OpenPnP (e.g. 7.18 at safe focus)
  • M212_readingsurfaceZ − LzRef from the firmware

So at the teach pose, Laser: should read near zero. When the board is lower, the delta goes negative; when higher, positive. Raw standoff and surface Z are still on the diagnostics webpage and in M114 / /status JSON.

flowchart LR
  subgraph motion [GcodeDriver - Marlin]
    XY[XY jog to probe point]
    Zcam[zCamera virtual Z at focus]
  end
  subgraph addon [LumenPnP-Addon - ESP32]
    M212[M212 laser read]
    delta["Laser: surfaceZ − LzRef"]
  end
  XY --> Zcam
  Zcam --> M212
  M212 --> delta
  delta --> capture["OpenPnP capturedZ = zCamera + delta"]
Loading

Force probe actuators (machine-level)

Force touch-off uses machine-level actuators on the same LumenPnP-Addon driver.

Machine Setup → Actuators → Add

ForceZProbe (Double):

Mapping Value
Driver LumenPnP-Addon
Actuator read command G38.2 Z-80 F80
Actuator read regex (?i)PRB:(?<Value>[-\d.]+)

Firmware arms the load cell. On contact:

Probe triggered!
PRB:0.000
ok

Tune Z-80 F80 and M600 S<grams> threshold for your mechanics. Use this actuator in nozzle tip calibration or custom scripts that need a physical touch.

ForceZTare (Boolean) — optional convenience actuator:

Mapping Value
Actuate boolean command {True:M600}{False:M999}

M600 tares the scale; M999 resets probe state.

Useful maintenance commands (serial or web G-code console):

Command Purpose
M600 Tare (zero) the scale
M600 S25 Set trigger threshold to 25 g (runtime; default 30 g)
M600 I1 / M600 I0 Invert sign on/off (default on: compression = positive g)
M114 Live grams, threshold, probe state, last laser mm
M999 / M112 Abort / reset probe state

Example machine.xml excerpts

These fragments are taken from a working LumenPnP OpenPnP config (~/.openpnp2/machine.xml). IDs (AXS…, ACT…, DRV…) are machine-specific — when copying, let OpenPnP generate new IDs or keep yours consistent. Adjust serial port names for your system.

Virtual camera Z axis (Machine → Axes):

<axis class="org.openpnp.machine.reference.axis.ReferenceVirtualAxis"
      id="AXS171084434ad04ba5" name="zCamera" type="Z">
   <home-coordinate value="0.0" units="Millimeters"/>
</axis>

Head: LaserZ actuator + Z probe (Machine → Heads → H1):

<!-- Inside <head> … -->
<actuators>
   <actuator class="org.openpnp.machine.reference.ReferenceActuator"
             id="ACT202606121201a" name="LaserZ"
             value-type="Double" value-type-confirmed="true"
             driver-id="DRV202606121200a"
             axis-X-id="AXS169824381580efcb"
             axis-Y-id="AXS16982438158c4660"
             axis-Z-id="AXS171084434ad04ba5"
             coordinated-before-actuate-enum="WaitForStillstand"
             coordinated-before-read-enum="WaitForStillstand">
      <!-- Y offset = laser ahead of top camera in head coordinates -->
      <head-offsets units="Millimeters" x="0.0" y="30.0" z="0.0" rotation="0.0"/>
   </actuator>
</actuators>
<z-probe-actuator-name>LaserZ</z-probe-actuator-name>

Top camera uses the same virtual Z (axis-Z-id="AXS171084434ad04ba5"). Default focus Z should match LzRef:

<default-z value="7.180000000000049" units="Millimeters"/>

Machine-level force actuators (Machine → Actuators):

<actuator class="org.openpnp.machine.reference.ReferenceActuator"
          id="ACT202606121202a" name="ForceZProbe"
          value-type="Double" driver-id="DRV202606121200a" …/>
<actuator class="org.openpnp.machine.reference.ReferenceActuator"
          id="ACT202606121203a" name="ForceZTare"
          value-type="Boolean" driver-id="DRV202606121200a" …/>

LumenPnP-Addon driver (Machine → Drivers — second driver, ESP32 on ttyUSB0):

<driver class="org.openpnp.machine.reference.driver.GcodeDriver"
        id="DRV202606121200a" name="LumenPnP-Addon"
        communications="serial"
        sync-initial-location="false"
        allow-unhomed-motion="true"
        visual-homing-enabled="false"
        connect-wait-time-milliseconds="1500"
        timeout-milliseconds="10000"
        units="Millimeters">
   <serial port-name="ttyUSB0" baud="115200" line-ending-type="LF"
           flow-control="Off" …/>
   <command type="COMMAND_CONFIRM_REGEX">
      <text><![CDATA[^ok.*]]></text>
   </command>
   <command type="CONNECT_COMMAND">
      <text><![CDATA[M115 ; LumenPnP addon ready check]]></text>
   </command>
   <!-- LaserZ (head actuator ACT202606121201a) -->
   <command head-mountable-id="ACT202606121201a" type="ACTUATOR_READ_COMMAND">
      <text><![CDATA[M212]]></text>
   </command>
   <command head-mountable-id="ACT202606121201a" type="ACTUATOR_READ_REGEX">
      <text><![CDATA[(?i)Laser:(?<Value>[-\d.]+)]]></text>
   </command>
   <!-- ForceZProbe -->
   <command head-mountable-id="ACT202606121202a" type="ACTUATOR_READ_COMMAND">
      <text><![CDATA[G38.2 Z-80 F80]]></text>
   </command>
   <command head-mountable-id="ACT202606121202a" type="ACTUATOR_READ_REGEX">
      <text><![CDATA[(?i)PRB:(?<Value>[-\d.]+)]]></text>
   </command>
   <!-- ForceZTare -->
   <command head-mountable-id="ACT202606121203a" type="ACTUATE_BOOLEAN_COMMAND">
      <text><![CDATA[{True:M600}{False:M999}]]></text>
   </command>
</driver>

The main motion GcodeDriver stays on the LumenPnP controller (e.g. ttyACM1 / Marlin). Only sensor commands go to LumenPnP-Addon.

HX711 calibration

  1. With nothing on the pad: M600 (tare).

  2. Place a known weight on the pad.

  3. Read grams with M114.

  4. Compute and set calibration factor:

    new_factor = (reading_grams / actual_grams) × current_factor
    
  5. Save via Settings page (/settings) or G-code is not available for cal factor over serial — use the web Settings form or set at runtime in test firmware then copy the value.

Persisted in NVS (hx711 namespace): calibration factor, invert flag.

Sign matters: if compression reads negative, toggle invert (M600 I1) or flip the factor sign.

Laser OpenPnP calibration (K / LzRef)

This is the most important laser setup step. The firmware maps standoff distance to machine surface Z using constant K:

K = machine_surface_z + laser_standoff_mm    (at the teach pose)
surface_z = K − standoff_mm
openpnp_delta = surface_z − LzRef

LzRef must equal the zCamera virtual Z OpenPnP shows at the teach pose. On a LumenPnP with Top camera default Z 7.18 mm, that is:

M212 V7.18

(M212 L7.18 is an alias.) This should match Machine Setup → Cameras → Top → Default Z.

Teach workflow (recommended)

  1. Home the machine and jog the Top camera over a known flat surface at your normal safe/focus Z (zCamera ≈ 7.18 mm in a typical config).

  2. Read the machine board surface Z from OpenPnP at that XY (e.g. 5.200 mm) — this is physical machine Z of the surface, not the virtual camera Z.

  3. Send over the addon serial (web G-code console, OpenPnP driver test, or pio device monitor):

    M212 Z5.200
    

    Firmware reads laser standoff, computes K = surface_z + distance, saves to flash, and prints the result.

  4. Set LzRef to the virtual Z from step 1 (check Top → Default Z or the DRO for zCamera):

    M212 V7.18
    
  5. Confirm with M212 at the same pose — Laser: should be a small value near 0.000.

  6. Run a Z probe on that head (board location or test script). OpenPnP should capture a sensible height without the nozzle touching the pad.

Reference: recommended starting-point calibration

Values below were read from a live, taught addon (M115 over USB + GET /api/settings on the diagnostics page). Use them to restore a fresh flash or as a baseline when tuning a new unit. Re-read M115 / /api/settings after any teach pass — K and LzRef are machine-specific.

OpenPnP + laser (NVS laser namespace)

Setting Recommended value G-code to set
OpenPnP K 56.200 M212 K56.200
OpenPnP LzRef 7.180 M212 V7.180
Delta mode on M212 O1
Avg samples 9 M212 S9
ROI SPADs 4 M212 R4
Hold jump 1.0 mm M212 H1.0
Hold alpha 0.35 M212 A0.35
Outlier gate 1.5 mm M212 U1.5
Max sigma 2.0 mm M212 G2.0
mm per bin 1.5 M212 P1.5
VL53 distance cal off (0.00 mm offset) (optional: M212 D<known_mm>)

One-shot restore after flash:

M212 K56.200 V7.180 O1 S9 R4 H1.0 A0.35 U1.5 G2.0 P1.5

HX711 force probe (NVS hx711 namespace)

Setting Recommended value Notes
Calibration factor -1000.00 Firmware default — calibrate with a known weight before relying on gram readings
Invert sign on M600 I1 (saved via Settings page)
Trigger threshold 30.0 g Runtime default; M600 S30 (not persisted across reboot)

Add after the laser restore line if you want the same runtime force-probe threshold:

M600 I1
M600 S30

Matching OpenPnP machine values

OpenPnP item Value in machine.xml
Top camera Default Z / units-per-pixel Z 7.18 mm
LaserZ head offset Y 30.0 mm
Head Z probe actuator LaserZ
Addon serial port ttyUSB0 @ 115200
Motion serial port ttyACM1 @ 115200

Manual K

If you already know K:

M212 K12.345

Distance calibration (optional)

At a known physical standoff (gauge block, shim stack):

M212 D25.0

Applies a sensor offset so corrected mm matches the reference.

Surface hold smoothing

Laser surface Z uses a hold/EMA filter to reject single-frame spikes while tracking real plateaus:

Parameter Default G-code Meaning
Jump threshold 1.0 mm M212 H1.0 Ignore jumps larger than this unless sustained
EMA alpha 0.35 M212 A0.35 Smoothing strength for in-range readings
Reset hold M212 N Clear hold / EMA state

The diagnostics page shows HELD (smoothed) vs raw instant surface Z. M212 uses the held value for OpenPnP delta output when delta mode is on.

Laser sensor tuning

Parameter Default G-code Notes
Average samples 9 M212 S9 1–15 samples per read
ROI size 4 M212 R4 Center SPADs per side (4–16)
Outlier gate 1.5 mm M212 U1.5 Reject per-sample spikes
Max sigma 2.0 mm M212 G2.0 Gate noisy reads
mm per histogram bin 1.5 M212 P1.5 Centroid scale
Delta mode on M212 O1 / M212 O0 OpenPnP delta vs raw mm
Clear VL53 cal M212 C Remove distance offset

All of the above (except M212 C and M212 N) are saved to flash in the laser NVS namespace.

Settings web page

Open http://<ip>/settings (or /settings on mDNS). Changes write to NVS and survive reboot:

  • HX711 threshold, calibration factor, invert, tare
  • OpenPnP K, LzRef, delta mode, teach-from-surface
  • Laser samples, ROI, outlier/sigma/mm-per-bin, hold jump/alpha
  • Actions: tare, reset surface hold, clear laser calibration

Equivalent G-code is listed at the bottom of the settings page.

Drift datalog

On the diagnostics page, Drift Datalog records 1 sample/sec for up to 10 minutes while the machine is stationary. Use it to analyze force/laser drift. Download CSV for offline plotting.

What persists in flash (NVS)

Namespace Keys Set via
hx711 calibration factor, invert Settings page
laser K, LzRef, delta, hold H/A, S/R/U/P/G tuning M212 …, Settings page
wifi SSID, password M710 …

Trigger threshold (M600 S) is runtime only unless changed again after reboot (default 30 g).


G-code reference

General

Command Description
M115 Firmware info, calibration, laser tuning, WiFi status
M114 Load cell grams + probe state + laser summary
M999 / M112 Reset / abort probe
Unknown ok ; unknown: <cmd> (keeps OpenPnP moving)

HX711 / force probe

Command Description
M600 Tare scale
M600 S<g> Set trigger threshold (grams)
M600 I0 / M600 I1 Sign invert off / on
G38.2 / G38.3 Arm probe; block until contact or timeout (30 s)

Laser / OpenPnP (M212 / READ_LASER)

Command Description
M212 Read laser; print Laser:<value> + metadata + ok
M212 S<n> Average sample count (1–15)
M212 R<n> ROI SPADs per side (4–16)
M212 Z<mm> Teach K from known surface Z at current pose
M212 K<mm> Set K directly
M212 L<mm> / M212 V<mm> Set LzRef (OpenPnP virtual Z)
M212 D<mm> Calibrate at known standoff distance
M212 H<mm> Surface hold jump threshold
M212 A<0-1> Surface hold EMA alpha
M212 N Reset surface hold
M212 O0 / M212 O1 Raw mm / OpenPnP delta output
M212 P<mm/bin> Histogram centroid scale
M212 G<sigma> Max per-sample sigma gate (mm)
M212 U<mm> Outlier gate (mm)
M212 C Clear laser distance calibration

Multiple M212 parameters can be combined on one line, e.g. M212 S9 R4 H0.8.

WiFi (M710)

Command Description
M710 WiFi status
M710 S"<ssid>" Set SSID
M710 P"<password>" Set password
M710 R Reconnect
M710 C Clear credentials

Diagnostics webpage features

Beyond basic force/laser readouts, the live page includes:

  • 10 s trend charts — force; laser BEFORE / AFTER / HELD / INSTANT (toggleable)
  • Histogram — photon counts vs timing bins (log/linear scale)
  • OpenPnP block — K, LzRef, delta, held vs raw surface Z
  • G-code console — send any command, see responses
  • Drift datalog — 10 min capture, CSV download, trend analysis plots

Project structure

Production firmware is modular C++ — main.cpp is a thin entry point; behavior lives in service classes under src/ with headers in include/.

lumenpnp-addons/
├── platformio.ini              # esp32dev (production), hx711_test, vl53_test
├── 3d_models/
│   └── z_laser_mount.stl       # Printable VL53L4CX head mount
├── photos/
│   ├── z_laser_mount_1.jpg     # Mount photos (see Installation)
│   └── z_laser_mount_2.jpg
├── include/
│   ├── pins.h                  # GPIO pin map
│   ├── addon_config.h          # Defaults, task stacks, NVS/datalog limits
│   ├── addon_types.h           # LiveStatus, M212Snapshot, DatalogEntry, LaserTuning
│   ├── addon_sync.h            # Null-safe FreeRTOS mutex helpers
│   ├── vl53l4cx_sensor.h       # VL53L4CX driver wrapper
│   ├── hx711_probe.h           # HX711 force probe + G38 state machine
│   ├── laser_service.h         # Laser polling, surface hold, OpenPnP K/LzRef math
│   ├── gcode_parser.h          # G-code string/parameter parsing (M710 SSID/password)
│   ├── gcode_handler.h         # Serial G-code dispatch (M212, M600, G38, M710, …)
│   ├── wifi_manager.h          # NVS WiFi credentials + connect/mDNS
│   ├── datalog_recorder.h      # 1 Hz drift capture buffer + CSV export
│   ├── diagnostics_web.h       # HTTP routes + JSON status/settings
│   ├── lumen_addon_app.h       # App wiring, multicore tasks, main loop
│   └── web/
│       ├── diagnostics_page.h  # Embedded HTML/JS for `/` (live charts, datalog)
│       └── settings_page.h     # Embedded HTML/JS for `/settings`
├── src/
│   ├── main.cpp                # setup() / loop() → LumenAddonApp
│   ├── lumen_addon_app.cpp     # Core assignment, sensor + web FreeRTOS tasks
│   ├── hx711_probe.cpp
│   ├── laser_service.cpp
│   ├── vl53l4cx_sensor.cpp
│   ├── gcode_parser.cpp
│   ├── gcode_handler.cpp
│   ├── wifi_manager.cpp
│   ├── datalog_recorder.cpp
│   ├── diagnostics_web.cpp
│   ├── hx711_test.cpp          # Wiring test only (env: esp32dev_hx711_test)
│   └── vl53l4cx_test.cpp       # Wiring test only (env: esp32dev_vl53_test)
└── README.md

Runtime layout

Task / loop Core Responsibility
loop() 1 USB serial, G-code input, HX711 G38.2 armed polling
sensor 0 VL53L4CX fast poll, surface hold, periodic M212 cache, datalog ticks
web 0 WebServer::handleClients(), WiFi reconnect

Shared state uses FreeRTOS mutexes per subsystem (HX711, laser, live-status snapshot, datalog).

HTTP API (diagnostics web)

Route Method Purpose
/ GET Diagnostics page (embedded UI)
/settings GET Settings page
/status GET Live JSON for polling (~450 ms in browser)
/api/settings GET/POST Read/write NVS-backed settings
/tare, /set, /send POST/GET Tare, threshold, G-code forward
/api/datalog/start POST Start drift capture
/api/datalog/stop POST Stop capture
/api/datalog/download GET CSV download

Troubleshooting

Symptom Things to check
HX711 not ready Split power wiring; wait_ready vs is_ready — use test firmware; keep wires short
Grams wrong sign M600 I1 or flip calibration factor
Laser:-1 I2C/XSHUT wiring; teach K with M212 Z<surface>
OpenPnP delta nonsense K not taught (M212 Z); LzRef ≠ Top camera Default Z / zCamera at teach pose; delta mode off (M212 O0); wrong head offset on LaserZ
Z probe does nothing / wrong height Head Z probe actuator not set to LaserZ; actuator on wrong driver; LaserZ axis Z not bound to zCamera virtual axis
WiFi won't connect M710 to verify SSID; M710 R; same LAN; serial shows IP on success
Web page stale after flash Hard refresh (Ctrl+Shift+R)
Web shows offline (no /status) Usually a firmware crash/reboot loop — check serial for assert failed or Reset reason: after boot; re-flash latest build
USB upload busy Close OpenPnP serial connection first

OpenPnP wiki:


Contributing

Hardware iterations and PRs welcome. Discuss on the LumenPnP / Opulo community (Discord).

About

Mods and modifications for LumenPnP

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors