micrOSDevToolKit 2.10.6__py3-none-any.whl → 2.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of micrOSDevToolKit might be problematic. Click here for more details.

Files changed (101) hide show
  1. env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
  2. env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +17 -1
  3. micrOS/micropython/esp32c6-GENERIC-20250415-v1.25.0.bin +0 -0
  4. micrOS/micropython/esp32s3-4MBflash-20241129-v1.24.1.bin +0 -0
  5. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +45 -49
  6. micrOS/source/Common.py +262 -87
  7. micrOS/source/Debug.py +44 -88
  8. micrOS/source/Espnow.py +1 -1
  9. micrOS/source/Files.py +21 -2
  10. micrOS/source/Hooks.py +60 -17
  11. micrOS/source/IO_esp32c6.py +16 -0
  12. micrOS/source/IO_esp32s3.py +37 -1
  13. micrOS/source/IO_m5stamp.py +35 -1
  14. micrOS/source/IO_qtpy.py +22 -17
  15. micrOS/source/IO_s3matrix.py +21 -0
  16. micrOS/source/IO_tinypico.py +38 -0
  17. micrOS/source/LM_VL53L0X.py +1 -1
  18. micrOS/source/LM_buzzer.py +6 -7
  19. micrOS/source/LM_cct.py +6 -5
  20. micrOS/source/LM_dimmer.py +6 -5
  21. micrOS/source/LM_espnow.py +15 -10
  22. micrOS/source/LM_i2c.py +3 -2
  23. micrOS/source/LM_neoeffects.py +173 -230
  24. micrOS/source/LM_neomatrix.py +305 -0
  25. micrOS/source/LM_neopixel.py +10 -10
  26. micrOS/source/LM_pacman.py +25 -21
  27. micrOS/source/LM_qmi8658.py +204 -0
  28. micrOS/source/LM_rgb.py +6 -6
  29. micrOS/source/LM_roboarm.py +5 -4
  30. micrOS/source/LM_switch.py +6 -4
  31. micrOS/source/LM_tcs3472.py +75 -0
  32. micrOS/source/LM_telegram.py +5 -4
  33. micrOS/source/Logger.py +46 -32
  34. micrOS/source/Shell.py +1 -1
  35. micrOS/source/Tasks.py +7 -4
  36. micrOS/source/Time.py +5 -3
  37. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  38. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  39. micrOS/source/micrOS.py +5 -2
  40. micrOS/source/microIO.py +8 -6
  41. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.11.0.dist-info}/METADATA +2 -1
  42. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.11.0.dist-info}/RECORD +92 -87
  43. toolkit/DevEnvUSB.py +5 -0
  44. toolkit/LM_to_compile.dat +1 -0
  45. toolkit/dashboard_apps/NeoEffectsDemo.py +8 -15
  46. toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
  47. toolkit/dashboard_apps/_app_base.py +2 -2
  48. toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
  49. toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
  50. toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
  51. toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
  52. toolkit/simulator_lib/machine.py +0 -1
  53. toolkit/simulator_lib/neopixel.py +3 -2
  54. toolkit/socketClient.py +3 -2
  55. toolkit/workspace/precompiled/Common.mpy +0 -0
  56. toolkit/workspace/precompiled/Debug.mpy +0 -0
  57. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  58. toolkit/workspace/precompiled/Files.mpy +0 -0
  59. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  60. toolkit/workspace/precompiled/IO_esp32c6.mpy +0 -0
  61. toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
  62. toolkit/workspace/precompiled/IO_m5stamp.mpy +0 -0
  63. toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
  64. toolkit/workspace/precompiled/IO_s3matrix.mpy +0 -0
  65. toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
  66. toolkit/workspace/precompiled/LM_VL53L0X.py +1 -1
  67. toolkit/workspace/precompiled/LM_buzzer.mpy +0 -0
  68. toolkit/workspace/precompiled/LM_cct.mpy +0 -0
  69. toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
  70. toolkit/workspace/precompiled/LM_espnow.py +15 -10
  71. toolkit/workspace/precompiled/LM_i2c.py +3 -2
  72. toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
  73. toolkit/workspace/precompiled/LM_neomatrix.mpy +0 -0
  74. toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
  75. toolkit/workspace/precompiled/LM_pacman.mpy +0 -0
  76. toolkit/workspace/precompiled/LM_qmi8658.py +204 -0
  77. toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
  78. toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
  79. toolkit/workspace/precompiled/LM_switch.mpy +0 -0
  80. toolkit/workspace/precompiled/LM_tcs3472.py +75 -0
  81. toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
  82. toolkit/workspace/precompiled/Logger.mpy +0 -0
  83. toolkit/workspace/precompiled/Shell.mpy +0 -0
  84. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  85. toolkit/workspace/precompiled/Time.mpy +0 -0
  86. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  87. toolkit/workspace/precompiled/microIO.mpy +0 -0
  88. micrOS/micropython/esp32s3-20240105-v1.22.1.bin +0 -0
  89. micrOS/source/LM_catgame.py +0 -75
  90. micrOS/source/LM_demo.py +0 -97
  91. micrOS/source/LM_intercon.py +0 -60
  92. micrOS/source/LM_ph_sensor.py +0 -51
  93. toolkit/workspace/precompiled/LM_catgame.py +0 -75
  94. toolkit/workspace/precompiled/LM_demo.py +0 -97
  95. toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
  96. toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
  97. /micrOS/micropython/{esp32s3-20241129-v1.24.1.bin → esp32s3-8MBflash-20241129-v1.24.1.bin} +0 -0
  98. {microsdevtoolkit-2.10.6.data → microsdevtoolkit-2.11.0.data}/scripts/devToolKit.py +0 -0
  99. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.11.0.dist-info}/WHEEL +0 -0
  100. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.11.0.dist-info}/licenses/LICENSE +0 -0
  101. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.11.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,305 @@
