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
@@ -1,5 +1,5 @@
1
1
  CP210x Macintosh OS VCP Driver v6 Release Notes
2
- Copyright (C) 2017-2021 Silicon Laboratories Inc.
2
+ Copyright (C) 2017-2025 Silicon Laboratories Inc.
3
3
 
4
4
  This release contains the following components:
5
5
 
@@ -32,6 +32,7 @@ Uninstalling the Driver
32
32
 
33
33
  Release Dates
34
34
  -------------
35
+ CP210x Macintosh OS VCP Driver 6.0.3 - May 23, 2025
35
36
  CP210x Macintosh OS VCP Driver 6.0.2 - October 26, 2021
36
37
  CP210x Macintosh OS VCP Driver 6.0.1 - March 26, 2021
37
38
  CP210x Macintosh OS VCP Driver 6.0 - December 18, 2020
@@ -73,6 +74,21 @@ Release Dates
73
74
 
74
75
  CP210x Macintosh OS Driver Revision History
75
76
  --------------------------------------------
77
+ Version 6.0.3
78
+ Added a feature where the CP210x device sends an XOFF signal to the connected device when macOS goes to sleep.
79
+ Resolved an issue with driver uninstallation on macOS 13.
80
+ Fixed an issue where the Mac OSX CP210x v6.0.2 VCP Driver could crash during stress testing.
81
+ Added VID/PID for UAB DIELEKTRIK.
82
+ Added VID/PID for Quell Tech Ltd.
83
+ Added VID/PID for Profoto.
84
+ Added VID/PID for Cambo Fotografische Industrie BV.
85
+ Added VID/PID for ZTC WND-7300.
86
+ Added VID/PID for Integrel Solutions Ltd.
87
+ Added VID/PID for BRAIN MAGIC.
88
+ Added VID/PID for ELV Elektronik AG.
89
+ Added VID/PID for TDK Corporation.
90
+ Added VID/PID for Regula Forensics.
91
+ Added VID/PID for ALC Embedded Systems Ltd.
76
92
  Version 6.0.2
77
93
  On macOS Big Sur:
