Skip to content

Commit 383d828

Browse files
committed
libs/unit/scroll.py: Add Scroll Unit.
Signed-off-by: lbuque <[email protected]>
1 parent ec9c2b7 commit 383d828

File tree

8 files changed

+325
-0
lines changed

8 files changed

+325
-0
lines changed

docs/en/refs/unit.scroll.ref

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.. |Scroll| image:: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/products/unit/UNIT-Scroll/4.webp
2+
:target: https://docs.m5stack.com/zh_CN/unit/UNIT-Scroll
3+
:height: 200px
4+
:width: 200px
5+
6+
7+
.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/example.png
8+
9+
.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/init.png
10+
11+
.. |get_rotary_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/get_rotary_status.png
12+
13+
.. |get_rotary_value.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/get_rotary_value.png
14+
15+
.. |get_button_status.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/get_button_status.png
16+
17+
.. |get_rotary_increments.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/get_rotary_increments.png
18+
19+
.. |reset_rotary_value.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/reset_rotary_value.png
20+
21+
.. |set_rotary_value.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/set_rotary_value.png
22+
23+
.. |set_color.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/set_color.png
24+
25+
.. |fill_color.png| image:: https://static-cdn.m5stack.com/mpy_docs/unit/scroll/fill_color.png
26+
27+
.. |get_bootloader_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/minijoyc/get_firmware_version.png
28+
29+
.. |get_firmware_version.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/minijoyc/get_firmware_version.png
30+
31+
.. |cores3_scroll_example.m5f2| raw:: html
32+
33+
<a
34+
href="https://uiflow2.m5stack.com/?example=https://raw.githubusercontent.com/m5stack/uiflow-micropython/develop/examples/unit/scroll/cores3_scroll_example.m5f2"
35+
target="_blank"
36+
>
37+
cores3_scroll_example.m5f2
38+
</a>

docs/en/units/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Unit
5656
relay.rst
5757
relay4.rst
5858
rgb.rst
59+
scroll.rst
5960
ssr.rst
6061
synth.rst
6162
thermal.rst