1
+ from neopixel import NeoPixel
2
+ from machine import Pin
3
+ from utime import sleep_ms
4
+
5
+ from microIO import bind_pin
6
+ from Types import resolve
7
+ from Common import manage_task, AnimationPlayer
8
+
9
+
10
+ class NeoPixelMatrix(AnimationPlayer):
11
+ INSTANCE = None
12
+ DEFAULT_COLOR = (100, 23, 0) # Default color for the matrix
13
+
14
+ def __init__(self, width: int = 8, height: int = 8, pin: int = 0):
15
+ super().__init__(tag="neomatrix")
16
+ self.width = width
17
+ self.height = height
18
+ self.num_pixels = width * height
19
+ self.pixels = NeoPixel(Pin(pin, Pin.OUT), self.num_pixels)
20
+ self._color_buffer = [(0, 0, 0)] * self.num_pixels # Store original RGB values
21
+ self._brightness = 0.25 # Brightness level, default 25%
22
+ NeoPixelMatrix.INSTANCE = self
23
+
24
+ def update(self, x:int, y:int, color:tuple[int, int, int]):
25
+ # Animation player will call this method to update pixels.
26
+ self.set_pixel(x, y, color)
27
+
28
+ def draw(self):
29
+ # Animation player will call this method to update the display.
30
+ self.pixels.write()
31
+
32
+ def clear(self):
33
+ # Animation player will call this method to clear the display.
34
+ for i in range(self.num_pixels):
35
+ # Write pixel buffer before write to ws2812
36
+ self.pixels[i] = (0, 0, 0)
37
+ # Send buffer to device
38
+ self.draw()
39
+
40
+ def _coord_to_index(self, x: int, y: int, zigzag:bool=True):
41
+ """
42
+ Zigzag layout: even rows left-to-right, odd rows right-to-left
43
+ """
44
+ if (zigzag is None or zigzag) and y % 2 == 0:
45
+ return y * self.width + x
46
+ return y * self.width + (self.width - 1 - x)
47
+
48
+ def _rgb_to_grb_with_br(self, color: tuple[int, int, int]):
49
+ """
50
+ Converts RGB to GRB with brightness adjustment.
51
+ """
52
+ def scale(val):
53
+ return max(0, min(255, int(val * self._brightness)))
54
+
55
+ return scale(color[1]), scale(color[0]), scale(color[2])
56
+
57
+ def set_pixel(self, x: int, y: int, color: tuple[int, int, int], zigzag:bool=True):
58
+ """
59
+ Set pixel at (x, y) with RGB
60
+ """
61
+ if 0 <= x < self.width and 0 <= y < self.height:
62
+ index = self._coord_to_index(x, y, zigzag=zigzag)
63
+ self._color_buffer[index] = color # store original RGB for brightness control
64
+ self.pixels[index] = self._rgb_to_grb_with_br(color)
65
+
66
+ def color(self, color: tuple[int, int, int]):
67
+ """
68
+ Fill color OR Animation color change.
69
+ :param color: tuple[int, int, int] range: 0-255
70
+ :return: str
71
+ """
72
+ r, g, b = max(0, min(color[0], 255)), max(0, min(color[1], 255)), max(0, min(color[2], 255))
73
+ color = (r, g, b)
74
+ NeoPixelMatrix.DEFAULT_COLOR = color
75
+ if manage_task(self._task_tag, "isbusy"):
76
+ return f"Set animation color to {color}"
77
+ for i in range(self.num_pixels):
78
+ self._color_buffer[i] = color
79
+ # Write pixel buffer before write to ws2812
80
+ self.pixels[i] = self._rgb_to_grb_with_br(color)
81
+ # Send buffer to device
82
+ self.draw()
83
+ return f"Set all pixels to {color}"
84
+
85
+ def brightness(self, br: int):
86
+ """
87
+ Change the brightness of all pixels.
88
+ """
89
+ br = max(0, min(br, 100)) # clamp brightness to 0–100%
90
+ self._brightness = br / 100.0
91
+ # Set color matrix brightness
92
+ for i, color in enumerate(self._color_buffer):
93
+ # Write pixel buffer before write to ws2812
94
+ self.pixels[i] = self._rgb_to_grb_with_br(color)
95
+ self.draw()
96
+ return f"Set brightness to {br}%"
97
+
98
+
99
+ def draw_colormap(self, bitmap:list):
100
+ """
101
+ Draw a bitmap on the Neopixel
102
+ bitmap: [(x, y, (r, g, b)),
103
+ (x, y, (r, g, b)), ...]
104
+ """
105
+ if len(bitmap) == 0:
106
+ self.clear()
107
+ return
108
+ for bm in bitmap:
109
+ x, y, color = bm
110
+ self.set_pixel(x, y, color, zigzag=False)
111
+ self.draw()
112
+
113
+
114
+ ##########################################################################################################
115
+ ##########################################################################################################
116
+ # --- Example usage with micrOS framework ---
117
+
118
+ def load(width=8, height=8):
119
+ """
120
+ Load NeoPixelMatrix instance. If not already loaded
121
+ """
122
+ if NeoPixelMatrix.INSTANCE is None:
123
+ NeoPixelMatrix(width=width, height=height, pin=bind_pin('neop'))
124
+ return NeoPixelMatrix.INSTANCE
125
+
126
+
127
+ def pixel(x, y, color=None, show=True):
128
+ """
129
+ Set pixel at (x,y) to RGB color.
130
+ """
131
+ color = NeoPixelMatrix.DEFAULT_COLOR if color is None else color
132
+ matrix = load()
133
+ matrix.set_pixel(x, y, color)
134
+ if show:
135
+ matrix.draw()
136
+ return "Set and draw color"
137
+ return "Set color"
138
+
139
+
140
+ def draw():
141
+ """
142
+ Draw the current frame manually on the screen.
143
+ """
144
+ load().draw()
145
+ return "Draw screen"
146
+
147
+
148
+ def clear():
149
+ """
150
+ Clear the screen.
151
+ """
152
+ load().clear()
153
+ return "Clear screen"
154
+
155
+
156
+ def color_fill(r: int, g: int, b: int):
157
+ """
158
+ Fill the screen with a solid color.
159
+ OR
160
+ Change animation color (when possible)
161
+ """
162
+ return load().color((r, g, b))
163
+
164
+
165
+ def brightness(br: int):
166
+ """
167
+ Change the brightness of the display. (0-100)
168
+ """
169
+ return load().brightness(br)
170
+
171
+
172
+ def control(speed_ms=None, bt_draw:bool=None):
173
+ """
174
+ Change the speed of frame generation for animations.
175
+ """
176
+ data = load().control(play_speed_ms=speed_ms, bt_draw=bt_draw)
177
+ _speed_ms = data.get("player_speed", None)
178
+ return f"Control state: {data} (speed: {_speed_ms}ms)"
179
+
180
+
181
+ def stop():
182
+ """
183
+ Stop the current animation
184
+ """
185
+ return load().stop()
186
+
187
+
188
+ def draw_colormap(bitmap):
189
+ try:
190
+ load().draw_colormap(bitmap)
191
+ except Exception as e:
192
+ return str(e)
193
+ return "Done."
194
+ # -----------------------------------------------------------------------------
195
+ # -----------------------------------------------------------------------------
196
+
197
+
198
+ def rainbow(speed_ms=0):
199
+ def effect_rainbow():
200
+ def hsv_to_rgb(h, s, v):
201
+ max_color = 150 #255
202
+ h = float(h)
203
+ s = float(s)
204
+ v = float(v)
205
+ i = int(h * 6.0)
206
+ f = (h * 6.0) - i
207
+ p = v * (1.0 - s)
208
+ q = v * (1.0 - s * f)
209
+ t = v * (1.0 - s * (1.0 - f))
210
+ i = i % 6
211
+ if i == 0:
212
+ r, g, b = v, t, p
213
+ elif i == 1:
214
+ r, g, b = q, v, p
215
+ elif i == 2:
216
+ r, g, b = p, v, t
217
+ elif i == 3:
218
+ r, g, b = p, q, v
219
+ elif i == 4:
220
+ r, g, b = t, p, v
221
+ elif i == 5:
222
+ r, g, b = v, p, q
223
+ return int(r * max_color), int(g * max_color), int(b * max_color)
224
+
225
+ width = 8
226
+ height = 8
227
+ total_frames = 64
228
+
229
+ for frame in range(total_frames):
230
+ for y in range(height):
231
+ for x in range(width):
232
+ index = y * width + x
233
+ hue = ((index + frame) % 64) / 64.0
234
+ r, g, b = hsv_to_rgb(hue, 1.0, 0.7)
235
+ yield x, y, (r, g, b)
236
+
237
+ return load().play(effect_rainbow, speed_ms=speed_ms, bt_draw=True, bt_size=8)
238
+
239
+
240
+ def snake(speed_ms:int=50, length:int=5):
241
+ def effect_snake():
242
+ clear_color = (0, 0, 0)
243
+ total_steps = 8 * 8 + length # run just past the end to clear tail
244
+
245
+ for step in range(total_steps):
246
+ # 1) clear the tail pixel once the snake is longer than `length`
247
+ if step >= length:
248
+ tail_idx = step - length
249
+ tx, ty = tail_idx % 8, tail_idx // 8
250
+ yield tx, ty, clear_color
251
+
252
+ # 2) move the head (only while head index < 64)
253
+ if step < 8 * 8:
254
+ hx, hy = step % 8, step // 8
255
+ yield hx, hy, NeoPixelMatrix.DEFAULT_COLOR
256
+
257
+ return load().play(effect_snake, speed_ms=speed_ms)
258
+
259
+
260
+ def cube(speed_ms=10):
261
+ def effect_cube(max_radius:int = 3):
262
+ """
263
+ Generator yielding (x, y, color) for a centered 2×2 square ("cube")
264
+ that expands outward to `max_radius` then collapses back.
265
+ """
266
+ width, height = 8, 8
267
+ # Center the 2×2 core in an 8×8 grid
268
+ cx, cy = width // 2 - 1, height // 2 - 1
269
+
270
+ # Expansion phase: radius 0 (2×2) up to max_radius
271
+ for r in range(0, max_radius + 1):
272
+ for dx in range(-r, r + 2):
273
+ for dy in range(-r, r + 2):
274
+ x, y = cx + dx, cy + dy
275
+ if 0 <= x < width and 0 <= y < height:
276
+ yield x, y, NeoPixelMatrix.DEFAULT_COLOR
277
+ # Clear matrix
278
+ try:
279
+ NeoPixelMatrix.INSTANCE.clear()
280
+ except:
281
+ pass
282
+ # Collapse phase: back down, skipping the largest to avoid duplicate
283
+ for r in range(max_radius - 1, -1, -1):
284
+ for dx in range(-r, r + 2):
285
+ for dy in range(-r, r + 2):
286
+ x, y = cx + dx, cy + dy
287
+ if 0 <= x < width and 0 <= y < height:
288
+ yield x, y, NeoPixelMatrix.DEFAULT_COLOR
289
+
290
+ return load().play(effect_cube, speed_ms=speed_ms)
291
+
292
+
293
+ def help(widgets=False):
294
+ return resolve(('load width=8 height=8',
295
+ 'pixel x y color=(10, 3, 0) show=True',
296
+ 'BUTTON clear',
297
+ 'COLOR color_fill r=<0-255-5> g=<0-255-5> b=<0-255-5>',
298
+ 'SLIDER brightness br=<0-60-2>',
299
+ 'BUTTON stop',
300
+ 'BUTTON snake speed_ms=50 length=5',
301
+ 'BUTTON rainbow',
302
+ 'BUTTON cube speed_ms=10',
303
+ 'SLIDER control speed_ms=<2-200-2> bt_draw=None',
304
+ 'draw_colormap bitmap=[(0,0,(10,2,0)),(x,y,color),...]'
305
+ ), widgets=widgets)
@@ -1,8 +1,7 @@
1
1
  from neopixel import NeoPixel