78
94
  Fix issue that Xoff state is not remembered when macOS goes to sleep.
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "files": {
3
3
  "Time.py": [
4
- 9.16,
5
- 7
4
+ 9.17,
5
+ 6
6
6
  ],
7
7
  "Files.py": [
8
- 9.6,
9
- 4
8
+ 9.35,
9
+ 8
10
10
  ],
11
11
  "micrOSloader.py": [
12
12
  7.33,
13
13
  1
14
14
  ],
15
15
  "Hooks.py": [
16
- 9.85,
16
+ 9.67,
17
17
  1
18
18
  ],
19
19
  "Server.py": [
@@ -46,22 +46,22 @@
46
46
  ],
47
47
  "Types.py": [
48
48
  8.91,
49
- 29
49
+ 28
50
50
  ],
51
51
  "Logger.py": [
52
- 8.9,
52
+ 9.05,
53
53
  4
54
54
  ],
55
55
  "Common.py": [
56
- 9.78,
57
- 35
56
+ 9.84,
57
+ 34
58
58
  ],
59
59
  "InterConnect.py": [
60
60
  9.41,
61
- 3
61
+ 2
62
62
  ],
63
63
  "Debug.py": [
64
- 8.4,
64
+ 7.37,
65
65
  18
66
66
  ],
67
67
  "Network.py": [
@@ -69,19 +69,19 @@
69
69
  8
70
70
  ],
71
71
  "Espnow.py": [
72
- 9.5,
73
- 1
72
+ 9.57,
73
+ 2
74
74
  ],
75
75
  "Scheduler.py": [
76
76
  9.62,
77
77
  1
78
78
  ],
79
79
  "microIO.py": [
80
- 9.43,
81
- 42
80
+ 9.44,
81
+ 43
82
82
  ],
83
83
  "micrOS.py": [
84
- 9.25,
84
+ 9.27,
85
85
  1
86
86
  ],
87
87
  "Interrupts.py": [
@@ -97,7 +97,7 @@
97
97
  5
98
98
  ],
99
99
  "LM_roboarm.py": [
100
- 7.76,
100
+ 7.78,
101
101
  0
102
102
  ],
103
103
  "LM_stepper.py": [
@@ -105,7 +105,7 @@
105
105
  1
106
106
  ],
107
107
  "LM_pacman.py": [
108
- 8.92,
108
+ 8.95,
109
109
  0
110
110
  ],
111
111
  "LM_genIO.py": [
@@ -124,6 +124,10 @@
124
124
  7.57,
125
125
  0
126
126
  ],
127
+ "LM_qmi8658.py": [
128
+ 9.1,
129
+ 0
130
+ ],
127
131
  "LM_co2.py": [
128
132
  8.5,
129
133
  0
@@ -132,6 +136,10 @@
132
136
  8.18,
133
137
  0
134
138
  ],
139
+ "LM_tcs3472.py": [
140
+ 9.02,
141
+ 0
142
+ ],
135
143
  "LM_oled.py": [
136
144
  9.09,
137
145
  4
@@ -148,21 +156,17 @@
148
156
  8.04,
149
157
  0
150
158
  ],
151
- "LM_ph_sensor.py": [
152
- 5.79,
153
- 0
154
- ],
155
159
  "LM_buzzer.py": [
156
- 8.8,
160
+ 9.11,
157
161
  0
158
162
  ],
159
163
  "LM_switch.py": [
160
- 8.66,
164
+ 8.68,
161
165
  1
162
166
  ],
163
167
  "LM_servo.py": [
164
168
  7.73,
165
- 3
169
+ 2
166
170
  ],
167
171
  "LM_rgbcct.py": [
168
172
  8.62,
@@ -181,11 +185,11 @@
181
185
  0
182
186
  ],
183
187
  "LM_neopixel.py": [
184
- 7.43,
188
+ 7.54,
185
189
  2
186
190
  ],
187
191
  "LM_cct.py": [
188
- 9.03,
192
+ 9.04,
189
193
  1
190
194
  ],
191
195
  "LM_L9110_DCmotor.py": [
@@ -193,7 +197,7 @@
193
197
  0
194
198
  ],
195
199
  "LM_neoeffects.py": [
196
- 6.86,
200
+ 9.09,
197
201
  0
198
202
  ],
199
203
  "LM_i2c.py": [
@@ -253,7 +257,7 @@
253
257
  0
254
258
  ],
255
259
  "LM_rgb.py": [
256
- 8.75,
260
+ 8.88,
257
261
  1
258
262
  ],
259
263
  "LM_distance.py": [
@@ -289,25 +293,13 @@
289
293
  0
290
294
  ],
291
295
  "LM_dimmer.py": [
292
- 8.32,
296
+ 8.33,
293
297
  1
294
298
  ],
295
- "LM_demo.py": [
296
- 8.57,
297
- 0
298
- ],
299
299
  "LM_gameOfLife.py": [
300
300
  9.29,
301
301
  3
302
302
  ],
303
- "LM_catgame.py": [
304
- 8.85,
305
- 0
306
- ],
307
- "LM_intercon.py": [
308
- 8.18,
309
- 0
310
- ],
311
303
  "LM_ds18.py": [
312
304
  6.0,
313
305
  2
@@ -319,16 +311,20 @@
319
311
  "LM_sdcard.py": [
320
312
  7.88,
321
313
  0
314
+ ],
315
+ "LM_neomatrix.py": [
316
+ 8.72,
317
+ 0
322
318
  ]
323
319
  },
324
320
  "summary": {
325
321
  "core": [
326
- 3554,
322
+ 3723,
327
323
  24
328
324
  ],
329
325
  "load": [
330
- 9227,
331
- 56
326
+ 9410,
327
+ 55
332
328
  ],
333
329
  "core_dep": [
334
330
  true,
@@ -336,10 +332,10 @@
336
332
  ],
337
333
  "load_dep": [
338
334
  true,
339
- 6
335
+ 5
340
336
  ],
341
- "core_score": 9.24,
342
- "load_score": 8.23,
343
- "version": "2.10.5-1"
337
+ "core_score": 9.19,
338
+ "load_score": 8.35,
339
+ "version": "2.11.0-0"
344
340
  }
345
341
  }
micrOS/source/Common.py CHANGED
@@ -5,92 +5,23 @@ micrOS Load Module programming Official API-s
5
5
  from Server import Server, WebCli
6
6
  from Debug import errlog_add, 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):
