# Exercise 25: MotionCal T-Beam Bridge This PlatformIO project turns a LilyGO T-Beam Supreme into a serial bridge for Paul Stoffregen's MotionCal desktop calibration tool. The firmware reads the onboard QMC magnetometer through SensorLib and streams MotionCal-compatible ASCII `Raw:` lines over USB serial. MotionCal can then estimate both hard-iron offsets and the soft-iron correction matrix for the board. ![](MotionCal_2026-04-25_11-22.png) ## Hardware Target board: ```text LilyGO T-Beam Supreme / ESP32-S3 QMC6310/QMC5883-family magnetometer SH1106 OLED display ``` The firmware probes these magnetometer I2C addresses: ```text 0x1C QMC6310U 0x3C QMC6310N 0x2C QMC5883P ``` The default PlatformIO environment is `cy`, with additional board labels available in `platformio.ini`: ```text amy bob cy dan ed flo guy ``` ## Serial Format MotionCal expects lines in this format: ```text Raw:accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z ``` This bridge only has a magnetometer, so it sends fixed placeholder accel/gyro values: ```text Raw:0,0,8192,0,0,0,mag_x,mag_y,mag_z ``` The placeholder values mean: ```text accel = 0,0,8192 approximately stationary upright in MotionCal's count convention gyro = 0,0,0 no gyro data supplied mag = live QMC magnetometer data ``` ## Magnetometer Units SensorLib returns QMC magnetometer readings in Gauss. The firmware converts them like this: ```text 1 gauss = 100 microtesla MotionCal count = microtesla * 10 1 MotionCal count = 0.1 microtesla ``` So a streamed value like this: ```text Raw:0,0,8192,0,0,0,-1735,428,793 ``` means: ```text X = -1735 counts = -173.5 uT = -1.735 gauss Y = 428 counts = 42.8 uT = 0.428 gauss Z = 793 counts = 79.3 uT = 0.793 gauss ``` These are MotionCal-compatible integer counts, not raw QMC register values. ## Build Activate the Python environment that contains PlatformIO: ```sh source /home/jlpoole/pioenv/bin/activate ``` Build the default environment: ```sh cd /usr/local/src/microReticulumTbeam/exercises/25_motioncal_tbeam pio run ``` Build a specific board label: ```sh pio run -e dan ``` ## Upload Upload the default environment: ```sh source /home/jlpoole/pioenv/bin/activate cd /usr/local/src/microReticulumTbeam/exercises/25_motioncal_tbeam pio run -t upload ``` Upload a specific board label: ```sh pio run -e dan -t upload ``` ## Serial Monitor After flashing, monitor the USB serial stream at 115200 baud: ```sh pio device monitor -b 115200 --port /dev/ttytDAN ``` Expected boot/status output includes lines like: ```text exercise=Exercise 25 MotionCal T-Beam bridge serial_format=Raw:accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z mag_units=MotionCal integer counts, 1 count = 0.1 uT magnetometer_init=ok label=QMC6310U addr=0x1C chip=0x.. ``` Expected streaming output looks like: ```text Raw:0,0,8192,0,0,0,-1578,447,1266 Raw:0,0,8192,0,0,0,-1583,438,1258 Raw:0,0,8192,0,0,0,-1585,442,1250 ``` If the firmware prints `read_fail` or `overflow` status lines, check I2C wiring/power and the configured magnetometer full-scale range. ## Running MotionCal Build MotionCal if needed: ```sh cd /usr/local/src/MotionCal make WXCONFIG=wx-config LDFLAGS="-lglut -lGLU -lGL -lm" ``` Run it: ```sh cd /usr/local/src/MotionCal GDK_BACKEND=x11 ./MotionCal ``` Then select the T-Beam USB serial port in MotionCal. Move and rotate the T-Beam through as many orientations as possible. The goal is to cover the sphere well, not just wave it flat on the table. Better 3D coverage improves both hard-iron and soft-iron calibration. ## Saving Calibration MotionCal computes: ```text magnetic_offset_uT hard-iron offset, in microtesla magnetic_mapping_matrix inverse soft-iron correction matrix magnetic_field_uT fitted local field magnitude ``` Example: ```text magnetic_offset_uT=-172.96843,43.0260162,78.8941956 magnetic_field_uT=52.4668198 magnetic_mapping_matrix= 0.943139076 0.0439298451 0.0595370531 0.0439298451 1.04979992 -0.0347476006 0.0595370531 -0.0347476006 1.01706612 ``` The firmware also accepts MotionCal's 68-byte calibration packet and echoes `Cal1:` and `Cal2:` lines so MotionCal can confirm the send. ## Applying Calibration MotionCal's calibration model is: ```text mag_uT = raw_motioncal_counts * 0.1 centered = mag_uT - magnetic_offset_uT corrected = magnetic_mapping_matrix * centered ``` Hard iron moves the center of the magnetometer cloud back to zero. Soft iron transforms the ellipsoid-shaped cloud back toward a sphere. ## Troubleshooting If MotionCal does not show points: ```text Confirm the serial port is correct. Confirm baud is 115200. Confirm monitor output contains Raw: lines. Close any serial monitor before opening the port in MotionCal. ``` If the values look 10x different from a notebook or script: ```text The notebook/script may be using MotionCal counts. MotionCal's saved magnetic_offset_uT is already in microtesla. Convert counts to uT with: uT = counts * 0.1 ``` If the fit is poor: ```text Collect more orientations. Rotate around all axes. Keep the board away from steel, speakers, motors, magnets, and high-current wiring. Try a different location and repeat the calibration. ```