2
2
  from machine import Pin
3
- from sys import platform
4
3
  from utime import sleep_ms
5
- from Common import transition_gen, micro_task
4
+ from Common import transition_gen, micro_task, data_dir
6
5
  from microIO import bind_pin, pinmap_search
7
6
  from random import randint
8
7
  from Types import resolve
@@ -19,6 +18,7 @@ class Data:
19
18
  PERSISTENT_CACHE = False
20
19
  RGB_TASK_TAG = "neopixel._tran"
21
20
  TASK_STATE = False
21
+ FILE_CACHE = data_dir('neopixel.cache')
22
22
 
23
23
 
24
24
  #########################################
@@ -40,7 +40,7 @@ def __init_NEOPIXEL(pin=None, n=24):
40
40
 
41
41
  def __persistent_cache_manager(mode):
42
42
  """
43
- pds - persistent data structure
43
+ File cache
44
44
  modes:
45
45
  r - recover, s - save
46
46
  """
@@ -48,12 +48,12 @@ def __persistent_cache_manager(mode):
48
48
  return
49
49
  if mode == 's':
50
50
  # SAVE CACHE
51
- with open('neopixel.pds', 'w') as f:
51
+ with open(Data.FILE_CACHE, 'w') as f:
52
52
  f.write(','.join([str(k) for k in Data.DCACHE]))