19
+ def micro_task(tag:str, task=None):
34
20
  """
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
41
- """
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:
@@ -216,3 +143,251 @@ def syslog(msg):
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
+ #####################################################################################
159
+ # CHANNEL: SIGNAL GENERATORS #
160
+ #####################################################################################
161
+
162
+ def transition(from_val, to_val, step_ms, interval_sec):
163
+ """
164
+ [LM] Single Generator for color/value transition:
165
+ :param from_val: from value - start from
166
+ :param to_val: to value - target value
167
+ :param step_ms: step to reach to_val - timirq_seq
168
+ :param interval_sec: time of full interval
169
+ """
170
+ if interval_sec > 0:
171
+ step_cnt = round((interval_sec*1000)/step_ms)
172
+ delta = abs((from_val-to_val)/step_cnt)
173
+ direc = -1 if from_val > to_val else 1
174
+ for cnt in range(0, step_cnt+1):
175
+ yield round(from_val + (cnt * delta) * direc)
176
+ else:
177
+ yield round(to_val)
178
+
179
+
180
+ def transition_gen(*args, interval_sec=1.0):
181
+ """
182
+ [LM] Multiple Generator for color/value transitions:
183
+ - calculate minimum step count -> step_ms
184
+ - autofill and use transition(from_val, to_val, step_ms, interval_sec)
185
+ :param args: ch1_from, ch1_to, ch2_from, ch2_to, etc...
186
+ :param interval_sec: interval in sec to calculate optimal fade/transition effect
187
+ return: gen, step_ms OR gen list, step_ms
188
+ """
189
+ step_ms_min = 5 # min calculated step is 5 ms - good enough
190
+ delta = max((abs(args[ch_from_i] - args[ch_from_i+1]) for ch_from_i in range(0, len(args)-1, 2)))
191
+ step_ms = 0 if delta == 0 else int(interval_sec*1000 / delta)
192
+ step_ms = step_ms_min if step_ms < step_ms_min else step_ms
193
+ 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)))
194
+ if len(transitions) == 1:
195
+ return transitions[0], step_ms
196
+ return list(transitions), step_ms
197
+
198
+ #####################################################################################
199
+ # EXTRAS #
200
+ #####################################################################################
201
+
202
+ class SmartADC:
203
+ """
204
+ [LM] General ADC implementation for auto scaled output: raw, percent, volt
205
+ https://docs.micropython.org/en/latest/esp32/quickref.html#adc-analog-to-digital-conversion
206
+ ADC.ATTN_0DB: 0 dB attenuation, resulting in a full-scale voltage range of 0-1.1V
207
+ ADC.ATTN_2_5DB: 2.5 dB ... of 0-1.5V
208
+ ADC.ATTN_6DB: 6 dB ... of 0-2.2V
209
+ ADC.ATTN_11DB: 11 dB ... of 0-2450mV/
210
+ Note that the absolute maximum voltage rating for input pins is 3.6V. Going near to this boundary risks damage to the IC!
211
+ """
212
+ OBJS = {}
213
+
214
+ def __init__(self, pin):
215
+ self.adp_prop = (65535, 2450) # raw value, 2450mV (so 2,45V)
216
+ self.adc = None
217
+ if not isinstance(pin, int):
218
+ pin = resolve_pin(pin)
219
+ self.adc = ADC(Pin(pin))
220
+ self.adc.atten(ADC.ATTN_11DB) # 2450mV measure range
221
+
222
+ def get(self):
223
+ raw = int((self.adc.read_u16() + self.adc.read_u16())/2) # 16-bit ADC value (0-65535)
224
+ percent = raw / self.adp_prop[0]
225
+ volt = round(percent * self.adp_prop[1] / 1000, 2) # devide with 1000 to get V from mV
226
+ return {'raw': raw, 'percent': round(percent*100, 1), 'volt': volt}
227
+
228
+ @staticmethod
229
+ def get_instance(pin):
230
+ if pin in SmartADC.OBJS.keys():
231
+ return SmartADC.OBJS[pin]
232
+ SmartADC.OBJS[pin] = SmartADC(pin)
233
+ return SmartADC.OBJS[pin]
234
+
235
+
236
+ class AnimationPlayer:
237
+ """
238
+ Generic async animation (generator) player.
239
+ """
240
+
241
+ def __init__(self, animation:callable=None, tag:str=None, batch_draw:bool=False, batch_size:int=None):
242
+ """
243
+ Initialize the AnimationPlayer with an optional animation.
244
+ :param animation: Function to GENERATE animation data
245
+ :param tag: Optional task tag for micro_task management.
246
+ :param batch_draw: If True - draw in batches
247
+ :param batch_size: Number of pixels per batch when drawing
248
+ """
249
+ self.animation:callable = None
250
+ self.batch_draw:bool = batch_draw
251
+ self.__max_batch_size:int = 256 # MAX BATCH SIZE - ASYNC PROTECTION
252
+ self.__batch_size:int = 8 # Default batch size: 8
253
+ self._set_batch_size(batch_size) # Set batch size from parameter
254
+ self._player_speed_ms:int = 10 # Default speed in ms between frames
255
+ main_tag:str = tag if tag else "animation"
256
+ self._task_tag:str = f"{main_tag}.player"
257
+ if animation is not None and not self._set_animation(animation):
258
+ raise Exception("Invalid animation function provided.")
259
+ self._running:bool = True
260
+
261
+ def _set_animation(self, animation:callable) -> bool:
262
+ """
263
+ Setter to change/set current animation.
264
+ """
265
+ if callable(animation):
266
+ self.animation = animation
267
+ return True
268
+ return False
269
+
270
+ def _set_batch_size(self, batch_size:int) -> None:
271
+ """
272
+ Setter to change/set batch size.
273
+ - with max batch size check (due to async event loop feeding)
274
+ """
275
+ if batch_size is None:
276
+ return
277
+ self.__batch_size = max(0, min(batch_size, self.__max_batch_size))
278
+
279
+ async def _render(self, my_task):
280
+ # Cache methods for speed
281
+ clear = self.clear
282
+ update = self.update
283
+ draw = self.draw
284
+ # Cache the current animation for comparison
285
+ current_animation = self.animation
286
+ frame_counter = 0
287
+ # Clear the display before each frame
288
+ if not self.batch_draw:
289
+ clear()
290
+ for data in self.animation():
291
+ # Check if animation has changed under the loop
292
+ if not self._running or self.animation != current_animation:
293
+ # Animation changed — break — clean and restart animation loop.
294
+ clear()
295
+ break
296
+ # Update data cache
297
+ update(*data)
298
+ if self.batch_draw:
299
+ # Batched draw mode
300
+ frame_counter += 1
301
+ if frame_counter >= self.__batch_size:
302
+ draw()
303
+ frame_counter = 0
304
+ await my_task.feed(sleep_ms=self._player_speed_ms)
305
+ else:
306
+ # Real-time draw mode
307
+ draw()
308
+ await my_task.feed(sleep_ms=self._player_speed_ms)
309
+
310
+ async def _player(self):
311
+ """
312
+ Async task to play the current animation.
313
+ """
314
+ with micro_task(tag=self._task_tag) as my_task:
315
+ while self._running:
316
+ my_task.out = f"Play {self.animation.__name__} ({self._player_speed_ms}ms/frame)"
317
+ try:
318
+ await self._render(my_task)
319
+ except IndexError:
320
+ # Draw after generator exhausted and Restart animation if IndexError occurs
321
+ self.draw()
322
+ await my_task.feed(sleep_ms=self._player_speed_ms)
323
+ my_task.out = "Restart animation"
324
+ except Exception as e:
325
+ my_task.out = f"Error: {e}"
326
+ break
327
+ my_task.out = f"Animation stopped...{my_task.out}"
328
+
329
+ def control(self, play_speed_ms:int, bt_draw:bool=None, bt_size:int=None):
330
+ """
331
+ Set/Get current play speed of the animation.
332
+ :param play_speed_ms: player loop speed in milliseconds.
333
+ :param bt_draw: batch drawing flag.
334
+ :param bt_size: batch drawing size.
335
+ """
336
+ if isinstance(play_speed_ms, int):
337
+ self._player_speed_ms = max(0, min(10000, int(play_speed_ms)))
338
+ if isinstance(bt_draw, bool):
339
+ self.batch_draw = bt_draw
340
+ if isinstance(bt_size, int):
341
+ self._set_batch_size(bt_size)
342
+ return {"realtime": not self.batch_draw, "batched": self.batch_draw,
343
+ "size": self.__batch_size, "speed_ms": self._player_speed_ms}
344
+
345
+
346
+ def play(self, animation=None, speed_ms=None, bt_draw=False, bt_size=None):
347
+ """
348
+ Play animation via generator function.
349
+ :param animation: Animation generator function.
350
+ :param speed_ms: Speed of the animation in milliseconds. (min.: 3ms)
351
+ :param bt_draw: batch drawing flag.
352
+ :param bt_size: batch drawing size.
353
+ """
354
+
355
+ if animation is not None:
356
+ if not self._set_animation(animation):
357
+ return "Invalid animation"
358
+ if self.animation is None:
359
+ return "No animation to play"
360
+ # Handle player settings
361
+ settings = self.control(play_speed_ms=speed_ms, bt_draw=bt_draw, bt_size=bt_size)
362
+ # Ensure async loop set up correctly. (After stop operation, it is needed)
363
+ self._running = True
364
+ # [!] ASYNC TASK CREATION
365
+ raw_state:bool = micro_task(tag=self._task_tag, task=self._player())
366
+ state = "starting" if raw_state else "running"
367
+ settings["state"] = state
368
+ return settings
369
+
370
+ def stop(self):
371
+ """
372
+ Stop the animation.
373
+ """
374
+ self._running = False
375
+ return "Stop animation player"
376
+
377
+ def update(self, *arg, **kwargs):
378
+ """
379
+ Child class must implement this method to handle drawing logic.
380
+ """
381
+ raise NotImplementedError("Child class must implement update method.")
382
+
383
+ def draw(self):
384
+ """
385
+ Draw the current frame.
386
+ """
387
+ raise NotImplementedError("Child class must implement draw method.")
388
+
389
+ def clear(self):
390
+ """
391
+ Clear the display.
392
+ """
393
+ raise NotImplementedError("Child class must implement clear method.")