micrOSDevToolKit 2.10.6__py3-none-any.whl → 2.13.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 (151) 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 +57 -61
  6. micrOS/source/Common.py +286 -91
  7. micrOS/source/Config.py +7 -7
  8. micrOS/source/Debug.py +50 -94
  9. micrOS/source/Espnow.py +7 -7
  10. micrOS/source/Files.py +23 -2
  11. micrOS/source/Hooks.py +62 -19
  12. micrOS/source/IO_esp32c6.py +16 -0
  13. micrOS/source/IO_esp32s3.py +37 -1
  14. micrOS/source/IO_m5stamp.py +35 -1
  15. micrOS/source/IO_qtpy.py +22 -17
  16. micrOS/source/IO_s3matrix.py +21 -0
  17. micrOS/source/IO_tinypico.py +38 -0
  18. micrOS/source/InterConnect.py +5 -5
  19. micrOS/source/Interrupts.py +2 -2
  20. micrOS/source/LM_VL53L0X.py +1 -1
  21. micrOS/source/LM_buzzer.py +6 -7
  22. micrOS/source/LM_cct.py +6 -5
  23. micrOS/source/LM_dashboard_be.py +2 -2
  24. micrOS/source/LM_dimmer.py +6 -5
  25. micrOS/source/LM_espnow.py +15 -10
  26. micrOS/source/LM_i2c.py +3 -2
  27. micrOS/source/LM_neoeffects.py +173 -230
  28. micrOS/source/LM_neomatrix.py +335 -0
  29. micrOS/source/LM_neopixel.py +10 -10
  30. micrOS/source/LM_pacman.py +40 -23
  31. micrOS/source/LM_qmi8658.py +204 -0
  32. micrOS/source/LM_rgb.py +6 -6
  33. micrOS/source/LM_roboarm.py +5 -4
  34. micrOS/source/LM_switch.py +6 -4
  35. micrOS/source/LM_tcs3472.py +75 -0
  36. micrOS/source/LM_telegram.py +5 -4
  37. micrOS/source/Logger.py +47 -33
  38. micrOS/source/Network.py +6 -6
  39. micrOS/source/Notify.py +2 -2
  40. micrOS/source/Scheduler.py +5 -5
  41. micrOS/source/Server.py +6 -6
  42. micrOS/source/Shell.py +4 -4
  43. micrOS/source/Tasks.py +20 -17
  44. micrOS/source/Time.py +12 -10
  45. micrOS/source/Types.py +2 -2
  46. micrOS/source/Web.py +20 -13
  47. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  48. micrOS/source/__pycache__/Debug.cpython-312.pyc +0 -0
  49. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  50. micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
  51. micrOS/source/micrOS.py +10 -7
  52. micrOS/source/micrOSloader.py +6 -6
  53. micrOS/source/microIO.py +8 -6
  54. micrOS/source/urequests.py +4 -4
  55. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.13.0.dist-info}/METADATA +24 -22
  56. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.13.0.dist-info}/RECORD +142 -132
  57. toolkit/DevEnvCompile.py +20 -15
  58. toolkit/DevEnvOTA.py +29 -8
  59. toolkit/DevEnvUSB.py +52 -10
  60. toolkit/LM_to_compile.dat +1 -0
  61. toolkit/MicrOSDevEnv.py +10 -2
  62. toolkit/MicrosFiles.py +26 -0
  63. toolkit/dashboard_apps/NeoEffectsDemo.py +8 -15
  64. toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
  65. toolkit/dashboard_apps/_app_base.py +2 -2
  66. toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
  67. toolkit/lib/LocalMachine.py +6 -1
  68. toolkit/lib/file_extensions.py +9 -3
  69. toolkit/micrOSlint.py +3 -1
  70. toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
  71. toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
  72. toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
  73. toolkit/simulator_lib/machine.py +0 -1
  74. toolkit/simulator_lib/neopixel.py +3 -2
  75. toolkit/socketClient.py +3 -2
  76. toolkit/workspace/precompiled/Common.cpython-312.pyc +0 -0
  77. toolkit/workspace/precompiled/Common.mpy +0 -0
  78. toolkit/workspace/precompiled/Config.mpy +0 -0
  79. toolkit/workspace/precompiled/Debug.mpy +0 -0
  80. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  81. toolkit/workspace/precompiled/Files.mpy +0 -0
  82. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  83. toolkit/workspace/precompiled/IO_esp32c6.mpy +0 -0
  84. toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
  85. toolkit/workspace/precompiled/IO_m5stamp.mpy +0 -0
  86. toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
  87. toolkit/workspace/precompiled/IO_s3matrix.mpy +0 -0
  88. toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
  89. toolkit/workspace/precompiled/InterConnect.mpy +0 -0
  90. toolkit/workspace/precompiled/Interrupts.mpy +0 -0
  91. toolkit/workspace/precompiled/LM_VL53L0X.py +1 -1
  92. toolkit/workspace/precompiled/LM_buzzer.mpy +0 -0
  93. toolkit/workspace/precompiled/LM_cct.mpy +0 -0
  94. toolkit/workspace/precompiled/LM_dashboard_be.py +2 -2
  95. toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
  96. toolkit/workspace/precompiled/LM_espnow.py +15 -10
  97. toolkit/workspace/precompiled/LM_i2c.py +3 -2
  98. toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
  99. toolkit/workspace/precompiled/LM_neomatrix.mpy +0 -0
  100. toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
  101. toolkit/workspace/precompiled/LM_pacman.mpy +0 -0
  102. toolkit/workspace/precompiled/LM_qmi8658.py +204 -0
  103. toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
  104. toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
  105. toolkit/workspace/precompiled/LM_switch.mpy +0 -0
  106. toolkit/workspace/precompiled/LM_tcs3472.py +75 -0
  107. toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
  108. toolkit/workspace/precompiled/Logger.cpython-312.pyc +0 -0
  109. toolkit/workspace/precompiled/Logger.mpy +0 -0
  110. toolkit/workspace/precompiled/Network.mpy +0 -0
  111. toolkit/workspace/precompiled/Notify.mpy +0 -0
  112. toolkit/workspace/precompiled/Scheduler.mpy +0 -0
  113. toolkit/workspace/precompiled/Server.cpython-312.pyc +0 -0
  114. toolkit/workspace/precompiled/Server.mpy +0 -0
  115. toolkit/workspace/precompiled/Shell.mpy +0 -0
  116. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  117. toolkit/workspace/precompiled/Time.mpy +0 -0
  118. toolkit/workspace/precompiled/Types.mpy +0 -0
  119. toolkit/workspace/precompiled/Web.mpy +0 -0
  120. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  121. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  122. toolkit/workspace/precompiled/microIO.mpy +0 -0
  123. toolkit/workspace/precompiled/urequests.mpy +0 -0
  124. micrOS/micropython/esp32s3-20240105-v1.22.1.bin +0 -0
  125. micrOS/source/LM_catgame.py +0 -75
  126. micrOS/source/LM_demo.py +0 -97
  127. micrOS/source/LM_intercon.py +0 -60
  128. micrOS/source/LM_ph_sensor.py +0 -51
  129. toolkit/workspace/precompiled/LM_catgame.py +0 -75
  130. toolkit/workspace/precompiled/LM_demo.py +0 -97
  131. toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
  132. toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
  133. /micrOS/micropython/{esp32s3-20241129-v1.24.1.bin → esp32s3-8MBflash-20241129-v1.24.1.bin} +0 -0
  134. /micrOS/source/{dashboard.html → web/dashboard.html} +0 -0
  135. /micrOS/source/{index.html → web/index.html} +0 -0
  136. /micrOS/source/{uapi.js → web/uapi.js} +0 -0
  137. /micrOS/source/{udashboard.js → web/udashboard.js} +0 -0
  138. /micrOS/source/{ustyle.css → web/ustyle.css} +0 -0
  139. /micrOS/source/{uwidgets.js → web/uwidgets.js} +0 -0
  140. /micrOS/source/{uwidgets_pro.js → web/uwidgets_pro.js} +0 -0
  141. {microsdevtoolkit-2.10.6.data → microsdevtoolkit-2.13.0.data}/scripts/devToolKit.py +0 -0
  142. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.13.0.dist-info}/WHEEL +0 -0
  143. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.13.0.dist-info}/licenses/LICENSE +0 -0
  144. {microsdevtoolkit-2.10.6.dist-info → microsdevtoolkit-2.13.0.dist-info}/top_level.txt +0 -0
  145. /toolkit/workspace/precompiled/{dashboard.html → web/dashboard.html} +0 -0
  146. /toolkit/workspace/precompiled/{index.html → web/index.html} +0 -0
  147. /toolkit/workspace/precompiled/{uapi.js → web/uapi.js} +0 -0
  148. /toolkit/workspace/precompiled/{udashboard.js → web/udashboard.js} +0 -0
  149. /toolkit/workspace/precompiled/{ustyle.css → web/ustyle.css} +0 -0
  150. /toolkit/workspace/precompiled/{uwidgets.js → web/uwidgets.js} +0 -0
  151. /toolkit/workspace/precompiled/{uwidgets_pro.js → web/uwidgets_pro.js} +0 -0