53
53
  return
54
54
  try:
55
55
  # RESTORE CACHE
56
- with open('neopixel.pds', 'r') as f:
56
+ with open(Data.FILE_CACHE, 'r') as f:
57
57
  Data.DCACHE = [float(data) for data in f.read().strip().split(',')]
58
58
  except:
59
59
  pass
@@ -77,7 +77,7 @@ def load(ledcnt=24, pin=None, cache=True):
77
77
  Initiate NeoPixel RGB module
78
78
  :param ledcnt: number of led segments
79
79
  :param pin: optional number to overwrite default pin
80
- :param cache: default True, store stages on disk + Load (.pds)
80
+ :param cache: default True, store stages on disk + Load (.cache)
81
81
  :return str: Cache state
82
82
  """
83
83
  Data.PERSISTENT_CACHE = cache
@@ -182,13 +182,13 @@ def segment(r=None, g=None, b=None, s=0, cache=False, write=True):
182
182
  :param g: green value 0-255
183
183
  :param b: blue value 0-255
184
184
  :param s: segment - index 0-ledcnt
185
- :param cache: cache color (update .pds file)
185
+ :param cache: cache color (update .cache file)
186
186
  :param write: send color buffer to neopixel (update LEDs)
187
187
  :return dict: rgb status - states: R, G, B, S
188
188
  """
