micrOSDevToolKit 2.1.5__py3-none-any.whl → 2.26.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.
Files changed (400) hide show
  1. env/driver_cp210x/.DS_Store +0 -0
  2. env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
  3. env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +17 -1
  4. micrOS/micropython/esp32-20251209-v1.27.0.bin +0 -0
  5. micrOS/micropython/esp32c3-20251209-v1.27.0.bin +0 -0
  6. micrOS/micropython/esp32c6-20251209-v1.27.0.bin +0 -0
  7. micrOS/micropython/esp32s2-20251209-v1.27.0.bin +0 -0
  8. micrOS/micropython/esp32s2-LOLIN_MINI-20251209-v1.27.0.bin +0 -0
  9. micrOS/micropython/esp32s3-4MBflash-20241129-v1.24.1.bin +0 -0
  10. micrOS/micropython/esp32s3-8MBflash-20251209-v1.27.0.bin +0 -0
  11. micrOS/micropython/esp32s3_spiram_oct-20251209-v1.27.0.bin +0 -0
  12. micrOS/micropython/rpi-pico-w-20251209-v1.27.0.uf2 +0 -0
  13. micrOS/micropython/tinypico-20251209-v1.27.0.bin +0 -0
  14. micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +191 -151
  15. micrOS/source/Auth.py +37 -0
  16. micrOS/source/Common.py +376 -102
  17. micrOS/source/Config.py +55 -25
  18. micrOS/source/Debug.py +54 -193
  19. micrOS/source/Espnow.py +404 -0
  20. micrOS/source/Files.py +207 -0
  21. micrOS/source/Hooks.py +88 -16
  22. micrOS/source/InterConnect.py +130 -46
  23. micrOS/source/Interrupts.py +8 -8
  24. micrOS/source/Logger.py +131 -0
  25. micrOS/source/Network.py +41 -21
  26. micrOS/source/Notify.py +74 -198
  27. micrOS/source/Pacman.py +326 -0
  28. micrOS/source/Scheduler.py +18 -55
  29. micrOS/source/Server.py +84 -217
  30. micrOS/source/Shell.py +103 -93
  31. micrOS/source/Tasks.py +239 -173
  32. micrOS/source/Time.py +21 -22
  33. micrOS/source/Types.py +89 -54
  34. micrOS/source/Web.py +485 -0
  35. micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
  36. micrOS/source/__pycache__/Debug.cpython-312.pyc +0 -0
  37. micrOS/source/__pycache__/Files.cpython-312.pyc +0 -0
  38. micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
  39. micrOS/source/__pycache__/Scheduler.cpython-312.pyc +0 -0
  40. micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
  41. micrOS/source/__pycache__/Shell.cpython-312.pyc +0 -0
  42. micrOS/source/__pycache__/replhelper.cpython-312.pyc +0 -0
  43. micrOS/source/helpers.py +132 -0
  44. micrOS/source/micrOS.py +25 -21
  45. micrOS/source/micrOSloader.py +14 -23
  46. micrOS/source/microIO.py +94 -57
  47. toolkit/simulator_lib/LP_darwin.py → micrOS/source/modules/IO_esp32.py +22 -11
  48. micrOS/source/{IO_esp32c3.py → modules/IO_esp32c3.py} +6 -1
  49. micrOS/source/modules/IO_esp32c6.py +38 -0
  50. micrOS/source/{IO_esp32s2.py → modules/IO_esp32s2.py} +6 -1
  51. micrOS/source/{IO_esp32s3.py → modules/IO_esp32s3.py} +43 -2
  52. micrOS/source/modules/IO_m5stamp.py +86 -0
  53. micrOS/source/{IO_qtpy.py → modules/IO_qtpy.py} +28 -18
  54. micrOS/source/{IO_tinypico.py → modules/IO_tinypico.py} +48 -3
  55. micrOS/source/modules/LM_L298N.py +161 -0
  56. {toolkit/workspace/precompiled → micrOS/source/modules}/LM_L9110_DCmotor.py +4 -4
  57. micrOS/source/{LM_OV2640.py → modules/LM_OV2640.py} +53 -42
  58. micrOS/source/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +5 -5
  59. micrOS/source/{LM_aht10.py → modules/LM_aht10.py} +12 -4
  60. micrOS/source/{LM_bme280.py → modules/LM_bme280.py} +13 -25
  61. micrOS/source/{LM_buzzer.py → modules/LM_buzzer.py} +42 -40
  62. micrOS/source/{LM_cct.py → modules/LM_cct.py} +22 -27
  63. micrOS/source/modules/LM_cluster.py +255 -0
  64. micrOS/source/{LM_co2.py → modules/LM_co2.py} +13 -6
  65. micrOS/source/{LM_dht11.py → modules/LM_dht11.py} +13 -29
  66. micrOS/source/{LM_dht22.py → modules/LM_dht22.py} +13 -28
  67. micrOS/source/{LM_dimmer.py → modules/LM_dimmer.py} +19 -16
  68. micrOS/source/modules/LM_distance.py +135 -0
  69. micrOS/source/{LM_ds18.py → modules/LM_ds18.py} +12 -4
  70. micrOS/source/{LM_esp32.py → modules/LM_esp32.py} +16 -4
  71. micrOS/source/modules/LM_espnow.py +53 -0
  72. micrOS/source/modules/LM_fileserver.py +265 -0
  73. micrOS/source/{LM_gameOfLife.py → modules/LM_gameOfLife.py} +5 -5
  74. micrOS/source/{LM_genIO.py → modules/LM_genIO.py} +49 -35
  75. micrOS/source/modules/LM_haptic.py +111 -0
  76. micrOS/source/modules/LM_i2c.py +61 -0
  77. micrOS/source/{LM_i2s_mic.py → modules/LM_i2s_mic.py} +20 -23
  78. micrOS/source/{LM_ld2410.py → modules/LM_ld2410.py} +3 -3
  79. micrOS/source/{LM_light_sensor.py → modules/LM_light_sensor.py} +22 -26
  80. micrOS/source/modules/LM_mh_z19c.py +198 -0
  81. micrOS/source/modules/LM_neoeffects.py +284 -0
  82. micrOS/source/{LM_neopixel.py → modules/LM_neopixel.py} +26 -31
  83. micrOS/source/{LM_oled.py → modules/LM_oled.py} +28 -20
  84. micrOS/source/{LM_oled_sh1106.py → modules/LM_oled_sh1106.py} +28 -24
  85. micrOS/source/{LM_oled_ui.py → modules/LM_oled_ui.py} +132 -174
  86. micrOS/source/modules/LM_pacman.py +320 -0
  87. micrOS/source/{LM_presence.py → modules/LM_presence.py} +24 -36
  88. micrOS/source/modules/LM_qmi8658.py +204 -0
  89. micrOS/source/{LM_rencoder.py → modules/LM_rencoder.py} +40 -11
  90. micrOS/source/modules/LM_rest.py +81 -0
  91. micrOS/source/{LM_rgb.py → modules/LM_rgb.py} +25 -34
  92. micrOS/source/{LM_rgbcct.py → modules/LM_rgbcct.py} +5 -5
  93. micrOS/source/{LM_roboarm.py → modules/LM_roboarm.py} +37 -45
  94. micrOS/source/modules/LM_robustness.py +137 -0
  95. micrOS/source/{LM_rp2w.py → modules/LM_rp2w.py} +3 -0
  96. micrOS/source/{LM_sdcard.py → modules/LM_sdcard.py} +3 -0
  97. micrOS/source/{LM_servo.py → modules/LM_servo.py} +4 -4
  98. micrOS/source/modules/LM_sound_event.py +751 -0
  99. micrOS/source/{LM_stepper.py → modules/LM_stepper.py} +8 -8
  100. micrOS/source/{LM_switch.py → modules/LM_switch.py} +21 -18
  101. micrOS/source/{LM_system.py → modules/LM_system.py} +96 -59
  102. micrOS/source/modules/LM_tcs3472.py +187 -0
  103. micrOS/source/modules/LM_telegram.py +388 -0
  104. micrOS/source/modules/LM_trackball.py +287 -0
  105. micrOS/source/modules/LM_veml7700.py +159 -0
  106. micrOS/source/modules/LM_web.py +38 -0
  107. micrOS/source/urequests.py +204 -91
  108. {toolkit/workspace/precompiled → micrOS/source/web}/dashboard.html +9 -4
  109. micrOS/source/web/editor.js +440 -0
  110. micrOS/source/web/filesui.html +178 -0
  111. micrOS/source/web/filesui.js +338 -0
  112. micrOS/source/{index.html → web/index.html} +44 -2
  113. micrOS/source/web/uapi.js +103 -0
  114. micrOS/source/web/udashboard.js +129 -0
  115. micrOS/source/web/ustyle.css +55 -0
  116. micrOS/source/web/uwidgets.js +172 -0
  117. micrOS/source/web/uwidgets_pro.js +99 -0
  118. micrOS/utests/__init__.py +0 -0
  119. micrOS/utests/test_scheduler.py +435 -0
  120. {micrOSDevToolKit-2.1.5.data → microsdevtoolkit-2.26.1.data}/scripts/devToolKit.py +47 -4
  121. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/METADATA +392 -279
  122. microsdevtoolkit-2.26.1.dist-info/RECORD +396 -0
  123. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/WHEEL +1 -1
  124. toolkit/DevEnvCompile.py +63 -33
  125. toolkit/DevEnvOTA.py +72 -22
  126. toolkit/DevEnvUSB.py +147 -77
  127. toolkit/Gateway.py +9 -9
  128. toolkit/LM_to_compile.dat +12 -4
  129. toolkit/MicrOSDevEnv.py +129 -51
  130. toolkit/WebRepl.py +73 -0
  131. toolkit/dashboard_apps/BackupRestore.py +171 -0
  132. toolkit/dashboard_apps/CCTDemo.py +12 -17
  133. toolkit/dashboard_apps/CCTTest.py +20 -24
  134. toolkit/dashboard_apps/CamStream.py +2 -6
  135. toolkit/dashboard_apps/CatGame.py +14 -16
  136. toolkit/dashboard_apps/Dimmer.py +11 -21
  137. toolkit/dashboard_apps/GetVersion.py +11 -19
  138. toolkit/dashboard_apps/MicrophoneTest.py +2 -7
  139. toolkit/dashboard_apps/NeoEffectsDemo.py +22 -35
  140. toolkit/dashboard_apps/NeopixelTest.py +20 -25
  141. toolkit/dashboard_apps/PresenceTest.py +2 -8
  142. toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
  143. toolkit/dashboard_apps/RGBTest.py +20 -24
  144. toolkit/dashboard_apps/RoboArm.py +24 -32
  145. toolkit/dashboard_apps/SED_test.py +10 -14
  146. toolkit/dashboard_apps/SensorsTest.py +61 -0
  147. toolkit/dashboard_apps/SystemTest.py +219 -117
  148. toolkit/dashboard_apps/Template_app.py +12 -19
  149. toolkit/dashboard_apps/_app_base.py +34 -0
  150. toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
  151. toolkit/dashboard_apps/uLightDemo.py +15 -24
  152. toolkit/index.html +6 -5
  153. toolkit/lib/LocalMachine.py +6 -1
  154. toolkit/lib/MicrosFiles.py +46 -0
  155. toolkit/lib/Repository.py +64 -0
  156. toolkit/lib/TerminalColors.py +4 -0
  157. toolkit/lib/macroScript.py +371 -0
  158. toolkit/lib/micrOSClient.py +124 -51
  159. toolkit/lib/micrOSClientHistory.py +156 -0
  160. toolkit/lib/pip_package_installer.py +31 -4
  161. toolkit/micrOSdashboard.py +16 -21
  162. toolkit/micrOSlint.py +28 -10
  163. toolkit/simulator_lib/.DS_Store +0 -0
  164. micrOS/source/IO_esp32.py → toolkit/simulator_lib/IO_darwin.py +3 -0
  165. toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
  166. toolkit/simulator_lib/__pycache__/aioespnow.cpython-312.pyc +0 -0
  167. toolkit/simulator_lib/__pycache__/camera.cpython-312.pyc +0 -0
  168. toolkit/simulator_lib/__pycache__/framebuf.cpython-312.pyc +0 -0
  169. toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
  170. toolkit/simulator_lib/__pycache__/micropython.cpython-312.pyc +0 -0
  171. toolkit/simulator_lib/__pycache__/mip.cpython-312.pyc +0 -0
  172. toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
  173. toolkit/simulator_lib/__pycache__/network.cpython-312.pyc +0 -0
  174. toolkit/simulator_lib/__pycache__/sim_common.cpython-312.pyc +0 -0
  175. toolkit/simulator_lib/__pycache__/simgc.cpython-312.pyc +0 -0
  176. toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
  177. toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
  178. toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
  179. toolkit/simulator_lib/__pycache__/urandom.cpython-312.pyc +0 -0
  180. toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
  181. toolkit/simulator_lib/__pycache__/ussl.cpython-312.pyc +0 -0
  182. toolkit/simulator_lib/aioespnow.py +28 -0
  183. toolkit/simulator_lib/camera.py +84 -0
  184. toolkit/simulator_lib/dht.py +1 -1
  185. toolkit/simulator_lib/framebuf.py +49 -1
  186. toolkit/simulator_lib/machine.py +32 -2
  187. toolkit/simulator_lib/micropython.py +3 -3
  188. toolkit/simulator_lib/mip.py +165 -0
  189. toolkit/simulator_lib/neopixel.py +3 -2
  190. toolkit/simulator_lib/network.py +2 -1
  191. toolkit/simulator_lib/node_config.json +2 -3
  192. toolkit/simulator_lib/ntptime.py +1 -1
  193. toolkit/simulator_lib/{sim_console.py → sim_common.py} +2 -3
  194. toolkit/simulator_lib/simgc.py +6 -2
  195. toolkit/simulator_lib/simulator.py +138 -46
  196. toolkit/simulator_lib/uasyncio.py +34 -3
  197. toolkit/simulator_lib/uos.py +147 -0
  198. toolkit/simulator_lib/urandom.py +4 -0
  199. toolkit/simulator_lib/usocket.py +5 -1
  200. toolkit/simulator_lib/view01.jpg +0 -0
  201. toolkit/simulator_lib/view02.jpg +0 -0
  202. toolkit/socketClient.py +43 -23
  203. toolkit/user_data/webhooks/generic.py +1 -1
  204. toolkit/user_data/webhooks/macro.py +44 -0
  205. toolkit/user_data/webhooks/template.macro +20 -0
  206. toolkit/user_data/webhooks/template.py +1 -1
  207. toolkit/workspace/precompiled/Auth.mpy +0 -0
  208. toolkit/workspace/precompiled/Common.mpy +0 -0
  209. toolkit/workspace/precompiled/Config.mpy +0 -0
  210. toolkit/workspace/precompiled/Debug.mpy +0 -0
  211. toolkit/workspace/precompiled/Espnow.mpy +0 -0
  212. toolkit/workspace/precompiled/Files.mpy +0 -0
  213. toolkit/workspace/precompiled/Hooks.mpy +0 -0
  214. toolkit/workspace/precompiled/InterConnect.mpy +0 -0
  215. toolkit/workspace/precompiled/Interrupts.mpy +0 -0
  216. toolkit/workspace/precompiled/Logger.mpy +0 -0
  217. toolkit/workspace/precompiled/Network.mpy +0 -0
  218. toolkit/workspace/precompiled/Notify.mpy +0 -0
  219. toolkit/workspace/precompiled/Pacman.mpy +0 -0
  220. toolkit/workspace/precompiled/Scheduler.mpy +0 -0
  221. toolkit/workspace/precompiled/Server.mpy +0 -0
  222. toolkit/workspace/precompiled/Shell.mpy +0 -0
  223. toolkit/workspace/precompiled/Tasks.mpy +0 -0
  224. toolkit/workspace/precompiled/Time.mpy +0 -0
  225. toolkit/workspace/precompiled/Types.mpy +0 -0
  226. toolkit/workspace/precompiled/Web.mpy +0 -0
  227. toolkit/workspace/precompiled/_mpy.version +1 -1
  228. toolkit/workspace/precompiled/config/_git.keep +0 -0
  229. toolkit/workspace/precompiled/helpers.mpy +0 -0
  230. toolkit/workspace/precompiled/micrOS.mpy +0 -0
  231. toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
  232. toolkit/workspace/precompiled/microIO.mpy +0 -0
  233. toolkit/workspace/precompiled/modules/IO_esp32.mpy +0 -0
  234. toolkit/workspace/precompiled/modules/IO_esp32c3.mpy +0 -0
  235. toolkit/workspace/precompiled/modules/IO_esp32c6.mpy +0 -0
  236. toolkit/workspace/precompiled/modules/IO_esp32s2.mpy +0 -0
  237. toolkit/workspace/precompiled/modules/IO_esp32s3.mpy +0 -0
  238. toolkit/workspace/precompiled/modules/IO_m5stamp.mpy +0 -0
  239. toolkit/workspace/precompiled/modules/IO_qtpy.mpy +0 -0
  240. toolkit/workspace/precompiled/modules/IO_rp2.mpy +0 -0
  241. toolkit/workspace/precompiled/modules/IO_tinypico.mpy +0 -0
  242. toolkit/workspace/precompiled/modules/LM_L298N.mpy +0 -0
  243. {micrOS/source → toolkit/workspace/precompiled/modules}/LM_L9110_DCmotor.py +4 -4
  244. toolkit/workspace/precompiled/modules/LM_OV2640.mpy +0 -0
  245. toolkit/workspace/precompiled/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +5 -5
  246. toolkit/workspace/precompiled/modules/LM_aht10.mpy +0 -0
  247. toolkit/workspace/precompiled/modules/LM_bme280.mpy +0 -0
  248. toolkit/workspace/precompiled/{LM_buzzer.mpy → modules/LM_buzzer.mpy} +0 -0
  249. toolkit/workspace/precompiled/modules/LM_cct.mpy +0 -0
  250. toolkit/workspace/precompiled/modules/LM_cluster.mpy +0 -0
  251. toolkit/workspace/precompiled/modules/LM_co2.mpy +0 -0
  252. toolkit/workspace/precompiled/modules/LM_dht11.mpy +0 -0
  253. toolkit/workspace/precompiled/modules/LM_dht22.mpy +0 -0
  254. toolkit/workspace/precompiled/modules/LM_dimmer.mpy +0 -0
  255. toolkit/workspace/precompiled/modules/LM_distance.mpy +0 -0
  256. toolkit/workspace/precompiled/modules/LM_ds18.mpy +0 -0
  257. toolkit/workspace/precompiled/{LM_esp32.py → modules/LM_esp32.py} +16 -4
  258. toolkit/workspace/precompiled/modules/LM_espnow.py +53 -0
  259. toolkit/workspace/precompiled/modules/LM_fileserver.mpy +0 -0
  260. toolkit/workspace/precompiled/{LM_gameOfLife.mpy → modules/LM_gameOfLife.mpy} +0 -0
  261. toolkit/workspace/precompiled/modules/LM_genIO.mpy +0 -0
  262. toolkit/workspace/precompiled/modules/LM_haptic.mpy +0 -0
  263. toolkit/workspace/precompiled/modules/LM_i2c.py +61 -0
  264. toolkit/workspace/precompiled/modules/LM_i2s_mic.mpy +0 -0
  265. toolkit/workspace/precompiled/{LM_ld2410.mpy → modules/LM_ld2410.mpy} +0 -0
  266. toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
  267. toolkit/workspace/precompiled/modules/LM_mh_z19c.py +198 -0
  268. toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
  269. toolkit/workspace/precompiled/modules/LM_neopixel.mpy +0 -0
  270. toolkit/workspace/precompiled/modules/LM_oled.mpy +0 -0
  271. toolkit/workspace/precompiled/modules/LM_oled_sh1106.mpy +0 -0
  272. toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
  273. toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
  274. toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
  275. toolkit/workspace/precompiled/modules/LM_qmi8658.py +204 -0
  276. toolkit/workspace/precompiled/{LM_rencoder.py → modules/LM_rencoder.py} +40 -11
  277. toolkit/workspace/precompiled/modules/LM_rest.mpy +0 -0
  278. toolkit/workspace/precompiled/modules/LM_rgb.mpy +0 -0
  279. toolkit/workspace/precompiled/{LM_rgbcct.mpy → modules/LM_rgbcct.mpy} +0 -0
  280. toolkit/workspace/precompiled/modules/LM_roboarm.mpy +0 -0
  281. toolkit/workspace/precompiled/modules/LM_robustness.py +137 -0
  282. toolkit/workspace/precompiled/{LM_rp2w.py → modules/LM_rp2w.py} +3 -0
  283. toolkit/workspace/precompiled/{LM_sdcard.py → modules/LM_sdcard.py} +3 -0
  284. toolkit/workspace/precompiled/{LM_servo.mpy → modules/LM_servo.mpy} +0 -0
  285. toolkit/workspace/precompiled/modules/LM_sound_event.mpy +0 -0
  286. toolkit/workspace/precompiled/{LM_stepper.mpy → modules/LM_stepper.mpy} +0 -0
  287. toolkit/workspace/precompiled/modules/LM_switch.mpy +0 -0
  288. toolkit/workspace/precompiled/modules/LM_system.mpy +0 -0
  289. toolkit/workspace/precompiled/modules/LM_tcs3472.py +187 -0
  290. toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
  291. toolkit/workspace/precompiled/{LM_tinyrgb.mpy → modules/LM_tinyrgb.mpy} +0 -0
  292. toolkit/workspace/precompiled/modules/LM_trackball.mpy +0 -0
  293. toolkit/workspace/precompiled/modules/LM_veml7700.mpy +0 -0
  294. toolkit/workspace/precompiled/modules/LM_web.mpy +0 -0
  295. toolkit/workspace/precompiled/urequests.mpy +0 -0
  296. {micrOS/source → toolkit/workspace/precompiled/web}/dashboard.html +9 -4
  297. toolkit/workspace/precompiled/web/editor.js +440 -0
  298. toolkit/workspace/precompiled/web/filesui.html +178 -0
  299. toolkit/workspace/precompiled/web/filesui.js +338 -0
  300. toolkit/workspace/precompiled/{index.html → web/index.html} +44 -2
  301. toolkit/workspace/precompiled/web/uapi.js +103 -0
  302. toolkit/workspace/precompiled/web/udashboard.js +129 -0
  303. toolkit/workspace/precompiled/web/ustyle.css +55 -0
  304. toolkit/workspace/precompiled/web/uwidgets.js +172 -0
  305. toolkit/workspace/precompiled/web/uwidgets_pro.js +99 -0
  306. env/driver_cp210x/CH34XSER_MAC/CH34X_DRV_INSTALL_INSTRUCTIONS.pdf +0 -0
  307. env/driver_cp210x/CH34XSER_MAC/CH34xVCPDriver.pkg +0 -0
  308. micrOS/micropython/esp32-20231005-v1.21.0.bin +0 -0
  309. micrOS/micropython/esp32c3-GENERIC-20240105-v1.22.1.bin +0 -0
  310. micrOS/micropython/esp32c3-GENERIC-20240222-v1.22.2.bin +0 -0
  311. micrOS/micropython/esp32s2-GENERIC-20240105-v1.22.1.bin +0 -0
  312. micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
  313. micrOS/micropython/esp32s3-GENERIC-20240105-v1.22.1.bin +0 -0
  314. micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
  315. micrOS/micropython/rpi-pico-w-20231005-v1.21.0.uf2 +0 -0
  316. micrOS/micropython/tinypico-20231005-v1.21.0.bin +0 -0
  317. micrOS/micropython/tinypico-usbc-UM-20240105-v1.22.1.bin +0 -0
  318. micrOS/source/LM_L298N_DCmotor.py +0 -86
  319. micrOS/source/LM_catgame.py +0 -74
  320. micrOS/source/LM_dashboard_be.py +0 -37
  321. micrOS/source/LM_demo.py +0 -85
  322. micrOS/source/LM_distance.py +0 -88
  323. micrOS/source/LM_i2c.py +0 -44
  324. micrOS/source/LM_intercon.py +0 -57
  325. micrOS/source/LM_keychain.py +0 -318
  326. micrOS/source/LM_lmpacman.py +0 -126
  327. micrOS/source/LM_neoeffects.py +0 -327
  328. micrOS/source/LM_pet_feeder.py +0 -76
  329. micrOS/source/LM_ph_sensor.py +0 -51
  330. micrOS/source/LM_rest.py +0 -40
  331. micrOS/source/LM_robustness.py +0 -73
  332. micrOS/source/LM_telegram.py +0 -96
  333. micrOS/source/reset.py +0 -11
  334. micrOS/source/uapi.js +0 -76
  335. micrOS/source/udashboard.js +0 -137
  336. micrOS/source/ustyle.css +0 -28
  337. micrOS/source/uwidgets.js +0 -179
  338. micrOSDevToolKit-2.1.5.dist-info/RECORD +0 -337
  339. toolkit/dashboard_apps/AirQualityBME280.py +0 -36
  340. toolkit/dashboard_apps/AirQualityDHT22_CO2.py +0 -36
  341. toolkit/lib/file_extensions.py +0 -16
  342. toolkit/simulator_lib/__pycache__/LP_darwin.cpython-312.pyc +0 -0
  343. toolkit/simulator_lib/__pycache__/LP_darwin.cpython-38.pyc +0 -0
  344. toolkit/simulator_lib/__pycache__/LP_darwin.cpython-39.pyc +0 -0
  345. toolkit/simulator_lib/__pycache__/sim_console.cpython-312.pyc +0 -0
  346. toolkit/simulator_lib/__pycache__/sim_console.cpython-38.pyc +0 -0
  347. toolkit/simulator_lib/__pycache__/sim_console.cpython-39.pyc +0 -0
  348. toolkit/workspace/precompiled/IO_esp32.mpy +0 -0
  349. toolkit/workspace/precompiled/IO_esp32c3.mpy +0 -0
  350. toolkit/workspace/precompiled/IO_esp32s2.mpy +0 -0
  351. toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
  352. toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
  353. toolkit/workspace/precompiled/IO_rp2.mpy +0 -0
  354. toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
  355. toolkit/workspace/precompiled/LM_L298N_DCmotor.mpy +0 -0
  356. toolkit/workspace/precompiled/LM_OV2640.mpy +0 -0
  357. toolkit/workspace/precompiled/LM_aht10.mpy +0 -0
  358. toolkit/workspace/precompiled/LM_bme280.mpy +0 -0
  359. toolkit/workspace/precompiled/LM_catgame.py +0 -74
  360. toolkit/workspace/precompiled/LM_cct.mpy +0 -0
  361. toolkit/workspace/precompiled/LM_co2.mpy +0 -0
  362. toolkit/workspace/precompiled/LM_dashboard_be.py +0 -37
  363. toolkit/workspace/precompiled/LM_demo.py +0 -85
  364. toolkit/workspace/precompiled/LM_dht11.mpy +0 -0
  365. toolkit/workspace/precompiled/LM_dht22.mpy +0 -0
  366. toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
  367. toolkit/workspace/precompiled/LM_distance.py +0 -88
  368. toolkit/workspace/precompiled/LM_ds18.mpy +0 -0
  369. toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
  370. toolkit/workspace/precompiled/LM_i2c.py +0 -44
  371. toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
  372. toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
  373. toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
  374. toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
  375. toolkit/workspace/precompiled/LM_lmpacman.mpy +0 -0
  376. toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
  377. toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
  378. toolkit/workspace/precompiled/LM_oled.mpy +0 -0
  379. toolkit/workspace/precompiled/LM_oled_sh1106.mpy +0 -0
  380. toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
  381. toolkit/workspace/precompiled/LM_pet_feeder.py +0 -76
  382. toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
  383. toolkit/workspace/precompiled/LM_presence.mpy +0 -0
  384. toolkit/workspace/precompiled/LM_rest.mpy +0 -0
  385. toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
  386. toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
  387. toolkit/workspace/precompiled/LM_robustness.py +0 -73
  388. toolkit/workspace/precompiled/LM_switch.mpy +0 -0
  389. toolkit/workspace/precompiled/LM_system.mpy +0 -0
  390. toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
  391. toolkit/workspace/precompiled/reset.mpy +0 -0
  392. toolkit/workspace/precompiled/uapi.js +0 -76
  393. toolkit/workspace/precompiled/udashboard.js +0 -137
  394. toolkit/workspace/precompiled/ustyle.css +0 -28
  395. toolkit/workspace/precompiled/uwidgets.js +0 -179
  396. /toolkit/user_data/node_config_archive/.include → /micrOS/source/config/_git.keep +0 -0
  397. /micrOS/source/{IO_rp2.py → modules/IO_rp2.py} +0 -0
  398. /micrOS/source/{LM_tinyrgb.py → modules/LM_tinyrgb.py} +0 -0
  399. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info/licenses}/LICENSE +0 -0
  400. {micrOSDevToolKit-2.1.5.dist-info → microsdevtoolkit-2.26.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,751 @@
1
+ """
2
+ Module for sound pattern recognition with an I2S microphone.
3
+ Pattern recognition is achieved by instance-based learning
4
+ (nearest neighbor algorithm) where the user can interactively
5
+ train the algorithm by labeling events. The training starts
6
+ with an empty dataset, and the user needs to label events while
7
+ observing system logs to see if there is a new event. After
8
+ recording a few events, the user may decide to turn on the
9
+ "auto-learn" feature to improve in-sample accuracy, however,
10
+ it may also result in overfitting if certain classes become
11
+ overrepresented.
12
+
13
+ Events may be labeled as None if there are no classes recorded, or
14
+ the algorithm is uncertain about which label to assign to the event.
15
+ Arbitrary number of classes may be created, or one can use binary
16
+ classification where events are labeled as '<class name 1>' or '<class name 2>'.
17
+
18
+ For the usage, execute "sound_event help" or check the section
19
+ "Functions for recording and clearing events from the dataset".
20
+
21
+ ----------------
22
+ Example:
23
+ ----------------
24
+ picocom --baud 115200 /dev/ttyACM0 <---- the device must be connected through USB
25
+ ...
26
+ Terminal ready
27
+ [info] sound_event: {'class': None}
28
+ [info] sound_event: {'class': None}
29
+ [info] sound_event: {'class': None} <---- at this point the user decides to label
30
+ this event by: record_last_event '<label>'
31
+
32
+ [info] sound_event: event of length 8320 will be recorded as "finger_snaps"
33
+ <---- resulting log entry after record_last_event
34
+
35
+ [info] sound_event: {'class': "finger_snaps"}
36
+ <---- resulting log entry after recognizing an event
37
+ """
38
+
39
+ import LM_i2s_mic
40
+ import json
41
+
42
+ from microIO import pinmap_search
43
+ from Common import micro_task, syslog, console
44
+ from Types import resolve
45
+
46
+
47
+ class Data:
48
+ CONTROL_TASK_TAG = 'sound_event._classifier'
49
+ DATASET = ''
50
+ EVENTS = []
51
+ EVENT_CALLBACKS = set()
52
+ AUTO_LEARN = False
53
+ PERFORMANCE = {}
54
+ DISTANCE_MATRICES = {}
55
+
56
+
57
+ ################################################################################
58
+ # DSP helper functions
59
+ ################################################################################
60
+
61
+
62
+ def _amplitude_envelope(samples, frame_size):
63
+ """
64
+ Calculate max amplitude per frame and return it as a list.
65
+ :param frame_size: size of the signal frame (int)
66
+ :param samples: list of sampled values
67
+ """
68
+ envelope = []
69
+ for k in range(int(len(samples)/frame_size)):
70
+ amplitude = [abs(s) for s in samples[k*frame_size: min(len(samples), (k+1)*frame_size)]]
71
+ envelope.append(max(amplitude))
72
+
73
+ return envelope
74
+
75
+
76
+ def _rmse(samples, frame_size):
77
+ """
78
+ Calculate root mean squared energy.
79
+ :param frame_size: size of the signal frame (int)
80
+ :param samples: list of sampled values
81
+ """
82
+ rmse = []
83
+ for k in range(int(len(samples)/frame_size)):
84
+ rmse_frame = (sum([s**2 for s in samples[k*frame_size: min(len(samples), (k+1)*frame_size)]])/frame_size)**(0.5)
85
+ rmse.append(rmse_frame)
86
+
87
+ return rmse
88
+
89
+
90
+ def _zero_crossing_rate(samples, frame_size):
91
+ """
92
+ Calculate the rate of the signal crossing the X axis.
93
+ :param frame_size: size of the signal frame (int)
94
+ :param samples: list of sampled values
95
+ """
96
+ rates = []
97
+ for k in range(int(len(samples)/frame_size)):
98
+ rate = 0
99
+ for i in range(k*frame_size, min(len(samples)-1, (k+1)*frame_size-1)):
100
+ rate += abs(samples[i]/abs(samples[i]) - samples[i+1]/abs(samples[i+1]))
101
+ rates.append(0.5 * rate)
102
+
103
+ return rates
104
+
105
+
106
+ def _crest_factor(samples, frame_size):
107
+ """
108
+ Calculate Crest factor (peak amplitude divided by RMSE).
109
+ :param frame_size: size of the signal frame (int)
110
+ :param samples: list of sampled values
111
+ """
112
+ cf = []
113
+ for k in range(int(len(samples)/frame_size)):
114
+ peak = max(samples[k*frame_size: min(len(samples), (k+1)*frame_size)], key=lambda x: abs(x))
115
+ rmse_frame = (sum([s**2 for s in samples[k*frame_size: min(len(samples), (k+1)*frame_size)]])/frame_size)**(0.5)
116
+ cf.append(peak/rmse_frame)
117
+
118
+ return cf
119
+
120
+
121
+ def _analyze_event(samples, frame_size):
122
+ return {
123
+ 'name': None,
124
+ 'features': {
125
+ 'ZCR': {'type': 'time-series', 'data': _zero_crossing_rate(samples,frame_size)},
126
+ 'RMSE': {'type': 'time-series', 'data': _rmse(samples,frame_size)},
127
+ 'envelope': {'type': 'time-series', 'data': _amplitude_envelope(samples, frame_size)},
128
+ 'Crest factor': {'type': 'time-series', 'data': _crest_factor(samples, frame_size)},
129
+ 'length': {'type': 'ratio', 'data': len(samples)},
130
+ 'max': {'type': 'ratio', 'data': max([abs(s) for s in samples])}
131
+ }
132
+ }
133
+
134
+ def _get_feature_values(event, feature):
135
+ return event['features'][feature]['data']
136
+
137
+ def _get_feature_names():
138
+ return ['ZCR', 'RMSE', 'envelope', 'Crest factor', 'length', 'max']
139
+
140
+
141
+ ######################################################################################
142
+ # Functions for classification (instance-based learning)
143
+ # Distance between time-dependent features is measured in two steps:
144
+ # 1. match the alignment of the signals by maximizing cross-correlation
145
+ # 2. measure mean squared error on the aligned signals
146
+ #
147
+ # Distance between instances is determined by measuring the closest instance by all
148
+ # features separately, and selecting the most frequent class by majority voting.
149
+ ######################################################################################
150
+
151
+
152
+ def _calculate_time_series_feature_distances(instances, i, j, distance_matrices):
153
+ """
154
+ calculate distance matrices time-series data
155
+ :param instances: dict - instances read from dataset
156
+ :param i: int - index of the instance to compare
157
+ :param j: int - index of the other instance to compare
158
+ :param distance_matrices: dict - initialized distance matrices
159
+ """
160
+ time_series_features = [f for f in instances[0]['features'].keys() if instances[0]['features'][f]['type'] == 'time-series']
161
+
162
+ if not time_series_features:
163
+ return
164
+
165
+ features_i = {}
166
+ features_j = {}
167
+ cross_corr = {}
168
+ l_f = None
169
+ l_g = None
170
+
171
+ for feature in time_series_features:
172
+ features_i[feature] = _get_feature_values(instances[i], feature)
173
+ features_j[feature] = _get_feature_values(instances[j], feature)
174
+ cross_corr[feature] = [0]
175
+
176
+ if l_f is None:
177
+ l_f = len(features_i[feature])
178
+ l_g = len(features_j[feature])
179
+ elif l_f != len(features_i[feature]) or l_g != len(features_j[feature]):
180
+ raise ValueError('Only time-series features of the same length can be computed at once!')
181
+
182
+
183
+ cross_corr_range_n = range(-(l_g - 1), l_f)
184
+
185
+ for n in cross_corr_range_n:
186
+ cross_corr_range_m = range(max(0, -n), min(l_f, l_g - n))
187
+
188
+ if len(cross_corr_range_m):
189
+ for feature in time_series_features:
190
+ cross_corr[feature].append(0)
191
+
192
+ for m in cross_corr_range_m:
193
+ for feature in time_series_features:
194
+ cross_corr[feature][-1] += features_i[feature][m] * features_j[feature][m+n]
195
+
196
+ # Best alignment of signals is averaged for all time-dependent features,
197
+ # thereby penalizing misalignment between features.
198
+ n = 0
199
+ for feature in time_series_features:
200
+ argmax_feature_corr = max(range(len(cross_corr[feature])), key=lambda x: cross_corr[feature][x])
201
+ best_alignment = list(cross_corr_range_n)[argmax_feature_corr]
202
+ n += best_alignment/len(time_series_features)
203
+
204
+ n = int(n)
205
+ # MSE (mean-squared error)
206
+ for feature in time_series_features:
207
+ measure = 0
208
+ for m in range(max(0, -n), min(l_f, l_g - n)):
209
+ measure += (features_i[feature][m] - features_j[feature][m + n])**2 / len(range(max(0, -n), min(l_f, l_g - n)))
210
+ distance_matrices[feature][i][j] = measure
211
+ distance_matrices[feature][j][i] = measure # Due to matrix symmetry
212
+
213
+
214
+ def _calculate_ratio_feature_distances(instances, i, j, distance_matrices):
215
+ """
216
+ calculate distance matrices ratio-type values
217
+ :param instances: dict - instances read from dataset
218
+ :param i: int - index of the instance to compare
219
+ :param j: int - index of the other instance to compare
220
+ :param distance_matrices: dict - initialized distance matrices
221
+ """
222
+ ratio_features = [f for f in instances[0]['features'].keys() if instances[0]['features'][f]['type'] == 'ratio']
223
+
224
+ if not ratio_features:
225
+ return
226
+
227
+ for feature in ratio_features:
228
+ distance = abs(_get_feature_values(instances[i], feature) - _get_feature_values(instances[j], feature))
229
+ distance_matrices[feature][i][j] = distance
230
+ distance_matrices[feature][j][i] = distance # Due to matrix symmetry
231
+
232
+
233
+ def _calculate_all_sound_feature_distances(instances):
234
+ """
235
+ calculate distance matrices from scratch,
236
+ should only be used during initialization
237
+ :param instances: dict - instances read from dataset
238
+ """
239
+ num_instances = len(instances)
240
+
241
+ if num_instances:
242
+ distance_matrices = {k: [] for k in set(instances[0]['features'].keys())}
243
+ else:
244
+ distance_matrices = {k: [] for k in _get_feature_names()}
245
+
246
+ for i in instances:
247
+ # Initialize empty distance matrices
248
+ for feature in distance_matrices.keys():
249
+ distance_matrices[feature].append([None]*num_instances)
250
+
251
+
252
+ for i_idx in range(len(instances)):
253
+ for j_idx in range(len(instances)):
254
+ if j_idx > i_idx: # Due to matrix symmetry
255
+ continue
256
+ _calculate_time_series_feature_distances(instances, i_idx, j_idx, distance_matrices)
257
+ _calculate_ratio_feature_distances(instances, i_idx, j_idx, distance_matrices)
258
+
259
+ return distance_matrices
260
+
261
+
262
+ def _calculate_sound_feature_distances(instances,
263
+ new_instance,
264
+ distance_matrices_):
265
+ """
266
+ calculate distance matrices for a new instance
267
+ :param instances: dict - instances read from dataset
268
+ :param new_instance: dict - newly recorded instance to be classified
269
+ :param distances: dict - arbitrary distance matrices
270
+ """
271
+ distance_matrices = {}
272
+
273
+ for feature in distance_matrices_.keys():
274
+ distance_matrices[feature] = [r.copy() for r in distance_matrices_[feature]]
275
+
276
+ for i in range(len(instances)):
277
+ # Initialize empty columns for the new instance in the distance matrices
278
+ distance_matrices[feature][i].append(0)
279
+
280
+ # Add new instance after the existing ones, and create empty row in the distance matrices
281
+ instances.append(new_instance.copy())
282
+
283
+ for feature in distance_matrices_.keys():
284
+ distance_matrices[feature].append([0]*(len(instances)))
285
+
286
+ for i_idx, i in enumerate(instances):
287
+ _calculate_time_series_feature_distances(instances, i_idx, len(instances)-1, distance_matrices)
288
+ _calculate_ratio_feature_distances(instances, i_idx, len(instances)-1, distance_matrices)
289
+ return distance_matrices
290
+
291
+
292
+ def _classify_instances(instances, distance_matrices):
293
+ """
294
+ classify all instances based on distance matrices
295
+ :param instances: dict - instances read from dataset
296
+ :param distance_matrices: dict - arbitrary distance matrices
297
+ """
298
+
299
+ nearest_neighbors = []
300
+ majority_votes = []
301
+
302
+ if not len(instances):
303
+ return None
304
+
305
+ for i in range(len(instances)):
306
+ votes = []
307
+ majority_vote = None
308
+
309
+ for d in distance_matrices.values():
310
+ # Distance of an instance from itself is ignored
311
+ votes.append(min(range(len(d[i])), key=lambda x: d[i][x] if x != i else float('inf')))
312
+ nearest_neighbors.append(votes)
313
+
314
+ classes = set(instances[v]['name'] for v in votes)
315
+ class_popularities = {c: sum(c == instances[x]['name'] for x in votes) for c in classes}
316
+ most_popular_class = max(class_popularities.keys(), key=lambda x: class_popularities[x])
317
+
318
+ if sum(class_popularities[c] == class_popularities[most_popular_class] for c in class_popularities.keys()) == 1:
319
+ # There is a majority class
320
+ majority_vote = most_popular_class
321
+ else:
322
+ # There is no majority class
323
+ # Calculate most common reciprocal vote
324
+ reciprocal_scores = []
325
+ for v in votes:
326
+ reciprocal_votes = []
327
+ for d in distance_matrices.values():
328
+ reciprocal_votes.append(min(range(len(d[v])), key=lambda x: d[v][x] if v != x else float('inf')))
329
+ reciprocal_scores.append(sum(instances[r]['name'] == instances[i]['name'] for r in reciprocal_votes))
330
+
331
+ if max(reciprocal_scores) and sum(r == max(reciprocal_scores) for r in reciprocal_scores) == 1:
332
+ reciprocal_candidate = max(range(len(reciprocal_scores)), key=lambda x: reciprocal_scores[x])
333
+ majority_vote = instances[votes[reciprocal_candidate]]['name']
334
+
335
+ majority_votes.append(majority_vote)
336
+
337
+ return nearest_neighbors, majority_votes
338
+
339
+
340
+ def classify_last_event():
341
+ """
342
+ assign a class to the last recorded event
343
+ """
344
+ instances = read_instances()
345
+ if not len(Data.EVENTS):
346
+ return None
347
+
348
+ last_event = Data.EVENTS[-1]
349
+
350
+ distance_matrices = \
351
+ _calculate_sound_feature_distances(instances, last_event, Data.DISTANCE_MATRICES)
352
+ nearest_neighbors, majority_votes = _classify_instances(instances,distance_matrices)
353
+
354
+ if Data.AUTO_LEARN:
355
+ performance_current = Data.PERFORMANCE['in-sample accuracy']
356
+ performance_new = _evaluate_votes(instances,nearest_neighbors,majority_votes)['in-sample accuracy']
357
+ if performance_current < performance_new:
358
+ record_last_event(majority_votes[-1])
359
+ console(f'[info] sound_event: added new instance ({majority_votes[-1]}) to the dataset')
360
+ console(f'[info] sound_event: in-sample accuracy increased from {performance_current} to {performance_new}')
361
+
362
+ return {'class': majority_votes[-1]}
363
+
364
+
365
+ ################################################################################
366
+ # I/O helper functions
367
+ # Should not be used manually.
368
+ ################################################################################
369
+
370
+
371
+ def read_instances():
372
+ """
373
+ read stored instances into a dict
374
+ """
375
+ try:
376
+ with open(Data.DATASET, 'r') as f:
377
+ return json.loads(f.read())
378
+ except OSError:
379
+ syslog(f'[ERR] sound_event: unable to read the dataset')
380
+ return []
381
+
382
+
383
+ def _write_instances(instances):
384
+ """
385
+ overwrite instances in the dataset
386
+ :param instances: dict - instances to store
387
+ """
388
+ with open(Data.DATASET, 'w+') as f:
389
+ f.write(json.dumps(instances))
390
+
391
+
392
+ def _init_matrices():
393
+ """
394
+ initialize distance matrices
395
+ """
396
+ instances = read_instances()
397
+ Data.DISTANCE_MATRICES = \
398
+ _calculate_all_sound_feature_distances(instances)
399
+
400
+ if len(instances):
401
+ Data.PERFORMANCE = _evaluate_dataset(instances,Data.DISTANCE_MATRICES)
402
+ else:
403
+ console('[info] sound_event: dataset is missing, initializing empty dataset')
404
+ _write_instances([])
405
+ Data.PERFORMANCE = {}
406
+
407
+
408
+ ################################################################################
409
+ # Functions for recording and clearing events from the dataset
410
+ # Can be used during training to save events used for classification.
411
+ ################################################################################
412
+
413
+ def record_last_event(label):
414
+ """
415
+ save the last recorded event in the dataset
416
+ :param label: str - name of the class
417
+ """
418
+ console('[info] sound_event: event of length %s will be recorded as "%s"' % (_get_feature_values(Data.EVENTS[-1],'length'), label))
419
+ Data.EVENTS[-1]['name'] = label
420
+ try:
421
+ instances = read_instances()
422
+ except Exception as e:
423
+ syslog(f'[ERR] sound_event: could not read instances (override): {e}')
424
+ instances = []
425
+
426
+ instances.append(Data.EVENTS[-1])
427
+ _write_instances(instances)
428
+ _init_matrices()
429
+
430
+
431
+ def remove_last_instance():
432
+ """
433
+ remove the last recorded instance from the dataset
434
+ """
435
+ instances = read_instances()
436
+ instances.pop()
437
+ _write_instances(instances)
438
+ _init_matrices()
439
+
440
+
441
+ def remove_instance_by_idx(idx):
442
+ """
443
+ remove an instance with an index from the dataset
444
+ :param idx: int - index of the instance to be deleted from the dataset
445
+ """
446
+ instances = read_instances()
447
+ del instances[idx]
448
+ _write_instances(instances)
449
+ _init_matrices()
450
+
451
+
452
+ def remove_classes():
453
+ """
454
+ remove all instances from the dataset
455
+ """
456
+ _write_instances([])
457
+ _init_matrices()
458
+
459
+
460
+ def remove_class(class_name):
461
+ """
462
+ remove all instances with the specified label
463
+ :param class_name: str - name of the class
464
+ """
465
+ instances = read_instances()
466
+ new_instances = []
467
+
468
+ for instance in instances:
469
+ if instance['name'] != class_name:
470
+ new_instances.append(instance)
471
+
472
+ _write_instances(new_instances)
473
+ _init_matrices()
474
+
475
+
476
+ def relabel_class(old_label, new_label):
477
+ """
478
+ rename class used for labeling instances in the dataset
479
+ :param class_name: str - name of the class
480
+ """
481
+ instances = read_instances()
482
+
483
+ for instance in instances:
484
+ if instance['name'] == old_label:
485
+ instance['name'] = new_label
486
+
487
+ _write_instances(instances)
488
+ _init_matrices()
489
+
490
+
491
+ ################################################################################
492
+ # Utility functions
493
+ ################################################################################
494
+
495
+
496
+ def get_classes():
497
+ """
498
+ get all unique classes (labels) stored in the dataset
499
+ """
500
+ instances = read_instances()
501
+ return set([i['name'] for i in instances])
502
+
503
+
504
+ def get_events():
505
+ """
506
+ return all recorded events
507
+ """
508
+ return Data.EVENTS
509
+
510
+
511
+ def _evaluate_votes(instances, nearest_neighbors, majority_votes):
512
+ """
513
+ evaluate classification performance of nearest neighbor results
514
+ with multiple features
515
+ :param instances: dict - instances read from dataset
516
+ :param nearest_neighbors: list - nearest neighbors per feature for each instance
517
+ :param majority_votes: list - names of the classes assigned to the instance
518
+ """
519
+ instances_by_class = {}
520
+ hits_by_class = {}
521
+ accuracy = 0
522
+
523
+ for idx, neighbors in enumerate(nearest_neighbors):
524
+ majority_vote = majority_votes[idx]
525
+ ground_truth = instances[idx]['name']
526
+
527
+ accuracy += (majority_vote == ground_truth)
528
+
529
+ if not ground_truth in instances_by_class.keys():
530
+ instances_by_class[ground_truth] = []
531
+ instances_by_class[ground_truth].append(majority_vote == ground_truth)
532
+
533
+ if not ground_truth in hits_by_class.keys():
534
+ hits_by_class[ground_truth] = 0
535
+ hits_by_class[ground_truth] += sum([instances[neighbor]['name'] == ground_truth for neighbor in neighbors])/len(neighbors)
536
+
537
+ return {
538
+ 'in-sample accuracy': accuracy/len(instances),
539
+ 'class accuracies': {c: sum(instances_by_class[c])/len(instances_by_class[c])
540
+ for c in instances_by_class.keys()},
541
+ 'class consistency': {c: hits_by_class[c]/len(instances_by_class[c])
542
+ for c in hits_by_class.keys()}
543
+ }
544
+
545
+
546
+ def _evaluate_dataset(instances,distance_matrices):
547
+ """
548
+ evaluate the in-sample classification performance
549
+ :param instances: dict - instances read from dataset
550
+ :param distance_matrices: dict - arbitrary distance matrices
551
+ """
552
+
553
+ nearest_neighbors, majority_votes = _classify_instances(instances, distance_matrices)
554
+ return _evaluate_votes(instances, nearest_neighbors, majority_votes)
555
+
556
+
557
+ def get_performance():
558
+ """
559
+ return the in-sample classification performance
560
+ """
561
+ return Data.PERFORMANCE
562
+
563
+
564
+ ################################################################################
565
+ # Event loop functions
566
+ ################################################################################
567
+
568
+
569
+ def autolearn(enabled = True):
570
+ """
571
+ enable/disable automated learning, use it when there are already labeled
572
+ instances in the dataset
573
+ :param enabled: bool
574
+ """
575
+ Data.AUTO_LEARN = enabled
576
+
577
+
578
+ def subscribe(callback, event):
579
+ """
580
+ subscribe to notifications of events
581
+ :callback: func - callback function
582
+ :param event: str - event to get notification for (use 'all' to be notified for all events)
583
+ """
584
+ Data.EVENT_CALLBACKS.add((callback, event))
585
+
586
+
587
+ def _notify(event):
588
+ """
589
+ notification function to invoke callbacks
590
+ :param event: str - class of the currently recorded event
591
+ """
592
+ for callback, observed_event in Data.EVENT_CALLBACKS:
593
+ if event == observed_event or observed_event == 'all':
594
+ callback(event)
595
+
596
+
597
+ def _detect_event(samples, frame_size, pause_frame_count, rmse_threshold = 0.1):
598
+ """
599
+ segment and classify events in samples while maintaining a pointer within
600
+ the raw samples to indicate the last processed sample of an event so that
601
+ the rest can be processed later
602
+ :param samples: list - raw audio samples
603
+ :param frame_size: int - size of an audio frame
604
+ :param pause_frame_count: int - how many pause frame to count for separating events
605
+ :param rmse_threshold: float - RMSE value above which to detect an event (0-1)
606
+ """
607
+ rmse_frames = _rmse(samples, frame_size)
608
+ current_event = []
609
+ events_features = []
610
+ event_pointer = 0
611
+ pause_frames = 0
612
+
613
+ # Audio segmentation based on RMSE, and extraction of features
614
+ for i in range(len(rmse_frames)):
615
+ if rmse_frames[i] >= rmse_threshold:
616
+ if not len(current_event):
617
+ event_pointer = i*frame_size
618
+ current_event.extend(samples[i*frame_size : (i+1)*frame_size])
619
+ pause_frames = 0
620
+ else:
621
+ pause_frames += 1
622
+ if len(current_event) and pause_frames <= pause_frame_count:
623
+ current_event.extend(samples[i*frame_size : (i+1)*frame_size])
624
+ else:
625
+ event_pointer = (i+1)*frame_size
626
+
627
+ # Finalize and classify event if there are enough pause frames
628
+ if len(current_event) and pause_frames >= pause_frame_count:
629
+ events_features.append(_analyze_event(current_event, frame_size))
630
+ current_event = []
631
+ pause_frames = 0
632
+
633
+ return events_features, event_pointer
634
+
635
+
636
+ async def __control_task(capture_duration_ms,
637
+ max_event_duration_ms,
638
+ frame_size_ms,
639
+ pause_duration_ms,
640
+ event_buffer_length):
641
+ """
642
+ micro task for event detection
643
+ :param capture_duration_ms: int - duration of samples to capture at once
644
+ :param max_event_duration_ms: int - maximum duration of an event
645
+ :param frame_size: int - size of the audio frames used for features
646
+ :param pause_duration_ms: int - duration of pause frames to distinguish events
647
+ :param event_buffer_length: int - number of events to store at once
648
+ """
649
+ with micro_task(tag=Data.CONTROL_TASK_TAG) as my_task:
650
+ samples = []
651
+ frame_size = int((frame_size_ms/1000)*LM_i2s_mic.Data.SAMPLING_RATE)
652
+ pause_duration = int(pause_duration_ms/frame_size_ms)
653
+
654
+ while True:
655
+ try:
656
+ new_samples = LM_i2s_mic.decode(await LM_i2s_mic._capture(capture_duration_ms/1000))
657
+ samples.extend(new_samples)
658
+
659
+ events, event_pointer = _detect_event(samples, frame_size, pause_duration)
660
+ # Store unprocessed samples to be extended by future samples
661
+ samples = samples[event_pointer:]
662
+
663
+ for event in events:
664
+ Data.EVENTS.append(event)
665
+ last_event_label = classify_last_event()
666
+ _notify(last_event_label['class'])
667
+ console(f'[info] sound_event: {last_event_label}')
668
+
669
+ # Discard old samples if the number of stored samples exceeds a threshold
670
+ max_samples = int((max_event_duration_ms/1000)*LM_i2s_mic.Data.SAMPLING_RATE)
671
+ if len(samples) > max_samples:
672
+ samples = samples[-max_samples:]
673
+
674
+ # Wait for new samples to be taken
675
+ ms_period = int(len(new_samples)/LM_i2s_mic.Data.SAMPLING_RATE)
676
+ await my_task.feed(sleep_ms=ms_period)
677
+ Data.EVENTS = Data.EVENTS[-event_buffer_length:]
678
+ except Exception as e:
679
+ console(f'[ERR] sound_event: {e}')
680
+
681
+
682
+ def load(dataset = 'sound_events.pds',
683
+ capture_duration_ms=192,
684
+ max_event_duration_ms = 3000,
685
+ frame_size_ms=80,
686
+ pause_duration_ms=500,
687
+ event_buffer_length=1,
688
+ sd_storage=False):
689
+ """
690
+ start micro task for event detection, and mount SD storage if enabled
691
+ :param capture_duration_ms: int - duration of samples to capture at once
692
+ :param max_event_duration_ms: int - maximum duration of an event
693
+ :param frame_size_ms: int - duration of the audio frames used for features
694
+ :param event_buffer_length: int - number of events to store at once
695
+ :param sd_storage: bool - use SD card storage for the dataset
696
+ """
697
+ if sd_storage:
698
+ from vfs import mount
699
+ from machine import SDCard
700
+ mount(SDCard(),"/sd")
701
+ Data.DATASET = f'/sd/{dataset}'
702
+ else:
703
+ Data.DATASET = dataset
704
+
705
+ _init_matrices()
706
+ micro_task(tag=Data.CONTROL_TASK_TAG,
707
+ task=__control_task(capture_duration_ms,
708
+ max_event_duration_ms,
709
+ frame_size_ms,
710
+ pause_duration_ms,
711
+ event_buffer_length))
712
+
713
+
714
+ #######################
715
+ # Helper LM functions #
716
+ #######################
717
+
718
+
719
+ def pinmap():
720
+ """
721
+ [i] micrOS LM naming convention
722
+ Shows logical pins - pin number(s) used by this Load module
723
+ - info which pins to use for this application
724
+ :return dict: pin name (str) - pin value (int) pairs
725
+ """
726
+ return pinmap_search(['i2s_sck', 'i2s_ws', 'i2s_sd'])
727
+
728
+
729
+ def help(widgets=False):
730
+ """
731
+ [i] micrOS LM naming convention - built-in help message
732
+ :return tuple:
733
+ (widgets=False) list of functions implemented by this application
734
+ (widgets=True) list of widget json for UI generation
735
+ """
736
+ return resolve(('load dataset=\'sound_events.pds\' capture_duration_ms=192 max_event_duration_ms=3000'\
737
+ 'frame_size_ms=80 pause_duration_ms=500 event_buffer_length=1 sd_storage=False ',\
738
+ 'TEXTBOX classify_last_event',\
739
+ 'read_instances',\
740
+ 'record_last_event label',\
741
+ 'remove_last_instance',\
742
+ 'remove_instance_by_idx idx',\
743
+ 'remove_classes',\
744
+ 'remove_class class_name',\
745
+ 'relabel_class old_label new_label',\
746
+ 'get_classes',\
747
+ 'get_events',\
748
+ 'TEXTBOX get_performance',\
749
+ 'autolearn enabled=True',\
750
+ 'subscribe callback event',\
751
+ 'pinmap'), widgets=widgets)