Week 15. Interface and Application Programming
Building an interface for calibrating a ball of addressable LEDs.
💡 Note that this development strategy fits well with "document as you go." By having this markdown file as well as the html, js, and ino files in the LLM context, it becomes easier to debug issues across these boundaries. Then it is one step to commit and publish changes.
Intro
During week 13 I iterated on two methods for mapping positions of an LED string around a ball. The first version used a math function to estimate the positions of the LEDs. The second version involved making a list of key positions, moving a white pixel around until the index was close to the estimated position, and then having the code interpolating the LED positions between those mapped indexes. This involved more manual work, and ended up being more accurate, but still ended up with some funky pixels.
As I was working through these versions, I had the ball stationary on the desk, and I moved the accelerometer independently to test the calibration. Only once I had a mapping that seemed correct did I integrate the cardboard prototype.
Once I had the wireless version in my hands with the central accelerometer, and through a conversation with Kris, I thought of a new way to do the calibration process.
For every pixel, or every nth pixel:
- Send a serial message with the LED index.
- Blink red for 3 seconds.
- The person moves the ball until that pixel is on top.
- Shine green for 1 second.
- The accelerometer value (which is the ball's orientation) is then stored for that pixel.
- Once the whole LED string is calibrated, a list is sent via serial message, that can be hardcoded into the positioning library and used in other Arduino programs for the ball.
This calibration program will be more flexible than the previous versions, and will be better suited for the organic simulated shapes that I hope will be the final form. Redesigning the LED path, or accelerometer position, will require another calibration pass. But I think it will be easier and more enjoyable than my past versions. This will let me iterate on the design.
Spec
A web app will listen to the serial messages via WebSerial, as in week 12's machine interface. The accelerometer's orientation will be visualized, and each LED position as it is mapped. The webapp will be able to query the device for any existing mapping, and be able to skip to any index.
Previous code
For reference. Don't change these files.
This version
Initial prompt
Without writing code, help me fill out subsections of this markdown spec. We have a working version, but want to redesign the calibration flow entirely. We also want to make a web visualization to make the process more interactive. Once all of the sections are filled out, we will have enough of a spec to start implementing.
Hardware Overview
- Seeed Studio XIAO RP2040 microcontroller
- 6DoF accelerometer and gyroscope – Adafruit LSM6DSOX with STEMMA QT
- LED rope with 250 addressable RGB LEDs – WS2812B chip
- Power supply (USB/battery considerations)
- Tactile housing/structure to support the LED rope in a spherical pattern
- Button input (existing in current code)
Libraries
- FastLED - For controlling the LED string
- Wire - For I2C communication with the accelerometer
- Adafruit_LSM6DS - For the specific accelerometer/gyroscope
- Adafruit_Sensor - Required for the LSM6DS library
Communication Protocol
- Serial communication at 115200 baud rate
- Text message format with initial string
- Message types:
START_CALIBRATION
: Begin the calibration processSET_INDEX
: Jump to specific LED indexLED_INFO
: Current LED index and statusACCEL_DATA
: Current accelerometer readingsSAVE_POSITION
: Save the current orientation for an LEDCALIBRATION_COMPLETE
: All LEDs have been calibratedGET_MAPPING
: Request current mapping dataMAPPING_DATA
: Response with calibration data
Calibration Process
-
Initialization Phase
- Web app connects to the device via WebSerial
- Device reports current configuration (LED count, existing mappings)
- User selects calibration mode (all LEDs or specified interval)
-
Calibration Loop
- Device shows current LED index (blinking red for 3 seconds)
- User rotates ball to position the current LED at the top
- User confirms position via web interface
- Device captures accelerometer orientation (showing green for 1 second)
- Process advances to next LED automatically
-
Completion Phase
- Device sends complete mapping data as JSON
- Web app generates C++ header file with mapping data
- User can save/download the header file for future use
Web App Interface
-
Connection Panel
- Connect/disconnect buttons for WebSerial
- Device status indicator
-
Calibration Controls
- Start/pause/resume calibration
- Skip to specific LED index
- Step forward/backward one LED
- Adjust calibration interval (every n LEDs)
- Reset calibration data
-
Visualization Area
- 3D representation of the ball with current orientation
- Highlighted current LED position
- Previously mapped LED positions
- Accelerometer axes visualization
-
Data Management
- Export calibration data as header file
- Import existing calibration data
- Real-time display of accelerometer values
Data Structure
- Mapping Format
// Coordinate systems
struct CartesianCoord {
float x, y, z; // Normalized pixel position on unit sphere
};
struct PolarCoord {
float theta; // Azimuthal angle (0 to 2π) - around equator
float phi; // Polar angle (0 to π) - from north pole to south pole
};
struct LEDPosition {
int ledIndex;
PolarCoord polarPos;
CartesianCoord cartesianPos;
bool isSet;
};
#define SPHERE_NUM_LEDS 250
LEDPosition sphereLedPositions[SPHERE_NUM_LEDS];
- Storage Strategy
- Temporary storage in device RAM during calibration
- Final mapping stored in Arduino header file
- Optional: EEPROM storage for persistent device storage
Error Handling and Recovery
- Automatic timeout if no user input after 30 seconds
- Ability to retake readings for specific LEDs
- Visual indicators for connection issues
- Data validation to ensure accelerometer readings are within expected ranges
- Auto-save of partial calibrations in case of interruption
Testing and Validation
- Visual feedback method to verify calibration accuracy
- Test mode to illuminate LEDs based on ball orientation
- Interpolation options for LEDs that aren't explicitly calibrated
Implementation Roadmap
Start with this structure, that will become /pixel-ball/ on my site.
- index.html: bare structure, one JS module import
- pixel-ball.js: no build, no libraries, clear vanilla JS
There should be no style, so all controls should use vanilla HTML elements. If WebSerial is not available in the browser, show a notice at the top of the page, but allow the viewer to see the app.
Now I have added pixel-ball-viz.js with the needed three.js import for the 3d vizualization.
We're going to make the accelerometer visualization with THREE.AxesHelper
in the middle of the canvas and scene.
Each calibrated and interpolated pixel should render as a small sphere around the accelerometer viz. Calibrated pixels should have their index as a text label.
- ← Previous
Week 14. Molding and Casting