189
- r = Data.DCACHE[0] if r is None else r
190
- g = Data.DCACHE[1] if g is None else g
191
- b = Data.DCACHE[2] if b is None else b
189
+ r = int(Data.DCACHE[0]) if r is None else r
190
+ g = int(Data.DCACHE[1]) if g is None else g
191
+ b = int(Data.DCACHE[2]) if b is None else b
192
192
  neo_n = __init_NEOPIXEL().n
193
193
  if s <= neo_n:
194
194
  Data.NEOPIXEL_OBJ[s] = (r, g, b)
@@ -1,6 +1,7 @@
1
1
  from sys import modules
2
2
  from Common import socket_stream
3
3
  from Files import _is_module, list_fs, ilist_fs, remove_fs
4
+ from Files import OSPath, path_join
4
5
 
5
6
 
6
7
  #############################################
@@ -42,7 +43,7 @@ def rm(path, allow_dir=False):
42
43
  def dirtree(path="/", raw=False):
43
44
  """Return only directories from a given path."""
44
45
  path = path if path.endswith('/') else f"{path}/"
45
- folders = [f"{path}/{item}" for item in ilist_fs(path, type_filter='d')]
46
+ folders = [path_join(path, item) for item in ilist_fs(path, type_filter='d')]
46
47
  folder_contents = {folder:list_fs(folder) for folder in folders}