micrOS/source/Common.py CHANGED
@@ -3,94 +3,25 @@ micrOS Load Module programming Official API-s
3
3
  Designed by Marcell Ban aka BxNxM
4
4
  """
5
5
  from Server import Server, WebCli
6
- from Debug import errlog_add, console_write
6
+ from Debug import syslog as debug_syslog, console_write
7
7
  from Logger import logger, log_get
8
+ from Files import OSPath, path_join
8
9
  from microIO import resolve_pin
9
10
  from Tasks import TaskBase, Manager, lm_exec
10
11
  from machine import Pin, ADC
11
12
  from Notify import Notify
12
13
 
13
- ################## Common LM features ##################
14
14
 
15
- def transition(from_val, to_val, step_ms, interval_sec):
16
- """
17
- [LM] Single Generator for color/value transition:
18
- :param from_val: from value - start from
19
- :param to_val: to value - target value
20
- :param step_ms: step to reach to_val - timirq_seq
21
- :param interval_sec: time of full interval
22
- """
23
- if interval_sec > 0:
24
- step_cnt = round((interval_sec*1000)/step_ms)
25
- delta = abs((from_val-to_val)/step_cnt)
26
- direc = -1 if from_val > to_val else 1
27
- for cnt in range(0, step_cnt+1):
28
- yield round(from_val + (cnt * delta) * direc)
29
- else:
30
- yield round(to_val)
15
+ #####################################################################################
16
+ # SYSTEM #
17
+ #####################################################################################
31
18
 
32
-
33
- def transition_gen(*args, interval_sec=1.0):
34
- """
35
- [LM] Multiple Generator for color/value transitions:
36
- - calculate minimum step count -> step_ms
37
- - autofill and use transition(from_val, to_val, step_ms, interval_sec)
38
- :param args: ch1_from, ch1_to, ch2_from, ch2_to, etc...
39
- :param interval_sec: interval in sec to calculate optimal fade/transition effect
40
- return: gen, step_ms OR gen list, step_ms
19
+ def micro_task(tag:str, task=None):
41
20
  """
42
- step_ms_min = 5 # min calculated step is 5 ms - good enough
43
- delta = max((abs(args[ch_from_i] - args[ch_from_i+1]) for ch_from_i in range(0, len(args)-1, 2)))
44
- step_ms = 0 if delta == 0 else int(interval_sec*1000 / delta)
45
- step_ms = step_ms_min if step_ms < step_ms_min else step_ms
46
- transitions = list((transition(args[ch_from_i], args[ch_from_i+1], step_ms, interval_sec) for ch_from_i in range(0, len(args)-1, 2)))
47
- if len(transitions) == 1:
48
- return transitions[0], step_ms
49
- return list(transitions), step_ms
50
-
51
-
52
- class SmartADC:
53
- """
54
- [LM] General ADC implementation for auto scaled output: raw, percent, volt
55
- https://docs.micropython.org/en/latest/esp32/quickref.html#adc-analog-to-digital-conversion
56
- ADC.ATTN_0DB: 0 dB attenuation, resulting in a full-scale voltage range of 0-1.1V
57
- ADC.ATTN_2_5DB: 2.5 dB ... of 0-1.5V
58
- ADC.ATTN_6DB: 6 dB ... of 0-2.2V
59
- ADC.ATTN_11DB: 11 dB ... of 0-2450mV/
60
- Note that the absolute maximum voltage rating for input pins is 3.6V. Going near to this boundary risks damage to the IC!
61
- """
62
- OBJS = {}
63
-
64
- def __init__(self, pin):
65
- self.adp_prop = (65535, 2450) # raw value, 2450mV (so 2,45V)
66
- self.adc = None
67
- if not isinstance(pin, int):
68
- pin = resolve_pin(pin)
69
- self.adc = ADC(Pin(pin))
70
- self.adc.atten(ADC.ATTN_11DB) # 2450mV measure range
71
-
72
- def get(self):
73
- raw = int((self.adc.read_u16() + self.adc.read_u16())/2) # 16-bit ADC value (0-65535)
74
- percent = raw / self.adp_prop[0]
75
- volt = round(percent * self.adp_prop[1] / 1000, 2) # devide with 1000 to get V from mV
76
- return {'raw': raw, 'percent': round(percent*100, 1), 'volt': volt}
77
-
78
- @staticmethod
79
- def get_instance(pin):
80
- if pin in SmartADC.OBJS.keys():
81
- return SmartADC.OBJS[pin]
82
- SmartADC.OBJS[pin] = SmartADC(pin)
83
- return SmartADC.OBJS[pin]
84
-
85
- ################# micrOS feature interfaces #################
86
-
87
- def micro_task(tag, task=None):
88
- """
89
- [LM] Async task creation
90
- :param tag:
91
- [1] tag=None: return task generator object
92
- [2] tag=taskID: return existing task object by tag
93
- :param task: coroutine to execute (with built-in overload protection and lcm)
21
+ [LM] Async task manager.
22
+ :param tag: task tag string
23
+ :param task: coroutine (or list of command arguments) to contract a task with the given async task callback
24
+ return bool|callable
94
25
  """
95
26
  if task is None:
96
27
  # [1] Task is None -> Get task mode by tag
@@ -103,11 +34,11 @@ def micro_task(tag, task=None):
103
34
  return None
104
35
  # [3] Create task (not running) + task coroutine was provided
105
36
  # RETURN task creation state - success (True) / fail (False)
106
- state = Manager().create_task(callback=task, tag=tag)
37
+ state:bool = Manager().create_task(callback=task, tag=tag)
107
38
  return state
108
39
 
109
40
 
110
- def manage_task(tag, operation):
41
+ def manage_task(tag:str, operation:str):
111
42
  """
