micrOSDevToolKit 2.9.1__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.
- env/driver_cp210x/macOS_VCP_Driver/SiLabsUSBDriverDisk.dmg +0 -0
- env/driver_cp210x/macOS_VCP_Driver/macOS_VCP_Driver_Release_Notes.txt +17 -1
- micrOS/micropython/esp32-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32c3-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32c6-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s2-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s2-LOLIN_MINI-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/{esp32s3-20241129-v1.24.1.bin → esp32s3-4MBflash-20241129-v1.24.1.bin} +0 -0
- micrOS/micropython/esp32s3-8MBflash-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20251209-v1.27.0.bin +0 -0
- micrOS/micropython/rpi-pico-w-20251209-v1.27.0.uf2 +0 -0
- micrOS/micropython/tinypico-20251209-v1.27.0.bin +0 -0
- micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +167 -163
- micrOS/source/Auth.py +37 -0
- micrOS/source/Common.py +361 -116
- micrOS/source/Config.py +32 -22
- micrOS/source/Debug.py +50 -94
- micrOS/source/Espnow.py +377 -100
- micrOS/source/Files.py +207 -0
- micrOS/source/Hooks.py +48 -20
- micrOS/source/InterConnect.py +126 -42
- micrOS/source/Interrupts.py +6 -6
- micrOS/source/Logger.py +63 -26
- micrOS/source/Network.py +41 -21
- micrOS/source/Notify.py +48 -22
- micrOS/source/Pacman.py +326 -0
- micrOS/source/Scheduler.py +14 -54
- micrOS/source/Server.py +67 -69
- micrOS/source/Shell.py +99 -91
- micrOS/source/Tasks.py +141 -95
- micrOS/source/Time.py +19 -18
- micrOS/source/Types.py +53 -9
- micrOS/source/Web.py +381 -76
- micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Debug.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Files.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Scheduler.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Server.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Shell.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/replhelper.cpython-312.pyc +0 -0
- micrOS/source/config/_git.keep +0 -0
- micrOS/source/helpers.py +132 -0
- micrOS/source/micrOS.py +17 -7
- micrOS/source/micrOSloader.py +5 -12
- micrOS/source/microIO.py +44 -20
- micrOS/source/modules/IO_esp32c6.py +38 -0
- micrOS/source/{IO_esp32s3.py → modules/IO_esp32s3.py} +37 -1
- micrOS/source/{IO_m5stamp.py → modules/IO_m5stamp.py} +35 -1
- micrOS/source/{IO_qtpy.py → modules/IO_qtpy.py} +22 -17
- micrOS/source/{IO_tinypico.py → modules/IO_tinypico.py} +38 -0
- micrOS/source/modules/LM_L298N.py +161 -0
- {toolkit/workspace/precompiled → micrOS/source/modules}/LM_L9110_DCmotor.py +3 -3
- micrOS/source/{LM_OV2640.py → modules/LM_OV2640.py} +45 -27
- micrOS/source/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +3 -3
- micrOS/source/{LM_aht10.py → modules/LM_aht10.py} +2 -2
- micrOS/source/{LM_bme280.py → modules/LM_bme280.py} +3 -3
- micrOS/source/{LM_buzzer.py → modules/LM_buzzer.py} +18 -25
- micrOS/source/{LM_cct.py → modules/LM_cct.py} +17 -21
- micrOS/source/modules/LM_cluster.py +255 -0
- micrOS/source/{LM_co2.py → modules/LM_co2.py} +3 -3
- micrOS/source/{LM_dht11.py → modules/LM_dht11.py} +2 -2
- micrOS/source/{LM_dht22.py → modules/LM_dht22.py} +2 -2
- micrOS/source/{LM_dimmer.py → modules/LM_dimmer.py} +9 -9
- micrOS/source/{LM_distance.py → modules/LM_distance.py} +4 -6
- micrOS/source/{LM_ds18.py → modules/LM_ds18.py} +2 -2
- micrOS/source/{LM_esp32.py → modules/LM_esp32.py} +5 -0
- micrOS/source/modules/LM_espnow.py +53 -0
- micrOS/source/modules/LM_fileserver.py +265 -0
- micrOS/source/{LM_genIO.py → modules/LM_genIO.py} +52 -37
- micrOS/source/{LM_haptic.py → modules/LM_haptic.py} +2 -2
- {toolkit/workspace/precompiled → micrOS/source/modules}/LM_i2c.py +5 -4
- micrOS/source/{LM_i2s_mic.py → modules/LM_i2s_mic.py} +6 -7
- micrOS/source/{LM_ld2410.py → modules/LM_ld2410.py} +2 -2
- micrOS/source/{LM_light_sensor.py → modules/LM_light_sensor.py} +10 -21
- micrOS/source/modules/LM_mh_z19c.py +198 -0
- micrOS/source/modules/LM_neoeffects.py +284 -0
- micrOS/source/{LM_neopixel.py → modules/LM_neopixel.py} +19 -23
- micrOS/source/{LM_oled.py → modules/LM_oled.py} +2 -2
- micrOS/source/{LM_oled_sh1106.py → modules/LM_oled_sh1106.py} +3 -3
- micrOS/source/{LM_oled_ui.py → modules/LM_oled_ui.py} +72 -64
- micrOS/source/modules/LM_pacman.py +320 -0
- micrOS/source/{LM_presence.py → modules/LM_presence.py} +11 -15
- micrOS/source/modules/LM_qmi8658.py +204 -0
- micrOS/source/{LM_rencoder.py → modules/LM_rencoder.py} +2 -2
- micrOS/source/{LM_rest.py → modules/LM_rest.py} +4 -6
- micrOS/source/{LM_rgb.py → modules/LM_rgb.py} +21 -29
- micrOS/source/{LM_roboarm.py → modules/LM_roboarm.py} +8 -8
- micrOS/source/modules/LM_robustness.py +137 -0
- micrOS/source/{LM_servo.py → modules/LM_servo.py} +3 -3
- micrOS/source/{LM_stepper.py → modules/LM_stepper.py} +5 -5
- micrOS/source/{LM_switch.py → modules/LM_switch.py} +11 -9
- micrOS/source/{LM_system.py → modules/LM_system.py} +38 -32
- micrOS/source/modules/LM_tcs3472.py +187 -0
- micrOS/source/{LM_telegram.py → modules/LM_telegram.py} +164 -116
- micrOS/source/{LM_trackball.py → modules/LM_trackball.py} +3 -3
- micrOS/source/{LM_veml7700.py → modules/LM_veml7700.py} +2 -2
- micrOS/source/modules/LM_web.py +38 -0
- micrOS/source/urequests.py +39 -15
- {toolkit/workspace/precompiled → micrOS/source/web}/dashboard.html +4 -0
- micrOS/source/web/editor.js +440 -0
- micrOS/source/web/filesui.html +178 -0
- micrOS/source/web/filesui.js +338 -0
- {toolkit/workspace/precompiled → micrOS/source/web}/index.html +44 -2
- micrOS/source/{uapi.js → web/uapi.js} +48 -7
- micrOS/source/{ustyle.css → web/ustyle.css} +6 -3
- micrOS/utests/__init__.py +0 -0
- micrOS/utests/test_scheduler.py +435 -0
- {micrOSDevToolKit-2.9.1.data → microsdevtoolkit-2.26.1.data}/scripts/devToolKit.py +33 -3
- {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info}/METADATA +327 -268
- microsdevtoolkit-2.26.1.dist-info/RECORD +396 -0
- {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info}/WHEEL +1 -1
- toolkit/DevEnvCompile.py +63 -33
- toolkit/DevEnvOTA.py +58 -22
- toolkit/DevEnvUSB.py +110 -55
- toolkit/Gateway.py +6 -6
- toolkit/LM_to_compile.dat +6 -4
- toolkit/MicrOSDevEnv.py +127 -57
- toolkit/WebRepl.py +73 -0
- toolkit/dashboard_apps/BackupRestore.py +20 -35
- toolkit/dashboard_apps/CCTDemo.py +12 -17
- toolkit/dashboard_apps/CCTTest.py +20 -24
- toolkit/dashboard_apps/CamStream.py +2 -6
- toolkit/dashboard_apps/CatGame.py +14 -16
- toolkit/dashboard_apps/Dimmer.py +11 -21
- toolkit/dashboard_apps/GetVersion.py +11 -19
- toolkit/dashboard_apps/MicrophoneTest.py +1 -6
- toolkit/dashboard_apps/NeoEffectsDemo.py +22 -35
- toolkit/dashboard_apps/NeopixelTest.py +20 -25
- toolkit/dashboard_apps/PresenceTest.py +2 -8
- toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
- toolkit/dashboard_apps/RGBTest.py +20 -24
- toolkit/dashboard_apps/RoboArm.py +24 -32
- toolkit/dashboard_apps/SED_test.py +10 -14
- toolkit/dashboard_apps/SensorsTest.py +61 -0
- toolkit/dashboard_apps/SystemTest.py +202 -105
- toolkit/dashboard_apps/Template_app.py +11 -23
- toolkit/dashboard_apps/_app_base.py +34 -0
- toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
- toolkit/dashboard_apps/uLightDemo.py +15 -24
- toolkit/index.html +4 -4
- toolkit/lib/LocalMachine.py +6 -1
- toolkit/lib/MicrosFiles.py +46 -0
- toolkit/lib/Repository.py +64 -0
- toolkit/lib/TerminalColors.py +4 -0
- toolkit/lib/macroScript.py +6 -0
- toolkit/lib/micrOSClient.py +123 -50
- toolkit/lib/micrOSClientHistory.py +156 -0
- toolkit/lib/pip_package_installer.py +5 -2
- toolkit/micrOSdashboard.py +12 -17
- toolkit/micrOSlint.py +20 -8
- toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/aioespnow.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/framebuf.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/micropython.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/mip.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/network.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_common.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/simgc.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/simulator.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uasyncio.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/uos.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/urandom.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/usocket.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/ussl.cpython-312.pyc +0 -0
- toolkit/simulator_lib/aioespnow.py +28 -0
- toolkit/simulator_lib/dht.py +1 -1
- toolkit/simulator_lib/framebuf.py +49 -1
- toolkit/simulator_lib/machine.py +17 -2
- toolkit/simulator_lib/micropython.py +3 -3
- toolkit/simulator_lib/mip.py +165 -0
- toolkit/simulator_lib/neopixel.py +3 -2
- toolkit/simulator_lib/network.py +2 -1
- toolkit/simulator_lib/node_config.json +2 -3
- toolkit/simulator_lib/ntptime.py +1 -1
- toolkit/simulator_lib/{sim_console.py → sim_common.py} +2 -3
- toolkit/simulator_lib/simgc.py +6 -2
- toolkit/simulator_lib/simulator.py +137 -59
- toolkit/simulator_lib/uasyncio.py +33 -2
- toolkit/simulator_lib/uos.py +147 -0
- toolkit/simulator_lib/urandom.py +4 -0
- toolkit/socketClient.py +43 -23
- toolkit/user_data/webhooks/generic.py +1 -1
- toolkit/user_data/webhooks/macro.py +1 -1
- toolkit/user_data/webhooks/template.py +1 -1
- toolkit/workspace/precompiled/Auth.mpy +0 -0
- toolkit/workspace/precompiled/Common.mpy +0 -0
- toolkit/workspace/precompiled/Config.mpy +0 -0
- toolkit/workspace/precompiled/Debug.mpy +0 -0
- toolkit/workspace/precompiled/Espnow.mpy +0 -0
- toolkit/workspace/precompiled/Files.mpy +0 -0
- toolkit/workspace/precompiled/Hooks.mpy +0 -0
- toolkit/workspace/precompiled/InterConnect.mpy +0 -0
- toolkit/workspace/precompiled/Interrupts.mpy +0 -0
- toolkit/workspace/precompiled/Logger.mpy +0 -0
- toolkit/workspace/precompiled/Network.mpy +0 -0
- toolkit/workspace/precompiled/Notify.mpy +0 -0
- toolkit/workspace/precompiled/Pacman.mpy +0 -0
- toolkit/workspace/precompiled/Scheduler.mpy +0 -0
- toolkit/workspace/precompiled/Server.mpy +0 -0
- toolkit/workspace/precompiled/Shell.mpy +0 -0
- toolkit/workspace/precompiled/Tasks.mpy +0 -0
- toolkit/workspace/precompiled/Time.mpy +0 -0
- toolkit/workspace/precompiled/Types.mpy +0 -0
- toolkit/workspace/precompiled/Web.mpy +0 -0
- toolkit/workspace/precompiled/_mpy.version +1 -1
- toolkit/workspace/precompiled/config/_git.keep +0 -0
- toolkit/workspace/precompiled/helpers.mpy +0 -0
- toolkit/workspace/precompiled/micrOS.mpy +0 -0
- toolkit/workspace/precompiled/micrOSloader.mpy +0 -0
- toolkit/workspace/precompiled/microIO.mpy +0 -0
- toolkit/workspace/precompiled/{IO_esp32.mpy → modules/IO_esp32.mpy} +0 -0
- toolkit/workspace/precompiled/{IO_esp32c3.mpy → modules/IO_esp32c3.mpy} +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32c6.mpy +0 -0
- toolkit/workspace/precompiled/{IO_esp32s2.mpy → modules/IO_esp32s2.mpy} +0 -0
- toolkit/workspace/precompiled/modules/IO_esp32s3.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_m5stamp.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_qtpy.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_rp2.mpy +0 -0
- toolkit/workspace/precompiled/modules/IO_tinypico.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_L298N.mpy +0 -0
- {micrOS/source → toolkit/workspace/precompiled/modules}/LM_L9110_DCmotor.py +3 -3
- toolkit/workspace/precompiled/modules/LM_OV2640.mpy +0 -0
- toolkit/workspace/precompiled/{LM_VL53L0X.py → modules/LM_VL53L0X.py} +3 -3
- toolkit/workspace/precompiled/{LM_aht10.mpy → modules/LM_aht10.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_bme280.mpy → modules/LM_bme280.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_buzzer.mpy → modules/LM_buzzer.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_cct.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_cluster.mpy +0 -0
- toolkit/workspace/precompiled/{LM_co2.mpy → modules/LM_co2.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_dht11.mpy → modules/LM_dht11.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_dht22.mpy → modules/LM_dht22.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_dimmer.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_distance.mpy +0 -0
- toolkit/workspace/precompiled/{LM_ds18.mpy → modules/LM_ds18.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_esp32.py → modules/LM_esp32.py} +5 -0
- toolkit/workspace/precompiled/modules/LM_espnow.py +53 -0
- toolkit/workspace/precompiled/modules/LM_fileserver.mpy +0 -0
- toolkit/workspace/precompiled/{LM_gameOfLife.mpy → modules/LM_gameOfLife.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_genIO.mpy +0 -0
- toolkit/workspace/precompiled/{LM_haptic.mpy → modules/LM_haptic.mpy} +0 -0
- {micrOS/source → toolkit/workspace/precompiled/modules}/LM_i2c.py +5 -4
- toolkit/workspace/precompiled/modules/LM_i2s_mic.mpy +0 -0
- toolkit/workspace/precompiled/{LM_ld2410.mpy → modules/LM_ld2410.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_light_sensor.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_mh_z19c.py +198 -0
- toolkit/workspace/precompiled/modules/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_neopixel.mpy +0 -0
- toolkit/workspace/precompiled/{LM_oled.mpy → modules/LM_oled.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_oled_sh1106.mpy → modules/LM_oled_sh1106.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_pacman.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_presence.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_qmi8658.py +204 -0
- toolkit/workspace/precompiled/{LM_rencoder.py → modules/LM_rencoder.py} +2 -2
- toolkit/workspace/precompiled/modules/LM_rest.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_rgb.mpy +0 -0
- toolkit/workspace/precompiled/{LM_rgbcct.mpy → modules/LM_rgbcct.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_roboarm.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_robustness.py +137 -0
- toolkit/workspace/precompiled/{LM_servo.mpy → modules/LM_servo.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_sound_event.mpy → modules/LM_sound_event.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_stepper.mpy → modules/LM_stepper.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_switch.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_system.mpy +0 -0
- toolkit/workspace/precompiled/modules/LM_tcs3472.py +187 -0
- toolkit/workspace/precompiled/modules/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/{LM_tinyrgb.mpy → modules/LM_tinyrgb.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_trackball.mpy → modules/LM_trackball.mpy} +0 -0
- toolkit/workspace/precompiled/{LM_veml7700.mpy → modules/LM_veml7700.mpy} +0 -0
- toolkit/workspace/precompiled/modules/LM_web.mpy +0 -0
- toolkit/workspace/precompiled/urequests.mpy +0 -0
- {micrOS/source → toolkit/workspace/precompiled/web}/dashboard.html +4 -0
- toolkit/workspace/precompiled/web/editor.js +440 -0
- toolkit/workspace/precompiled/web/filesui.html +178 -0
- toolkit/workspace/precompiled/web/filesui.js +338 -0
- {micrOS/source → toolkit/workspace/precompiled/web}/index.html +44 -2
- toolkit/workspace/precompiled/{uapi.js → web/uapi.js} +48 -7
- toolkit/workspace/precompiled/{ustyle.css → web/ustyle.css} +6 -3
- micrOS/micropython/esp32-20241129-v1.24.1.bin +0 -0
- micrOS/micropython/esp32c3-20240222-v1.22.2.bin +0 -0
- micrOS/micropython/esp32s2-20240602-v1.23.0.bin +0 -0
- micrOS/micropython/esp32s2-LOLIN_MINI-20220618-v1.19.1.bin +0 -0
- micrOS/micropython/esp32s2-LOLIN_MINI-20240602-v1.23.0.bin +0 -0
- micrOS/micropython/esp32s3-20240105-v1.22.1.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20231005-v1.21.0.bin +0 -0
- micrOS/micropython/esp32s3_spiram_oct-20241129-v1.24.1.bin +0 -0
- micrOS/micropython/rpi-pico-w-20241129-v1.24.1.uf2 +0 -0
- micrOS/micropython/tinypico-20241129-v1.24.1.bin +0 -0
- micrOS/source/LM_L298N_DCmotor.py +0 -86
- micrOS/source/LM_catgame.py +0 -75
- micrOS/source/LM_dashboard_be.py +0 -37
- micrOS/source/LM_demo.py +0 -97
- micrOS/source/LM_espnow.py +0 -23
- micrOS/source/LM_intercon.py +0 -57
- micrOS/source/LM_keychain.py +0 -322
- micrOS/source/LM_lmpacman.py +0 -126
- micrOS/source/LM_neoeffects.py +0 -331
- micrOS/source/LM_oledui.py +0 -972
- micrOS/source/LM_pet_feeder.py +0 -78
- micrOS/source/LM_ph_sensor.py +0 -51
- micrOS/source/LM_robustness.py +0 -74
- micrOS/source/reset.py +0 -11
- micrOSDevToolKit-2.9.1.dist-info/RECORD +0 -365
- toolkit/dashboard_apps/AirQualityBME280.py +0 -36
- toolkit/dashboard_apps/AirQualityDHT22_CO2.py +0 -36
- toolkit/lib/file_extensions.py +0 -16
- toolkit/simulator_lib/__pycache__/sim_console.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_console.cpython-38.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_console.cpython-39.pyc +0 -0
- toolkit/workspace/precompiled/IO_esp32s3.mpy +0 -0
- toolkit/workspace/precompiled/IO_m5stamp.mpy +0 -0
- toolkit/workspace/precompiled/IO_qtpy.mpy +0 -0
- toolkit/workspace/precompiled/IO_rp2.mpy +0 -0
- toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
- toolkit/workspace/precompiled/LM_L298N_DCmotor.mpy +0 -0
- toolkit/workspace/precompiled/LM_OV2640.mpy +0 -0
- toolkit/workspace/precompiled/LM_catgame.py +0 -75
- toolkit/workspace/precompiled/LM_cct.mpy +0 -0
- toolkit/workspace/precompiled/LM_dashboard_be.py +0 -37
- toolkit/workspace/precompiled/LM_demo.py +0 -97
- toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
- toolkit/workspace/precompiled/LM_distance.mpy +0 -0
- toolkit/workspace/precompiled/LM_espnow.py +0 -23
- toolkit/workspace/precompiled/LM_genIO.mpy +0 -0
- toolkit/workspace/precompiled/LM_i2s_mic.mpy +0 -0
- toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
- toolkit/workspace/precompiled/LM_keychain.mpy +0 -0
- toolkit/workspace/precompiled/LM_light_sensor.mpy +0 -0
- toolkit/workspace/precompiled/LM_lmpacman.mpy +0 -0
- toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
- toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/LM_oledui.mpy +0 -0
- toolkit/workspace/precompiled/LM_pet_feeder.py +0 -78
- toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
- toolkit/workspace/precompiled/LM_presence.mpy +0 -0
- toolkit/workspace/precompiled/LM_rest.mpy +0 -0
- toolkit/workspace/precompiled/LM_rgb.mpy +0 -0
- toolkit/workspace/precompiled/LM_roboarm.mpy +0 -0
- toolkit/workspace/precompiled/LM_robustness.py +0 -74
- toolkit/workspace/precompiled/LM_switch.mpy +0 -0
- toolkit/workspace/precompiled/LM_system.mpy +0 -0
- toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/node_config.json +0 -1
- toolkit/workspace/precompiled/reset.mpy +0 -0
- /micrOS/source/{IO_esp32.py → modules/IO_esp32.py} +0 -0
- /micrOS/source/{IO_esp32c3.py → modules/IO_esp32c3.py} +0 -0
- /micrOS/source/{IO_esp32s2.py → modules/IO_esp32s2.py} +0 -0
- /micrOS/source/{IO_rp2.py → modules/IO_rp2.py} +0 -0
- /micrOS/source/{LM_gameOfLife.py → modules/LM_gameOfLife.py} +0 -0
- /micrOS/source/{LM_rgbcct.py → modules/LM_rgbcct.py} +0 -0
- /micrOS/source/{LM_rp2w.py → modules/LM_rp2w.py} +0 -0
- /micrOS/source/{LM_sdcard.py → modules/LM_sdcard.py} +0 -0
- /micrOS/source/{LM_sound_event.py → modules/LM_sound_event.py} +0 -0
- /micrOS/source/{LM_tinyrgb.py → modules/LM_tinyrgb.py} +0 -0
- /micrOS/source/{udashboard.js → web/udashboard.js} +0 -0
- /micrOS/source/{uwidgets.js → web/uwidgets.js} +0 -0
- /micrOS/source/{uwidgets_pro.js → web/uwidgets_pro.js} +0 -0
- {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info/licenses}/LICENSE +0 -0
- {micrOSDevToolKit-2.9.1.dist-info → microsdevtoolkit-2.26.1.dist-info}/top_level.txt +0 -0
- /toolkit/workspace/precompiled/{LM_rp2w.py → modules/LM_rp2w.py} +0 -0
- /toolkit/workspace/precompiled/{LM_sdcard.py → modules/LM_sdcard.py} +0 -0
- /toolkit/workspace/precompiled/{udashboard.js → web/udashboard.js} +0 -0
- /toolkit/workspace/precompiled/{uwidgets.js → web/uwidgets.js} +0 -0
- /toolkit/workspace/precompiled/{uwidgets_pro.js → web/uwidgets_pro.js} +0 -0
micrOS/source/Files.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module is responsible high level micropython file system opeartions
|
|
3
|
+
[IMPORTANT] This module must never use any micrOS specific functions or classes.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from uos import ilistdir, remove, stat, getcwd, mkdir, rmdir
|
|
7
|
+
from sys import path as upath
|
|
8
|
+
|
|
9
|
+
################################ Helper functions #####################################
|
|
10
|
+
|
|
11
|
+
def _filter(path:str='/', ext:tuple=None, prefix:tuple=None, hide_core:bool=True) -> bool:
|
|
12
|
+
"""
|
|
13
|
+
Filter files in the micrOS filesystem.
|
|
14
|
+
|
|
15
|
+
:param path: file path to check
|
|
16
|
+
:param ext: tuple of extensions to filter by, default: None (all)
|
|
17
|
+
:param prefix: tuple of prefixes to match (e.g. ('LM', 'IO')), default: None
|
|
18
|
+
:param hide_core: if True, hides core .py/.mpy files in the root (current) directory
|
|
19
|
+
:return: bool, whether the file passes the filter
|
|
20
|
+
"""
|
|
21
|
+
parent = "/".join(path.split("/")[:-1]) or "/"
|
|
22
|
+
fname = path.split("/")[-1]
|
|
23
|
+
_ext = fname.split(".")[-1]
|
|
24
|
+
|
|
25
|
+
# --- Hide core logic ---
|
|
26
|
+
# Core = any .py/.mpy in the current (root) working directory
|
|
27
|
+
if hide_core and _ext in ("mpy", "py") and parent in ('/', ""):
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
# --- General matching rules ---
|
|
31
|
+
if ext is None and prefix is None:
|
|
32
|
+
return True
|
|
33
|
+
if isinstance(prefix, tuple) and fname.split("_")[0] in prefix:
|
|
34
|
+
return True
|
|
35
|
+
if isinstance(ext, tuple) and _ext in ext:
|
|
36
|
+
return True
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_protected(path:str='/') -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Check is file/dir protected
|
|
43
|
+
- protected root folders and files: /*
|
|
44
|
+
- protected system files by name
|
|
45
|
+
"""
|
|
46
|
+
protected_files = ("node_config.json", "LM_system.mpy", "LM_pacman.mpy", "LM_cluster.mpy")
|
|
47
|
+
# Detect parent directory
|
|
48
|
+
parent = "/".join(path.split("/")[:-1]) or "/"
|
|
49
|
+
# Get file/folder name
|
|
50
|
+
fname = path.split("/")[-1]
|
|
51
|
+
# Disallow: root folders and protected files
|
|
52
|
+
return parent in ("/", "") or fname in protected_files
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _type_mask_to_str(item_type:int=None) -> str:
|
|
56
|
+
# Map the raw bit-mask to a single character
|
|
57
|
+
if item_type & 0x4000: # Dir bit-mask
|
|
58
|
+
item_type = 'd'
|
|
59
|
+
elif item_type & 0x8000: # File bit-mask
|
|
60
|
+
item_type = 'f'
|
|
61
|
+
else:
|
|
62
|
+
item_type = 'o'
|
|
63
|
+
return item_type
|
|
64
|
+
|
|
65
|
+
########################### Public functions #############################
|
|
66
|
+
def is_dir(path):
|
|
67
|
+
try:
|
|
68
|
+
return stat(path)[0] & 0x4000
|
|
69
|
+
except OSError:
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def is_file(path):
|
|
74
|
+
try:
|
|
75
|
+
return stat(path)[0] & 0x8000
|
|
76
|
+
except OSError:
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def ilist_fs(path:str="/", type_filter:str='*', select:str='*', core:bool=False):
|
|
81
|
+
"""
|
|
82
|
+
Linux like ls command - list app resources and app folders
|
|
83
|
+
:param path: path to list, default: /
|
|
84
|
+
:param type_filter: content type, default all (*), f-file, d-dir can be selected
|
|
85
|
+
:param select: select specific application resource type by prefix: LM or IO
|
|
86
|
+
:param core: list core files resources as well, default: False
|
|
87
|
+
return iterator:
|
|
88
|
+
when content is all (*) output: [(item_type, item), ...]
|
|
89
|
+
OR
|
|
90
|
+
content type was selected (not *) output: [item, ...]
|
|
91
|
+
"""
|
|
92
|
+
path = path if path.endswith('/') else f"{path}/"
|
|
93
|
+
# Info: uos.ilistdir: (name, type, inode[, size])
|
|
94
|
+
for item, item_type, *_ in ilistdir(path):
|
|
95
|
+
item_type = _type_mask_to_str(item_type)
|
|
96
|
+
if type_filter in ("*", item_type):
|
|
97
|
+
# Mods only
|
|
98
|
+
_select = None if select == "*" else (select,)
|
|
99
|
+
if item_type == 'f' and not _filter(path_join(path, item), prefix=_select, hide_core=not core):
|
|
100
|
+
continue
|
|
101
|
+
if select != '*' and item_type == 'd':
|
|
102
|
+
continue
|
|
103
|
+
# Create result
|
|
104
|
+
if type_filter == "*":
|
|
105
|
+
yield item_type, item
|
|
106
|
+
else:
|
|
107
|
+
yield item
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def list_fs(path:str="/", type_filter:str='*', select:str='*', core:bool=False) -> list[str,] | list[tuple[str, str],]:
|
|
111
|
+
"""
|
|
112
|
+
Wrapper of ilist_fs
|
|
113
|
+
Return list
|
|
114
|
+
"""
|
|
115
|
+
return list(ilist_fs(path, type_filter, select, core))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def remove_file(path, force=False):
|
|
119
|
+
"""
|
|
120
|
+
Linux like rm command - delete app resources and folders
|
|
121
|
+
:param path: file to delete
|
|
122
|
+
:param force: pypass file protection check - sudo mode
|
|
123
|
+
"""
|
|
124
|
+
# protect some resources
|
|
125
|
+
if not force and is_protected(path):
|
|
126
|
+
return f'Protected resource, skip deletion: {path}'
|
|
127
|
+
if is_file(path):
|
|
128
|
+
remove(path)
|
|
129
|
+
return f"{path} deleted"
|
|
130
|
+
return f"Cannot delete (not a file): {path}"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def remove_dir(path, force=False):
|
|
134
|
+
"""
|
|
135
|
+
Recursively delete a folder and all its contents.
|
|
136
|
+
:param path: folder to delete
|
|
137
|
+
:param force: pypass dir protection check - sudo mode
|
|
138
|
+
"""
|
|
139
|
+
# protect some resources
|
|
140
|
+
if not force and is_protected(path):
|
|
141
|
+
return f'Protected resource, skip deletion: {path}'
|
|
142
|
+
for entry in ilistdir(path):
|
|
143
|
+
content_path = path_join(path, entry[0])
|
|
144
|
+
if is_dir(content_path): # directory flag
|
|
145
|
+
remove_dir(content_path)
|
|
146
|
+
else:
|
|
147
|
+
remove(content_path)
|
|
148
|
+
rmdir(path)
|
|
149
|
+
return f"{path} deleted"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def path_join(*parts):
|
|
153
|
+
path = "/".join(part.strip("/") for part in parts if part)
|
|
154
|
+
if parts and parts[0].startswith("/"):
|
|
155
|
+
path = path if path.startswith("/") else "/" + path
|
|
156
|
+
return path
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def abs_path(path):
|
|
160
|
+
parts = path.split("/")
|
|
161
|
+
stack = []
|
|
162
|
+
for p in parts:
|
|
163
|
+
if not p or p == ".":
|
|
164
|
+
continue
|
|
165
|
+
if p == "..":
|
|
166
|
+
if stack:
|
|
167
|
+
stack.pop() # prevent escaping root
|
|
168
|
+
else:
|
|
169
|
+
stack.append(p)
|
|
170
|
+
return "/" + "/".join(stack)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# micrOS system file structure
|
|
174
|
+
class OSPath:
|
|
175
|
+
_ROOT = getcwd()
|
|
176
|
+
LOGS = path_join(_ROOT, '/logs') # Logs (.log)
|
|
177
|
+
DATA = path_join(_ROOT,'/data') # Application data (.dat, .cache, etc.)
|
|
178
|
+
WEB = path_join(_ROOT,'/web') # Web resources (.html, .css, .js, .json, etc.)
|
|
179
|
+
MODULES = path_join(_ROOT, '/modules') # Application modules (.mpy, .py)
|
|
180
|
+
CONFIG = path_join(_ROOT, '/config') # System configuration files (node_config.json, etc.)
|
|
181
|
+
LIB = path_join(_ROOT, '/lib') # Official and Custom package installation target path
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def init_micros_dirs():
|
|
185
|
+
"""
|
|
186
|
+
Init micrOS root file system directories
|
|
187
|
+
"""
|
|
188
|
+
# ENABLE MODULES ACCESS
|
|
189
|
+
if OSPath.MODULES not in upath:
|
|
190
|
+
upath.insert(0, OSPath.MODULES)
|
|
191
|
+
# ENABLE LIB ACCESS
|
|
192
|
+
if OSPath.LIB not in upath:
|
|
193
|
+
upath.insert(0, OSPath.LIB)
|
|
194
|
+
|
|
195
|
+
root_dirs = [
|
|
196
|
+
getattr(OSPath, key)
|
|
197
|
+
for key in dir(OSPath)
|
|
198
|
+
if not key.startswith("_") and isinstance(getattr(OSPath, key), str)
|
|
199
|
+
]
|
|
200
|
+
print(f"[BOOT] rootFS validation: {root_dirs}")
|
|
201
|
+
for dir_path in root_dirs:
|
|
202
|
+
if not is_dir(dir_path):
|
|
203
|
+
try:
|
|
204
|
+
mkdir(dir_path)
|
|
205
|
+
print(f"[BOOT] init dir: {dir_path}")
|
|
206
|
+
except Exception as e:
|
|
207
|
+
print(f"[ERR][BOOT] cannot init dir {dir_path}: {e}")
|
micrOS/source/Hooks.py
CHANGED
|
@@ -19,8 +19,9 @@ Designed by Marcell Ban aka BxNxM
|
|
|
19
19
|
#################################################################
|
|
20
20
|
from Config import cfgget, cfgput
|
|
21
21
|
from microIO import detect_platform
|
|
22
|
-
from Debug import console_write,
|
|
22
|
+
from Debug import console_write, syslog
|
|
23
23
|
from Tasks import exec_lm_pipe
|
|
24
|
+
from Auth import resolve_secret
|
|
24
25
|
from micropython import mem_info
|
|
25
26
|
from machine import freq
|
|
26
27
|
try:
|
|
@@ -43,34 +44,23 @@ def bootup():
|
|
|
43
44
|
"""
|
|
44
45
|
# Execute LMs from boothook config parameter
|
|
45
46
|
console_write("[BOOT] EXECUTION...")
|
|
46
|
-
boot_cause()
|
|
47
47
|
bootasks = cfgget('boothook')
|
|
48
48
|
if bootasks is not None and bootasks.lower() != 'n/a':
|
|
49
49
|
console_write(f"|-[BOOT] TASKS: {bootasks}")
|
|
50
|
-
if exec_lm_pipe(bootasks):
|
|
50
|
+
if exec_lm_pipe(resolve_secret(bootasks)):
|
|
51
51
|
console_write("|-[BOOT] DONE")
|
|
52
52
|
else:
|
|
53
53
|
console_write("|-[BOOT] ERROR")
|
|
54
54
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
if cfgget('boostmd') is True:
|
|
58
|
-
console_write(f"[BOOT HOOKS] Set up CPU high Hz - boostmd: {cfgget('boostmd')}")
|
|
59
|
-
if platform == 'esp32c3':
|
|
60
|
-
freq(160_000_000) # 160 Mhz (max)
|
|
61
|
-
elif 'esp32' in platform:
|
|
62
|
-
freq(240_000_000) # 240 Mhz (max)
|
|
63
|
-
else:
|
|
64
|
-
console_write(f"[BOOT HOOKS] Set up CPU low Hz - boostmd: {cfgget('boostmd')}")
|
|
65
|
-
if platform == 'esp32c3':
|
|
66
|
-
freq(80_000_000) # 80 Mhz / Half the max CPU clock
|
|
67
|
-
elif 'esp32' in platform:
|
|
68
|
-
freq(160_000_000) # 160 Mhz / Half the max CPU clock
|
|
55
|
+
# Load and Save boot cause
|
|
56
|
+
boot_cause()
|
|
69
57
|
# Autotune queue size
|
|
70
|
-
|
|
58
|
+
_tune_queue_size()
|
|
59
|
+
# Configure CPU performance
|
|
60
|
+
_tune_performance()
|
|
71
61
|
|
|
72
62
|
|
|
73
|
-
def
|
|
63
|
+
def _tune_queue_size():
|
|
74
64
|
"""
|
|
75
65
|
Tune queue size based on available ram
|
|
76
66
|
between 5-50
|
|
@@ -84,6 +74,29 @@ def tune_queue_size():
|
|
|
84
74
|
cfgput('aioqueue', est_queue)
|
|
85
75
|
|
|
86
76
|
|
|
77
|
+
def _tune_performance():
|
|
78
|
+
# {(platforms, ...): (min_clock, max_clock), ...}
|
|
79
|
+
cpu_clocks = {
|
|
80
|
+
('esp32c3', 'esp32c6'): (80_000_000, 160_000_000),
|
|
81
|
+
('esp32',): (160_000_000, 240_000_000), # default
|
|
82
|
+
}
|
|
83
|
+
platform = detect_platform()
|
|
84
|
+
cpu_min_max = cpu_clocks[('esp32',)]
|
|
85
|
+
for platforms, clocks in cpu_clocks.items():
|
|
86
|
+
if platform in platforms:
|
|
87
|
+
cpu_min_max = clocks
|
|
88
|
+
break
|
|
89
|
+
# Set boosted (boost mode)
|
|
90
|
+
if cfgget('boostmd'):
|
|
91
|
+
max_hz = cpu_min_max[1]
|
|
92
|
+
console_write(f"[BOOT HOOKS] CPU boost mode ON: {max_hz} Hz")
|
|
93
|
+
freq(max_hz)
|
|
94
|
+
else:
|
|
95
|
+
min_hz = cpu_min_max[0]
|
|
96
|
+
console_write(f"[BOOT HOOKS] CPU boost mode OFF: {min_hz} Hz")
|
|
97
|
+
freq(min_hz)
|
|
98
|
+
|
|
99
|
+
|
|
87
100
|
def profiling_info(label=""):
|
|
88
101
|
"""
|
|
89
102
|
Runtime memory measurements
|
|
@@ -109,5 +122,20 @@ def boot_cause():
|
|
|
109
122
|
reason = 4, "DSWakeUp"
|
|
110
123
|
elif reset_reason == SOFT_RESET:
|
|
111
124
|
reason = 5, "SoftReset"
|
|
112
|
-
|
|
125
|
+
syslog(f"[BOOT] info: {reason[1]}")
|
|
113
126
|
return reason
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def enableESPNow() -> str:
|
|
130
|
+
"""
|
|
131
|
+
Enable ESP-NOW communication
|
|
132
|
+
"""
|
|
133
|
+
if cfgget('espnow'):
|
|
134
|
+
try:
|
|
135
|
+
from Espnow import ESPNowSS
|
|
136
|
+
verdict = ESPNowSS().start_server()
|
|
137
|
+
console_write(f"[TASK] Start ESPNow-InterCon server: {verdict}")
|
|
138
|
+
except Exception as e:
|
|
139
|
+
syslog(f"[ERR] Start ESPNow-InterCon server: {e}")
|
|
140
|
+
return str(e)
|
|
141
|
+
return "ESPNow disabled"
|
micrOS/source/InterConnect.py
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module is responsible for device-device communication
|
|
3
|
+
dedicated to micrOS framework.
|
|
4
|
+
Built-in-function:
|
|
5
|
+
- Socket InterConnect interface
|
|
6
|
+
- ESPNow redirection
|
|
7
|
+
|
|
8
|
+
Designed by
|
|
9
|
+
Marcell Ban aka BxNxM
|
|
10
|
+
Kristof Kasza aka KKristof452
|
|
11
|
+
"""
|
|
12
|
+
|
|
1
13
|
from socket import getaddrinfo, SOCK_STREAM
|
|
2
|
-
from re import compile
|
|
3
|
-
|
|
4
|
-
from
|
|
14
|
+
from re import compile as re_compile
|
|
15
|
+
from json import loads
|
|
16
|
+
from binascii import hexlify
|
|
17
|
+
from uasyncio import open_connection
|
|
18
|
+
|
|
19
|
+
from Debug import syslog
|
|
5
20
|
from Config import cfgget
|
|
6
21
|
from Server import Server
|
|
7
22
|
from Tasks import NativeTask
|
|
8
23
|
|
|
24
|
+
if cfgget('espnow'):
|
|
25
|
+
from Espnow import ESPNowSS
|
|
26
|
+
else:
|
|
27
|
+
ESPNowSS = None
|
|
28
|
+
|
|
9
29
|
|
|
10
30
|
class InterCon:
|
|
11
|
-
CONN_MAP = {}
|
|
31
|
+
CONN_MAP: dict[str, str] = {} # hostname: IP address pairs
|
|
32
|
+
NO_ESPNOW: list[str] = [] # disabled ESPNow hostname list (cache for fallback speed-up)
|
|
12
33
|
PORT = cfgget('socport')
|
|
13
34
|
|
|
14
35
|
def __init__(self):
|
|
@@ -19,10 +40,10 @@ class InterCon:
|
|
|
19
40
|
|
|
20
41
|
@staticmethod
|
|
21
42
|
def validate_ipv4(str_in):
|
|
22
|
-
pattern =
|
|
43
|
+
pattern = re_compile(r'^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$')
|
|
23
44
|
return bool(pattern.match(str_in))
|
|
24
45
|
|
|
25
|
-
async def send_cmd(self, host, cmd):
|
|
46
|
+
async def send_cmd(self, host:str, cmd:list):
|
|
26
47
|
"""
|
|
27
48
|
Async Main method to implement device-device communication with
|
|
28
49
|
- dhcp host resolve and IP caching
|
|
@@ -34,14 +55,19 @@ class InterCon:
|
|
|
34
55
|
# Check if host is a hostname (example.local) and resolve its IP address
|
|
35
56
|
if not InterCon.validate_ipv4(host):
|
|
36
57
|
hostname = host
|
|
37
|
-
#
|
|
58
|
+
# Lookup hostname without .domain (sub-hostname matching)
|
|
59
|
+
if '.' not in hostname:
|
|
60
|
+
_hosts = list([d for d in InterCon.CONN_MAP if hostname in d])
|
|
61
|
+
if len(_hosts) > 0:
|
|
62
|
+
hostname = _hosts[0]
|
|
63
|
+
# Retrieve IP address by hostname.domain dynamically
|
|
38
64
|
if InterCon.CONN_MAP.get(hostname, None) is None:
|
|
39
65
|
try:
|
|
40
66
|
addr_info = getaddrinfo(host, InterCon.PORT, 0, SOCK_STREAM)
|
|
41
67
|
host = addr_info[-1][4][0]
|
|
42
68
|
except OSError as e:
|
|
43
69
|
Server.reply_all(f"[intercon] NoHost: {e}")
|
|
44
|
-
|
|
70
|
+
syslog(f"[intercon] send_cmd {host} oserr: {e}")
|
|
45
71
|
return ''
|
|
46
72
|
else:
|
|
47
73
|
# Restore IP from cache by hostname
|
|
@@ -50,12 +76,12 @@ class InterCon:
|
|
|
50
76
|
if InterCon.validate_ipv4(host):
|
|
51
77
|
try:
|
|
52
78
|
# Create socket object
|
|
53
|
-
self.reader, self.writer = await
|
|
79
|
+
self.reader, self.writer = await open_connection(host, InterCon.PORT)
|
|
54
80
|
# Send command over TCP/IP
|
|
55
81
|
output = await self.__run_command(cmd, hostname)
|
|
56
82
|
except OSError as e:
|
|
57
83
|
Server.reply_all(f"[intercon] NoHost: {e}")
|
|
58
|
-
|
|
84
|
+
syslog(f"[intercon] send_cmd {host} oserr: {e}")
|
|
59
85
|
output = None
|
|
60
86
|
finally:
|
|
61
87
|
if self.writer:
|
|
@@ -68,26 +94,23 @@ class InterCon:
|
|
|
68
94
|
InterCon.CONN_MAP[hostname] = None if output is None else host
|
|
69
95
|
# None: ServerBusy(or \0) or Prompt mismatch (auto delete cached IP), STR: valid comm. output
|
|
70
96
|
return output
|
|
71
|
-
|
|
72
|
-
errlog_add(f"[ERR][intercon] Invalid host: {host}")
|
|
97
|
+
syslog(f"[ERR][intercon] Invalid host: {host}")
|
|
73
98
|
return ''
|
|
74
99
|
|
|
75
|
-
async def __run_command(self, cmd, hostname):
|
|
100
|
+
async def __run_command(self, cmd:list, hostname:str):
|
|
76
101
|
"""
|
|
77
102
|
Implements receive data on open connection, command query and result collection
|
|
78
103
|
:param cmd: command string to server socket shell
|
|
79
104
|
:param hostname: hostname for prompt checking
|
|
80
105
|
Return None here will trigger retry mechanism... + deletes cached IP
|
|
81
106
|
"""
|
|
82
|
-
cmd = str.encode(cmd)
|
|
83
107
|
data, prompt = await self.__receive_data()
|
|
84
108
|
if "Connection is busy. Bye!" in prompt:
|
|
85
109
|
return None
|
|
86
110
|
# Compare prompt |node01 $| with hostname 'node01.local'
|
|
87
111
|
if hostname is None or prompt is None or str(prompt).replace('$', '').strip() == str(hostname).split('.')[0]:
|
|
88
112
|
# Run command on validated device
|
|
89
|
-
|
|
90
|
-
self.writer.write(cmd)
|
|
113
|
+
self.writer.write(str.encode(' '.join(cmd)))
|
|
91
114
|
await self.writer.drain()
|
|
92
115
|
data, _ = await self.__receive_data(prompt=prompt)
|
|
93
116
|
if data == '\0':
|
|
@@ -104,13 +127,13 @@ class InterCon:
|
|
|
104
127
|
await self.writer.drain()
|
|
105
128
|
data, prompt = await self.__receive_data(prompt=prompt)
|
|
106
129
|
except Exception as e:
|
|
107
|
-
|
|
130
|
+
syslog(f'[intercon][ERR] Auth: {e}')
|
|
108
131
|
data = 'AuthFailed'
|
|
109
132
|
if 'AuthOk' in data:
|
|
110
133
|
return True # AuthOk
|
|
111
134
|
return False # AuthFailed
|
|
112
135
|
|
|
113
|
-
async def __receive_data(self, prompt=None):
|
|
136
|
+
async def __receive_data(self, prompt=None) -> tuple[str, str]:
|
|
114
137
|
"""
|
|
115
138
|
Implements data receive loop until prompt / [configure] / Bye!
|
|
116
139
|
:param prompt: socket shell prompt
|
|
@@ -140,8 +163,34 @@ class InterCon:
|
|
|
140
163
|
data = data.replace(prompt, '').replace('\n', ' ')
|
|
141
164
|
return data, prompt
|
|
142
165
|
|
|
166
|
+
async def auto_espnow_handshake(self, host:str) -> dict:
|
|
167
|
+
"""
|
|
168
|
+
[1] Check espnow.server running on host
|
|
169
|
+
[2] Get MAC address for host (from system info)
|
|
170
|
+
[3] Execute ESPNowSS.handshake
|
|
171
|
+
"""
|
|
172
|
+
response = await self.send_cmd(host, ["task", "list", ">json"])
|
|
173
|
+
if not response:
|
|
174
|
+
return {None: f"[ERR] ESPNow auto handshake: task list >>{host}: {response}"}
|
|
175
|
+
|
|
176
|
+
active_tasks = loads(response).get("active")
|
|
177
|
+
if active_tasks and "espnow.server" in active_tasks:
|
|
178
|
+
response = await self.send_cmd(host, ["system", "info", ">json"])
|
|
179
|
+
if not response:
|
|
180
|
+
return {None: "[ERR] ESPNow auto handshake: system info"}
|
|
181
|
+
try:
|
|
182
|
+
host_mac = loads(response).get("mac")
|
|
183
|
+
except Exception as ex:
|
|
184
|
+
return {None: f"[ERR] ESPNow auto handshake: {ex}"}
|
|
185
|
+
|
|
186
|
+
return ESPNowSS().handshake(host_mac)
|
|
187
|
+
|
|
188
|
+
if not InterCon.validate_ipv4(host):
|
|
189
|
+
InterCon.NO_ESPNOW.append(str(host).split(".")[0]) # host.local -> host
|
|
190
|
+
return {None: f"ESPNow auto handshake: espnow disabled on host {host}"}
|
|
191
|
+
|
|
143
192
|
|
|
144
|
-
async def
|
|
193
|
+
async def _socket_send_cmd(host:str, cmd:list, com_obj:InterCon) -> None:
|
|
145
194
|
"""
|
|
146
195
|
Async send command wrapper for further async task integration and sync send_cmd usage (main)
|
|
147
196
|
:param host: hostname / IP address
|
|
@@ -149,42 +198,77 @@ async def _send_cmd(host, cmd, com_obj):
|
|
|
149
198
|
:param com_obj: InterCon object to utilize send_cmd method and task status updates
|
|
150
199
|
"""
|
|
151
200
|
# Send command
|
|
201
|
+
for _ in range(0, 2): # Retry mechanism
|
|
202
|
+
out = await com_obj.send_cmd(host, cmd) # Send CMD
|
|
203
|
+
if out is not None: # Retry mechanism
|
|
204
|
+
break
|
|
205
|
+
await com_obj.task.feed(sleep_ms=100) # Retry mechanism
|
|
206
|
+
com_obj.task.out = '' if out is None else out
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
async def _send_cmd(host:str, cmd:list|str, com_obj:InterCon):
|
|
210
|
+
"""
|
|
211
|
+
Top level InterConnect callback function
|
|
212
|
+
[1] node01.domain -> ESPNow, Socket
|
|
213
|
+
(domain: .local, .net, etc.)
|
|
214
|
+
[2] node01 -> ESPNow, Socket fallback in case found in InterConnect cache
|
|
215
|
+
[3] IP address -> Socket
|
|
216
|
+
"""
|
|
152
217
|
with com_obj.task:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
218
|
+
if ESPNowSS:
|
|
219
|
+
# [1] ESPNow Active
|
|
220
|
+
name = str(host).split(".")[0] # host.local -> host
|
|
221
|
+
if name not in InterCon.NO_ESPNOW and name in list(ESPNowSS().devices.values()):
|
|
222
|
+
com_obj.task.out = "Redirected to ESPNow"
|
|
223
|
+
if isinstance(cmd, list):
|
|
224
|
+
cmd = ' '.join(cmd)
|
|
225
|
+
# Send command and retrieve result
|
|
226
|
+
sender = ESPNowSS().send(peer=name, msg=cmd)
|
|
227
|
+
sender_task = NativeTask.TASKS.get(list(sender.keys())[0])
|
|
228
|
+
result = await sender_task.await_result(timeout=10)
|
|
229
|
+
if result != "Timeout has beed exceeded":
|
|
230
|
+
# Successful command execution
|
|
231
|
+
com_obj.task.out = result # Output mirroring: Child -> Parent
|
|
232
|
+
sender_task.out = "Redirected to ParentTask" # Remove redundant data in embedded mode
|
|
233
|
+
return
|
|
160
234
|
|
|
235
|
+
# Handle legacy string input
|
|
236
|
+
if isinstance(cmd, str):
|
|
237
|
+
cmd = cmd.split()
|
|
238
|
+
# [1][2] Socket send (default and fallback)
|
|
239
|
+
await _socket_send_cmd(host, cmd, com_obj)
|
|
161
240
|
|
|
162
|
-
|
|
241
|
+
if ESPNowSS and name not in InterCon.NO_ESPNOW:
|
|
242
|
+
# [3] Automatic ESPNow handshake
|
|
243
|
+
verdict = await com_obj.auto_espnow_handshake(host)
|
|
244
|
+
if list(verdict.keys())[0] is None:
|
|
245
|
+
syslog(str(list(verdict.values())[0]))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def send_cmd(host:str, cmd:list|str) -> dict:
|
|
163
249
|
"""
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
:param cmd: command string to server socket shell
|
|
250
|
+
Top level InterConnect send task creation
|
|
251
|
+
Handles ESPNow and socket communication
|
|
167
252
|
"""
|
|
168
253
|
def _tagify():
|
|
169
254
|
nonlocal host, cmd
|
|
170
|
-
_mod = cmd
|
|
255
|
+
_mod = cmd[0]
|
|
171
256
|
if InterCon.validate_ipv4(host):
|
|
172
257
|
return f"{'.'.join(host.split('.')[-2:])}.{_mod}"
|
|
173
|
-
|
|
258
|
+
target = ".".join(host.split(".")[0:-1]) if "." in host else host
|
|
259
|
+
return f"{target}.{_mod}"
|
|
174
260
|
|
|
175
261
|
com_obj = InterCon()
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if started:
|
|
179
|
-
result = {"verdict": f"Task started: task show {tag}", "tag": tag}
|
|
180
|
-
else:
|
|
181
|
-
result = {"verdict": "Task is Busy", "tag": tag}
|
|
182
|
-
return result
|
|
262
|
+
task_id = f"con.{_tagify()}" # CHECK TASK ID CONFLICT
|
|
263
|
+
return com_obj.task.create(callback=_send_cmd(host, cmd, com_obj), tag=task_id)
|
|
183
264
|
|
|
184
265
|
|
|
185
|
-
def host_cache():
|
|
266
|
+
def host_cache() -> dict:
|
|
186
267
|
"""
|
|
187
268
|
Dump InterCon connection cache
|
|
188
269
|
"""
|
|
189
|
-
|
|
190
|
-
|
|
270
|
+
if ESPNowSS is None:
|
|
271
|
+
return InterCon.CONN_MAP
|
|
272
|
+
all_devs = dict({name:hexlify(mac, ':').decode() for mac, name in ESPNowSS().devices.items()})
|
|
273
|
+
all_devs.update(InterCon.CONN_MAP)
|
|
274
|
+
return all_devs
|
micrOS/source/Interrupts.py
CHANGED
|
@@ -19,7 +19,7 @@ Reference: https://docs.micropython.org/en/latest/library/machine.Pin.html
|
|
|
19
19
|
from machine import Pin
|
|
20
20
|
from utime import ticks_ms, ticks_diff
|
|
21
21
|
from Config import cfgget
|
|
22
|
-
from Debug import console_write,
|
|
22
|
+
from Debug import console_write, syslog
|
|
23
23
|
from Tasks import exec_lm_pipe_schedule
|
|
24
24
|
from microIO import resolve_pin
|
|
25
25
|
if cfgget('cron'):
|
|
@@ -71,8 +71,9 @@ def enableInterrupt():
|
|
|
71
71
|
|
|
72
72
|
def enableCron():
|
|
73
73
|
"""
|
|
74
|
-
Set time stump based scheduler aka cron
|
|
74
|
+
Set time stump based scheduler aka cron on Timer1
|
|
75
75
|
Input: cron(bool), crontasks(str)
|
|
76
|
+
This is for low frequency sampling, like 12 or 6 / minute (due to low power mode compatibility)
|
|
76
77
|
"""
|
|
77
78
|
timer_period = 5000 # Timer period ms: 12 check/min
|
|
78
79
|
console_write(f"[IRQ] CRON IRQ SETUP: {cfgget('cron')} SEQ: {timer_period}")
|
|
@@ -80,11 +81,10 @@ def enableCron():
|
|
|
80
81
|
if cfgget("cron") and cfgget('crontasks').lower() != 'n/a':
|
|
81
82
|
from machine import Timer
|
|
82
83
|
# INIT TIMER 1 IRQ with callback function wrapper
|
|
83
|
-
|
|
84
|
-
sample = int(timer_period/1000)
|
|
84
|
+
sampling = int(timer_period/1000)
|
|
85
85
|
timer = Timer(1)
|
|
86
86
|
timer.init(period=timer_period, mode=Timer.PERIODIC,
|
|
87
|
-
callback=lambda timer: scheduler(
|
|
87
|
+
callback=lambda timer: scheduler(sampling))
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
#################################################################
|
|
@@ -157,7 +157,7 @@ def initEventIRQs():
|
|
|
157
157
|
try:
|
|
158
158
|
return resolve_pin(_p)
|
|
159
159
|
except Exception as e:
|
|
160
|
-
|
|
160
|
+
syslog(f'[ERR][!] EVENT {_p} IO error: {e}')
|
|
161
161
|
return None
|
|
162
162
|
|
|
163
163
|
# Load External IRQ (1-4) execution data set from node config
|