47
48
  if raw:
48
49
  return folder_contents
@@ -124,39 +125,42 @@ def moduls(unload=None):
124
125
 
125
126
 
126
127
  @socket_stream
127
- def cachedump(delpds=None, msgobj=None):
128
+ def cachedump(delete=None, msgobj=None):
128
129
  """
129
- Cache system persistent data storage files (.pds)
130
- :param delpds: cache name to delete
130
+ Cache system persistent data storage files (.cache)
131
+ :param delete: cache name to delete
131
132
  """
132
- if delpds is None:
133
- # List pds files aka application cache
133
+ data_dir = OSPath.DATA
134
+ if delete is None:
135
+ # List cache files aka application cache
134
136
  msg_buf = []
135
- for pds in (_pds for _pds in ilist_fs(type_filter='f') if _pds.endswith('.pds')):
136
- with open(pds, 'r') as f:
137
+ for cache in (c for c in ilist_fs(data_dir, type_filter='f') if c.endswith('.cache')):
138
+ _path = path_join(data_dir, cache)
139
+ with open(_path, 'r') as f:
137
140
  if msgobj is None:
138
- msg_buf.append(f'{pds}: {f.read()}')
141
+ msg_buf.append(f'{_path}: {f.read()}')
139
142
  else:
140
- msgobj(f'{pds}: {f.read()}')
143
+ msgobj(f'{_path}: {f.read()}')
141
144
  return msg_buf if len(msg_buf) > 0 else ''
142
- # Remove given pds file
145
+ # Remove given cache file
143
146
  try:
144
- verdict = remove_fs(f'{delpds}.pds')
145
- return f'{delpds}.pds delete done.: {verdict}'
147
+ delete_cache = path_join(data_dir, f"{delete}.cache")
148
+ verdict = remove_fs(delete_cache)
149
+ return f'{delete_cache} delete done.: {verdict}'
146
150
  except:
147
- return f'{delpds}.pds not exists'
151
+ return f'{delete}.cache not exists'
148
152
 
149
153
 
150
- def dat_dump():
154
+ def datdump():
151
155
  """
152
156
  Generic .dat file dump
153
- - logged data from LMs, sensor datat, etc...
157
+ - logged data from LMs, sensor data, etc...
154
158
  """
155
- logs_dir = "/logs/"
156
- dats = (f for f in ilist_fs(type_filter='f') if f.endswith('.dat'))
159
+ data_dir = OSPath.DATA
160
+ dats = (f for f in ilist_fs(data_dir, type_filter='f') if f.endswith('.dat'))
157
161
  out = {}
158
162
  for dat in dats:
159
- with open(f"{logs_dir}{dat}", 'r') as f:
163
+ with open(path_join(data_dir, dat), 'r') as f:
160
164
  out[dat] = f.read()
161
165
  return out
162
166
 