112
43
  [LM] Async task management
113
44
  :param tag: task tag
@@ -125,19 +56,15 @@ def manage_task(tag, operation):
125
56
  raise Exception(f"Invalid operation: {operation}")
126
57
 
127
58
 
128
- def exec_cmd(cmd:list, jsonify:bool=None, skip_check=False):
59
+ def exec_cmd(cmd:list, jsonify:bool=None, skip_check=None):
129
60
  """
130
61
  [LM] Single (sync) LM execution
131
62
  :param cmd: command string list, ex.: ['system', 'clock']
132
63
  :param jsonify: request json output
133
- :param skip_check: skip cmd type check, micropython bug
64
+ :param skip_check: legacy (check was removed) - remove parameter
134
65
  return state, output
135
66
  """
136
- # [BUG] Solution with isinstance/type is not reliable... micropython 1.22
137
- # Invalid type, must be list: <class list>" ...
138
- if skip_check:
139
- return lm_exec(cmd, jsonify=jsonify)
140
- return lm_exec(cmd, jsonify=jsonify) if isinstance(cmd, list) else False, f"CMD {type(cmd)}, must be list!"
67
+ return lm_exec(cmd, jsonify=jsonify)
141
68
 
142
69
 
143
70
  def notify(text=None) -> bool:
