Skip to content

Commit e50704a

Browse files
committed
refactor(steami_config): Migrate calibrate_magnetometer to steami_screen UI.
1 parent f01b9c5 commit e50704a

1 file changed

Lines changed: 112 additions & 90 deletions

File tree

Lines changed: 112 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,162 @@
1-
"""Calibrate the LIS2MDL magnetometer and save to persistent config.
1+
"""Calibrate the LIS2MDL magnetometer using the steami_screen UI.
22
3-
This example runs a 3D min/max calibration by collecting samples while
4-
the user rotates the board in all directions. The computed hard-iron
5-
offsets and soft-iron scale factors are stored in the config zone and
6-
survive power cycles.
3+
This example walks through the full calibration flow:
4+
5+
- press MENU to start the calibration,
6+
- rotate the board in all directions while samples are acquired,
7+
- compute hard-iron offsets and soft-iron scale factors,
8+
- save the calibration to persistent SteamiConfig storage,
9+
- reload and verify the calibration survives a fresh config load.
710
811
Instructions and a countdown are displayed on the SSD1327 OLED screen.
9-
Press MENU to start the calibration.
1012
"""
1113

1214
import gc
1315
from time import sleep_ms
1416

17+
import ssd1327
1518
from daplink_bridge import DaplinkBridge
1619
from lis2mdl import LIS2MDL
1720
from machine import I2C, SPI, Pin
18-
from ssd1327 import WS_OLED_128X128_SPI
1921
from steami_config import SteamiConfig
22+
from steami_screen import Screen, SSD1327Display
2023

2124
# --- Hardware init ---
2225

2326
i2c = I2C(1)
24-
oled = WS_OLED_128X128_SPI(
25-
SPI(1),
26-
Pin("DATA_COMMAND_DISPLAY"),
27-
Pin("RST_DISPLAY"),
28-
Pin("CS_DISPLAY"),
27+
28+
spi = SPI(1)
29+
dc = Pin("DATA_COMMAND_DISPLAY")
30+
res = Pin("RST_DISPLAY")
31+
cs = Pin("CS_DISPLAY")
32+
33+
display = SSD1327Display(
34+
ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs)
2935
)
36+
37+
screen = Screen(display)
38+
3039
btn_menu = Pin("MENU_BUTTON", Pin.IN, Pin.PULL_UP)
3140

3241
bridge = DaplinkBridge(i2c)
3342
config = SteamiConfig(bridge)
3443
config.load()
44+
3545
mag = LIS2MDL(i2c)
3646
config.apply_magnetometer_calibration(mag)
3747

3848

39-
# --- Helper functions ---
40-
41-
42-
def show(lines):
43-
"""Display centered text lines on the round OLED screen."""
44-
oled.fill(0)
45-
th = len(lines) * 12
46-
ys = max(0, (128 - th) // 2)
47-
for i, line in enumerate(lines):
48-
x = max(0, (128 - len(line) * 8) // 2)
49-
oled.text(line, x, ys + i * 12, 15)
50-
oled.show()
51-
52-
53-
def draw_degree(x, y, col=15):
54-
"""Draw a tiny degree symbol (3x3 circle) at pixel position."""
55-
oled.pixel(x + 1, y, col)
56-
oled.pixel(x, y + 1, col)
57-
oled.pixel(x + 2, y + 1, col)
58-
oled.pixel(x + 1, y + 2, col)
59-
49+
# --- Helpers ---
6050

6151
def wait_menu():
62-
"""Wait for MENU button press then release."""
6352
while btn_menu.value() == 1:
6453
sleep_ms(10)
6554
while btn_menu.value() == 0:
6655
sleep_ms(10)
6756

6857

69-
# --- Step 1: Display instructions and wait for MENU ---
58+
def show_intro():
59+
screen.clear()
60+
screen.title("COMPAS")
61+
62+
screen.subtitle(
63+
"Tournez la",
64+
"carte dans",
65+
"toutes les",
66+
"directions",
67+
)
68+
screen.text("Menu=demarrer", at="S")
69+
70+
screen.show()
71+
72+
73+
def show_progress(remaining):
74+
screen.clear()
75+
screen.title("COMPAS")
76+
77+
screen.text("Acquisition...", at=(12, 44))
78+
screen.value(remaining, unit="s")
79+
screen.subtitle("Tournez", "la carte")
80+
81+
screen.show()
82+
83+
84+
def show_message(*lines):
85+
screen.clear()
86+
screen.title("COMPAS")
87+
screen.subtitle(*lines)
88+
screen.show()
89+
90+
91+
def show_results(readings):
92+
screen.clear()
93+
screen.title("COMPAS")
94+
95+
screen.text("Resultats:", at=(24, 34))
96+
97+
y = 48
98+
for i, heading in enumerate(readings):
99+
line = "{}: {} deg".format(i + 1, int(heading))
100+
screen.text(line, at=(16, y))
101+
y += 12
102+
103+
screen.text("Termine !", at=(28, 112))
104+
screen.show()
105+
106+
107+
# --- Step 1: Instructions ---
70108

71109
print("=== Magnetometer Calibration ===\n")
72-
print("Current offsets: x={:.1f} y={:.1f} z={:.1f}".format(
73-
mag.x_off, mag.y_off, mag.z_off))
74-
print("Current scales: x={:.3f} y={:.3f} z={:.3f}\n".format(
75-
mag.x_scale, mag.y_scale, mag.z_scale))
76-
77-
show([
78-
"COMPAS",
79-
"",
80-
"Tournez la",
81-
"carte dans",
82-
"toutes les",
83-
"directions",
84-
"",
85-
"MENU = demarrer",
86-
])
110+
111+
show_intro()
87112

88113
print("Press MENU to start calibration...")
89114
wait_menu()
90115
print("Starting calibration...\n")
91116

92-
# --- Step 2: Acquisition with countdown ---
117+
118+
# --- Step 2: Acquisition ---
93119

94120
samples = 600
95121
delay = 20
96122
total_sec = (samples * delay) // 1000
123+
97124
xmin = ymin = zmin = 1e9
98125
xmax = ymax = zmax = -1e9
99126

100127
for s in range(samples):
101128
x, y, z = mag.magnetic_field()
129+
102130
xmin = min(xmin, x)
103131
xmax = max(xmax, x)
104132
ymin = min(ymin, y)
105133
ymax = max(ymax, y)
106134
zmin = min(zmin, z)
107135
zmax = max(zmax, z)
136+
108137
if s % 50 == 0:
109-
remain = total_sec - (s * delay) // 1000
110-
show([
111-
"COMPAS",
112-
"",
113-
"Acquisition...",
114-
"",
115-
"Continuez a",
116-
"tourner",
117-
"",
118-
"{} sec".format(remain),
119-
])
138+
remaining = total_sec - (s * delay) // 1000
139+
show_progress(remaining)
140+
120141
sleep_ms(delay)
121142

143+
144+
# --- Compute calibration ---
145+
122146
mag.x_off = (xmax + xmin) / 2.0
123147
mag.y_off = (ymax + ymin) / 2.0
124148
mag.z_off = (zmax + zmin) / 2.0
149+
125150
mag.x_scale = (xmax - xmin) / 2.0 or 1.0
126151
mag.y_scale = (ymax - ymin) / 2.0 or 1.0
127152
mag.z_scale = (zmax - zmin) / 2.0 or 1.0
128153

129154
print("Calibration complete!")
130-
print(" Hard-iron offsets: x={:.1f} y={:.1f} z={:.1f}".format(
131-
mag.x_off, mag.y_off, mag.z_off))
132-
print(" Soft-iron scales: x={:.3f} y={:.3f} z={:.3f}\n".format(
133-
mag.x_scale, mag.y_scale, mag.z_scale))
134155

135-
# --- Step 3: Save to config zone ---
136156

137-
show(["COMPAS", "", "Sauvegarde..."])
157+
# --- Step 3: Save ---
158+
159+
show_message("Sauvegarde...")
138160

139161
config.set_magnetometer_calibration(
140162
hard_iron_x=mag.x_off,
@@ -144,42 +166,42 @@ def wait_menu():
144166
soft_iron_y=mag.y_scale,
145167
soft_iron_z=mag.z_scale,
146168
)
169+
147170
config.save()
148-
print("Calibration saved to config zone.\n")
149171
sleep_ms(500)
150172

173+
151174
# --- Step 4: Verify ---
152175

153-
show(["COMPAS", "", "Sauvegarde OK", "", "Verification..."])
176+
show_message("Sauvegarde OK", "", "Verification...")
154177

155178
gc.collect()
179+
156180
config2 = SteamiConfig(bridge)
157181
config2.load()
158182

159183
mag2 = LIS2MDL(i2c)
160184
config2.apply_magnetometer_calibration(mag2)
161185

162-
print("Verification (5 heading readings after reload):")
163-
result_lines = ["COMPAS", "", "Resultats:"]
186+
print("Verification (5 readings):")
187+
188+
readings = []
189+
164190
for i in range(5):
165191
heading = mag2.heading_flat_only()
166-
line = " {}: cap={:.0f}".format(i + 1, heading)
167-
print(" Reading {}: heading={:.1f} deg".format(i + 1, heading))
168-
result_lines.append(line)
192+
readings.append(heading)
193+
194+
screen.clear()
195+
screen.title("COMPAS")
196+
screen.value(int(heading), unit="deg", label="Mesure {}".format(i + 1))
197+
screen.show()
198+
199+
print("Reading {}: {:.1f} deg".format(i + 1, heading))
169200
sleep_ms(500)
170201

171-
result_lines.append("")
172-
result_lines.append("Termine !")
173-
174-
# Draw results with degree symbols
175-
oled.fill(0)
176-
th = len(result_lines) * 12
177-
ys = max(0, (128 - th) // 2)
178-
for i, line in enumerate(result_lines):
179-
x = max(0, (128 - len(line) * 8) // 2)
180-
oled.text(line, x, ys + i * 12, 15)
181-
if "cap=" in line:
182-
draw_degree(x + len(line) * 8 + 1, ys + i * 12)
183-
oled.show()
184-
185-
print("\nDone! Calibration is stored and will be restored at next boot.")
202+
203+
# --- Done ---
204+
205+
show_results(readings)
206+
207+
print("\nDone! Calibration stored.")

0 commit comments

Comments
 (0)