micrOSDevToolKit 2.10.5__py3-none-any.whl → 2.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of micrOSDevToolKit might be problematic. Click here for more details.
- 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/esp32c6-GENERIC-20250415-v1.25.0.bin +0 -0
- micrOS/micropython/esp32s3-4MBflash-20241129-v1.24.1.bin +0 -0
- micrOS/release_info/micrOS_ReleaseInfo/system_analysis_sum.json +47 -51
- micrOS/source/Common.py +262 -87
- micrOS/source/Debug.py +44 -88
- micrOS/source/Espnow.py +1 -1
- micrOS/source/Files.py +21 -2
- micrOS/source/Hooks.py +60 -17
- micrOS/source/IO_esp32c6.py +16 -0
- micrOS/source/IO_esp32s3.py +37 -1
- micrOS/source/IO_m5stamp.py +35 -1
- micrOS/source/IO_qtpy.py +22 -17
- micrOS/source/IO_s3matrix.py +21 -0
- micrOS/source/IO_tinypico.py +38 -0
- micrOS/source/LM_VL53L0X.py +1 -1
- micrOS/source/LM_buzzer.py +6 -7
- micrOS/source/LM_cct.py +6 -5
- micrOS/source/LM_dimmer.py +6 -5
- micrOS/source/LM_espnow.py +15 -10
- micrOS/source/LM_i2c.py +3 -2
- micrOS/source/LM_neoeffects.py +173 -230
- micrOS/source/LM_neomatrix.py +305 -0
- micrOS/source/LM_neopixel.py +10 -10
- micrOS/source/LM_oled_ui.py +18 -5
- micrOS/source/LM_pacman.py +25 -21
- micrOS/source/LM_qmi8658.py +204 -0
- micrOS/source/LM_rest.py +3 -3
- micrOS/source/LM_rgb.py +6 -6
- micrOS/source/LM_roboarm.py +5 -4
- micrOS/source/LM_switch.py +6 -4
- micrOS/source/LM_tcs3472.py +75 -0
- micrOS/source/LM_telegram.py +5 -4
- micrOS/source/Logger.py +46 -32
- micrOS/source/Shell.py +11 -10
- micrOS/source/Tasks.py +7 -4
- micrOS/source/Time.py +5 -3
- micrOS/source/__pycache__/Common.cpython-312.pyc +0 -0
- micrOS/source/__pycache__/Logger.cpython-312.pyc +0 -0
- micrOS/source/micrOS.py +5 -2
- micrOS/source/microIO.py +8 -6
- {microsdevtoolkit-2.10.5.dist-info → microsdevtoolkit-2.11.0.dist-info}/METADATA +2 -1
- {microsdevtoolkit-2.10.5.dist-info → microsdevtoolkit-2.11.0.dist-info}/RECORD +101 -96
- toolkit/DevEnvUSB.py +5 -0
- toolkit/Gateway.py +3 -3
- toolkit/LM_to_compile.dat +1 -0
- toolkit/dashboard_apps/NeoEffectsDemo.py +8 -15
- toolkit/dashboard_apps/QMI8685_GYRO.py +68 -0
- toolkit/dashboard_apps/_app_base.py +2 -2
- toolkit/dashboard_apps/_gyro_visualizer.py +78 -0
- toolkit/simulator_lib/__pycache__/IO_darwin.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/machine.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/neopixel.cpython-312.pyc +0 -0
- toolkit/simulator_lib/__pycache__/sim_console.cpython-312.pyc +0 -0
- toolkit/simulator_lib/machine.py +0 -1
- toolkit/simulator_lib/neopixel.py +3 -2
- toolkit/socketClient.py +3 -2
- 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/Common.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/IO_esp32c6.mpy +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_s3matrix.mpy +0 -0
- toolkit/workspace/precompiled/IO_tinypico.mpy +0 -0
- toolkit/workspace/precompiled/LM_VL53L0X.py +1 -1
- toolkit/workspace/precompiled/LM_buzzer.mpy +0 -0
- toolkit/workspace/precompiled/LM_cct.mpy +0 -0
- toolkit/workspace/precompiled/LM_dimmer.mpy +0 -0
- toolkit/workspace/precompiled/LM_espnow.py +15 -10
- toolkit/workspace/precompiled/LM_i2c.py +3 -2
- toolkit/workspace/precompiled/LM_neoeffects.mpy +0 -0
- toolkit/workspace/precompiled/LM_neomatrix.mpy +0 -0
- toolkit/workspace/precompiled/LM_neopixel.mpy +0 -0
- toolkit/workspace/precompiled/LM_oled_ui.mpy +0 -0
- toolkit/workspace/precompiled/LM_pacman.mpy +0 -0
- toolkit/workspace/precompiled/LM_qmi8658.py +204 -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_switch.mpy +0 -0
- toolkit/workspace/precompiled/LM_tcs3472.py +75 -0
- toolkit/workspace/precompiled/LM_telegram.mpy +0 -0
- toolkit/workspace/precompiled/Logger.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/micrOS.mpy +0 -0
- toolkit/workspace/precompiled/microIO.mpy +0 -0
- micrOS/micropython/esp32s3-20240105-v1.22.1.bin +0 -0
- micrOS/source/LM_catgame.py +0 -75
- micrOS/source/LM_demo.py +0 -97
- micrOS/source/LM_intercon.py +0 -60
- micrOS/source/LM_ph_sensor.py +0 -51
- toolkit/workspace/precompiled/LM_catgame.py +0 -75
- toolkit/workspace/precompiled/LM_demo.py +0 -97
- toolkit/workspace/precompiled/LM_intercon.mpy +0 -0
- toolkit/workspace/precompiled/LM_ph_sensor.py +0 -51
- /micrOS/micropython/{esp32s3-20241129-v1.24.1.bin → esp32s3-8MBflash-20241129-v1.24.1.bin} +0 -0
- {microsdevtoolkit-2.10.5.data → microsdevtoolkit-2.11.0.data}/scripts/devToolKit.py +0 -0
- {microsdevtoolkit-2.10.5.dist-info → microsdevtoolkit-2.11.0.dist-info}/WHEEL +0 -0
- {microsdevtoolkit-2.10.5.dist-info → microsdevtoolkit-2.11.0.dist-info}/licenses/LICENSE +0 -0
- {microsdevtoolkit-2.10.5.dist-info → microsdevtoolkit-2.11.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A simple driver for the QMI8658 IMU.
|
|
3
|
+
https://github.com/echo-lalia/qmi8658-micropython/blob/main/qmi8685.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import struct
|
|
7
|
+
import time
|
|
8
|
+
from machine import Pin, I2C
|
|
9
|
+
from micropython import const
|
|
10
|
+
from microIO import bind_pin, pinmap_search
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Sensor constants
|
|
14
|
+
_QMI8685_PARTID = const(0x05)
|
|
15
|
+
_REG_PARTID = const(0x00)
|
|
16
|
+
_REG_REVISION = const(0x01)
|
|
17
|
+
|
|
18
|
+
_REG_CTRL1 = const(0x02) # Serial interface and sensor enable
|
|
19
|
+
_REG_CTRL2 = const(0x03) # Accelerometer settings
|
|
20
|
+
_REG_CTRL3 = const(0x04) # Gyroscope settings
|
|
21
|
+
_REG_CTRL4 = const(0x05) # Magnetomer settings (support not implemented in this driver yet)
|
|
22
|
+
_REG_CTRL5 = const(0x06) # Sensor data processing settings
|
|
23
|
+
_REG_CTRL6 = const(0x07) # Attitude Engine ODR and Motion on Demand
|
|
24
|
+
_REG_CTRL7 = const(0x08) # Enable Sensors and Configure Data Reads
|
|
25
|
+
|
|
26
|
+
_REG_TEMP = const(0x33) # Temperature sensor.
|
|
27
|
+
|
|
28
|
+
_REG_AX_L = const(0x35) # Read accelerometer
|
|
29
|
+
_REG_AX_H = const(0x36)
|
|
30
|
+
_REG_AY_L = const(0x37)
|
|
31
|
+
_REG_AY_H = const(0x38)
|
|
32
|
+
_REG_AZ_L = const(0x39)
|
|
33
|
+
_REG_AZ_H = const(0x3A)
|
|
34
|
+
|
|
35
|
+
_REG_GX_L = const(0x3B) # read gyro
|
|
36
|
+
_REG_GX_H = const(0x3C)
|
|
37
|
+
_REG_GY_L = const(0x3D)
|
|
38
|
+
_REG_GY_H = const(0x3E)
|
|
39
|
+
_REG_GZ_L = const(0x3F)
|
|
40
|
+
_REG_GZ_H = const(0x40)
|
|
41
|
+
|
|
42
|
+
_QMI8658_I2CADDR_DEFAULT = const(0X6B)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_ACCELSCALE_RANGE_2G = const(0b00)
|
|
46
|
+
_ACCELSCALE_RANGE_4G = const(0b01)
|
|
47
|
+
_ACCELSCALE_RANGE_8G = const(0b10)
|
|
48
|
+
_ACCELSCALE_RANGE_16G = const(0b11)
|
|
49
|
+
|
|
50
|
+
_GYROSCALE_RANGE_16DPS = const(0b000)
|
|
51
|
+
_GYROSCALE_RANGE_32DPS = const(0b001)
|
|
52
|
+
_GYROSCALE_RANGE_64DPS = const(0b010)
|
|
53
|
+
_GYROSCALE_RANGE_128DPS = const(0b011)
|
|
54
|
+
_GYROSCALE_RANGE_256DPS = const(0b100)
|
|
55
|
+
_GYROSCALE_RANGE_512DPS = const(0b101)
|
|
56
|
+
_GYROSCALE_RANGE_1024DPS = const(0b110)
|
|
57
|
+
_GYROSCALE_RANGE_2048DPS = const(0b111)
|
|
58
|
+
|
|
59
|
+
_ODR_8000HZ = const(0b0000)
|
|
60
|
+
_ODR_4000HZ = const(0b0001)
|
|
61
|
+
_ODR_2000HZ = const(0b0010)
|
|
62
|
+
_ODR_1000HZ = const(0b0011)
|
|
63
|
+
_ODR_500HZ = const(0b0100)
|
|
64
|
+
_ODR_250HZ = const(0b0101)
|
|
65
|
+
_ODR_125HZ = const(0b0110)
|
|
66
|
+
_ODR_62_5HZ = const(0b0111)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class QMI8658:
|
|
70
|
+
"""QMI8658 inertial measurement unit."""
|
|
71
|
+
INSTANCE = None
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
i2c_bus: I2C,
|
|
76
|
+
address: int = _QMI8658_I2CADDR_DEFAULT,
|
|
77
|
+
accel_scale: int = _ACCELSCALE_RANGE_8G,
|
|
78
|
+
gyro_scale: int = _GYROSCALE_RANGE_256DPS):
|
|
79
|
+
"""Read from a sensor on the given I2C bus, at the given address."""
|
|
80
|
+
self.i2c = i2c_bus
|
|
81
|
+
self.address = address
|
|
82
|
+
# Cache the sensor instance globally for easy access
|
|
83
|
+
QMI8658.INSTANCE = self
|
|
84
|
+
|
|
85
|
+
# Verify sensor part ID
|
|
86
|
+
if self._read_u8(_REG_PARTID) != _QMI8685_PARTID:
|
|
87
|
+
raise AttributeError("Cannot find a QMI8658")
|
|
88
|
+
|
|
89
|
+
# Setup initial configuration
|
|
90
|
+
self._configure_sensor(accel_scale, gyro_scale)
|
|
91
|
+
|
|
92
|
+
# Configure scales/divisors for the driver
|
|
93
|
+
self.acc_scale_divisor = {
|
|
94
|
+
_ACCELSCALE_RANGE_2G: 1 << 14,
|
|
95
|
+
_ACCELSCALE_RANGE_4G: 1 << 13,
|
|
96
|
+
_ACCELSCALE_RANGE_8G: 1 << 12,
|
|
97
|
+
_ACCELSCALE_RANGE_16G: 1 << 11,
|
|
98
|
+
}[accel_scale]
|
|
99
|
+
|
|
100
|
+
self.gyro_scale_divisor = {
|
|
101
|
+
_GYROSCALE_RANGE_16DPS: 2048,
|
|
102
|
+
_GYROSCALE_RANGE_32DPS: 1024,
|
|
103
|
+
_GYROSCALE_RANGE_64DPS: 512,
|
|
104
|
+
_GYROSCALE_RANGE_128DPS: 256,
|
|
105
|
+
_GYROSCALE_RANGE_256DPS: 128,
|
|
106
|
+
_GYROSCALE_RANGE_512DPS: 64,
|
|
107
|
+
_GYROSCALE_RANGE_1024DPS: 32,
|
|
108
|
+
_GYROSCALE_RANGE_2048DPS: 16,
|
|
109
|
+
}[gyro_scale]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _configure_sensor(self, accel_scale: int, gyro_scale: int):
|
|
113
|
+
# Initialize accelerometer and gyroscope settings
|
|
114
|
+
self._write_u8(_REG_CTRL1, 0x60) # Set SPI auto increment and big endian (Ctrl 1)
|
|
115
|
+
self._write_u8(_REG_CTRL2, (accel_scale << 4) | _ODR_1000HZ) # Accel Config
|
|
116
|
+
self._write_u8(_REG_CTRL3, (gyro_scale << 4) | _ODR_1000HZ) # Gyro Config
|
|
117
|
+
self._write_u8(_REG_CTRL5, 0x01) # Low-pass filter enable
|
|
118
|
+
self._write_u8(_REG_CTRL7, 0x03) # Enable accel and gyro
|
|
119
|
+
time.sleep_ms(100)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Helper functions for register operations
|
|
123
|
+
def _read_u8(self, reg:int) -> int:
|
|
124
|
+
return self.i2c.readfrom_mem(self.address, reg, 1)[0]
|
|
125
|
+
|
|
126
|
+
def _read_xyz(self, reg:int) -> tuple[int, int, int]:
|
|
127
|
+
data = self.i2c.readfrom_mem(self.address, reg, 6)
|
|
128
|
+
return struct.unpack('<hhh', data)
|
|
129
|
+
|
|
130
|
+
def _write_u8(self, reg: int, value: int):
|
|
131
|
+
self.i2c.writeto_mem(self.address, reg, bytes([value]))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def temperature(self) -> float:
|
|
136
|
+
"""Get the device temperature."""
|
|
137
|
+
temp_raw = self._read_u8(_REG_TEMP)
|
|
138
|
+
return temp_raw / 256
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def acceleration(self) -> tuple[float, float, float]:
|
|
142
|
+
"""Get current acceleration reading."""
|
|
143
|
+
raw_accel = self._read_xyz(_REG_AX_L)
|
|
144
|
+
return tuple(val / self.acc_scale_divisor for val in raw_accel)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def gyro(self) -> tuple[float, float, float]:
|
|
148
|
+
"""Get current gyroscope reading."""
|
|
149
|
+
raw_gyro = self._read_xyz(_REG_GX_L)
|
|
150
|
+
return tuple(val / self.gyro_scale_divisor for val in raw_gyro)
|
|
151
|
+
|
|
152
|
+
#######################
|
|
153
|
+
# Public functions #
|
|
154
|
+
#######################
|
|
155
|
+
|
|
156
|
+
def load():
|
|
157
|
+
"""
|
|
158
|
+
Load the QMI8658 sensor instance.
|
|
159
|
+
The QMI8658 is a motion sensor that measures acceleration, angular velocity (gyroscope), and temperature
|
|
160
|
+
"""
|
|
161
|
+
if QMI8658.INSTANCE is None:
|
|
162
|
+
QMI8658(I2C(0, sda=Pin(bind_pin('i2c_sda')), scl=Pin(bind_pin('i2c_scl'))))
|
|
163
|
+
return QMI8658.INSTANCE
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def temperature():
|
|
167
|
+
return load().temperature
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def acceleration():
|
|
171
|
+
return load().acceleration
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def gyro():
|
|
175
|
+
return load().gyro
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def measure():
|
|
179
|
+
inst = load()
|
|
180
|
+
return {"temp": inst.temperature, "accel": inst.acceleration, "gyro": inst.gyro}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
#######################
|
|
184
|
+
# LM helper functions #
|
|
185
|
+
#######################
|
|
186
|
+
|
|
187
|
+
def pinmap():
|
|
188
|
+
"""
|
|
189
|
+
[i] micrOS LM naming convention
|
|
190
|
+
Shows logical pins - pin number(s) used by this Load module
|
|
191
|
+
- info which pins to use for this application
|
|
192
|
+
:return dict: pin name (str) - pin value (int) pairs
|
|
193
|
+
"""
|
|
194
|
+
return pinmap_search(['i2c_scl', 'i2c_sda'])
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def help(widgets=False):
|
|
198
|
+
"""
|
|
199
|
+
[i] micrOS LM naming convention - built-in help message
|
|
200
|
+
:return tuple:
|
|
201
|
+
(widgets=False) list of functions implemented by this application
|
|
202
|
+
(widgets=True) list of widget json for UI generation
|
|
203
|
+
"""
|
|
204
|
+
return 'load', 'temperature', 'acceleration', 'gyro', 'measure', 'pinmap'
|
micrOS/source/LM_rest.py
CHANGED
|
@@ -10,7 +10,7 @@ class Rest:
|
|
|
10
10
|
def load(gateway_url=None):
|
|
11
11
|
"""
|
|
12
12
|
Set gateway url aka main domain
|
|
13
|
-
:param gateway_url: base url of gateway, like: http://gateway.local:
|
|
13
|
+
:param gateway_url: base url of gateway, like: http://gateway.local:5005
|
|
14
14
|
"""
|
|
15
15
|
if gateway_url is None:
|
|
16
16
|
if Rest.GATEWAY_HOST is None:
|
|
@@ -77,6 +77,6 @@ def help(widgets=False):
|
|
|
77
77
|
(widgets=False) list of functions implemented by this application
|
|
78
78
|
(widgets=True) list of widget json for UI generation
|
|
79
79
|
"""
|
|
80
|
-
return ('load gateway_url=<http://gateway.local:
|
|
80
|
+
return ('load gateway_url=<http://gateway.local:5005>',
|
|
81
81
|
'url subdomain=</webhooks/template>',
|
|
82
|
-
'aurl subdomain=</webhooks/template>')
|
|
82
|
+
'aurl subdomain=</webhooks/template>')
|
micrOS/source/LM_rgb.py
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
# ANALOG rgb CONTROLLER PARAMS #
|
|
3
3
|
#########################################
|
|
4
4
|
from machine import Pin, PWM
|
|
5
|
-
from
|
|
6
|
-
from Common import transition_gen, micro_task
|
|
5
|
+
from Common import transition_gen, micro_task, data_dir
|
|
7
6
|
from utime import sleep_ms
|
|
8
7
|
from microIO import bind_pin, pinmap_search
|
|
9
8
|
from random import randint
|
|
@@ -20,6 +19,7 @@ class Data:
|
|
|
20
19
|
CH_MAX = 1000 # maximum value per channel
|
|
21
20
|
TASK_STATE = False
|
|
22
21
|
RGB_TASK_TAG = "rgb._tran"
|
|
22
|
+
FILE_CACHE = data_dir('rgb.cache')
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
#########################################
|
|
@@ -39,7 +39,7 @@ def __RGB_init(red_pin=None, green_pin=None, blue_pin=None):
|
|
|
39
39
|
|
|
40
40
|
def __persistent_cache_manager(mode):
|
|
41
41
|
"""
|
|
42
|
-
|
|
42
|
+
File state cache
|
|
43
43
|
modes:
|
|
44
44
|
r - recover, s - save
|
|
45
45
|
"""
|
|
@@ -47,12 +47,12 @@ def __persistent_cache_manager(mode):
|
|
|
47
47
|
return
|
|
48
48
|
if mode == 's':
|
|
49
49
|
# SAVE CACHE
|
|
50
|
-
with open(
|
|
50
|
+
with open(Data.FILE_CACHE, 'w') as f:
|
|
51
51
|
f.write(','.join([str(k) for k in Data.RGB_CACHE]))
|
|
52
52
|
return
|
|
53
53
|
try:
|
|
54
54
|
# RESTORE CACHE
|
|
55
|
-
with open(
|
|
55
|
+
with open(Data.FILE_CACHE, 'r') as f:
|
|
56
56
|
Data.RGB_CACHE = [float(data) for data in f.read().strip().split(',')]
|
|
57
57
|
except:
|
|
58
58
|
pass
|
|
@@ -74,7 +74,7 @@ def load(red_pin=None, green_pin=None, blue_pin=None, cache=True):
|
|
|
74
74
|
:param red_pin: optional red color pin to overwrite built-in
|
|
75
75
|
:param green_pin: optional green color pin to overwrite built-in
|
|
76
76
|
:param blue_pin: optional blue color pin to overwrite built-in
|
|
77
|
-
:param cache: file state machine cache: True/False (.
|
|
77
|
+
:param cache: file state machine cache: True/False (.cache), default=True
|
|
78
78
|
:return str: Cache state
|
|
79
79
|
"""
|
|
80
80
|
Data.PERSISTENT_CACHE = cache
|
micrOS/source/LM_roboarm.py
CHANGED
|
@@ -2,7 +2,7 @@ from utime import sleep_ms
|
|
|
2
2
|
from random import randint
|
|
3
3
|
import LM_servo as servo
|
|
4
4
|
from LM_switch import set_state, pinmap as switch_pinmap
|
|
5
|
-
from Common import transition, micro_task
|
|
5
|
+
from Common import transition, micro_task, data_dir
|
|
6
6
|
from Types import resolve
|
|
7
7
|
|
|
8
8
|
|
|
@@ -13,23 +13,24 @@ class RoboArm:
|
|
|
13
13
|
SPEED_MS = 10 # Set default speed between steps (ms)
|
|
14
14
|
MOVE_RECORD = [] # Buffer for XY move record/replay
|
|
15
15
|
PLAY_TAG = 'roboarm._play'
|
|
16
|
+
FILE_CACHE = data_dir('rarm.cache')
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def __persistent_cache_manager(mode):
|
|
19
20
|
"""
|
|
20
|
-
|
|
21
|
+
File state cache
|
|
21
22
|
modes:
|
|
22
23
|
r - recover, s - save
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
26
|
if mode == 's':
|
|
26
27
|
# SAVE CACHE
|
|
27
|
-
with open(
|
|
28
|
+
with open(RoboArm.FILE_CACHE, 'w') as f:
|
|
28
29
|
f.write(','.join([str(k) for k in RoboArm.MOVE_RECORD]))
|
|
29
30
|
return
|
|
30
31
|
try:
|
|
31
32
|
# RESTORE CACHE
|
|
32
|
-
with open(
|
|
33
|
+
with open(RoboArm.FILE_CACHE, 'r') as f:
|
|
33
34
|
RoboArm.MOVE_RECORD = [int(data) for data in f.read().strip().split(',')]
|
|
34
35
|
except:
|
|
35
36
|
pass
|
micrOS/source/LM_switch.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from machine import Pin
|
|
2
2
|
from microIO import bind_pin, pinmap_search
|
|
3
|
+
from Common import data_dir
|
|
3
4
|
from Types import resolve
|
|
4
5
|
|
|
5
6
|
#########################################
|
|
@@ -8,6 +9,7 @@ from Types import resolve
|
|
|
8
9
|
__SWITCH_OBJ = [None, None, None, None]
|
|
9
10
|
__PERSISTENT_CACHE = False
|
|
10
11
|
__SWITCH_STATE = [0, 0, 0, 0]
|
|
12
|
+
__FILE_CACHE = data_dir('switch.cache')
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
#########################################
|
|
@@ -16,7 +18,7 @@ __SWITCH_STATE = [0, 0, 0, 0]
|
|
|
16
18
|
|
|
17
19
|
def __persistent_cache_manager(mode):
|
|
18
20
|
"""
|
|
19
|
-
|
|
21
|
+
File cache
|
|
20
22
|
modes:
|
|
21
23
|
r - recover, s - save
|
|
22
24
|
"""
|
|
@@ -25,12 +27,12 @@ def __persistent_cache_manager(mode):
|
|
|
25
27
|
global __SWITCH_STATE
|
|
26
28
|
if mode == 's':
|
|
27
29
|
# SAVE CACHE
|
|
28
|
-
with open(
|
|
30
|
+
with open(__FILE_CACHE, 'w') as f:
|
|
29
31
|
f.write(','.join([str(k) for k in __SWITCH_STATE]))
|
|
30
32
|
return
|
|
31
33
|
try:
|
|
32
34
|
# RESTORE CACHE
|
|
33
|
-
with open(
|
|
35
|
+
with open(__FILE_CACHE, 'r') as f:
|
|
34
36
|
__SWITCH_STATE = [int(data) for data in f.read().strip().split(',')]
|
|
35
37
|
except:
|
|
36
38
|
pass
|
|
@@ -54,7 +56,7 @@ def load(cache=None, ch_init=None):
|
|
|
54
56
|
"""
|
|
55
57
|
Initiate switch module (4 switch pack)
|
|
56
58
|
:param cache bool: file state machine cache: True/False/None(default: automatic True)
|
|
57
|
-
- Load .
|
|
59
|
+
- Load .cache (state machine cache) for this load module
|
|
58
60
|
- Apply loaded states to gpio pins (boot function)
|
|
59
61
|
:return str: Cache state
|
|
60
62
|
"""
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A MicroPython library for the TCS3472 light sensing chip
|
|
3
|
+
https://github.com/tti0/tcs3472-micropython
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2021 tti0
|
|
6
|
+
Licensed under the MIT License
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from machine import I2C, Pin
|
|
10
|
+
from microIO import bind_pin, pinmap_search
|
|
11
|
+
import struct
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TCS3472:
|
|
15
|
+
INSTANCE = None
|
|
16
|
+
|
|
17
|
+
def __init__(self, bus, address=0x29):
|
|
18
|
+
self._bus = bus
|
|
19
|
+
self._i2c_address = address
|
|
20
|
+
|
|
21
|
+
self._bus.start()
|
|
22
|
+
|
|
23
|
+
self._bus.writeto(self._i2c_address, b'\x80\x03')
|
|
24
|
+
self._bus.writeto(self._i2c_address, b'\x81\x2b')
|
|
25
|
+
|
|
26
|
+
TCS3472.INSTANCE = self
|
|
27
|
+
|
|
28
|
+
def scaled(self):
|
|
29
|
+
crgb = self.raw()
|
|
30
|
+
if crgb[0] > 0:
|
|
31
|
+
return tuple(float(x) / crgb[0] for x in crgb[1:])
|
|
32
|
+
|
|
33
|
+
return (0, 0, 0)
|
|
34
|
+
|
|
35
|
+
def rgb(self):
|
|
36
|
+
return tuple(int(x * 255) for x in self.scaled())
|
|
37
|
+
|
|
38
|
+
def light(self):
|
|
39
|
+
return self.raw()[0]
|
|
40
|
+
|
|
41
|
+
def brightness(self, level=65.535):
|
|
42
|
+
return int((self.light() / level))
|
|
43
|
+
|
|
44
|
+
def valid(self):
|
|
45
|
+
self._bus.writeto(self._i2c_address, b'\x93')
|
|
46
|
+
return self._bus.readfrom(self._i2c_address, 1)[0] & 1
|
|
47
|
+
|
|
48
|
+
def raw(self):
|
|
49
|
+
self._bus.writeto(self._i2c_address, b'\xb4')
|
|
50
|
+
return struct.unpack("<HHHH", self._bus.readfrom(self._i2c_address, 8))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
############################ Exposed functions ############################
|
|
54
|
+
|
|
55
|
+
def load():
|
|
56
|
+
"""
|
|
57
|
+
Load the TCS3472 Color sensor instance.
|
|
58
|
+
"""
|
|
59
|
+
if TCS3472.INSTANCE is None:
|
|
60
|
+
bus = I2C(sda=Pin(bind_pin('i2c_sda')), scl=Pin(bind_pin('i2c_scl')))
|
|
61
|
+
TCS3472.INSTANCE = TCS3472(bus)
|
|
62
|
+
return TCS3472.INSTANCE
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def pinmap():
|
|
66
|
+
return pinmap_search(['i2c_scl', 'i2c_sda'])
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def measure():
|
|
70
|
+
sensor = load()
|
|
71
|
+
return {"rgb": sensor.rgb(), "light": sensor.light(), "brightness": sensor.brightness()}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def help(widgest=False):
|
|
75
|
+
return 'load', 'measure'
|
micrOS/source/LM_telegram.py
CHANGED
|
@@ -2,7 +2,7 @@ from sys import modules
|
|
|
2
2
|
import urequests
|
|
3
3
|
from Notify import Notify
|
|
4
4
|
from Config import cfgget
|
|
5
|
-
from Common import micro_task, syslog, console_write
|
|
5
|
+
from Common import micro_task, syslog, console_write, data_dir
|
|
6
6
|
from LM_system import ifconfig
|
|
7
7
|
from utime import localtime
|
|
8
8
|
|
|
@@ -18,6 +18,7 @@ class Telegram(Notify):
|
|
|
18
18
|
_CHAT_IDS = set() # Telegram bot chat IDs - multi group support - persistent caching
|
|
19
19
|
_API_PARAMS = "?offset=-1&limit=1&timeout=2" # Generic API params - optimization
|
|
20
20
|
_IN_MSG_ID = None
|
|
21
|
+
_FILE_CACHE = data_dir('telegram.cache')
|
|
21
22
|
|
|
22
23
|
def __init__(self):
|
|
23
24
|
# Subscribe to the notification system - provide send_msg method (over self)
|
|
@@ -26,20 +27,20 @@ class Telegram(Notify):
|
|
|
26
27
|
@staticmethod
|
|
27
28
|
def __id_cache(mode):
|
|
28
29
|
"""
|
|
29
|
-
|
|
30
|
+
File cache
|
|
30
31
|
modes:
|
|
31
32
|
r - recover, s - save
|
|
32
33
|
"""
|
|
33
34
|
if mode == 's':
|
|
34
35
|
# SAVE CACHE
|
|
35
36
|
console_write("[NTFY] Save chatIDs cache...")
|
|
36
|
-
with open(
|
|
37
|
+
with open(Telegram._FILE_CACHE, 'w') as f:
|
|
37
38
|
f.write(','.join([str(k) for k in Telegram._CHAT_IDS]))
|
|
38
39
|
return
|
|
39
40
|
try:
|
|
40
41
|
# RESTORE CACHE
|
|
41
42
|
console_write("[NTFY] Restore chatIDs cache...")
|
|
42
|
-
with open(
|
|
43
|
+
with open(Telegram._FILE_CACHE, 'r') as f:
|
|
43
44
|
# set() comprehension
|
|
44
45
|
Telegram._CHAT_IDS = {int(k) for k in f.read().strip().split(',')}
|
|
45
46
|
except:
|
micrOS/source/Logger.py
CHANGED
|
@@ -6,48 +6,52 @@ Designed by Marcell Ban aka BxNxM
|
|
|
6
6
|
"""
|
|
7
7
|
from time import localtime
|
|
8
8
|
from re import match
|
|
9
|
-
from uos import remove, mkdir
|
|
10
|
-
from Files import ilist_fs, is_dir
|
|
9
|
+
from uos import remove, mkdir
|
|
10
|
+
from Files import OSPath, path_join, ilist_fs, is_dir
|
|
11
11
|
|
|
12
12
|
#############################################
|
|
13
13
|
# LOGGING WITH DATA ROTATION #
|
|
14
14
|
#############################################
|
|
15
|
-
LOG_FOLDER = None
|
|
16
15
|
|
|
17
16
|
def _init_logger():
|
|
18
17
|
""" Init /logs folder """
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
if not is_dir(OSPath.LOGS):
|
|
19
|
+
try:
|
|
20
|
+
mkdir(OSPath.LOGS)
|
|
21
|
+
syslog(f"[BOOT] log dir {OSPath.LOGS} init")
|
|
22
|
+
except Exception as e:
|
|
23
|
+
OSPath.LOGS = OSPath.ROOT
|
|
24
|
+
syslog(f"[BOOT] log dir {OSPath.LOGS} fallback: {e}")
|
|
25
|
+
return OSPath.LOGS
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _dir_select(f_name:str) -> str:
|
|
29
|
+
"""
|
|
30
|
+
Select log dir based on file extension
|
|
31
|
+
:param f_name: filename with extension to detect target dir
|
|
32
|
+
"""
|
|
33
|
+
if f_name.endswith(".log"):
|
|
34
|
+
return OSPath.LOGS
|
|
35
|
+
return OSPath.DATA
|
|
30
36
|
|
|
31
37
|
|
|
32
|
-
def logger(data, f_name, limit):
|
|
38
|
+
def logger(data, f_name:str, limit:int):
|
|
33
39
|
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- automatic time stump
|
|
37
|
-
:param data: input string data to log
|
|
40
|
+
Generic logger function with line rotation and time
|
|
41
|
+
:param data: data to log
|
|
38
42
|
:param f_name: file name to use
|
|
39
43
|
:param limit: line limit for log rotation
|
|
40
44
|
return write verdict - true / false
|
|
41
45
|
INFO: hardcoded max data number = 30
|
|
42
46
|
"""
|
|
43
47
|
def _logger(f_mode='r+'):
|
|
44
|
-
nonlocal data,
|
|
48
|
+
nonlocal data, f_path, limit
|
|
45
49
|
limit = min(limit, 30) # Hardcoded max data line = 30
|
|
46
50
|
# [1] GET TIME STUMP
|
|
47
51
|
ts_buff = [str(k) for k in localtime()]
|
|
48
52
|
ts = ".".join(ts_buff[0:3]) + "-" + ":".join(ts_buff[3:6])
|
|
49
53
|
# [2] OPEN FILE - WRITE DATA WITH TS
|
|
50
|
-
with open(
|
|
54
|
+
with open(f_path, f_mode) as f:
|
|
51
55
|
_data = f"{ts} {data}\n"
|
|
52
56
|
# read file lines and filter by time stump chunks (hack for replace truncate)
|
|
53
57
|
lines = [_l for _l in f.readlines() if '-' in _l and '.' in _l]
|
|
@@ -61,7 +65,7 @@ def logger(data, f_name, limit):
|
|
|
61
65
|
# write file
|
|
62
66
|
f.write(''.join(lines))
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
f_path = path_join(_dir_select(f_name), f_name)
|
|
65
69
|
# Run logger
|
|
66
70
|
try:
|
|
67
71
|
# There is file - append 'r+'
|
|
@@ -75,17 +79,17 @@ def logger(data, f_name, limit):
|
|
|
75
79
|
return True
|
|
76
80
|
|
|
77
81
|
|
|
78
|
-
def log_get(f_name, msgobj=None):
|
|
82
|
+
def log_get(f_name:str, msgobj=None):
|
|
79
83
|
"""
|
|
80
|
-
|
|
81
|
-
-
|
|
84
|
+
Generic file getter for .log files
|
|
85
|
+
- log content critical [ERR] counter
|
|
82
86
|
"""
|
|
83
|
-
|
|
87
|
+
f_path = path_join(_dir_select(f_name), f_name)
|
|
84
88
|
err_cnt = 0
|
|
85
89
|
try:
|
|
86
90
|
if msgobj is not None:
|
|
87
|
-
msgobj(
|
|
88
|
-
with open(
|
|
91
|
+
msgobj(f_path)
|
|
92
|
+
with open(f_path, 'r') as f:
|
|
89
93
|
eline = f.readline().strip()
|
|
90
94
|
while eline:
|
|
91
95
|
# GET error from log line (tag: [ERR])
|
|
@@ -100,10 +104,16 @@ def log_get(f_name, msgobj=None):
|
|
|
100
104
|
|
|
101
105
|
|
|
102
106
|
def syslog(data=None, msgobj=None):
|
|
107
|
+
"""
|
|
108
|
+
System log setter/getter
|
|
109
|
+
:param data: None - read logs, str - write logs
|
|
110
|
+
:param msgobj: function to stream .log files
|
|
111
|
+
"""
|
|
103
112
|
if data is None:
|
|
104
|
-
|
|
113
|
+
# READ LOGS
|
|
114
|
+
err_cnt = sum([log_get(f, msgobj) for f in ilist_fs(OSPath.LOGS, type_filter='f') if f.endswith(".sys.log")])
|
|
105
115
|
return err_cnt
|
|
106
|
-
|
|
116
|
+
# WRITE LOGS - [target].sys.log automatic log level detection
|
|
107
117
|
_match = match(r"^\[([^\[\]]+)\]", data)
|
|
108
118
|
log_lvl = _match.group(1).lower() if _match else 'user'
|
|
109
119
|
f_name = f"{log_lvl}.sys.log" if log_lvl in ("err", "warn", "boot") else 'user.sys.log'
|
|
@@ -111,9 +121,13 @@ def syslog(data=None, msgobj=None):
|
|
|
111
121
|
|
|
112
122
|
|
|
113
123
|
def log_clean(msgobj=None):
|
|
114
|
-
|
|
124
|
+
"""
|
|
125
|
+
Clean logs folder
|
|
126
|
+
"""
|
|
127
|
+
logs_dir = OSPath.LOGS
|
|
128
|
+
to_del = [file for file in ilist_fs(logs_dir, type_filter='f') if file.endswith('.log')]
|
|
115
129
|
for _del in to_del:
|
|
116
|
-
_del =
|
|
130
|
+
_del = path_join(logs_dir, _del)
|
|
117
131
|
if msgobj is not None:
|
|
118
132
|
msgobj(f" Delete: {_del}")
|
|
119
133
|
remove(_del)
|