micrOSDevToolKit 2.13.1__py3-none-any.whl → 2.17.1__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 (220) hide show
  1. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +125 -121
  2. micrOS/source/Common.py +48 -26
  3. micrOS/source/Config.py +13 -5
  4. micrOS/source/Espnow.py +100 -58
  5. micrOS/source/Files.py +77 -41
  6. micrOS/source/Hooks.py +18 -34
  7. micrOS/source/Logger.py +2 -7
  8. micrOS/source/Network.py +36 -16
  9. micrOS/source/Server.py +22 -8
  10. micrOS/source/Shell.py +9 -6
  11. micrOS/source/Tasks.py +34 -13
  12. micrOS/source/Web.py +69 -31
  13. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  14. micrOS/source/__pycache__/Files.cpython-312.pyc +0 -0
  15. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  16. micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
  17. micrOS/source/config/_git.keep +0 -0
  18. micrOS/source/micrOS.py +7 -0
  19. micrOS/source/micrOSloader.py +4 -10
  20. micrOS/source/microIO.py +2 -2
  21. micrOS/source/modules/IO_esp32c6.py +38 -0
  22. micrOS/source/modules/LM_L298N.py +161 -0
  23. micrOS/source/modules/LM_cluster.py +255 -0
  24. {toolkit/workspace/precompiled → micrOS/source/modules}/LM_esp32.py +5 -0
  25. micrOS/source/modules/LM_espnow.py +36 -0
  26. micrOS/source/{LM_i2c.py → modules/LM_i2c.py} +1 -1
  27. micrOS/source/{LM_light_sensor.py → modules/LM_light_sensor.py} +2 -2
  28. micrOS/source/modules/LM_mqtt_client.py +246 -0
  29. micrOS/source/{LM_neoeffects.py → modules/LM_neoeffects.py} +14 -4
  30. micrOS/source/{LM_neomatrix.py → modules/LM_neomatrix.py} +140 -38
  31. micrOS/source/{LM_oled_ui.py → modules/LM_oled_ui.py} +2 -2
  32. micrOS/source/{LM_oledui.py → modules/LM_oledui.py} +2 -2
  33. micrOS/source/{LM_pacman.py → modules/LM_pacman.py} +74 -29
  34. micrOS/source/{LM_presence.py → modules/LM_presence.py} +2 -2
  35. micrOS/source/{LM_robustness.py → modules/LM_robustness.py} +49 -2
  36. micrOS/source/{LM_tcs3472.py → modules/LM_tcs3472.py} +4 -6
  37. micrOS/source/web/dashboard.html +2 -0
  38. micrOS/source/web/matrix_draw.html +390 -0
  39. micrOS/source/web/uapi.js +9 -6
  40. {microsdevtoolkit-2.13.1.dist-info → microsdevtoolkit-2.17.1.dist-info}/METADATA +30 -37
  41. {microsdevtoolkit-2.13.1.dist-info → microsdevtoolkit-2.17.1.dist-info}/RECORD +200 -190
  42. toolkit/DevEnvCompile.py +21 -12
  43. toolkit/DevEnvOTA.py +27 -16
  44. toolkit/DevEnvUSB.py +35 -21
  45. toolkit/LM_to_compile.dat +3 -1
  46. toolkit/MicrOSDevEnv.py +37 -21
  47. toolkit/dashboard_apps/QMI8685_GYRO.py +1 -1
  48. toolkit/dashboard_apps/SystemTest.py +8 -5
  49. toolkit/{MicrosFiles.py → lib/MicrosFiles.py} +24 -4
  50. toolkit/micrOSdashboard.py +2 -2
  51. toolkit/micrOSlint.py +17 -7
  52. toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
  53. toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
  54. toolkit/simulator_lib/mqtt_as/Note.md +15 -0
  55. toolkit/simulator_lib/mqtt_as/__init__.py +950 -0
  56. toolkit/simulator_lib/mqtt_as/__pycache__/__init__.cpython-312.pyc +0 -0
  57. toolkit/simulator_lib/mqtt_as/clean.py +69 -0
  58. toolkit/simulator_lib/mqtt_as/mqtt_v5_properties.py +239 -0
  59. toolkit/simulator_lib/mqtt_as/range.py +90 -0
  60. toolkit/simulator_lib/mqtt_as/range_ex.py +119 -0
  61. toolkit/simulator_lib/simulator.py +14 -1
  62. toolkit/simulator_lib/uos.py +2 -0
  63. toolkit/workspace/precompiled/Common.mpy +0 -0
  64. toolkit/workspace/precompiled/Config.mpy +0 -0
  65. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  66. toolkit/workspace/precompiled/Files.mpy +0 -0
  67. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  68. toolkit/workspace/precompiled/Logger.mpy +0 -0
  69. toolkit/workspace/precompiled/Network.mpy +0 -0
  70. toolkit/workspace/precompiled/Server.mpy +0 -0
  71. toolkit/workspace/precompiled/Shell.mpy +0 -0
  72. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  73. toolkit/workspace/precompiled/Web.mpy +0 -0
  74. toolkit/workspace/precompiled/config/_git.keep +0 -0
  75. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  76. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  77. toolkit/workspace/precompiled/microIO.mpy +0 -0
  78. toolkit/workspace/precompiled/{IO_esp32.mpy → modules/IO_esp32.mpy} +0 -0
  79. toolkit/workspace/precompiled/{IO_esp32c3.mpy → modules/IO_esp32c3.mpy} +0 -0
  80. toolkit/workspace/precompiled/modules/IO_esp32c6.mpy +0 -0
  81. toolkit/workspace/precompiled/{IO_esp32s2.mpy → modules/IO_esp32s2.mpy} +0 -0
  82. toolkit/workspace/precompiled/{IO_esp32s3.mpy → modules/IO_esp32s3.mpy} +0 -0
  83. toolkit/workspace/precompiled/{IO_m5stamp.mpy → modules/IO_m5stamp.mpy} +0 -0
  84. toolkit/workspace/precompiled/{IO_qtpy.mpy → modules/IO_qtpy.mpy} +0 -0
  85. toolkit/workspace/precompiled/modules/IO_rp2.mpy +0 -0
  86. toolkit/workspace/precompiled/modules/IO_s3matrix.mpy +0 -0
  87. toolkit/workspace/precompiled/{IO_tinypico.mpy → modules/IO_tinypico.mpy} +0 -0
  88. toolkit/workspace/precompiled/modules/LM_L298N.mpy +0 -0
  89. toolkit/workspace/precompiled/{LM_OV2640.mpy → modules/LM_OV2640.mpy} +0 -0
  90. toolkit/workspace/precompiled/{LM_aht10.mpy → modules/LM_aht10.mpy} +0 -0
  91. toolkit/workspace/precompiled/{LM_bme280.mpy → modules/LM_bme280.mpy} +0 -0
  92. toolkit/workspace/precompiled/{LM_buzzer.mpy → modules/LM_buzzer.mpy} +0 -0
  93. toolkit/workspace/precompiled/{LM_cct.mpy → modules/LM_cct.mpy} +0 -0
  94. toolkit/workspace/precompiled/modules/LM_cluster.mpy +0 -0
  95. toolkit/workspace/precompiled/{LM_co2.mpy → modules/LM_co2.mpy} +0 -0
  96. toolkit/workspace/precompiled/{LM_dht11.mpy → modules/LM_dht11.mpy} +0 -0
  97. toolkit/workspace/precompiled/{LM_dht22.mpy → modules/LM_dht22.mpy} +0 -0
  98. toolkit/workspace/precompiled/{LM_dimmer.mpy → modules/LM_dimmer.mpy} +0 -0
  99. toolkit/workspace/precompiled/{LM_distance.mpy → modules/LM_distance.mpy} +0 -0
  100. toolkit/workspace/precompiled/{LM_ds18.mpy → modules/LM_ds18.mpy} +0 -0
  101. {micrOS/source → toolkit/workspace/precompiled/modules}/LM_esp32.py +5 -0
  102. toolkit/workspace/precompiled/modules/LM_espnow.py +36 -0
  103. toolkit/workspace/precompiled/{LM_gameOfLife.mpy → modules/LM_gameOfLife.mpy} +0 -0
  104. toolkit/workspace/precompiled/{LM_genIO.mpy → modules/LM_genIO.mpy} +0 -0
  105. toolkit/workspace/precompiled/{LM_haptic.mpy → modules/LM_haptic.mpy} +0 -0
  106. toolkit/workspace/precompiled/{LM_i2c.py → modules/LM_i2c.py} +1 -1
  107. toolkit/workspace/precompiled/{LM_i2s_mic.mpy → modules/LM_i2s_mic.mpy} +0 -0
  108. toolkit/workspace/precompiled/{LM_keychain.mpy → modules/LM_keychain.mpy} +0 -0
  109. toolkit/workspace/precompiled/{LM_ld2410.mpy → modules/LM_ld2410.mpy} +0 -0
  110. toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
  111. toolkit/workspace/precompiled/modules/LM_mqtt_client.mpy +0 -0
  112. toolkit/workspace/precompiled/{LM_neoeffects.mpy → modules/LM_neoeffects.mpy} +0 -0
  113. toolkit/workspace/precompiled/modules/LM_neomatrix.mpy +0 -0
  114. toolkit/workspace/precompiled/{LM_neopixel.mpy → modules/LM_neopixel.mpy} +0 -0
  115. toolkit/workspace/precompiled/{LM_oled.mpy → modules/LM_oled.mpy} +0 -0
  116. toolkit/workspace/precompiled/{LM_oled_sh1106.mpy → modules/LM_oled_sh1106.mpy} +0 -0
  117. toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
  118. toolkit/workspace/precompiled/modules/LM_oledui.mpy +0 -0
  119. toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
  120. toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
  121. toolkit/workspace/precompiled/{LM_rest.mpy → modules/LM_rest.mpy} +0 -0
  122. toolkit/workspace/precompiled/{LM_rgb.mpy → modules/LM_rgb.mpy} +0 -0
  123. toolkit/workspace/precompiled/{LM_rgbcct.mpy → modules/LM_rgbcct.mpy} +0 -0
  124. toolkit/workspace/precompiled/{LM_roboarm.mpy → modules/LM_roboarm.mpy} +0 -0
  125. toolkit/workspace/precompiled/{LM_robustness.py → modules/LM_robustness.py} +49 -2
  126. toolkit/workspace/precompiled/{LM_servo.mpy → modules/LM_servo.mpy} +0 -0
  127. toolkit/workspace/precompiled/{LM_sound_event.mpy → modules/LM_sound_event.mpy} +0 -0
  128. toolkit/workspace/precompiled/{LM_stepper.mpy → modules/LM_stepper.mpy} +0 -0
  129. toolkit/workspace/precompiled/{LM_switch.mpy → modules/LM_switch.mpy} +0 -0
  130. toolkit/workspace/precompiled/{LM_system.mpy → modules/LM_system.mpy} +0 -0
  131. toolkit/workspace/precompiled/{LM_tcs3472.py → modules/LM_tcs3472.py} +4 -6
  132. toolkit/workspace/precompiled/{LM_telegram.mpy → modules/LM_telegram.mpy} +0 -0
  133. toolkit/workspace/precompiled/{LM_tinyrgb.mpy → modules/LM_tinyrgb.mpy} +0 -0
  134. toolkit/workspace/precompiled/{LM_trackball.mpy → modules/LM_trackball.mpy} +0 -0
  135. toolkit/workspace/precompiled/{LM_veml7700.mpy → modules/LM_veml7700.mpy} +0 -0
  136. toolkit/workspace/precompiled/web/dashboard.html +2 -0
  137. toolkit/workspace/precompiled/web/matrix_draw.html +390 -0
  138. toolkit/workspace/precompiled/web/uapi.js +9 -6
  139. micrOS/source/IO_esp32c6.py +0 -16
  140. micrOS/source/LM_L298N_DCmotor.py +0 -86
  141. micrOS/source/LM_espnow.py +0 -57
  142. micrOS/source/LM_mqtt_pro.py +0 -211
  143. toolkit/lib/file_extensions.py +0 -22
  144. toolkit/workspace/precompiled/Common.cpython-312.pyc +0 -0
  145. toolkit/workspace/precompiled/IO_esp32c6.mpy +0 -0
  146. toolkit/workspace/precompiled/IO_rp2.mpy +0 -0
  147. toolkit/workspace/precompiled/IO_s3matrix.mpy +0 -0
  148. toolkit/workspace/precompiled/LM_L298N_DCmotor.mpy +0 -0
  149. toolkit/workspace/precompiled/LM_espnow.py +0 -57
  150. toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
  151. toolkit/workspace/precompiled/LM_mqtt_pro.py +0 -211
  152. toolkit/workspace/precompiled/LM_neomatrix.mpy +0 -0
  153. toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
  154. toolkit/workspace/precompiled/LM_oledui.mpy +0 -0
  155. toolkit/workspace/precompiled/LM_pacman.mpy +0 -0
  156. toolkit/workspace/precompiled/LM_presence.mpy +0 -0
  157. toolkit/workspace/precompiled/Logger.cpython-312.pyc +0 -0
  158. toolkit/workspace/precompiled/Server.cpython-312.pyc +0 -0
  159. /micrOS/source/{IO_esp32.py → modules/IO_esp32.py} +0 -0
  160. /micrOS/source/{IO_esp32c3.py → modules/IO_esp32c3.py} +0 -0
  161. /micrOS/source/{IO_esp32s2.py → modules/IO_esp32s2.py} +0 -0
  162. /micrOS/source/{IO_esp32s3.py → modules/IO_esp32s3.py} +0 -0
  163. /micrOS/source/{IO_m5stamp.py → modules/IO_m5stamp.py} +0 -0
  164. /micrOS/source/{IO_qtpy.py → modules/IO_qtpy.py} +0 -0
  165. /micrOS/source/{IO_rp2.py → modules/IO_rp2.py} +0 -0
  166. /micrOS/source/{IO_s3matrix.py → modules/IO_s3matrix.py} +0 -0
  167. /micrOS/source/{IO_tinypico.py → modules/IO_tinypico.py} +0 -0
  168. /micrOS/source/{LM_L9110_DCmotor.py → modules/LM_L9110_DCmotor.py} +0 -0
  169. /micrOS/source/{LM_OV2640.py → modules/LM_OV2640.py} +0 -0
  170. /micrOS/source/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +0 -0
  171. /micrOS/source/{LM_aht10.py → modules/LM_aht10.py} +0 -0
  172. /micrOS/source/{LM_bme280.py → modules/LM_bme280.py} +0 -0
  173. /micrOS/source/{LM_buzzer.py → modules/LM_buzzer.py} +0 -0
  174. /micrOS/source/{LM_cct.py → modules/LM_cct.py} +0 -0
  175. /micrOS/source/{LM_co2.py → modules/LM_co2.py} +0 -0
  176. /micrOS/source/{LM_dashboard_be.py → modules/LM_dashboard_be.py} +0 -0
  177. /micrOS/source/{LM_dht11.py → modules/LM_dht11.py} +0 -0
  178. /micrOS/source/{LM_dht22.py → modules/LM_dht22.py} +0 -0
  179. /micrOS/source/{LM_dimmer.py → modules/LM_dimmer.py} +0 -0
  180. /micrOS/source/{LM_distance.py → modules/LM_distance.py} +0 -0
  181. /micrOS/source/{LM_ds18.py → modules/LM_ds18.py} +0 -0
  182. /micrOS/source/{LM_gameOfLife.py → modules/LM_gameOfLife.py} +0 -0
  183. /micrOS/source/{LM_genIO.py → modules/LM_genIO.py} +0 -0
  184. /micrOS/source/{LM_haptic.py → modules/LM_haptic.py} +0 -0
  185. /micrOS/source/{LM_i2s_mic.py → modules/LM_i2s_mic.py} +0 -0
  186. /micrOS/source/{LM_keychain.py → modules/LM_keychain.py} +0 -0
  187. /micrOS/source/{LM_ld2410.py → modules/LM_ld2410.py} +0 -0
  188. /micrOS/source/{LM_neopixel.py → modules/LM_neopixel.py} +0 -0
  189. /micrOS/source/{LM_oled.py → modules/LM_oled.py} +0 -0
  190. /micrOS/source/{LM_oled_sh1106.py → modules/LM_oled_sh1106.py} +0 -0
  191. /micrOS/source/{LM_pet_feeder.py → modules/LM_pet_feeder.py} +0 -0
  192. /micrOS/source/{LM_qmi8658.py → modules/LM_qmi8658.py} +0 -0
  193. /micrOS/source/{LM_rencoder.py → modules/LM_rencoder.py} +0 -0
  194. /micrOS/source/{LM_rest.py → modules/LM_rest.py} +0 -0
  195. /micrOS/source/{LM_rgb.py → modules/LM_rgb.py} +0 -0
  196. /micrOS/source/{LM_rgbcct.py → modules/LM_rgbcct.py} +0 -0
  197. /micrOS/source/{LM_roboarm.py → modules/LM_roboarm.py} +0 -0
  198. /micrOS/source/{LM_rp2w.py → modules/LM_rp2w.py} +0 -0
  199. /micrOS/source/{LM_sdcard.py → modules/LM_sdcard.py} +0 -0
  200. /micrOS/source/{LM_servo.py → modules/LM_servo.py} +0 -0
  201. /micrOS/source/{LM_sound_event.py → modules/LM_sound_event.py} +0 -0
  202. /micrOS/source/{LM_stepper.py → modules/LM_stepper.py} +0 -0
  203. /micrOS/source/{LM_switch.py → modules/LM_switch.py} +0 -0
  204. /micrOS/source/{LM_system.py → modules/LM_system.py} +0 -0
  205. /micrOS/source/{LM_telegram.py → modules/LM_telegram.py} +0 -0
  206. /micrOS/source/{LM_tinyrgb.py → modules/LM_tinyrgb.py} +0 -0
  207. /micrOS/source/{LM_trackball.py → modules/LM_trackball.py} +0 -0
  208. /micrOS/source/{LM_veml7700.py → modules/LM_veml7700.py} +0 -0
  209. {microsdevtoolkit-2.13.1.data → microsdevtoolkit-2.17.1.data}/scripts/devToolKit.py +0 -0
  210. {microsdevtoolkit-2.13.1.dist-info → microsdevtoolkit-2.17.1.dist-info}/WHEEL +0 -0
  211. {microsdevtoolkit-2.13.1.dist-info → microsdevtoolkit-2.17.1.dist-info}/licenses/LICENSE +0 -0
  212. {microsdevtoolkit-2.13.1.dist-info → microsdevtoolkit-2.17.1.dist-info}/top_level.txt +0 -0
  213. /toolkit/workspace/precompiled/{LM_L9110_DCmotor.py → modules/LM_L9110_DCmotor.py} +0 -0
  214. /toolkit/workspace/precompiled/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +0 -0
  215. /toolkit/workspace/precompiled/{LM_dashboard_be.py → modules/LM_dashboard_be.py} +0 -0
  216. /toolkit/workspace/precompiled/{LM_pet_feeder.py → modules/LM_pet_feeder.py} +0 -0
  217. /toolkit/workspace/precompiled/{LM_qmi8658.py → modules/LM_qmi8658.py} +0 -0
  218. /toolkit/workspace/precompiled/{LM_rencoder.py → modules/LM_rencoder.py} +0 -0
  219. /toolkit/workspace/precompiled/{LM_rp2w.py → modules/LM_rp2w.py} +0 -0
  220. /toolkit/workspace/precompiled/{LM_sdcard.py → modules/LM_sdcard.py} +0 -0