@@ -153,7 +80,7 @@ def notify(text=None) -> bool:
153
80
  try:
154
81
  out = Notify.notify(text)
155
82
  except Exception as e:
156
- errlog_add(f"[ERR] Notify: {e}")
83
+ debug_syslog(f"[ERR] Notify: {e}")
157
84
  out = str(e)
158
85
  if out is not None and (out.startswith('Sent') or out.endswith('disabled')):
159
86
  return True
@@ -209,10 +136,278 @@ def data_logger(f_name, data=None, limit=12, msgobj=None):
209
136
 
210
137
 
211
138
  def syslog(msg):
212
- """ Wrapper of errlog_add """
213
- return errlog_add(f"{msg}")
139
+ """ Wrapper of debug_syslog """
140
+ return debug_syslog(f"{msg}")
214
141
 
215
142
 
216
143
  def console(msg):
217
144
  """ Wrapper of console_write """
218
145
  return console_write(msg)
146
+
147
+
148
+ def data_dir(f_name=None):
149
+ """
150
+ Access for data dir path
151
+ :param f_name: if given, returns full path, otherwise returns data dir root path
152
+ """
153
+ root_path = OSPath.DATA
154
+ if f_name is None:
155
+ return root_path
156
+ return path_join(root_path, f_name)
157
+
158
+ def web_dir(f_name=None):
159
+ """
160
+ Access for web dir path
161
+ :param f_name: if given, returns full path, otherwise returns web dir root path
162
+ """
163
+ root_path = OSPath.WEB
164
+ if f_name is None:
165
+ return root_path
166
+ return path_join(root_path, f_name)
167
+
168
+ #####################################################################################
169
+ # CHANNEL: SIGNAL GENERATORS #
170
+ #####################################################################################
171
+
172
+ def transition(from_val, to_val, step_ms, interval_sec):
173
+ """
174
+ [LM] Single Generator for color/value transition:
175
+ :param from_val: from value - start from
176
+ :param to_val: to value - target value
177
+ :param step_ms: step to reach to_val - timirq_seq
178
+ :param interval_sec: time of full interval
179
+ """
180
+ if interval_sec > 0:
181
+ step_cnt = round((interval_sec*1000)/step_ms)
182
+ delta = abs((from_val-to_val)/step_cnt)
183
+ direc = -1 if from_val > to_val else 1
184
+ for cnt in range(0, step_cnt+1):
185
+ yield round(from_val + (cnt * delta) * direc)
186
+ else:
187
+ yield round(to_val)
188
+
189
+
190
+ def transition_gen(*args, interval_sec=1.0):
191
+ """
192
+ [LM] Multiple Generator for color/value transitions:
193
+ - calculate minimum step count -> step_ms
194
+ - autofill and use transition(from_val, to_val, step_ms, interval_sec)
195
+ :param args: ch1_from, ch1_to, ch2_from, ch2_to, etc...
196
+ :param interval_sec: interval in sec to calculate optimal fade/transition effect
197
+ return: gen, step_ms OR gen list, step_ms
198
+ """
199
+ step_ms_min = 5 # min calculated step is 5 ms - good enough
200
+ delta = max((abs(args[ch_from_i] - args[ch_from_i+1]) for ch_from_i in range(0, len(args)-1, 2)))
201
+ step_ms = 0 if delta == 0 else int(interval_sec*1000 / delta)
202
+ step_ms = step_ms_min if step_ms < step_ms_min else step_ms
203
+ transitions = list((transition(args[ch_from_i], args[ch_from_i+1], step_ms, interval_sec) for ch_from_i in range(0, len(args)-1, 2)))
204
+ if len(transitions) == 1:
205
+ return transitions[0], step_ms
206
+ return list(transitions), step_ms
207
+
208
+ #####################################################################################
209
+ # EXTRAS #
210
+ #####################################################################################
211
+
212
+ class SmartADC:
213
+ """
214
+ [LM] General ADC implementation for auto scaled output: raw, percent, volt
215
+ https://docs.micropython.org/en/latest/esp32/quickref.html#adc-analog-to-digital-conversion
216
+ ADC.ATTN_0DB: 0 dB attenuation, resulting in a full-scale voltage range of 0-1.1V
217
+ ADC.ATTN_2_5DB: 2.5 dB ... of 0-1.5V
218
+ ADC.ATTN_6DB: 6 dB ... of 0-2.2V
219
+ ADC.ATTN_11DB: 11 dB ... of 0-2450mV/
220
+ Note that the absolute maximum voltage rating for input pins is 3.6V. Going near to this boundary risks damage to the IC!
221
+ """
222
+ OBJS = {}
223
+
224
+ def __init__(self, pin):
225
+ self.adp_prop = (65535, 2450) # raw value, 2450mV (so 2,45V)
226
+ self.adc = None
227
+ if not isinstance(pin, int):
228
+ pin = resolve_pin(pin)
229
+ self.adc = ADC(Pin(pin))
230
+ self.adc.atten(ADC.ATTN_11DB) # 2450mV measure range
231
+
232
+ def get(self):
233
+ raw = int((self.adc.read_u16() + self.adc.read_u16())/2) # 16-bit ADC value (0-65535)
234
+ percent = raw / self.adp_prop[0]
235
+ volt = round(percent * self.adp_prop[1] / 1000, 2) # devide with 1000 to get V from mV
236
+ return {'raw': raw, 'percent': round(percent*100, 1), 'volt': volt}
237
+
238
+ @staticmethod
239
+ def get_instance(pin):
240
+ if pin in SmartADC.OBJS.keys():
241
+ return SmartADC.OBJS[pin]
242
+ SmartADC.OBJS[pin] = SmartADC(pin)
243
+ return SmartADC.OBJS[pin]
244
+
245
+
246
+ class AnimationPlayer:
247
+ """
248
+ Generic async animation (generator) player.
249
+ """
250
+
251
+ def __init__(self, animation:callable=None, tag:str=None, batch_draw:bool=False, batch_size:int=None, loop:bool=True):
252
+ """
253
+ Initialize the AnimationPlayer with an optional animation.
254
+ :param animation: Function to GENERATE animation data
255
+ :param tag: Optional task tag for micro_task management.
256
+ :param batch_draw: If True - draw in batches
257
+ :param batch_size: Number of pixels per batch when drawing
258
+ :param loop: If True - loop the animation (default)
259
+ """
260
+ self.animation:callable = None
261
+ self.batch_draw:bool = batch_draw
262
+ self.__max_batch_size:int = 256 # MAX BATCH SIZE - ASYNC PROTECTION
263
+ self.__batch_size:int = 8 # Default batch size: 8
264
+ self.__loop:bool = loop # Loop the animation (generator)
265
+ self._set_batch_size(batch_size) # Set batch size from parameter
266
+ self._player_speed_ms:int = 10 # Default speed in ms between frames
267
+ main_tag:str = tag if tag else "animation"
268
+ self._task_tag:str = f"{main_tag}.player"
269
+ if animation is not None and not self._set_animation(animation):
270
+ raise Exception("Invalid animation function provided.")
271
+ self.__running:bool = True
272
+
273
+ def _set_animation(self, animation:callable) -> bool:
274
+ """
275
+ Setter to change/set current animation.
276
+ """
277
+ if callable(animation):
278
+ self.animation = animation
279
+ return True
280
+ return False
281
+
282
+ def _set_batch_size(self, batch_size:int) -> None:
283
+ """
284
+ Setter to change/set batch size.
285
+ - with max batch size check (due to async event loop feeding)
286
+ """
287
+ if batch_size is None:
288
+ return
289
+ self.__batch_size = max(0, min(batch_size, self.__max_batch_size))
290
+
291
+ async def _render(self, my_task):
292
+ # Cache methods for speed
293
+ clear = self.clear
294
+ update = self.update
295
+ draw = self.draw
296
+ # Cache the current animation for comparison
297
+ current_animation = self.animation
298
+ frame_counter = 0
299
+ # Clear the display before each frame
300
+ if not self.batch_draw:
301
+ clear()
302
+ for data in self.animation():
303
+ # Check if animation has changed under the loop
304
+ if not self.__running or self.animation != current_animation:
305
+ # Animation changed — break — clean and restart animation loop.
306
+ clear()
307
+ break
308
+ # Update data cache
309
+ update(*data)
310
+ if self.batch_draw:
311
+ # Batched draw mode
312
+ frame_counter += 1
313
+ if frame_counter >= self.__batch_size:
314
+ draw()
315
+ frame_counter = 0
316
+ await my_task.feed(sleep_ms=self._player_speed_ms)
317
+ else:
318
+ # Real-time draw mode
319
+ draw()
320
+ await my_task.feed(sleep_ms=self._player_speed_ms)
321
+
322
+ async def _player(self):
323
+ """
324
+ Async task to play the current animation.
325
+ """
326
+ with micro_task(tag=self._task_tag) as my_task:
327
+ while self.__running:
328
+ my_task.out = f"Play {self.animation.__name__} ({self._player_speed_ms}ms/frame)"
329
+ try:
330
+ await self._render(my_task)
331
+ except IndexError:
332
+ # Draw after generator exhausted and Restart animation if IndexError occurs
333
+ self.draw()
334
+ if not self.__loop:
335
+ break
336
+ await my_task.feed(sleep_ms=self._player_speed_ms)
337
+ my_task.out = "Restart animation"
338
+ except Exception as e:
339
+ my_task.out = f"Error: {e}"
340
+ break
341
+ my_task.out = f"Animation stopped...{my_task.out}"
342
+
343
+ def control(self, play_speed_ms:int, bt_draw:bool=None, bt_size:int=None, loop:bool=None):
344
+ """
345
+ Set/Get current play speed of the animation.
346
+ :param play_speed_ms: player loop speed in milliseconds.
347
+ :param bt_draw: batch drawing flag.
348
+ :param bt_size: batch drawing size.
349
+ :param loop: loop flag.
350
+ """
351
+ if isinstance(play_speed_ms, int):
352
+ self._player_speed_ms = max(0, min(10000, int(play_speed_ms)))
353
+ if isinstance(bt_draw, bool):
354
+ self.batch_draw = bt_draw
355
+ if isinstance(bt_size, int):
356
+ self._set_batch_size(bt_size)
357
+ if isinstance(loop, bool):
358
+ self.__loop = loop
359
+ return {"realtime": not self.batch_draw, "batched": self.batch_draw,
360
+ "size": self.__batch_size, "speed_ms": self._player_speed_ms,
361
+ "loop": self.__loop}
362
+
363
+
364
+ def play(self, animation=None, speed_ms=None, bt_draw=False, bt_size=None, loop=True):
365
+ """
366
+ Play animation via generator function.
367
+ :param animation: Animation generator function.
368
+ :param speed_ms: Speed of the animation in milliseconds. (min.: 3ms)
369
+ :param bt_draw: batch drawing flag.
370
+ :param bt_size: batch drawing size.
371
+ :param loop: Loop the animation.
372
+ :return: Player settings.
373
+ """
374
+
375
+ if animation is not None:
376
+ if not self._set_animation(animation):
377
+ return "Invalid animation"
378
+ if self.animation is None:
379
+ return "No animation to play"
380
+ # Handle player settings
381
+ settings = self.control(play_speed_ms=speed_ms, bt_draw=bt_draw, bt_size=bt_size, loop=loop)
382
+ # Ensure async loop set up correctly. (After stop operation, it is needed)
383
+ self.__running = True
384
+ # [!] ASYNC TASK CREATION
385
+ raw_state:bool = micro_task(tag=self._task_tag, task=self._player())
386
+ state = "starting" if raw_state else "running"
387
+ settings["state"] = state
388
+ return settings
389
+
390
+ def stop(self):
391
+ """
392
+ Stop the animation.
393
+ """
394
+ self.__running = False
395
+ return "Stop animation player"
396
+
397
+ def update(self, *arg, **kwargs):
398
+ """
399
+ Child class must implement this method to handle drawing logic.
400
+ """
401
+ raise NotImplementedError("Child class must implement update method.")
402
+
403
+ def draw(self):
404
+ """
405
+ Draw the current frame.
406
+ """
407
+ raise NotImplementedError("Child class must implement draw method.")
408
+
409
+ def clear(self):
410
+ """
411
+ Clear the display.
412
+ """
413
+ raise NotImplementedError("Child class must implement clear method.")
micrOS/source/Config.py CHANGED
@@ -16,11 +16,11 @@ from re import search
16
16
  from json import load, dump