@@ -220,8 +224,8 @@ def help(widgets=False):
220
224
  """
221
225
  return ('listmods', 'delmod mod=<module>.py/.mpy or .js/.html/.css', 'del_duplicates',
222
226
  'moduls unload="LM_rgb/None"',
223
- 'cachedump delpds="rgb/None"',
224
- 'dat_dump',
227
+ 'cachedump delete=None',
228
+ 'datdump',
225
229
  'download url="BxNxM/micrOS/master/toolkit/workspace/precompiled/LM_robustness.py"',
226
230
  'micros_checksum',
227
231
  'ls path="/" content="*/f/d" select="*/LM/IO"',
@@ -0,0 +1,204 @@
1
+ """
2
+ A simple driver for the QMI8658 IMU.
3
+ https://github.com/echo-lalia/qmi8658-micropython/blob/main/qmi8685.py
4
+ """
5
+
6
+ import struct
7
+ import time
8
+ from machine import Pin, I2C
9
+ from micropython import const
10
+ from microIO import bind_pin, pinmap_search
11
+
12
+
13
+ # Sensor constants
14
+ _QMI8685_PARTID = const(0x05)
15
+ _REG_PARTID = const(0x00)
16
+ _REG_REVISION = const(0x01)
17
+
18
+ _REG_CTRL1 = const(0x02) # Serial interface and sensor enable
19
+ _REG_CTRL2 = const(0x03) # Accelerometer settings
20
+ _REG_CTRL3 = const(0x04) # Gyroscope settings
21
+ _REG_CTRL4 = const(0x05) # Magnetomer settings (support not implemented in this driver yet)
22
+ _REG_CTRL5 = const(0x06) # Sensor data processing settings
23
+ _REG_CTRL6 = const(0x07) # Attitude Engine ODR and Motion on Demand
24
+ _REG_CTRL7 = const(0x08) # Enable Sensors and Configure Data Reads
25
+
26
+ _REG_TEMP = const(0x33) # Temperature sensor.
27
+
28
+ _REG_AX_L = const(0x35) # Read accelerometer
29
+ _REG_AX_H = const(0x36)
30
+ _REG_AY_L = const(0x37)
31
+ _REG_AY_H = const(0x38)
32
+ _REG_AZ_L = const(0x39)
33
+ _REG_AZ_H = const(0x3A)
34
+
35
+ _REG_GX_L = const(0x3B) # read gyro
36
+ _REG_GX_H = const(0x3C)
37
+ _REG_GY_L = const(0x3D)
38
+ _REG_GY_H = const(0x3E)
39
+ _REG_GZ_L = const(0x3F)
40
+ _REG_GZ_H = const(0x40)
41
+
42
+ _QMI8658_I2CADDR_DEFAULT = const(0X6B)
43
+
44
+
45
+ _ACCELSCALE_RANGE_2G = const(0b00)
46
+ _ACCELSCALE_RANGE_4G = const(0b01)
47
+ _ACCELSCALE_RANGE_8G = const(0b10)
48
+ _ACCELSCALE_RANGE_16G = const(0b11)
49
+
50
+ _GYROSCALE_RANGE_16DPS = const(0b000)
51
+ _GYROSCALE_RANGE_32DPS = const(0b001)
52
+ _GYROSCALE_RANGE_64DPS = const(0b010)
53
+ _GYROSCALE_RANGE_128DPS = const(0b011)
54
+ _GYROSCALE_RANGE_256DPS = const(0b100)
55
+ _GYROSCALE_RANGE_512DPS = const(0b101)
56
+ _GYROSCALE_RANGE_1024DPS = const(0b110)
57
+ _GYROSCALE_RANGE_2048DPS = const(0b111)
58
+
59
+ _ODR_8000HZ = const(0b0000)
60
+ _ODR_4000HZ = const(0b0001)
61
+ _ODR_2000HZ = const(0b0010)
62
+ _ODR_1000HZ = const(0b0011)
63
+ _ODR_500HZ = const(0b0100)
64
+ _ODR_250HZ = const(0b0101)
65
+ _ODR_125HZ = const(0b0110)
66
+ _ODR_62_5HZ = const(0b0111)
67
+
68
+
69
+ class QMI8658:
70
+ """QMI8658 inertial measurement unit."""
71
+ INSTANCE = None
72
+
73
+ def __init__(
74
+ self,
75
+ i2c_bus: I2C,
76
+ address: int = _QMI8658_I2CADDR_DEFAULT,
77
+ accel_scale: int = _ACCELSCALE_RANGE_8G,
78
+ gyro_scale: int = _GYROSCALE_RANGE_256DPS):
79
+ """Read from a sensor on the given I2C bus, at the given address."""
80
+ self.i2c = i2c_bus
81
+ self.address = address
82
+ # Cache the sensor instance globally for easy access
83
+ QMI8658.INSTANCE = self
84
+
85
+ # Verify sensor part ID
86
+ if self._read_u8(_REG_PARTID) != _QMI8685_PARTID:
87
+ raise AttributeError("Cannot find a QMI8658")
88
+
89
+ # Setup initial configuration
90
+ self._configure_sensor(accel_scale, gyro_scale)
91
+
92
+ # Configure scales/divisors for the driver
93
+ self.acc_scale_divisor = {
94
+ _ACCELSCALE_RANGE_2G: 1 << 14,
95
+ _ACCELSCALE_RANGE_4G: 1 << 13,
96
+ _ACCELSCALE_RANGE_8G: 1 << 12,
97
+ _ACCELSCALE_RANGE_16G: 1 << 11,
98
+ }[accel_scale]
99
+
100
+ self.gyro_scale_divisor = {
101
+ _GYROSCALE_RANGE_16DPS: 2048,
102
+ _GYROSCALE_RANGE_32DPS: 1024,
103
+ _GYROSCALE_RANGE_64DPS: 512,
104
+ _GYROSCALE_RANGE_128DPS: 256,
105
+ _GYROSCALE_RANGE_256DPS: 128,
106
+ _GYROSCALE_RANGE_512DPS: 64,
107
+ _GYROSCALE_RANGE_1024DPS: 32,
108
+ _GYROSCALE_RANGE_2048DPS: 16,
109
+ }[gyro_scale]
110
+
111
+
112
+ def _configure_sensor(self, accel_scale: int, gyro_scale: int):
113
+ # Initialize accelerometer and gyroscope settings
114
+ self._write_u8(_REG_CTRL1, 0x60) # Set SPI auto increment and big endian (Ctrl 1)
115
+ self._write_u8(_REG_CTRL2, (accel_scale << 4) | _ODR_1000HZ) # Accel Config
116
+ self._write_u8(_REG_CTRL3, (gyro_scale << 4) | _ODR_1000HZ) # Gyro Config
117
+ self._write_u8(_REG_CTRL5, 0x01) # Low-pass filter enable
118
+ self._write_u8(_REG_CTRL7, 0x03) # Enable accel and gyro
119
+ time.sleep_ms(100)
120
+
121
+
122
+ # Helper functions for register operations
123
+ def _read_u8(self, reg:int) -> int:
124
+ return self.i2c.readfrom_mem(self.address, reg, 1)[0]
125
+
126
+ def _read_xyz(self, reg:int) -> tuple[int, int, int]:
127
+ data = self.i2c.readfrom_mem(self.address, reg, 6)
128
+ return struct.unpack('<hhh', data)
129
+
130
+ def _write_u8(self, reg: int, value: int):
131
+ self.i2c.writeto_mem(self.address, reg, bytes([value]))
132
+
133
+
134
+ @property
135
+ def temperature(self) -> float:
136
+ """Get the device temperature."""
137
+ temp_raw = self._read_u8(_REG_TEMP)
138
+ return temp_raw / 256
139
+
140
+ @property
141
+ def acceleration(self) -> tuple[float, float, float]:
142
+ """Get current acceleration reading."""
143
+ raw_accel = self._read_xyz(_REG_AX_L)
144
+ return tuple(val / self.acc_scale_divisor for val in raw_accel)
145
+
146
+ @property
147
+ def gyro(self) -> tuple[float, float, float]:
148
+ """Get current gyroscope reading."""
149
+ raw_gyro = self._read_xyz(_REG_GX_L)
150
+ return tuple(val / self.gyro_scale_divisor for val in raw_gyro)
151
+
152
+ #######################
153
+ # Public functions #
154
+ #######################
155
+
156
+ def load():
157
+ """
158
+ Load the QMI8658 sensor instance.
159
+ The QMI8658 is a motion sensor that measures acceleration, angular velocity (gyroscope), and temperature
160
+ """
161
+ if QMI8658.INSTANCE is None:
162
+ QMI8658(I2C(0, sda=Pin(bind_pin('i2c_sda')), scl=Pin(bind_pin('i2c_scl'))))
163
+ return QMI8658.INSTANCE
164
+
165
+
166
+ def temperature():
167
+ return load().temperature
168
+
169
+
170
+ def acceleration():
171
+ return load().acceleration
172
+
173
+
174
+ def gyro():
175
+ return load().gyro
176
+
177
+
178
+ def measure():
179
+ inst = load()
180
+ return {"temp": inst.temperature, "accel": inst.acceleration, "gyro": inst.gyro}
181
+
182
+
183
+ #######################
184
+ # LM helper functions #
185
+ #######################
186
+
187
+ def pinmap():
188
+ """
189
+ [i] micrOS LM naming convention
190
+ Shows logical pins - pin number(s) used by this Load module
191
+ - info which pins to use for this application
192
+ :return dict: pin name (str) - pin value (int) pairs
193
+ """
194
+ return pinmap_search(['i2c_scl', 'i2c_sda'])
195
+
196
+
197
+ def help(widgets=False):
198
+ """
199
+ [i] micrOS LM naming convention - built-in help message
200
+ :return tuple:
201
+ (widgets=False) list of functions implemented by this application
202
+ (widgets=True) list of widget json for UI generation
203
+ """
204
+ return 'load', 'temperature', 'acceleration', 'gyro', 'measure', 'pinmap'