docs/en/units/scroll.rst

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
Scroll Unit
2+
===========
3+
4+
.. include:: ../refs/unit.scroll.ref
5+
6+
The following products are supported:
7+
8+
|Scroll|
9+
10+
11+
Micropython Example:
12+
13+
.. literalinclude:: ../../../examples/unit/scroll/cores3_scroll_example.py
14+
:language: python
15+
:linenos:
16+
17+
18+
UIFLOW2 Example:
19+
20+
|example.png|
21+
22+
23+
.. only:: builder_html
24+
25+
|cores3_scroll_example.m5f2|
26+
27+
28+
class ScrollUnit
29+
-----------------
30+
31+
Constructors
32+
------------
33+
34+
.. class:: ScrollUnit(i2c, address: int | list | tuple = 0x40)
35+
36+
Creates a Rotary object.
37+
38+
:param i2c: I2C object.
39+
:param address: I2C address, Default is 0x40.
40+
41+
UIFLOW2:
42+
43+
|init.png|
44+
45+
46+
Methods
47+
-------
48+
49+
.. method:: ScrollUnit.get_rotary_status() -> bool
50+
51+
Gets the rotation status of the Rotary object.
52+
53+
UIFLOW2:
54+
55+
|get_rotary_status.png|
56+
57+
58+
.. method:: ScrollUnit.get_rotary_value() -> int
59+
60+
Gets the rotation value of the Rotary object.
61+
62+
UIFLOW2:
63+
64+
|get_rotary_value.png|
65+
66+
67+
.. method:: ScrollUnit.get_rotary_increments() -> int
68+
69+
Gets the rotation increment of the Rotary object. Can be used to determine
70+
the direction of rotation.
71+
72+
UIFLOW2:
73+
74+
|get_rotary_increments.png|
75+
76+
77+
.. method:: ScrollUnit.reset_rotary_value() -> None
78+
79+
Resets the rotation value of the Rotary object.
80+
81+
UIFLOW2:
82+
83+
|reset_rotary_value.png|
84+
85+
86+
.. method:: ScrollUnit.set_rotary_value(new_value: int) -> None
87+
88+
Sets the rotation value of the Rotary object.
89+
90+
:param int new_value: adjust the current value.
91+
92+
UIFLOW2:
93+
94+
|set_rotary_value.png|
95+
96+
97+
.. method:: ScrollUnit.get_button_status() -> bool
98+
99+
Get the current status of the rotary encoder keys.
100+
101+
UIFLOW2:
102+
103+
|get_button_status.png|
104+
105+
106+
.. method:: ScrollUnit.fill_color(rgb: int) -> None
107+
108+
Set the color of the LED
109+
110+
:param int rgb: the color of the LED, 0x000000 - 0xFFFFFF.
111+
112+
UIFLOW2:
113+
114+
|fill_color.png|
115+
116+
117+
.. method:: ScrollUnit.get_bootloader_version() -> str
118+
119+
Get the bootloader version.
120+
121+
:return: bootloader version
122+
123+
UIFLOW2:
124+
125+
|get_bootloader_version.png|
126+
127+
128+
.. method:: ScrollUnit.get_firmware_version() -> str
129+
130+
Get the firmware version.
131+
132+
:return: firmware version
133+
134+
UIFLOW2:
135+
136+
|get_firmware_version.png|
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"V2.0","versionNumber":"V2.1.2","type":"cores3","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__cores3_screen","createTime":1723607973362,"x":0,"y":0,"width":320,"height":240,"backgroundColor":"#222222","size":0,"isSelected":true},{"name":"label0","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"bC@spH7Gx=eAKnTo","createTime":1723608011967,"x":73,"y":75,"color":"#ffffff","backgroundColor":"#222222","text":"label0","engine":"gfx","font":"Widgets.FONTS.DejaVu56","rotation":0,"isSelected":false,"width":172,"height":63},{"name":"label1","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"oyh`2W#Znj$r_MTi","createTime":1723608011967,"x":135,"y":138,"color":"#ffffff","backgroundColor":"#222222","text":"label1","engine":"gfx","font":"Widgets.FONTS.DejaVu12","rotation":0,"isSelected":false,"width":39,"height":14},{"name":"label2","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"b=paJgXv7OKv^K_S","createTime":1723608101584,"x":9,"y":9,"color":"#ffffff","backgroundColor":"#222222","text":"label2","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false,"width":54,"height":20},{"name":"label3","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"p$=9HijT-gGOob%r","createTime":1723608101584,"x":10,"y":31,"color":"#ffffff","backgroundColor":"#222222","text":"label3","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false,"width":54,"height":20}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","speaker","touch","als","mic","i2c"]},{"unit":["unit_scroll"]}],"units":[{"type":"unit_scroll","name":"scroll_0","portList":["A","PAHUB","Custom"],"portType":"A","userPort":[22,21],"id":"dR^H^xyh#S2r8-ac","createTime":1723607980933,"bus":"i2c0","pahubPortList":[0,1,2,3,4,5],"pahubPort":0,"initBlockId":"g0Fu+{?OP{U%zAG=Pt=B"}],"hats":[],"bases":[],"i2cs":[{"id":"i2c0","portType":"A","userPort":[22,21],"freq":"100000","blockId":"|SH=1+gR@=im3ZRQN+IF"}],"blockly":"<block type=\"basic_on_setup\" id=\"setup_block\" deletable=\"false\" x=\"50\" y=\"50\"><mutation isBegin=\"true\"></mutation><field name=\"UPDATEOP\">true</field><statement name=\"FUNC\"><block type=\"system_m5_begin\" id=\"system_m5_begin\"><next><block type=\"i2c_init\" id=\"|SH=1+gR@=im3ZRQN+IF\"><field name=\"NAME\">0</field><field name=\"FREQ\">100000</field><value name=\"SCL\"><shadow type=\"math_number\" id=\"jrE[eo#B~]URO%@;,@/G\"><mutation max=\"Infinity\" min=\"-Infinity\" precision=\"0\"></mutation><field name=\"NUM\">1</field></shadow></value><value name=\"SDA\"><shadow type=\"math_number\" id=\"R+|!%FW.g*vF)Pd0bT/$\"><mutation max=\"Infinity\" min=\"-Infinity\" precision=\"0\"></mutation><field name=\"NUM\">2</field></shadow></value><next><block type=\"unit_scroll_init\" id=\"g0Fu+{?OP{U%zAG=Pt=B\"><field name=\"NAME\">scroll_0</field><value name=\"ADDR\"><shadow type=\"unit_scroll_addr_option\" id=\"GE2y{yDR`9E-iO[sw|-u\"><field name=\"VALUE\">0x40</field></shadow></value><next><block type=\"label_set_text\" id=\"_Lud1]=28UY9gBru^$w=\"><field name=\"NAME\">label2</field><value name=\"TEXT\"><shadow type=\"text\" id=\"ei26%VcScryKV%DTT`IB\"><field name=\"TEXT\">Label</field></shadow><block type=\"text_add_str\" id=\"XkMXry-NSE}[AFkELXtt\"><value name=\"VALUE1\"><shadow type=\"text\" id=\"2ht|=+5nPxg7q-US`|+G\"><field name=\"TEXT\">bootloader ver: </field></shadow></value><value name=\"VALUE2\"><block type=\"unit_scroll_get_bootloader_version\" id=\"RK7[e$itN9{Y)t/Sm3Nu\"><field name=\"NAME\">scroll_0</field></block></value></block></value><next><block type=\"label_set_text\" id=\"sxu)/c~=I$p5K$W8R2.v\"><field name=\"NAME\">label3</field><value name=\"TEXT\"><shadow type=\"text\" id=\"[Tug(z`0-^y62i5uKt._\"><field name=\"TEXT\">Label</field></shadow><block type=\"text_add_str\" id=\"FO}gE[Z-z6z/c}AM8s%9\"><value name=\"VALUE1\"><shadow type=\"text\" id=\"vP-UI.MP{Mh:zT1IAWlv\"><field name=\"TEXT\">firmware ver: </field></shadow></value><value name=\"VALUE2\"><block type=\"unit_scroll_get_firmware_version\" id=\"=ewu42MpXir8Mo6Q^#4y\"><field name=\"NAME\">scroll_0</field></block></value></block></value><next><block type=\"unit_scroll_set_value\" id=\"vEn(,6(pK``7zds@?L(`\"><field name=\"NAME\">scroll_0</field><value name=\"VALUE\"><shadow type=\"math_number\" id=\"K3=WFN2Rge}8Gx//Nk~a\"><mutation max=\"Infinity\" min=\"-Infinity\" precision=\"0\"></mutation><field name=\"NUM\">100</field></shadow></value></block></next></block></next></block></next></block></next></block></next></block></statement></block><block type=\"basic_on_loop\" id=\"loop_block\" deletable=\"false\" x=\"50\" y=\"450\"><mutation isUpdate=\"true\"></mutation><field name=\"UPDATEOP\">true</field><statement name=\"FUNC\"><block type=\"system_m5_update\" id=\"system_m5_update\"><next><block type=\"controls_if\" id=\"EAo}V],SZ1x:n)1ON?dA\"><value name=\"IF0\"><block type=\"unit_scroll_get_rotary_status\" id=\"4bA`~#QM_fM!YWp_(bJ@\"><field name=\"NAME\">scroll_0</field></block></value><statement name=\"DO0\"><block type=\"label_set_text\" id=\"+TT:(]Qr~-s?QzolqXri\"><field name=\"NAME\">label0</field><value name=\"TEXT\"><shadow type=\"text\" id=\"#_Z2iyr_(Ncf$M7zL.8C\"><field name=\"TEXT\">Label</field></shadow><block type=\"unit_scroll_get_rotary_value\" id=\"gRxF=GzEA4#zPqdO2dO5\"><field name=\"NAME\">scroll_0</field></block></value><next><block type=\"label_set_text\" id=\"qHyKi{NP7ZI,uvgmS#C,\"><field name=\"NAME\">label1</field><value name=\"TEXT\"><shadow type=\"text\" id=\"#_Z2iyr_(Ncf$M7zL.8C\"><field name=\"TEXT\">Label</field></shadow><block type=\"unit_scroll_get_increments\" id=\":2MrT-fII395m={^roP4\"><field name=\"NAME\">scroll_0</field></block></value></block></next></block></statement><next><block type=\"controls_ifelse\" id=\"ls,5h1`t?ot~Tpg9x4h[\"><value name=\"IF0\"><block type=\"unit_scroll_get_button_status\" id=\"*yLQkVQ+mv6ZYprvp$%?\"><field name=\"NAME\">scroll_0</field></block></value><statement name=\"DO0\"><block type=\"unit_scroll_reset_value\" id=\"Ti,nRkv==-L%[!u*S!km\"><field name=\"NAME\">scroll_0</field><next><block type=\"unit_scroll_fill_color\" id=\"Q9.VBZqEMg]b/NFMiX_-\"><field name=\"NAME\">scroll_0</field><value name=\"COLOR\"><block type=\"color_rgb_palette\" id=\"E*^_^w}^U%ex{ChN{AA4\"><mutation mode=\"palette\"></mutation><field name=\"MODE\">palette</field><field name=\"COLOR\">#33cc00</field></block></value></block></next></block></statement><statement name=\"ELSE\"><block type=\"unit_scroll_fill_color\" id=\"(:5*GiJw1:+Zu?Qs;+Y8\"><field name=\"NAME\">scroll_0</field><value name=\"COLOR\"><block type=\"color_rgb_palette\" id=\"qsP$nsxsN%{+kLi:0B7-\"><mutation mode=\"palette\"></mutation><field name=\"MODE\">palette</field><field name=\"COLOR\">#ff0000</field></block></value></block></statement></block></next></block></next></block></statement></block>","screen":[{"simulationName":"Built-in","type":"builtin","width":320,"height":240,"scale":0.78,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1723607973361}],"logicWhenNum":0,"customList":[]}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import os, sys, io
6+
import M5
7+
from M5 import *
8+
from hardware import *
9+
from unit import ScrollUnit
10+
11+
12+
label0 = None
13+
label1 = None
14+
label2 = None
15+
label3 = None
16+
i2c0 = None
17+
scroll_0 = None
18+
19+
20+
def setup():
21+
global label0, label1, label2, label3, i2c0, scroll_0
22+
23+
M5.begin()
24+
Widgets.fillScreen(0x222222)
25+
label0 = Widgets.Label("label0", 73, 75, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu56)
26+
label1 = Widgets.Label("label1", 135, 138, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu12)
27+
label2 = Widgets.Label("label2", 9, 9, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
28+
label3 = Widgets.Label("label3", 10, 31, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
29+
30+
i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000)
31+
scroll_0 = ScrollUnit(i2c0, 0x40)
32+
label2.setText(str((str("bootloader ver: ") + str((scroll_0.get_bootloader_version())))))
33+
label3.setText(str((str("firmware ver: ") + str((scroll_0.get_firmware_version())))))
34+
scroll_0.set_rotary_value(100)
35+
36+
37+
def loop():
38+
global label0, label1, label2, label3, i2c0, scroll_0
39+
M5.update()
40+
if scroll_0.get_rotary_status():
41+
label0.setText(str(scroll_0.get_rotary_value()))
42+
label1.setText(str(scroll_0.get_rotary_increments()))
43+
if scroll_0.get_button_status():
44+
scroll_0.reset_rotary_value()
45+
scroll_0.fill_color(0x33CC00)
46+
else:
47+
scroll_0.fill_color(0xFF0000)
48+
49+
50+
if __name__ == "__main__":
51+
try:
52+
setup()
53+
while True:
54+
loop()
55+
except (Exception, KeyboardInterrupt) as e:
56+
try:
57+
from utility import print_error_msg
58+
59+
print_error_msg(e)
60+
except ImportError:
61+
print("please update to latest firmware")

m5stack/libs/unit/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"RS485Unit": "rs485",
9090
"RTC8563Unit": "rtc8563",
9191
"ScalesUnit": "scales",
92+
"ScrollUnit": "scroll",
9293
"Servos8Unit": "servos8",
9394
"SSRUnit": "ssr",
9495
"SynthUnit": "synth",

m5stack/libs/unit/manifest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"rs485.py",
9191
"rtc8563.py",
9292
"scales.py",
93+
"scroll.py",
9394
"servos8.py",
9495
"ssr.py",
9596
"synth.py",

m5stack/libs/unit/scroll.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
from .unit_helper import UnitError
6+
import struct
7+
8+
9+
class ScrollUnit:
10+
_ENCODER_COUNTER_VALUE_REG = 0x10
11+
_ENCODER_BUTTON_STATUS_REG = 0x20
12+
_ENCODER_RGB_LED_REG = 0x30
13+
_ENCODER_RESET_REG = 0x40
14+
_ENCODER_INCREMENTS_REG = 0x50
15+
_ENCODER_BL_REG = 0xFC
16+
_ENCODER_FW_REG = 0xFE
17+
18+
def __init__(self, i2c, address: int | list | tuple = 0x40) -> None:
19+
self._i2c = i2c
20+
self._address = address
21+
self._buffer = memoryview(bytearray(4))
22+
if self._address not in self._i2c.scan():
23+
raise UnitError("Scroll Unit maybe not connect")
24+
self.reset_rotary_value()
25+
self._last_value = self._get_rotary_value()
26+
self._zero_value = self._last_value
27+
self._start_value = 0
28+
29+
def get_rotary_status(self):
30+
val = self._get_rotary_value()
31+
if val != self._last_value:
32+
return True
33+
return False
34+
35+
def get_rotary_value(self):
36+
self._last_value = self._get_rotary_value()
37+
return self._start_value + self._last_value - self._zero_value
38+
39+
def get_rotary_increments(self):
40+
buf = self._read_reg_bytes(self._ENCODER_INCREMENTS_REG, 4)
41+
return struct.unpack("<h", buf)[0]
42+
43+
def _get_rotary_value(self):
44+
buf = self._read_reg_bytes(self._ENCODER_COUNTER_VALUE_REG, 4)
45+
return struct.unpack("<h", buf)[0]
46+
47+
def set_rotary_value(self, value: int) -> None:
48+
self._start_value = value
49+
50+
def reset_rotary_value(self):
51+
self._write_reg_bytes(self._ENCODER_RESET_REG, b"\x01")
52+
self._zero_value = 0
53+
self._last_value = 0
54+
self._start_value = 0
55+
56+
def get_button_status(self) -> bool:
57+
buf = self._read_reg_bytes(self._ENCODER_BUTTON_STATUS_REG, 2)
58+
return not bool(buf[0])
59+
60+
def set_color(self, index: int = 0, rgb: int = 0) -> None:
61+
buf = self._buffer[1:4]
62+
buf = rgb.to_bytes(3, "big")
63+
self._write_reg_bytes(self._ENCODER_RGB_LED_REG + 1, buf)
64+
65+
def fill_color(self, rgb: int = 0) -> None:
66+
self.set_color(0, rgb)
67+
68+
def get_bootloader_version(self) -> str:
69+
return str(self._read_reg_bytes(self._ENCODER_BL_REG, 1)[0])
70+
71+
def get_firmware_version(self) -> str:
72+
return str(self._read_reg_bytes(self._ENCODER_FW_REG, 1)[0])
73+
74+
def _read_reg_bytes(self, reg: int = 0, length: int = 0) -> bytearray:
75+
buf = self._buffer[0:1]
76+
buf[0] = reg
77+
self._i2c.writeto(self._address, buf)
78+
buf = self._buffer[0:length]
79+
self._i2c.readfrom_into(self._address, buf)
80+
return buf
81+
82+
def _write_reg_bytes(self, reg, data):
83+
buf = self._buffer[0 : 1 + len(data)]
84+
buf[0] = reg
85+
buf[1:] = bytes(data)
86+
self._i2c.writeto(self._address, buf)

0 commit comments

Comments
 (0)