17
17
  from uos import remove
18
18
  from utime import sleep
19
- from Debug import DebugCfg, console_write, errlog_add
19
+ from Debug import DebugCfg, console_write, syslog
20
20
  try:
21
21
  from microIO import set_pinmap
22
22
  except:
23
- errlog_add("[ERR] LogicalPins import: set_pinmap")
23
+ syslog("[ERR] LogicalPins import: set_pinmap")
24
24
  set_pinmap = None
25
25
 
26
26
 
@@ -109,7 +109,7 @@ class Data:
109
109
  Data.write_cfg_file()
110
110
  console_write("[CONF] Save conf successful")
111
111
  except Exception as e:
112
- errlog_add(f"[ERR] Save (__inject) conf failed: {e}")
112
+ syslog(f"[ERR] Save (__inject) conf failed: {e}")
113
113
  finally:
114
114
  del liveconf
115
115
 
@@ -127,7 +127,7 @@ class Data:
127
127
  if nosafe:
128
128
  break
129
129
  sleep(0.2)
130
- errlog_add(f'[ERR] read_cfg_file error: {e}')
130
+ syslog(f'[ERR] read_cfg_file error: {e}')
131
131
  # Return config cache
132
132
  return conf
133
133
 
@@ -140,7 +140,7 @@ class Data:
140
140
  dump(Data.CONFIG_CACHE, f)
