UC2-REST 0.2.0.22__tar.gz → 0.2.0.24__tar.gz
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.
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/PKG-INFO +3 -2
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/UC2_REST.egg-info/PKG-INFO +3 -2
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/UC2_REST.egg-info/SOURCES.txt +3 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/UC2Client.py +10 -2
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/__version__.py +1 -1
- uc2_rest-0.2.0.24/uc2rest/gripper.py +107 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/home.py +6 -6
- uc2_rest-0.2.0.24/uc2rest/ledmatrix.py +324 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/motor.py +134 -37
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/mserial.py +135 -45
- uc2_rest-0.2.0.24/uc2rest/objective.py +230 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/LICENSE +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/README.md +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/UC2_REST.egg-info/dependency_links.txt +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/UC2_REST.egg-info/not-zip-safe +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/UC2_REST.egg-info/requires.txt +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/UC2_REST.egg-info/top_level.txt +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/setup.cfg +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/setup.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/MockSerial.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/__init__.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/analog.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/camera.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/cmdrecorder.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/config.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/config_.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/digitalout.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/galvo.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/laser.py +0 -0
- /uc2_rest-0.2.0.22/uc2rest/ledmatrix.py → /uc2_rest-0.2.0.24/uc2rest/ledmatrix_.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/logger.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/message.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/modules.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/pid.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/rotator.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/slm.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/state.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/temperature.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/utils.py +0 -0
- {uc2_rest-0.2.0.22 → uc2_rest-0.2.0.24}/uc2rest/wifi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: UC2-REST
|
|
3
|
-
Version: 0.2.0.
|
|
3
|
+
Version: 0.2.0.24
|
|
4
4
|
Summary: This pacage will help you to drive the ESP32-driven microscopy control modules from UC2
|
|
5
5
|
Home-page: https://github.com/openUC2/UC2-REST
|
|
6
6
|
Author: Benedict Diederich
|
|
@@ -24,6 +24,7 @@ Dynamic: description-content-type
|
|
|
24
24
|
Dynamic: home-page
|
|
25
25
|
Dynamic: keywords
|
|
26
26
|
Dynamic: license
|
|
27
|
+
Dynamic: license-file
|
|
27
28
|
Dynamic: requires-dist
|
|
28
29
|
Dynamic: requires-python
|
|
29
30
|
Dynamic: summary
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: UC2-REST
|
|
3
|
-
Version: 0.2.0.
|
|
3
|
+
Version: 0.2.0.24
|
|
4
4
|
Summary: This pacage will help you to drive the ESP32-driven microscopy control modules from UC2
|
|
5
5
|
Home-page: https://github.com/openUC2/UC2-REST
|
|
6
6
|
Author: Benedict Diederich
|
|
@@ -24,6 +24,7 @@ Dynamic: description-content-type
|
|
|
24
24
|
Dynamic: home-page
|
|
25
25
|
Dynamic: keywords
|
|
26
26
|
Dynamic: license
|
|
27
|
+
Dynamic: license-file
|
|
27
28
|
Dynamic: requires-dist
|
|
28
29
|
Dynamic: requires-python
|
|
29
30
|
Dynamic: summary
|
|
@@ -18,14 +18,17 @@ uc2rest/config.py
|
|
|
18
18
|
uc2rest/config_.py
|
|
19
19
|
uc2rest/digitalout.py
|
|
20
20
|
uc2rest/galvo.py
|
|
21
|
+
uc2rest/gripper.py
|
|
21
22
|
uc2rest/home.py
|
|
22
23
|
uc2rest/laser.py
|
|
23
24
|
uc2rest/ledmatrix.py
|
|
25
|
+
uc2rest/ledmatrix_.py
|
|
24
26
|
uc2rest/logger.py
|
|
25
27
|
uc2rest/message.py
|
|
26
28
|
uc2rest/modules.py
|
|
27
29
|
uc2rest/motor.py
|
|
28
30
|
uc2rest/mserial.py
|
|
31
|
+
uc2rest/objective.py
|
|
29
32
|
uc2rest/pid.py
|
|
30
33
|
uc2rest/rotator.py
|
|
31
34
|
uc2rest/slm.py
|
|
@@ -11,7 +11,9 @@ from .galvo import Galvo
|
|
|
11
11
|
from .config import config
|
|
12
12
|
from .ledmatrix import LedMatrix
|
|
13
13
|
from .motor import Motor
|
|
14
|
+
from .gripper import Gripper
|
|
14
15
|
from .home import Home
|
|
16
|
+
from .objective import Objective
|
|
15
17
|
from .state import State
|
|
16
18
|
from .laser import Laser
|
|
17
19
|
from .wifi import Wifi
|
|
@@ -41,7 +43,7 @@ class UC2Client(object):
|
|
|
41
43
|
# BAUDRATE = 500000
|
|
42
44
|
BAUDRATE = 500000
|
|
43
45
|
|
|
44
|
-
def __init__(self, host=None, port=31950, serialport=None, identity="UC2_Feather", baudrate=BAUDRATE, NLeds=64, SerialManager=None, DEBUG=False, logger=None):
|
|
46
|
+
def __init__(self, host=None, port=31950, serialport=None, identity="UC2_Feather", baudrate=BAUDRATE, NLeds=64, SerialManager=None, DEBUG=False, logger=None, skipFirmwareCheck=False):
|
|
45
47
|
'''
|
|
46
48
|
This client connects to the UC2-REST microcontroller that can be found here
|
|
47
49
|
https://github.com/openUC2/UC2-REST
|
|
@@ -64,7 +66,7 @@ class UC2Client(object):
|
|
|
64
66
|
# initialize communication channel (# connect to wifi or usb)
|
|
65
67
|
if serialport is not None:
|
|
66
68
|
# use USB connection
|
|
67
|
-
self.serial = Serial(serialport, baudrate, parent=self, identity=identity, DEBUG=DEBUG)
|
|
69
|
+
self.serial = Serial(serialport, baudrate, parent=self, identity=identity, DEBUG=DEBUG, skipFirmwareCheck=skipFirmwareCheck)
|
|
68
70
|
self.is_serial = True
|
|
69
71
|
self.is_connected = self.serial.is_connected
|
|
70
72
|
self.serial.DEBUG = DEBUG
|
|
@@ -109,12 +111,18 @@ class UC2Client(object):
|
|
|
109
111
|
# initilize motor
|
|
110
112
|
self.motor = Motor(self)
|
|
111
113
|
|
|
114
|
+
# initialize gripper
|
|
115
|
+
self.gripper = Gripper(self)
|
|
116
|
+
|
|
112
117
|
# initialize rotator
|
|
113
118
|
self.rotator = Rotator(self)
|
|
114
119
|
|
|
115
120
|
# initiliaze homing
|
|
116
121
|
self.home = Home(self)
|
|
117
122
|
|
|
123
|
+
# initialize objective
|
|
124
|
+
self.objective = Objective(self)
|
|
125
|
+
|
|
118
126
|
# initialize temperature
|
|
119
127
|
self.temperature = Temperature(self)
|
|
120
128
|
|
|
@@ -6,7 +6,7 @@ __version__.py
|
|
|
6
6
|
|
|
7
7
|
__title__ = 'UC2-REST'
|
|
8
8
|
__description__ = 'This pacage will help you to drive the ESP32-driven microscopy control modules from UC2'
|
|
9
|
-
__version__ = "v0.2.0.
|
|
9
|
+
__version__ = "v0.2.0.24"
|
|
10
10
|
__author__ = 'Benedict Diederich'
|
|
11
11
|
__author_email__ = 'benedictdied@gmail.com'
|
|
12
12
|
__license__ = 'GPL v3'
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
class Gripper(object):
|
|
5
|
+
"""
|
|
6
|
+
Example Python class for controlling a gripper servo via JSON commands
|
|
7
|
+
to the MCU, similar in style to the Objective class provided.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, parent):
|
|
11
|
+
"""
|
|
12
|
+
parent: an object that handles the low-level post_json(path, payload, getReturn, timeout, nResponses)
|
|
13
|
+
similar to the 'Home' or 'Objective' parent in the template.
|
|
14
|
+
"""
|
|
15
|
+
self._parent = parent
|
|
16
|
+
self.timeout = 10 # default timeout for blocking commands
|
|
17
|
+
# Default angles
|
|
18
|
+
self.close_angle = 0
|
|
19
|
+
self.open_angle = 180
|
|
20
|
+
|
|
21
|
+
def close(self, isBlocking=False):
|
|
22
|
+
"""
|
|
23
|
+
Closes the gripper to close_angle (0° by default).
|
|
24
|
+
Sends {"task":"/gripper_act","action":"close"} over Serial/JSON.
|
|
25
|
+
"""
|
|
26
|
+
path = "/gripper_act"
|
|
27
|
+
payload = {
|
|
28
|
+
"task": path,
|
|
29
|
+
"action": "close"
|
|
30
|
+
}
|
|
31
|
+
# Possibly wait for the MCU response
|
|
32
|
+
nResponses = 1 if isBlocking else 0
|
|
33
|
+
r = self._parent.post_json(
|
|
34
|
+
path,
|
|
35
|
+
payload,
|
|
36
|
+
getReturn=isBlocking,
|
|
37
|
+
timeout=self.timeout if isBlocking else 0,
|
|
38
|
+
nResponses=nResponses
|
|
39
|
+
)
|
|
40
|
+
return r
|
|
41
|
+
|
|
42
|
+
def open(self, isBlocking=False):
|
|
43
|
+
"""
|
|
44
|
+
Opens the gripper to open_angle (180° by default).
|
|
45
|
+
Sends {"task":"/gripper_act","action":"open"}.
|
|
46
|
+
"""
|
|
47
|
+
path = "/gripper_act"
|
|
48
|
+
payload = {
|
|
49
|
+
"task": path,
|
|
50
|
+
"action": "open"
|
|
51
|
+
}
|
|
52
|
+
nResponses = 1 if isBlocking else 0
|
|
53
|
+
r = self._parent.post_json(
|
|
54
|
+
path,
|
|
55
|
+
payload,
|
|
56
|
+
getReturn=isBlocking,
|
|
57
|
+
timeout=self.timeout if isBlocking else 0,
|
|
58
|
+
nResponses=nResponses
|
|
59
|
+
)
|
|
60
|
+
return r
|
|
61
|
+
|
|
62
|
+
def setAngle(self, angle, isBlocking=False):
|
|
63
|
+
"""
|
|
64
|
+
Moves the gripper servo to an arbitrary angle (0–180).
|
|
65
|
+
Sends {"task":"/gripper_act","action":"degree","value":<angle>}.
|
|
66
|
+
"""
|
|
67
|
+
path = "/gripper_act"
|
|
68
|
+
payload = {
|
|
69
|
+
"task": path,
|
|
70
|
+
"action": "degree",
|
|
71
|
+
"value": angle
|
|
72
|
+
}
|
|
73
|
+
nResponses = 1 if isBlocking else 0
|
|
74
|
+
r = self._parent.post_json(
|
|
75
|
+
path,
|
|
76
|
+
payload,
|
|
77
|
+
getReturn=isBlocking,
|
|
78
|
+
timeout=self.timeout if isBlocking else 0,
|
|
79
|
+
nResponses=nResponses
|
|
80
|
+
)
|
|
81
|
+
return r
|
|
82
|
+
|
|
83
|
+
def getstatus(self):
|
|
84
|
+
"""
|
|
85
|
+
Query the current gripper status.
|
|
86
|
+
Expects MCU to return something like:
|
|
87
|
+
{
|
|
88
|
+
"gripper":{
|
|
89
|
+
"pos": 90,
|
|
90
|
+
"isOpen": 0 or 1,
|
|
91
|
+
"state": 1,
|
|
92
|
+
"isRunning": 0
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
"""
|
|
96
|
+
path = "/gripper_get"
|
|
97
|
+
payload = {"task": path}
|
|
98
|
+
r = self._parent.post_json(path, payload, timeout=1)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
# The last item in r is assumed to contain the "gripper" field
|
|
102
|
+
status = r[-1]["gripper"]
|
|
103
|
+
except:
|
|
104
|
+
# Fallback if parsing fails
|
|
105
|
+
status = {"pos": 0, "isOpen": None, "state": 0, "isRunning": 0}
|
|
106
|
+
|
|
107
|
+
return status
|
|
@@ -15,7 +15,7 @@ class Home(object):
|
|
|
15
15
|
# axis = 1 corresponds to 'X'
|
|
16
16
|
axis = 1
|
|
17
17
|
self.home(axis=axis,
|
|
18
|
-
|
|
18
|
+
endstoptimeout=timeout,
|
|
19
19
|
speed = speed,
|
|
20
20
|
direction = direction,
|
|
21
21
|
endposrelease=endposrelease,
|
|
@@ -26,7 +26,7 @@ class Home(object):
|
|
|
26
26
|
# axis = 2 corresponds to 'Y'
|
|
27
27
|
axis = 2
|
|
28
28
|
self.home(axis=axis,
|
|
29
|
-
|
|
29
|
+
endstoptimeout=timeout,
|
|
30
30
|
speed = speed,
|
|
31
31
|
direction = direction,
|
|
32
32
|
endposrelease=endposrelease,
|
|
@@ -37,7 +37,7 @@ class Home(object):
|
|
|
37
37
|
# axisa = 3 corresponds to 'Z'
|
|
38
38
|
axis = 3
|
|
39
39
|
self.home(axis=axis,
|
|
40
|
-
|
|
40
|
+
endstoptimeout=timeout,
|
|
41
41
|
speed = speed,
|
|
42
42
|
direction = direction,
|
|
43
43
|
endposrelease=endposrelease,
|
|
@@ -48,14 +48,14 @@ class Home(object):
|
|
|
48
48
|
# axis = 0 corresponds to 'A'
|
|
49
49
|
axis = 0
|
|
50
50
|
self.home(axis=axis,
|
|
51
|
-
|
|
51
|
+
endstoptimeout=timeout,
|
|
52
52
|
speed = speed,
|
|
53
53
|
direction = direction,
|
|
54
54
|
endposrelease=endposrelease,
|
|
55
55
|
endstoppolarity=endstoppolarity,
|
|
56
56
|
isBlocking=isBlocking)
|
|
57
57
|
|
|
58
|
-
def home(self, axis=None, timeout=None, speed=None, direction=None, endposrelease=None, endstoppolarity=None, isBlocking=False):
|
|
58
|
+
def home(self, axis=None, timeout=None, speed=None, direction=None, endposrelease=None, endstoppolarity=None, endstoptimeout=10000, isBlocking=False):
|
|
59
59
|
'''
|
|
60
60
|
axis = 0,1,2,3 or 'A, 'X','Y','Z'
|
|
61
61
|
timeout => when to stop homing (it's a while loop on the MCU)
|
|
@@ -88,7 +88,7 @@ class Home(object):
|
|
|
88
88
|
"steppers": [
|
|
89
89
|
{
|
|
90
90
|
"stepperid": axis,
|
|
91
|
-
"timeout":
|
|
91
|
+
"timeout":endstoptimeout,
|
|
92
92
|
"speed":abs(speed),
|
|
93
93
|
"direction":direction,
|
|
94
94
|
"endstoppolarity":endstoppolarity
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
gTimeout = 2
|
|
4
|
+
|
|
5
|
+
class LedMatrix(object):
|
|
6
|
+
def __init__(self, parent, NLeds=64):
|
|
7
|
+
"""
|
|
8
|
+
This class handles sending JSON commands for controlling a NeoPixel or LED matrix device.
|
|
9
|
+
The 'parent' is assumed to be an object providing post_json(...) and get_json(...) methods.
|
|
10
|
+
"""
|
|
11
|
+
self.NLeds = NLeds
|
|
12
|
+
self.Nx = self.Ny = int(np.sqrt(NLeds)) if NLeds > 0 else 8
|
|
13
|
+
|
|
14
|
+
# We assume the pattern is binary (0 or 1) for an NxN grid, stored in ledpattern NxN or flattened.
|
|
15
|
+
# We store an RGB for each LED if needed. -1 indicates "unset" or no color assigned.
|
|
16
|
+
self.ledpattern = np.ones((self.NLeds, 3)) * -1
|
|
17
|
+
|
|
18
|
+
self._parent = parent # must have post_json() etc.
|
|
19
|
+
self.timeout = 1
|
|
20
|
+
self.intensity = (255, 255, 255)
|
|
21
|
+
|
|
22
|
+
# Maps textual modes to an integer, used for backward compatibility
|
|
23
|
+
self.ledArrayModes = {
|
|
24
|
+
"array": 0,
|
|
25
|
+
"full": 1,
|
|
26
|
+
"single": 2,
|
|
27
|
+
"off": 3,
|
|
28
|
+
"left": 4,
|
|
29
|
+
"right": 5,
|
|
30
|
+
"top": 6,
|
|
31
|
+
"bottom": 7,
|
|
32
|
+
"multi": 8
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self.currentLedArrayMode = "full"
|
|
36
|
+
|
|
37
|
+
######################################################################################################
|
|
38
|
+
# BASE / LEGACY METHODS
|
|
39
|
+
######################################################################################################
|
|
40
|
+
|
|
41
|
+
def send_LEDMatrix_array(self, led_pattern, getReturn=True, timeout=gTimeout):
|
|
42
|
+
"""
|
|
43
|
+
Send an LED array pattern e.g. a list of { id, r, g, b } or a NumPy array shape (N,3).
|
|
44
|
+
Produces JSON:
|
|
45
|
+
{
|
|
46
|
+
"task": "/ledarr_act",
|
|
47
|
+
"led": {
|
|
48
|
+
"action": "array",
|
|
49
|
+
"LEDArrMode": 0,
|
|
50
|
+
"led_array": [
|
|
51
|
+
{"id":0,"r":...,"g":...,"b":...},
|
|
52
|
+
...
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
"""
|
|
57
|
+
path = "/ledarr_act"
|
|
58
|
+
|
|
59
|
+
# If led_pattern is a NumPy array, convert it to a list of dict
|
|
60
|
+
if not isinstance(led_pattern, list):
|
|
61
|
+
if len(led_pattern.shape) == 3:
|
|
62
|
+
# flatten 3D into 2D
|
|
63
|
+
led_pattern = np.reshape(led_pattern, (led_pattern.shape[0]*led_pattern.shape[1], led_pattern.shape[2]))
|
|
64
|
+
|
|
65
|
+
pattern_list = []
|
|
66
|
+
for i in range(led_pattern.shape[0]):
|
|
67
|
+
pattern_list.append({
|
|
68
|
+
"id": i,
|
|
69
|
+
"r": int(led_pattern[i, 0]),
|
|
70
|
+
"g": int(led_pattern[i, 1]),
|
|
71
|
+
"b": int(led_pattern[i, 2])
|
|
72
|
+
})
|
|
73
|
+
else:
|
|
74
|
+
pattern_list = led_pattern
|
|
75
|
+
|
|
76
|
+
payload = {
|
|
77
|
+
"task": path,
|
|
78
|
+
"qid": 0, # or fill in dynamically if you like
|
|
79
|
+
"led": {
|
|
80
|
+
"action": "array",
|
|
81
|
+
"LEDArrMode": self.ledArrayModes["array"],
|
|
82
|
+
"led_array": pattern_list
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
87
|
+
if not getReturn or timeout == 0:
|
|
88
|
+
r = {"success": 1}
|
|
89
|
+
self.currentLedArrayMode = "array"
|
|
90
|
+
return r
|
|
91
|
+
|
|
92
|
+
def send_LEDMatrix_full(self, intensity=(255, 255, 255), getReturn=True, timeout=gTimeout):
|
|
93
|
+
"""
|
|
94
|
+
Fill all LEDs with the same (r,g,b).
|
|
95
|
+
Creates JSON with: "action": "fill"
|
|
96
|
+
"""
|
|
97
|
+
path = "/ledarr_act"
|
|
98
|
+
payload = {
|
|
99
|
+
"task": path,
|
|
100
|
+
"qid": 0,
|
|
101
|
+
"led": {
|
|
102
|
+
"action": "fill",
|
|
103
|
+
"LEDArrMode": self.ledArrayModes["full"],
|
|
104
|
+
"r": int(intensity[0]),
|
|
105
|
+
"g": int(intensity[1]),
|
|
106
|
+
"b": int(intensity[2])
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
111
|
+
if not getReturn or timeout == 0:
|
|
112
|
+
r = {"success": 1}
|
|
113
|
+
self.currentLedArrayMode = "full"
|
|
114
|
+
return r
|
|
115
|
+
|
|
116
|
+
def send_LEDMatrix_single(self, indexled=0, intensity=(255, 255, 255), getReturn=True, timeout=gTimeout):
|
|
117
|
+
"""
|
|
118
|
+
Update only a single LED with color (r,g,b).
|
|
119
|
+
Creates JSON: "action":"single"
|
|
120
|
+
"""
|
|
121
|
+
path = "/ledarr_act"
|
|
122
|
+
payload = {
|
|
123
|
+
"task": path,
|
|
124
|
+
"qid": 0,
|
|
125
|
+
"led": {
|
|
126
|
+
"action": "single",
|
|
127
|
+
"LEDArrMode": self.ledArrayModes["single"],
|
|
128
|
+
"ledIndex": int(indexled),
|
|
129
|
+
"r": int(intensity[0]),
|
|
130
|
+
"g": int(intensity[1]),
|
|
131
|
+
"b": int(intensity[2])
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
136
|
+
if not getReturn or timeout == 0:
|
|
137
|
+
r = {"success": 1}
|
|
138
|
+
self.currentLedArrayMode = "single"
|
|
139
|
+
return r
|
|
140
|
+
|
|
141
|
+
def get_LEDMatrix(self, timeout=gTimeout):
|
|
142
|
+
"""
|
|
143
|
+
Request info from the device about pin/LED count, etc.
|
|
144
|
+
Usually the device responds with JSON containing these details.
|
|
145
|
+
"""
|
|
146
|
+
path = "/ledarr_get"
|
|
147
|
+
payload = {"task": path}
|
|
148
|
+
r = self._parent.post_json(path, payload, getReturn=True, timeout=timeout)
|
|
149
|
+
return r
|
|
150
|
+
|
|
151
|
+
def setSingle(self, indexled, state):
|
|
152
|
+
"""
|
|
153
|
+
High-level usage: set a single LED's "state" (0 or 1), then send it with intensity scaling.
|
|
154
|
+
"""
|
|
155
|
+
ix = indexled // self.Nx
|
|
156
|
+
iy = indexled % self.Nx
|
|
157
|
+
self.ledpattern[indexled] = (state, state, state)
|
|
158
|
+
# If your wiring is zig-zag or reversed, you may need to adapt index
|
|
159
|
+
if ix % 2 != 0:
|
|
160
|
+
indexled = (ix * self.Nx) + (self.Ny - iy - 1)
|
|
161
|
+
|
|
162
|
+
intensityScaled = np.array(state) * np.array(self.intensity)
|
|
163
|
+
self.send_LEDMatrix_single(indexled=indexled, intensity=intensityScaled, timeout=self.timeout)
|
|
164
|
+
return self.ledpattern
|
|
165
|
+
|
|
166
|
+
def setAll(self, state: int, intensity:int =None, getReturn=True):
|
|
167
|
+
"""
|
|
168
|
+
Turn on or off all LEDs at a certain intensity.
|
|
169
|
+
If state is a boolean array or a single boolean, we set them all.
|
|
170
|
+
"""
|
|
171
|
+
onval = np.sum(state) > 0
|
|
172
|
+
if intensity is not None:
|
|
173
|
+
self.intensity = intensity
|
|
174
|
+
intensity2display = np.array(self.intensity) * onval
|
|
175
|
+
self.send_LEDMatrix_full(intensity=intensity2display, getReturn=getReturn)
|
|
176
|
+
self.ledpattern = onval * np.ones((self.NLeds, 3))
|
|
177
|
+
return self.ledpattern
|
|
178
|
+
|
|
179
|
+
def setIntensity(self, intensity:int, getReturn:bool=True):
|
|
180
|
+
self.intensity = intensity
|
|
181
|
+
self.setPattern(getReturn=getReturn)
|
|
182
|
+
|
|
183
|
+
def setPattern(self, getReturn:bool=True, ledpattern=None):
|
|
184
|
+
"""
|
|
185
|
+
If ledpattern is provided, store it, then send to device.
|
|
186
|
+
If the entire pattern is "on", we do a 'full' fill for speed,
|
|
187
|
+
otherwise we do 'array' updates for each LED.
|
|
188
|
+
"""
|
|
189
|
+
if ledpattern is not None:
|
|
190
|
+
self.ledpattern = ledpattern
|
|
191
|
+
pattern2send = (self.ledpattern >= 1) * self.intensity
|
|
192
|
+
|
|
193
|
+
if np.sum(self.ledpattern, 0)[0] == self.ledpattern.shape[0]:
|
|
194
|
+
# All on => just do a fill
|
|
195
|
+
self.send_LEDMatrix_full(pattern2send[0, :], getReturn=getReturn)
|
|
196
|
+
else:
|
|
197
|
+
# partial => send an array of per-LED
|
|
198
|
+
self.send_LEDMatrix_array(pattern2send, getReturn=getReturn)
|
|
199
|
+
return self.ledpattern
|
|
200
|
+
|
|
201
|
+
def getPattern(self):
|
|
202
|
+
return self.ledpattern
|
|
203
|
+
|
|
204
|
+
def set_led(self, colour=(0, 0, 0)):
|
|
205
|
+
"""
|
|
206
|
+
(Legacy) Possibly sets a single color.
|
|
207
|
+
Demo only - not used in the new command style.
|
|
208
|
+
"""
|
|
209
|
+
path = "/led"
|
|
210
|
+
payload = {"r": colour[0], "g": colour[1], "b": colour[2]}
|
|
211
|
+
r = self._parent.post_json(path, payload)
|
|
212
|
+
return r
|
|
213
|
+
|
|
214
|
+
def get_ledpin(self):
|
|
215
|
+
"""
|
|
216
|
+
Another legacy method that might do a GET request.
|
|
217
|
+
"""
|
|
218
|
+
path = "/ledarr_get"
|
|
219
|
+
r = self._parent.get_json(path, getReturn=True, timeout=1)
|
|
220
|
+
return r
|
|
221
|
+
|
|
222
|
+
######################################################################################################
|
|
223
|
+
# NEW METHODS FOR "OFF", "RINGS", "CIRCLES", "HALVES", etc.
|
|
224
|
+
######################################################################################################
|
|
225
|
+
def send_LEDMatrix_off(self, getReturn=True, timeout=gTimeout):
|
|
226
|
+
"""
|
|
227
|
+
Turn all LEDs off.
|
|
228
|
+
JSON: "action":"off"
|
|
229
|
+
"""
|
|
230
|
+
path = "/ledarr_act"
|
|
231
|
+
payload = {
|
|
232
|
+
"task": path,
|
|
233
|
+
"qid": 0,
|
|
234
|
+
"led": {
|
|
235
|
+
"action": "off"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
239
|
+
return r
|
|
240
|
+
|
|
241
|
+
def send_LEDMatrix_halves(self, region="left", intensity=(255,255,255), getReturn=True, timeout=gTimeout):
|
|
242
|
+
"""
|
|
243
|
+
Light only "left"/"right"/"top"/"bottom" half in color, rest is dark.
|
|
244
|
+
JSON: "action":"halves", "region": <string>
|
|
245
|
+
"""
|
|
246
|
+
if type(intensity)==int:
|
|
247
|
+
intensity = (intensity, intensity, intensity)
|
|
248
|
+
path = "/ledarr_act"
|
|
249
|
+
payload = {
|
|
250
|
+
"task": path,
|
|
251
|
+
"qid": 0,
|
|
252
|
+
"led": {
|
|
253
|
+
"action": "halves",
|
|
254
|
+
"region": region,
|
|
255
|
+
"r": int(intensity[0]),
|
|
256
|
+
"g": int(intensity[1]),
|
|
257
|
+
"b": int(intensity[2])
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
261
|
+
return r
|
|
262
|
+
|
|
263
|
+
def send_LEDMatrix_rings(self, radius=4, intensity=(255,255,255), getReturn=True, timeout=gTimeout):
|
|
264
|
+
"""
|
|
265
|
+
Draw a ring of radius N. Often done by filling the circle and carving out center to make it hollow.
|
|
266
|
+
JSON: "action":"rings", "radius": <int>
|
|
267
|
+
"""
|
|
268
|
+
if type(intensity)==int:
|
|
269
|
+
intensity = (intensity, intensity, intensity)
|
|
270
|
+
path = "/ledarr_act"
|
|
271
|
+
payload = {
|
|
272
|
+
"task": path,
|
|
273
|
+
"qid": 0,
|
|
274
|
+
"led": {
|
|
275
|
+
"action": "rings",
|
|
276
|
+
"radius": radius,
|
|
277
|
+
"r": int(intensity[0]),
|
|
278
|
+
"g": int(intensity[1]),
|
|
279
|
+
"b": int(intensity[2])
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
283
|
+
return r
|
|
284
|
+
|
|
285
|
+
def send_LEDMatrix_circles(self, radius=4, intensity=(255,255,255), getReturn=True, timeout=gTimeout):
|
|
286
|
+
"""
|
|
287
|
+
Draw a filled circle of radius N in color.
|
|
288
|
+
JSON: "action":"circles", "radius": <int>
|
|
289
|
+
"""
|
|
290
|
+
if type(intensity)==int:
|
|
291
|
+
intensity = (intensity, intensity, intensity)
|
|
292
|
+
path = "/ledarr_act"
|
|
293
|
+
payload = {
|
|
294
|
+
"task": path,
|
|
295
|
+
"qid": 0,
|
|
296
|
+
"led": {
|
|
297
|
+
"action": "circles",
|
|
298
|
+
"radius": radius,
|
|
299
|
+
"r": int(intensity[0]),
|
|
300
|
+
"g": int(intensity[1]),
|
|
301
|
+
"b": int(intensity[2])
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
r = self._parent.post_json(path, payload, getReturn=getReturn, timeout=timeout)
|
|
305
|
+
return r
|
|
306
|
+
|
|
307
|
+
def send_LEDMatrix_status(self, status="idle"):
|
|
308
|
+
"""
|
|
309
|
+
Set the status of the LED matrix to "idle" or "busy".
|
|
310
|
+
JSON: "action":"status", "status": <string>
|
|
311
|
+
"""
|
|
312
|
+
if status not in ["error", "idle", "warn", "success", "busy", "rainbow"]:
|
|
313
|
+
status = "idle"
|
|
314
|
+
path = "/ledarr_act"
|
|
315
|
+
payload = {
|
|
316
|
+
"task": path,
|
|
317
|
+
"qid": 0,
|
|
318
|
+
"led": {
|
|
319
|
+
"action": "status",
|
|
320
|
+
"status": status
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
r = self._parent.post_json(path, payload)
|
|
324
|
+
return r
|