-
Notifications
You must be signed in to change notification settings - Fork 1
feat(ism330dl): Add OLED spirit level example. #376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| """Spirit level example using ISM330DL accelerometer and SSD1327 OLED. | ||
|
|
||
| Displays a digital bubble level. The bubble moves according to the board's tilt. | ||
| When the board is perfectly flat, the bubble centers and the background lights up. | ||
| """ | ||
|
|
||
| from time import sleep_ms | ||
|
|
||
| import ssd1327 | ||
| from ism330dl import ISM330DL | ||
| from machine import I2C, SPI, Pin | ||
|
|
||
| # Layout & Physics Constants | ||
| SCREEN_CENTER_X = 64 | ||
| SCREEN_CENTER_Y = 64 | ||
| BUBBLE_RADIUS = 8 | ||
|
|
||
| # Maximum pixel distance the bubble can travel from the center | ||
| MAX_OFFSET = 50 | ||
|
|
||
| # Tilt thresholds (in g) to consider the board "level/flat" | ||
| LEVEL_THRESHOLD = 0.05 | ||
|
|
||
| # Display Colors (0 to 15 greyscale) | ||
| COLOR_BG_TILTED = 0 | ||
| COLOR_BG_LEVEL = 4 | ||
| COLOR_FG = 15 | ||
|
|
||
| # Loop delay | ||
| POLL_RATE_MS = 20 | ||
|
|
||
| def fill_circle(fbuf, x0, y0, r, c): | ||
| """Helper to draw a filled circle since framebuf lacks it natively.""" | ||
| for y in range(-r, r + 1): | ||
| for x in range(-r, r + 1): | ||
| if x * x + y * y <= r * r: | ||
| fbuf.pixel(x0 + x, y0 + y, c) | ||
|
Comment on lines
+35
to
+37
|
||
|
|
||
| # Hardware Initialization | ||
| # I2C and ISM330DL | ||
| i2c = I2C(1) | ||
| imu = ISM330DL(i2c) | ||
|
|
||
| # SPI and SSD1327 OLED | ||
| spi = SPI(1) | ||
| dc = Pin("DATA_COMMAND_DISPLAY") | ||
| res = Pin("RST_DISPLAY") | ||
| cs = Pin("CS_DISPLAY") | ||
| display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) | ||
|
|
||
| print("=======================") | ||
| print(" Spirit Level ") | ||
| print("=======================") | ||
| print("Tilt the board to move the bubble.") | ||
| print("Press Ctrl+C to exit.") | ||
|
|
||
| try: | ||
| while True: | ||
| # Read acceleration in g-forces | ||
| ax, ay, _az = imu.acceleration_g() | ||
|
|
||
| # Level Detection | ||
| # If both X and Y axes are close to 0g, the board is flat | ||
| is_level = abs(ax) < LEVEL_THRESHOLD and abs(ay) < LEVEL_THRESHOLD | ||
|
|
||
| bg_color = COLOR_BG_LEVEL if is_level else COLOR_BG_TILTED | ||
|
|
||
| # Map Acceleration to Pixel Offset | ||
| # We cap the acceleration at 1.0g to avoid the bubble leaving the screen | ||
| clamped_ax = max(-1.0, min(1.0, ax)) | ||
| clamped_ay = max(-1.0, min(1.0, ay)) | ||
|
|
||
|
|
||
| # Axis Mapping & Inversion: | ||
| # We swap X and Y to match the display's physical orientation. | ||
| # The negative sign on 'ay' inverts the axis so the indicator | ||
| # behaves like a physical air bubble (moving to the highest point). | ||
| offset_x = int(-clamped_ay * MAX_OFFSET) | ||
| offset_y = int(clamped_ax * MAX_OFFSET) | ||
|
|
||
| bubble_x = SCREEN_CENTER_X + offset_x | ||
| bubble_y = SCREEN_CENTER_Y + offset_y | ||
|
|
||
| bubble_x = max(BUBBLE_RADIUS, min(127 - BUBBLE_RADIUS, bubble_x)) | ||
| bubble_y = max(BUBBLE_RADIUS, min(127 - BUBBLE_RADIUS, bubble_y)) | ||
|
|
||
| # Drawing Phase | ||
| display.fill(bg_color) | ||
|
|
||
| # Draw the crosshair (Target reference) | ||
| display.framebuf.hline(SCREEN_CENTER_X - 20, SCREEN_CENTER_Y, 40, COLOR_FG) | ||
| display.framebuf.vline(SCREEN_CENTER_X, SCREEN_CENTER_Y - 20, 40, COLOR_FG) | ||
|
|
||
| # Draw the center circle (Target zone) | ||
| display.framebuf.rect(SCREEN_CENTER_X - BUBBLE_RADIUS - 2, | ||
| SCREEN_CENTER_Y - BUBBLE_RADIUS - 2, | ||
| (BUBBLE_RADIUS * 2) + 4, | ||
| (BUBBLE_RADIUS * 2) + 4, | ||
| COLOR_FG) | ||
|
|
||
| # Draw the actual bubble | ||
| fill_circle(display.framebuf, bubble_x, bubble_y, BUBBLE_RADIUS, COLOR_FG) | ||
|
|
||
| display.show() | ||
| sleep_ms(POLL_RATE_MS) | ||
|
|
||
| except KeyboardInterrupt: | ||
| print("\nSpirit level stopped.") | ||
| finally: | ||
| # Clean up and power off display on exit | ||
| display.fill(0) | ||
| display.show() | ||
| sleep_ms(100) | ||
| display.power_off() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR description mentions adding this example to a README examples table, but
lib/ism330dl/README.mdcurrently lists only basic_read/static_orientation/motion_orientation and does not includespirit_level.py. Either update the docs or adjust the PR description so they match.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, I missed staging the README.md file in my previous commit. It's now properly updated in the examples table and included in this push.