141
141
  break
142
142
  except Exception as e:
143
- errlog_add(f'[ERR] write_cfg_file {Data.CONFIG_PATH} (json): {e}')
143
+ syslog(f'[ERR] write_cfg_file {Data.CONFIG_PATH} (json): {e}')
144
144
  sleep(0.2)
145
145
  return True
146
146
 
@@ -217,7 +217,7 @@ def cfgget(key=None):
217
217
  return Data.disk_keys(key)
218
218
  return val
219
219
  except Exception as e:
220
- errlog_add(f'[ERR] cfgget {key} error: {e}')
220
+ syslog(f'[ERR] cfgget {key} error: {e}')
221
221
  return None
222
222
 
223
223
  def cfgput(key, value, type_check=False):
@@ -242,7 +242,7 @@ def cfgput(key, value, type_check=False):
242
242
  del value
243
243
  return True
244
244
  except Exception as e:
245
- errlog_add(f'[ERR] cfgput {key} error: {e}')
245
+ syslog(f'[ERR] cfgput {key} error: {e}')
246
246
  return False
247
247
 
248
248
  #################################################################
micrOS/source/Debug.py CHANGED
@@ -1,12 +1,18 @@
1
+ """
2
+ micrOS Console and Log write interface implementations.
3
+ - with progress led feature, simple and custom
4
+ Designed by Marcell Ban aka BxNxM
5
+ """
6
+
1
7
  from machine import Pin