@@ -0,0 +1,38 @@
1
+ from micropython import const
2
+
3
+ """
4
+ Seeed Studio XIAO ESP32-C6
5
+
6
+ BOARD MICROPYTHON
7
+ D0/A0 - GPIO 0
8
+ D1/A1 - GPIO 1
9
+ D2/A2 - GPIO 2
10
+ D3 - GPIO 21
11
+ D4 SDA - GPIO 22
12
+ D5 SCL - GPIO 23
13
+ D6 TX0 - GPIO 16
14
+ D7 RX0 - GPIO 17
15
+ D8 SCK - GPIO 19
16
+ D9 MISO - GPIO 20
17
+ D10 MOSI - GPIO 18
18
+ BUILTIN LED - GPIO 15
19
+ """
20
+
21
+ # BUILTIN LED
22
+ builtin = const(-15) # BUILT IN LED - progress_led
23
+
24
+
25
+ # I2C BUS
26
+ i2c_sda = const(22) # D4 - data
27
+ i2c_scl = const(23) # D5 - clock
28
+
29
+
30
+ # EXTERNAL EVENT IRQ
31
+ irq1 = const(21) # D3 - event irq1 pin
32
+ irq2 = const(2) # D2 - event irq2 pin
33
+ irq3 = const(1) # D1 - event irq3 pin
34
+ irq4 = const(0) # D0 - event irq4 pin
35
+
36
+
37
+ neop = const(18) # D10 - neopixel OneWire bus [PWM]
38
+
@@ -0,0 +1,161 @@
1
+ from machine import Pin, PWM
2
+ from microIO import bind_pin, pinmap_search
3
+
4
+
5
+ # Cache of last set motor speeds (motor1, motor2) to ensure state() reports the latest values
6
+ # even if hardware PWM duty readout is delayed or unsupported on some boards
7
+ __MOTOR_SPEEDS = [0, 0] # motor1, motor2
8
+ __L298N_OBJS = []
9
+
10
+ PWM_FREQ = 50
11
+ # Deprecated LM_L298N_DCmotor: l298speed, l298dir_1, l298dir_2
12
+ PIN_BINDINGS = [
13
+ ('l298n_ENA', 10), ('l298n_INA', 12), ('l298n_INB', 11), # motor 1
14
+ ('l298n_ENB', 3), ('l298n_INC', 9), ('l298n_IND', 40), # motor 2
15
+ ]
16
+ STATE_MAP = {
17
+ (1, 0): 'forward',
18
+ (0, 1): 'backward',
19
+ (0, 0): 'coast',
20
+ (1, 1): 'brake'
21
+ }
22
+
23
+
24
+ def __l298n_init():
25
+ global __L298N_OBJS
26
+ if not __L298N_OBJS:
27
+ for index, (name, pin) in enumerate(PIN_BINDINGS):
28
+ if index % 3 == 0: # PWM pin
29
+ pwm = PWM(bind_pin(name, pin), freq=PWM_FREQ)
30
+ pwm.duty(0)
31
+ __L298N_OBJS.append(pwm)
32
+ else:
33
+ p = Pin(bind_pin(name, pin), Pin.OUT)
34
+ p.value(0)
35
+ __L298N_OBJS.append(p)
36
+ return __L298N_OBJS
37
+
38
+
39
+ def __get_motor_state(motor_index):
40
+ objlist = __l298n_init()
41
+ pwm_index = motor_index * 3
42
+ in1, in2 = objlist[pwm_index + 1].value(), objlist[pwm_index + 2].value()
43
+ state = STATE_MAP.get((in1, in2), 'unknown')
44
+ return {'speed': __MOTOR_SPEEDS[motor_index], 'state': state}
45
+
46
+
47
+ #########################
48
+ # Application functions #
49
+ #########################
50
+
51
+ def load(pwm_freq:int=None):
52
+ """
53
+ [i] micrOS LM naming convention
54
+ Load the L298N motor driver module
55
+ """
56
+ global PWM_FREQ
57
+ if pwm_freq is not None:
58
+ PWM_FREQ = pwm_freq
59
+ __l298n_init()
60
+ return "Motor driver loaded successfully."
61
+
62
+
63
+ def state(motor=0):
64
+ """
65
+ [i] micrOS LM naming convention
66
+ Get the current state of a motor or all motors
67
+ :param motor: Motor number (1 or 2) or None for all motors
68
+ """
69
+ if motor == 1:
70
+ return {'motor1': __get_motor_state(0)}
71
+ elif motor == 2:
72
+ return {'motor2': __get_motor_state(1)}
73
+ else:
74
+ return {
75
+ 'motor1': __get_motor_state(0),
76
+ 'motor2': __get_motor_state(1)
77
+ }
78
+
79
+
80
+ def _control_motor(motor, in1, in2):
81
+ objlist = __l298n_init()
82
+ motor_index = 0 if motor == 1 else 3
83
+ objlist[motor_index + 1].value(in1)
84
+ objlist[motor_index + 2].value(in2)
85
+
86
+
87
+ def speed(motor, speed:int=1023):
88
+ """
89
+ Set the speed of a motor
90
+ :param motor: Motor number (1 or 2)
91
+ :param speed: Speed value (0-1023)
92
+ :return: Current motor state
93
+ """
94
+ if not (0 <= speed <= 1023):
95
+ return {'speed': 'value range error'}
96
+ pwm_index = 0 if motor == 1 else 3
97
+ motor_index = 0 if motor == 1 else 1
98
+ __l298n_init()[pwm_index].duty(speed)
99
+ __MOTOR_SPEEDS[motor_index] = speed
100
+ return state(motor)
101
+
102
+
103
+ def direction(motor, forward: bool=True):
104
+ """
105
+ Set the direction of a motor
106
+ :param motor: Motor number (1 or 2)
107
+ :param forward: True if motor should move forward, False if backward
108
+ :return: Current motor state
109
+ """
110
+ _control_motor(motor, 1 if forward else 0, 0 if forward else 1)
111
+ return state(motor)
112
+
113
+
114
+ def coast(motor):
115
+ """
116
+ Coast the motor
117
+ :param motor: Motor number (1 or 2)
118
+ """
119
+ _control_motor(motor, 0, 0)
120
+ return state(motor)
121
+
122
+
123
+ def brake(motor):
124
+ """
125
+ Brake the motor
126
+ :param motor: Motor number (1 or 2)
127
+ :return: Current motor state
128
+ """
129
+ _control_motor(motor, 1, 1)
130
+ return state(motor)
131
+
132
+
133
+ #######################
134
+ # LM helper functions #
135
+ #######################
136
+
137
+ def pinmap():
138
+ """
139
+ [i] micrOS LM naming convention
140
+ Shows logical pins - pin number(s) used by this Load module
141
+ - info which pins to use for this application
142
+ :return dict: pin name (str) - pin value (int) pairs
143
+ """
144
+ return pinmap_search([name for name, _ in PIN_BINDINGS])
145
+
146
+
147
+ def help(widgets=False):
148
+ """
149
+ [i] micrOS LM naming convention - built-in help message
150
+ :return tuple:
151
+ (widgets=False) list of functions implemented by this application
152
+ (widgets=True) list of widget json for UI generation
153
+ """
154
+ return (
155
+ 'speed motor=<1/2> speed=<0-1023>',
156
+ 'direction motor=<1/2> forward=<True/False>',
157
+ 'coast motor=<1/2>',
158
+ 'brake motor=<1/2>',
159
+ 'state motor=<0/1/2>',
160
+ 'pinmap'
161
+ )
@@ -0,0 +1,255 @@
1
+ """
2
+ micrOS ESPNow cluster module.
3
+ -- STA/AP Channel sync [OK]
4
+ -- wifi settings sync [OK]
5
+ -- health (cluster availability) [OK]
6
+ -- cluster run <cmd> [OK]
7
+ ShellCli (socket) extension (todo)
8
+ -- health (cluster availability)
9
+ """
10
+
11
+ from binascii import hexlify
12
+ from machine import soft_reset
13
+ from Config import cfgget, cfgput
14
+ from Network import _select_available_wifi_nw, ifconfig, WLAN, STA_IF, AP_IF
15
+ from Common import micro_task, exec_cmd, syslog
16
+ if cfgget("espnow"):
17
+ from Espnow import ESPNowSS
18
+ ESPNOW = ESPNowSS()
19
+ else:
20
+ ESPNOW = None
21
+
22
+ class Cluster:
23
+ CHANNEL = 6 # Leader channel to align with
24
+ ANCHOR_SSID_POSTFIX = "-mCSA" # micrOS (Wifi) Channel Sync Anchor
25
+ ANCHOR_ENABLED = False # Anchor indicator
26
+ REFRESH_MS = 20 * 60_000 # 20min - refresh channel sync period (AP mode)
27
+
28
+ #########################################################
29
+ # CLUSTER - LOCAL EXECUTION "ON TARGET" #
30
+ #########################################################
31
+
32
+ @micro_task("cluster", _wrap=True)
33
+ async def _reboot(tag:str):
34
+ """
35
+ Restart in the background
36
+ (Enable function return before restart...)
37
+ """
38
+ with micro_task(tag) as my_task:
39
+ await my_task.feed(5000)
40
+ soft_reset()
41
+
42
+
43
+ def _update_wifi(ssid:str, pwd:str):
44
+ """
45
+ Local Config Setter - Add WiFi settings
46
+ :param ssid: str - SSID
47
+ :param pwd: str - Password
48
+ """
49
+ def _update_sta_config():
50
+ nonlocal _ssid, _pwd
51
+ essids = cfgget("staessid")
52
+ passwords = cfgget("stapwd")
53
+ if _ssid in essids:
54
+ # [INFO] No password refresh is supported
55
+ return f"Wifi already known: {ssid}"
56
+ # Deserialize wifi settings from config file
57
+ essids_list = essids.split(";")
58
+ passwords_list = passwords.split(";")
59
+ # Extend with new wifi settings
60
+ essids_list.append(_ssid)
61
+ passwords_list.append(_pwd)
62
+ # Serialize back wifi settings to config file
63
+ cfgput("staessid",";".join(essids_list))
64
+ cfgput("stapwd", ";".join(passwords_list))
65
+ # REBOOT
66
+ _reboot()
67
+ return "Soft reboot, apply wifi settings"
68
+
69
+ if ';' in ssid or ';' in pwd:
70
+ return "Unsupported character in ssid or pwd ;"
71
+ # Check Wifi settings
72
+ if cfgget("nwmd").upper() == "STA":
73
+ # Check current wifi state
74
+ if ifconfig()[0] == "STA":
75
+ return "Device already connected to WiFi"
76
+ # Validate wifi essid is available
77
+ _ssid, _pwd = _select_available_wifi_nw(ssid, pwd)
78
+ if _ssid is None:
79
+ return f"Wifi SSID not available: {ssid}"
80
+ return _update_sta_config()
81
+ return "Wifi is in Access Point mode, skip sync"
82
+
83
+
84
+ def _run(cmd:str):
85
+ state, out = exec_cmd(cmd.split(), secure=True)
86
+ return f"[{'OK' if state else 'NOK'}] {out}"
87
+
88
+
89
+ @micro_task("cluster", _wrap=True)
90
+ async def _align_channel(tag:str, sta:STA_IF, ap:AP_IF):
91
+ _initialized = False
92
+
93
+ with micro_task(tag) as my_task:
94
+ while True:
95
+ my_task.out = "Sync in progress..."
96
+ ch = Cluster.CHANNEL
97
+ _refresh_ms = Cluster.REFRESH_MS
98
+ for s, b, c, *_ in sta.scan():
99
+ sta_name = (s.decode() if isinstance(s, bytes) else s)
100
+ if sta_name.endswith(Cluster.ANCHOR_SSID_POSTFIX):
101
+ my_task.out = f"Anchor {sta_name} channel: {c} (refresh: {int(_refresh_ms/60_000)}min)"
102
+ ch = c
103
+ break
104
+
105
+ if not _initialized or ch != Cluster.CHANNEL:
106
+ Cluster.CHANNEL = ch
107
+ if not sta.isconnected():
108
+ # ENABLE ESPNOW STA MAC ACCESS - AFTER THIS ALL DEVICES CAN REACH EACH OTHER ON STA MAC ADDRESS
109
+ try: sta.config(channel=ch)
110
+ except Exception as e:
111
+ my_task.out = f"STA Failed to set {ch} channel: {e}"
112
+ # ensure AP advertises/operates on the same channel (important when STA not connected yet)
113
+ try: ap.config(channel=ch)
114
+ except Exception as e:
115
+ my_task.out = f"AP Failed to set {ch} channel: {e}"
116
+ _initialized = True
117
+ await my_task.feed(_refresh_ms)
118
+
119
+
120
+ @micro_task("cluster", _wrap=True)
121
+ async def _network(tag:str, anchor=False):
122
+ """
123
+ Support hybrid AP/STA ESPNow communication
124
+ For ESPNow communication all device should use the same Wifi channel!
125
+ - LEADER: STA*/AP mode - *channel is set by the router and can change
126
+ - AP deices should use the same channel as ANCHOR (discovery by leader/anchor(STA) over AP anchor)
127
+ - ESPNOW: STA mac addresses are stored on ANCHOR
128
+ :param anchor: enable anchor mode (AP) for ESPNow channel synchronization
129
+ """
130
+ def _conf_ap_anchor():
131
+ _ap = WLAN(AP_IF)
132
+ if not _ap.active(): _ap.active(True)
133
+ anchor_ssid = f"{cfgget('devfid')}{Cluster.ANCHOR_SSID_POSTFIX}"
134
+ try:
135
+ _ap.config(essid=anchor_ssid, password=cfgget("appwd"), authmode=3, max_clients=5)
136
+ return True
137
+ except Exception as e:
138
+ syslog(f"[ERR] Anchor configuration failed: {e}")
139
+ return False
140
+ def _enable_sta():
141
+ sta = WLAN(STA_IF)
142
+ if not sta.active(): sta.active(True)
143
+ return sta
144
+
145
+ with micro_task(tag) as my_task:
146
+ # CONFIGURE WIFI CHANNEL SYNC ROLES
147
+ my_task.out = "Configure cluster network"
148
+ micros_nw = ifconfig()[0]
149
+ # MICROS in STA MODE
150
+ if micros_nw == "STA":
151
+ if anchor:
152
+ # CREATE CHANNEL ANCHOR - AP channel is inherited from STA
153
+ Cluster.ANCHOR_ENABLED = _conf_ap_anchor()
154
+ else:
155
+ # MICROS in AP MODE: CLUSTER FOLLOWER - WIFI CHANNEL SYNC WITH ANCHOR
156
+ ap, sta = WLAN(AP_IF), _enable_sta()
157
+ # Sync Channel with Cluster Anchor
158
+ _align_channel(sta, ap)
159
+ if anchor:
160
+ # MICROS in AP MODE - RECONFIGURE AP NAME to match Anchor
161
+ # EXPERIMENTAL FEATURE: Scans and Advertise the channel as well.
162
+ Cluster.ANCHOR_ENABLED = _conf_ap_anchor()
163
+ my_task.out = status()
164
+
165
+ #########################################################
166
+ # CLUSTER WIDE FEATURES #
167
+ #########################################################
168
+
169
+ def sync_wifi():
170
+ """
171
+ ESPNow Cluster Wifi Settings Sync for Station (STA) Connection Mode
172
+ - Join devices to the same wifi network...
173
+ """
174
+ if ESPNOW is None:
175
+ return "ESPNow is disabled"
176
+ # Check current wifi state
177
+ if ifconfig()[0] != "STA":
178
+ return "No WiFi STA Connection: no verified settings to share"
179
+ # [1] Get current wifi ssid and password
180
+ _ssid, _pwd = _select_available_wifi_nw(cfgget("staessid"), cfgget("stapwd"))
181
+ # [2] Send command: 'cluster _update_wifi "<ssid>" "<pwd>"'
182
+ command = f"cluster _update_wifi '{_ssid}' '{_pwd}'"
183
+ sync_tasks = ESPNOW.cluster_send(command)
184
+ return sync_tasks
185
+
186
+
187
+ def run(cmd:str):
188
+ """
189
+ Run a command on the cluster
190
+ :param cmd: str - command to run on the cluster
191
+ Example: cmd='system heartbeat'
192
+ """
193
+ command = f"cluster _run '{cmd}'"
194
+ sync_tasks = ESPNOW.cluster_send(command)
195
+ return sync_tasks
196
+
197
+
198
+ def health():
199
+ """
200
+ Simple connection check
201
+ - with default espnow-micros hello message
202
+ """
203
+ if ESPNOW is None:
204
+ return "ESPNow is disabled"
205
+ return ESPNOW.cluster_send("hello")
206
+
207
+
208
+ def members():
209
+ """
210
+ List all members in the cluster
211
+ """
212
+ if ESPNOW is None:
213
+ return "ESPNow is disabled"
214
+ return {hexlify(mac, ':').decode(): name for mac, name in ESPNOW.devices.items()}
215
+
216
+
217
+ def status():
218
+ """
219
+ Cluster setup status
220
+ """
221
+ nw_if = ifconfig()[0]
222
+ cluster_settings = {"channel": Cluster.CHANNEL, "primary_nw": nw_if,
223
+ "anchor": Cluster.ANCHOR_ENABLED}
224
+ if nw_if == "AP":
225
+ cluster_settings["refresh"] = Cluster.REFRESH_MS
226
+ return cluster_settings
227
+
228
+
229
+ def load(anchor:bool=False, refresh:int=None):
230
+ """
231
+ Enable ESPNow protocol 'cluster' module access
232
+ :param anchor: create AP as Channel Anchor (Channel Leader Role)
233
+ :param refresh: refresh channel sync period in minute (AP)
234
+ """
235
+ if ESPNOW is None:
236
+ return "ESPNow is disabled"
237
+ if refresh is not None:
238
+ Cluster.REFRESH_MS = 60_000 if refresh < 1 else refresh * 60_000
239
+ # Configure cluster network - after micrOS network setup
240
+ _network(anchor)
241
+ return f"Enable cluster module access"
242
+
243
+
244
+ def help(widgets=True):
245
+ """
246
+ [BETA]
247
+ Show help for cluster commands
248
+ """
249
+ return ("load anchor=False refresh=20",
250
+ "run 'command'",
251
+ "sync_wifi",
252
+ "health",
253
+ "members",
254
+ "status",
255
+ "[Info] Get command results:\n\ttask show con.espnow.*")
@@ -18,6 +18,11 @@ def temp():
18
18
  """
19
19
  Measure CPU temperature
20
20
  """