2
8
  try:
3
- from Logger import syslog
9
+ from Logger import syslog as logger_syslog
4
10
  except:
5
- syslog = None
11
+ logger_syslog = None
6
12
  try:
7
- from microIO import resolve_pin, pinmap_search, detect_platform
13
+ from microIO import resolve_pin, pinmap_search, register_pin
8
14
  except:
9
- detect_platform = None
15
+ pinmap_search = None
10
16
 
11
17
 
12
18
  #############################################
@@ -17,110 +23,60 @@ except:
17
23
  class DebugCfg:
18
24
  DEBUG = True # DEBUG PRINT ON/OFF - SET FROM ConfigHandler
19
25
  PLED_STEP = None # PROGRESS LED OBJECT - init in init_pled
20
- NEO_WHEEL = None # NEOPIXEL (ws2812/esp32s3) color wheel object
21
- COLOR_INDEX = 0 # APA102 TinyPico color wheel counter
22
26
 
23
27
  @staticmethod
24
28
  def init_pled():
25
29
  # CALL FROM ConfigHandler
26
- if detect_platform is None:
30
+ if pinmap_search is None:
27
31
  # Check LogicalPins module loadable (robustness...)
28
32
  return
29
- micro_platform = detect_platform()
30
- if micro_platform == "tinypico":
31
- # Progress led for TinyPico
32
- DebugCfg._init_apa102()
33
- return
34
33
  pled = pinmap_search('builtin')['builtin']
35
- if pled is not None:
36
- if isinstance(pled, int):
37
- # SET PROGRESS LED WITH BUILT-IN step FUNCTION
38
- if micro_platform == "esp32s3":
39
- # Progress led for esp32s3
40
- DebugCfg._init_ws2812()
41
- else:
42
- # Progress led for esp32/etc
43
- DebugCfg._init_simple()
44
- else:
45
- # OVERRIDE PROGRESS LED WITH CUSTOM step FUNCTION
46
- DebugCfg.PLED_STEP = pled
34
+ if pled is None:
35
+ # No available builtin pin, skip pled init...
36
+ return
37
+ # CONFIGURE PROGRESS LED
38
+ if isinstance(pled, int):
39
+ # [MODE] Simple flashing progress LED
40
+ try:
41
+ # Progress led for esp32/etc
42
+ led_obj = Pin(abs(resolve_pin('builtin')), Pin.OUT)
43
+ if resolve_pin('builtin') < 0:
44
+ # Pin number start with (-), like -8 (means inverted output)
45
+ led_obj.value(1) # Turn OFF built-in LED state invert (1:OFF)
46
+ # Set function callback for step function (simple led - blink)
47
+ DebugCfg.PLED_STEP = lambda: led_obj.value(not led_obj.value()) # # double-blink: return None
48
+ except Exception as e:
49
+ syslog(f"[PLED] led error: {e}")
50
+ elif callable(pled):
51
+ # [MODE] OVERRIDE PROGRESS LED WITH CUSTOM step FUNCTION
52
+ DebugCfg.PLED_STEP = pled
53
+ DebugCfg._auto_register_pin()
54
+ else:
55
+ syslog(f"[WARN] pled type not supported: {pled}")
47
56
 
48
- @staticmethod
49
- def step():
50
- """
51
- DEBUG LED FEEDBACK
52
- - handle 3 types of builtin LEDs: analog, neopixel(ws2812), apa102
53
- - automatic selection based on board type + builtin logical pin number
54
- """
55
- try:
56
- if callable(DebugCfg.PLED_STEP):
57
- return DebugCfg.PLED_STEP() # Run step function (return None: double-blink OR True: no d-b)
58
- except Exception as e:
59
- errlog_add(f"[PLED] step error: {e}")
60
- return True
61
57
 
62
58
  @staticmethod
63
- def _init_simple():
59
+ def _auto_register_pin():
64
60
  try:
65
- # Progress led for esp32/etc
66
- led_obj = Pin(abs(resolve_pin('builtin')), Pin.OUT)
67
- if resolve_pin('builtin') < 0: # Pin number start with (-), like -8 (means inverted output)
68
- led_obj.value(1) # Turn OFF built-in LED state invert (1:OFF)
69
- # Set function callback for step function (simple led - blink)
70
- DebugCfg.PLED_STEP = lambda: led_obj.value(not led_obj.value()) # # double-blink: return None
61
+ pin = DebugCfg.PLED_STEP(pin=True)
62
+ if isinstance(pin, int):
63
+ register_pin('builtin', pin)
71
64
  except Exception as e:
72
- errlog_add(f"[PLED] led error: {e}")
65
+ syslog(f"[ERR] pled pin registration: {e}", console=False)
73
66
 
74
- @staticmethod
75
- def _init_apa102():
76
- try:
77
- from machine import SoftSPI
78
- from dotstar import DotStar
79
- from tinypico import DOTSTAR_CLK, DOTSTAR_DATA, SPI_MISO, set_dotstar_power, dotstar_color_wheel
80
- spi = SoftSPI(sck=Pin(DOTSTAR_CLK), mosi=Pin(DOTSTAR_DATA), miso=Pin(SPI_MISO))
81
- # Create a DotStar instance
82
- dotstar = DotStar(spi, 1, brightness=0.4) # Just one DotStar, half brightness
83
- # Turn on the power to the DotStar
84
- set_dotstar_power(True)
85
- DebugCfg.PLED_STEP = lambda: DebugCfg._step_apa102(led_obj=dotstar, color_wheel=dotstar_color_wheel)
86
- except Exception as e:
87
- errlog_add(f"[PLED] apa102 error: {e}")
88
-
89
- @staticmethod
90
- def _step_apa102(led_obj, color_wheel):
91
- # Get the R,G,B values of the next colour
92
- r, g, b = color_wheel(DebugCfg.COLOR_INDEX*2)
93
- # Set the colour on the DOTSTAR
94
- led_obj[0] = (int(r * 0.6), g, b, 0.4)
95
- # Increase the wheel index
96
- DebugCfg.COLOR_INDEX = 0 if DebugCfg.COLOR_INDEX > 1000 else DebugCfg.COLOR_INDEX + 2
97
- return True # No double-blink
98
67
 
99
68
  @staticmethod
100
- def _init_ws2812():
69
+ def step():
70
+ """
71
+ DEBUG LED FLASHING FEEDBACK
72
+ - handle step callback function execution
73
+ """
101
74
  try:
102
- from neopixel import NeoPixel
103
- neo_pin = Pin(resolve_pin('builtin'))
104
- led_obj = NeoPixel(neo_pin, 1)
105
- DebugCfg.PLED_STEP = lambda: DebugCfg._step_ws2812(led_obj)
75
+ if callable(DebugCfg.PLED_STEP):
76
+ return DebugCfg.PLED_STEP() # Run step function (return None: double-blink OR True: no d-b)
106
77
  except Exception as e:
107
- errlog_add(f"[PLED] ws2812 error: {e}")
108
-
109
- @staticmethod
110
- def _step_ws2812(led_obj):
111
- def _color_wheel():
112
- while True:
113
- yield 10, 0, 0
114
- yield 5, 5, 0
115
- yield 0, 10, 0
116
- yield 0, 5, 5
117
- yield 0, 0, 10
118
- yield 5, 0, 5
119
- if DebugCfg.NEO_WHEEL is None:
120
- DebugCfg.NEO_WHEEL = _color_wheel()
121
- led_obj[0] = next(DebugCfg.NEO_WHEEL)
122
- led_obj.write()
123
- return True # No double-blink
78
+ syslog(f"[PLED] step error: {e}")
79
+ return True
124
80
 
125
81
 
126
82
  def console_write(msg):
@@ -131,10 +87,10 @@ def console_write(msg):
131
87
  if analog is None:
132
88
  DebugCfg.step() # Double-blink
133
89
  except Exception as e:
134
- errlog_add(f"[ERR] console_write: {e}", console=False)
90
+ syslog(f"[ERR] console_write: {e}", console=False)
135
91
 
136
92
 
137
- def errlog_add(data, console=True):
93
+ def syslog(data, console=True):
138
94
  """
139
95
  :param data: msg string / data
140
96
  :param console: activate console_write (default: True)
@@ -142,4 +98,4 @@ def errlog_add(data, console=True):
142
98
  """
143
99
  if console:
144
100
  console_write(data)
145
- return False if syslog is None else syslog(data)
101
+ return False if logger_syslog is None else logger_syslog(data)