21
+ try:
22
+ cpu_temp = esp32.mcu_temperature()
23
+ return {'MCU temp [C]': int(cpu_temp)}
24
+ except:
25
+ pass
21
26
  try:
22
27
  raw_temp = esp32.raw_temperature()
23
28
  except Exception as e:
@@ -0,0 +1,36 @@
1
+ from Espnow import ESPNowSS
2
+ ESPNOW = ESPNowSS()
3
+
4
+
5
+ def send(peer:bytes|str, cmd:str='modules'):
6
+ """
7
+ Send message to peer (by mac address)
8
+ :param peer: mac address of espnow device
9
+ :param cmd: message string/load module call
10
+ """
11
+ return ESPNOW.send(peer, cmd)
12
+
13
+ def stats():
14
+ """
15
+ Get ESPNOW stats
16
+ """
17
+ return ESPNOW.stats()
18
+
19
+
20
+ def handshake(peer:bytes|str):
21
+ """
22
+ Handshake with ESPNow Peer
23
+ :param peer: mac address of espnow device
24
+ - device name detection
25
+ - address:name caching
26
+ """
27
+ return ESPNOW.handshake(peer)
28
+
29
+
30
+ def help():
31
+ """
32
+ ESPNOW sender/receiver with LM execution
33
+ """
34
+ return ('handshake peer=<mac-address>',
35
+ 'send peer=<peer-name> cmd="hello"',
36
+ 'stats')
@@ -26,7 +26,7 @@ def discover():
26
26
  """
27
27
  known_addresses = {hex(0x0A): "TRACKBALL", hex(0x3c): "OLED",
28
28
  hex(0x76): "BME280", hex(0x10): 'VEML7700',
29
- hex(0x6b): "QMI8658"}
29
+ hex(0x6b): "QMI8658", hex(0x29): 'TCS3472'}
30
30
  devices = scan()
31
31
  output = {"unknown": []}
32
32
  for k in devices:
@@ -73,13 +73,13 @@ async def _task(on, off, threshold, tolerance=2, check_ms=5000):
73
73
  if percent <= threshold:
74
74
  if on != last_ev:
75
75
  host = on[0]
76
- state, _ = exec_cmd(on[1:] + [f">>{host}"], jsonify=True, skip_check=True)
76
+ state, _ = exec_cmd(on[1:] + [f">>{host}"], jsonify=True)
77
77
  my_task.out = f"{percent}% <= threshold: {threshold}% - ON [{'OK' if state else 'NOK'}]"
78
78
  last_ev = on
79
79
  elif percent > threshold+tolerance: # +tolerance to avoid "on/off/on/off" on threshold limit
80
80
  if off != last_ev:
81
81
  host = off[0]
82
- state, _ = exec_cmd(off[1:] + [f">>{host}"], jsonify=True, skip_check=True)
82
+ state, _ = exec_cmd(off[1:] + [f">>{host}"], jsonify=True)
83
83
  my_task.out = f"{percent}% > threshold: {threshold+tolerance}% - OFF [{'OK' if state else 'NOK'}]"
84
84
  last_ev = off
85
85
  await my_task.feed(sleep_ms=check_ms) # Sample every <check_ms> sec