bluer-sbc 8.3.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.
Potentially problematic release.
This version of bluer-sbc might be problematic. Click here for more details.
- bluer_sbc/.abcli/abcli.sh +12 -0
- bluer_sbc/.abcli/actions.sh +11 -0
- bluer_sbc/.abcli/adafruit_rgb_matrix.sh +14 -0
- bluer_sbc/.abcli/alias.sh +5 -0
- bluer_sbc/.abcli/blue_sbc.sh +11 -0
- bluer_sbc/.abcli/camera.sh +20 -0
- bluer_sbc/.abcli/grove.sh +43 -0
- bluer_sbc/.abcli/hat.sh +22 -0
- bluer_sbc/.abcli/install/adafruit_rgb_matrix.sh +15 -0
- bluer_sbc/.abcli/install/grove.sh +29 -0
- bluer_sbc/.abcli/install/lepton.sh +33 -0
- bluer_sbc/.abcli/install/rpi.sh +65 -0
- bluer_sbc/.abcli/install/scroll_phat_hd.sh +14 -0
- bluer_sbc/.abcli/install/sparkfun_top_phat.sh +33 -0
- bluer_sbc/.abcli/install/template.sh +9 -0
- bluer_sbc/.abcli/install/unicorn_16x16.sh +16 -0
- bluer_sbc/.abcli/lepton.sh +15 -0
- bluer_sbc/.abcli/scroll_phat_hd.sh +14 -0
- bluer_sbc/.abcli/session.sh +39 -0
- bluer_sbc/.abcli/sparkfun_top_phat.sh +27 -0
- bluer_sbc/.abcli/tests/README.sh +8 -0
- bluer_sbc/.abcli/tests/camera.sh +47 -0
- bluer_sbc/.abcli/tests/help.sh +65 -0
- bluer_sbc/.abcli/tests/version.sh +8 -0
- bluer_sbc/.abcli/unicorn_16x16.sh +14 -0
- bluer_sbc/README.py +51 -0
- bluer_sbc/__init__.py +17 -0
- bluer_sbc/__main__.py +16 -0
- bluer_sbc/algo/__init__.py +0 -0
- bluer_sbc/algo/diff.py +81 -0
- bluer_sbc/config.env +30 -0
- bluer_sbc/env.py +35 -0
- bluer_sbc/hardware/__init__.py +38 -0
- bluer_sbc/hardware/adafruit_rgb_matrix.py +30 -0
- bluer_sbc/hardware/display.py +112 -0
- bluer_sbc/hardware/grove.py +104 -0
- bluer_sbc/hardware/hardware.py +58 -0
- bluer_sbc/hardware/hat/__init__.py +0 -0
- bluer_sbc/hardware/hat/__main__.py +91 -0
- bluer_sbc/hardware/hat/abstract.py +136 -0
- bluer_sbc/hardware/hat/prototype.py +161 -0
- bluer_sbc/hardware/screen.py +17 -0
- bluer_sbc/hardware/scroll_phat_hd.py +35 -0
- bluer_sbc/hardware/sparkfun_top_phat/__init__.py +0 -0
- bluer_sbc/hardware/sparkfun_top_phat/__main__.py +51 -0
- bluer_sbc/hardware/sparkfun_top_phat/classes.py +104 -0
- bluer_sbc/hardware/unicorn_16x16.py +44 -0
- bluer_sbc/help/__init__.py +0 -0
- bluer_sbc/help/__main__.py +10 -0
- bluer_sbc/help/adafruit_rgb_matrix.py +23 -0
- bluer_sbc/help/camera.py +71 -0
- bluer_sbc/help/functions.py +52 -0
- bluer_sbc/help/grove.py +59 -0
- bluer_sbc/help/hat.py +56 -0
- bluer_sbc/help/lepton.py +39 -0
- bluer_sbc/help/scroll_phat_hd.py +23 -0
- bluer_sbc/help/session.py +26 -0
- bluer_sbc/help/sparkfun_top_phat.py +26 -0
- bluer_sbc/help/unicorn_16x16.py +23 -0
- bluer_sbc/host.py +11 -0
- bluer_sbc/imager/__init__.py +16 -0
- bluer_sbc/imager/camera/__init__.py +3 -0
- bluer_sbc/imager/camera/__main__.py +69 -0
- bluer_sbc/imager/camera/classes.py +259 -0
- bluer_sbc/imager/camera/constants.py +30 -0
- bluer_sbc/imager/classes.py +25 -0
- bluer_sbc/imager/lepton/__init__.py +3 -0
- bluer_sbc/imager/lepton/__main__.py +51 -0
- bluer_sbc/imager/lepton/classes.py +35 -0
- bluer_sbc/imager/lepton/python2.py +70 -0
- bluer_sbc/logger.py +5 -0
- bluer_sbc/sample.env +1 -0
- bluer_sbc/session/__init__.py +0 -0
- bluer_sbc/session/__main__.py +27 -0
- bluer_sbc/session/classes.py +318 -0
- bluer_sbc/session/functions.py +22 -0
- bluer_sbc/urls.py +1 -0
- bluer_sbc-8.3.1.dist-info/METADATA +58 -0
- bluer_sbc-8.3.1.dist-info/RECORD +82 -0
- bluer_sbc-8.3.1.dist-info/WHEEL +5 -0
- bluer_sbc-8.3.1.dist-info/licenses/LICENSE +121 -0
- bluer_sbc-8.3.1.dist-info/top_level.txt +1 -0
bluer_sbc/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
NAME = "bluer_sbc"
|
|
2
|
+
|
|
3
|
+
ICON = "🌀"
|
|
4
|
+
|
|
5
|
+
DESCRIPTION = f"{ICON} AI for single board computers."
|
|
6
|
+
|
|
7
|
+
VERSION = "8.3.1"
|
|
8
|
+
|
|
9
|
+
REPO_NAME = "bluer-sbc"
|
|
10
|
+
|
|
11
|
+
MARQUEE = "https://github.com/kamangir/blue-bracket/blob/main/images/helmet-3.jpg"
|
|
12
|
+
|
|
13
|
+
ALIAS = "@sbc"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def fullname() -> str:
|
|
17
|
+
return f"{NAME}-{VERSION}"
|
bluer_sbc/__main__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from blueness.argparse.generic import main
|
|
2
|
+
|
|
3
|
+
from bluer_sbc import NAME, VERSION, DESCRIPTION, ICON, README
|
|
4
|
+
from bluer_sbc.logger import logger
|
|
5
|
+
|
|
6
|
+
main(
|
|
7
|
+
ICON=ICON,
|
|
8
|
+
NAME=NAME,
|
|
9
|
+
DESCRIPTION=DESCRIPTION,
|
|
10
|
+
VERSION=VERSION,
|
|
11
|
+
main_filename=__file__,
|
|
12
|
+
tasks={
|
|
13
|
+
"build_README": lambda _: README.build(),
|
|
14
|
+
},
|
|
15
|
+
logger=logger,
|
|
16
|
+
)
|
|
File without changes
|
bluer_sbc/algo/diff.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
import numpy as np
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from blueness import module
|
|
6
|
+
from bluer_options import string
|
|
7
|
+
from bluer_options.logger import crash_report
|
|
8
|
+
|
|
9
|
+
from bluer_sbc import NAME
|
|
10
|
+
from bluer_sbc.logger import logger
|
|
11
|
+
|
|
12
|
+
NAME = module.name(__file__, NAME)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Diff:
|
|
16
|
+
def __init__(self, threshold=0.1):
|
|
17
|
+
self.size = (60, 80)
|
|
18
|
+
self.threshold = threshold
|
|
19
|
+
|
|
20
|
+
self.last_diff = 0.0
|
|
21
|
+
self.last_same_period = 0.0
|
|
22
|
+
self.last_diff_time = time.time()
|
|
23
|
+
|
|
24
|
+
self.previous = None
|
|
25
|
+
|
|
26
|
+
def same(self, image):
|
|
27
|
+
if self.threshold < 0:
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
image_scaled = cv2.resize(image, self.size)
|
|
31
|
+
|
|
32
|
+
if self.previous is None:
|
|
33
|
+
self.previous = image_scaled
|
|
34
|
+
self.last_diff_time = time.time()
|
|
35
|
+
|
|
36
|
+
logger.info(
|
|
37
|
+
f"{NAME}.diff.same({string.pretty_shape_of_matrix(image)}): initialized."
|
|
38
|
+
)
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
self.last_diff = float(
|
|
43
|
+
np.percentile(
|
|
44
|
+
np.abs(
|
|
45
|
+
image_scaled.astype(np.float) - self.previous.astype(np.float)
|
|
46
|
+
),
|
|
47
|
+
90,
|
|
48
|
+
)
|
|
49
|
+
/ 255
|
|
50
|
+
)
|
|
51
|
+
is_same = self.last_diff <= self.threshold
|
|
52
|
+
|
|
53
|
+
logger.info(
|
|
54
|
+
"{}.diff.same({}): {:.03f} - {}{}".format(
|
|
55
|
+
NAME,
|
|
56
|
+
string.pretty_shape_of_matrix(image),
|
|
57
|
+
self.last_diff,
|
|
58
|
+
("!same,same".split(","))[int(is_same)],
|
|
59
|
+
" - {} since last diff.".format(
|
|
60
|
+
string.pretty_duration(
|
|
61
|
+
time.time() - self.last_diff_time,
|
|
62
|
+
include_ms=True,
|
|
63
|
+
largest=True,
|
|
64
|
+
short=True,
|
|
65
|
+
)
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if not is_same:
|
|
71
|
+
self.last_same_period = time.time() - self.last_diff_time
|
|
72
|
+
self.last_diff_time = time.time()
|
|
73
|
+
|
|
74
|
+
self.previous = image_scaled
|
|
75
|
+
|
|
76
|
+
return is_same
|
|
77
|
+
except Exception as e:
|
|
78
|
+
crash_report(e)
|
|
79
|
+
|
|
80
|
+
self.previous = None
|
|
81
|
+
return False
|
bluer_sbc/config.env
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
BLUER_SBC_CAMERA_HI_RES=1
|
|
2
|
+
BLUER_SBC_CAMERA_WIDTH=728
|
|
3
|
+
BLUER_SBC_CAMERA_HEIGHT=600
|
|
4
|
+
BLUER_SBC_CAMERA_ROTATION=0
|
|
5
|
+
|
|
6
|
+
BLUER_SBC_DISPLAY_FULLSCREEN=1
|
|
7
|
+
|
|
8
|
+
BLUER_SBC_HARDWARE_KIND=other
|
|
9
|
+
|
|
10
|
+
BLUER_SBC_SESSION_OBJECT_TAGS=validation
|
|
11
|
+
|
|
12
|
+
BLUER_SBC_SESSION_PLUGIN=bluer_sbc
|
|
13
|
+
|
|
14
|
+
BLUER_SBC_SESSION_IMAGER=other
|
|
15
|
+
|
|
16
|
+
BLUER_SBC_SESSION_IMAGER_DIFF=0.1
|
|
17
|
+
BLUER_SBC_SESSION_IMAGER_ENABLED=1
|
|
18
|
+
|
|
19
|
+
BLUER_SBC_SESSION_MONITOR_ENABLED=1
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
BLUER_SBC_SESSION_OUTBOUND_QUEUE=stream
|
|
23
|
+
|
|
24
|
+
BLUER_SBC_SESSION_AUTO_UPLOAD=1
|
|
25
|
+
|
|
26
|
+
BLUER_SBC_SESSION_IMAGER_PERIOD=300
|
|
27
|
+
BLUER_SBC_SESSION_MESSENGER_PERIOD=60
|
|
28
|
+
BLUER_SBC_SESSION_REBOOT_PERIOD=14400
|
|
29
|
+
BLUER_SBC_SESSION_SCREEN_PERIOD=4
|
|
30
|
+
BLUER_SBC_SESSION_TEMPERATURE_PERIOD=300
|
bluer_sbc/env.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from bluer_options.env import load_config, load_env, get_env
|
|
2
|
+
|
|
3
|
+
load_config(__name__)
|
|
4
|
+
load_env(__name__)
|
|
5
|
+
|
|
6
|
+
BLUER_SBC_CAMERA_HI_RES = get_env("BLUER_SBC_CAMERA_HI_RES", True)
|
|
7
|
+
BLUER_SBC_CAMERA_WIDTH = get_env("BLUER_SBC_CAMERA_WIDTH", 728)
|
|
8
|
+
BLUER_SBC_CAMERA_HEIGHT = get_env("BLUER_SBC_CAMERA_HEIGHT", 600)
|
|
9
|
+
BLUER_SBC_CAMERA_ROTATION = get_env("BLUER_SBC_CAMERA_ROTATION", 0)
|
|
10
|
+
|
|
11
|
+
BLUER_SBC_DISPLAY_FULLSCREEN = get_env("BLUER_SBC_DISPLAY_FULLSCREEN", True)
|
|
12
|
+
|
|
13
|
+
BLUER_SBC_HARDWARE_KIND = get_env("BLUER_SBC_HARDWARE_KIND")
|
|
14
|
+
|
|
15
|
+
BLUER_SBC_SESSION_PLUGIN = get_env("BLUER_SBC_SESSION_PLUGIN")
|
|
16
|
+
|
|
17
|
+
BLUER_SBC_SESSION_IMAGER = get_env("BLUER_SBC_SESSION_IMAGER")
|
|
18
|
+
BLUER_SBC_SESSION_IMAGER_DIFF = get_env("BLUER_SBC_SESSION_IMAGER_DIFF", 0.1)
|
|
19
|
+
BLUER_SBC_SESSION_IMAGER_ENABLED = get_env("BLUER_SBC_SESSION_IMAGER_ENABLED", True)
|
|
20
|
+
|
|
21
|
+
BLUER_SBC_SESSION_MONITOR_ENABLED = get_env("BLUER_SBC_SESSION_MONITOR_ENABLED", True)
|
|
22
|
+
|
|
23
|
+
BLUER_SBC_SESSION_OBJECT_TAGS = get_env("BLUER_SBC_SESSION_OBJECT_TAGS")
|
|
24
|
+
|
|
25
|
+
BLUER_SBC_SESSION_OUTBOUND_QUEUE = get_env("BLUER_SBC_SESSION_OUTBOUND_QUEUE")
|
|
26
|
+
|
|
27
|
+
BLUER_SBC_SESSION_AUTO_UPLOAD = get_env("BLUER_SBC_SESSION_AUTO_UPLOAD", True)
|
|
28
|
+
|
|
29
|
+
BLUER_SBC_SESSION_IMAGER_PERIOD = get_env("BLUER_SBC_SESSION_IMAGER_PERIOD", 300)
|
|
30
|
+
BLUER_SBC_SESSION_MESSENGER_PERIOD = get_env("BLUER_SBC_SESSION_MESSENGER_PERIOD", 60)
|
|
31
|
+
BLUER_SBC_SESSION_REBOOT_PERIOD = get_env("BLUER_SBC_SESSION_REBOOT_PERIOD", 14400)
|
|
32
|
+
BLUER_SBC_SESSION_SCREEN_PERIOD = get_env("BLUER_SBC_SESSION_SCREEN_PERIOD", 4)
|
|
33
|
+
BLUER_SBC_SESSION_TEMPERATURE_PERIOD = get_env(
|
|
34
|
+
"BLUER_SBC_SESSION_TEMPERATURE_PERIOD", 300
|
|
35
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from blueness import module
|
|
2
|
+
from bluer_options import host
|
|
3
|
+
|
|
4
|
+
from bluer_sbc import NAME
|
|
5
|
+
from bluer_sbc.env import BLUER_SBC_HARDWARE_KIND
|
|
6
|
+
from bluer_sbc.hardware.hardware import Hardware as Hardware_Class
|
|
7
|
+
from bluer_sbc.logger import logger
|
|
8
|
+
|
|
9
|
+
NAME = module.name(__file__, NAME)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if host.is_mac():
|
|
13
|
+
from bluer_sbc.hardware.display import Display as Hardware_Class
|
|
14
|
+
elif BLUER_SBC_HARDWARE_KIND == "adafruit_rgb_matrix":
|
|
15
|
+
from bluer_sbc.hardware.adafruit_rgb_matrix import (
|
|
16
|
+
Adafruit_Rgb_Matrix as Hardware_Class,
|
|
17
|
+
)
|
|
18
|
+
elif BLUER_SBC_HARDWARE_KIND == "grove":
|
|
19
|
+
from bluer_sbc.hardware.grove import Grove as Hardware_Class
|
|
20
|
+
elif BLUER_SBC_HARDWARE_KIND == "prototype_hat":
|
|
21
|
+
if host.is_headless():
|
|
22
|
+
from bluer_sbc.hardware.hat.prototype import Prototype_Hat as Hardware_Class
|
|
23
|
+
else:
|
|
24
|
+
from bluer_sbc.hardware.display import Display as Hardware_Class
|
|
25
|
+
elif BLUER_SBC_HARDWARE_KIND == "scroll_phat_hd":
|
|
26
|
+
from bluer_sbc.hardware.scroll_phat_hd import Scroll_Phat_HD as Hardware_Class
|
|
27
|
+
elif BLUER_SBC_HARDWARE_KIND == "sparkfun-top-phat":
|
|
28
|
+
from bluer_sbc.hardware.sparkfun_top_phat.classes import (
|
|
29
|
+
Sparkfun_Top_phat as Hardware_Class,
|
|
30
|
+
)
|
|
31
|
+
elif BLUER_SBC_HARDWARE_KIND == "unicorn_16x16":
|
|
32
|
+
from bluer_sbc.hardware.unicorn_16x16 import Unicorn_16x16 as Hardware_Class
|
|
33
|
+
else:
|
|
34
|
+
raise NameError(f"bluer-sbc: {BLUER_SBC_HARDWARE_KIND}: hardware not found.")
|
|
35
|
+
|
|
36
|
+
hardware = Hardware_Class()
|
|
37
|
+
|
|
38
|
+
logger.info(f"{NAME}: {BLUER_SBC_HARDWARE_KIND}: {hardware.__class__.__name__}")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
from PIL import Image
|
|
3
|
+
|
|
4
|
+
from bluer_sbc.hardware.screen import Screen
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Adafruit_Rgb_Matrix(Screen):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super(Adafruit_Rgb_Matrix, self).__init__()
|
|
10
|
+
self.size = (32, 32)
|
|
11
|
+
self.animated = True
|
|
12
|
+
|
|
13
|
+
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
|
14
|
+
|
|
15
|
+
options = RGBMatrixOptions()
|
|
16
|
+
options.rows = 32
|
|
17
|
+
options.chain_length = 1
|
|
18
|
+
options.parallel = 1
|
|
19
|
+
options.hardware_mapping = "adafruit-hat" # adafruit-hat/regular
|
|
20
|
+
|
|
21
|
+
self.matrix = RGBMatrix(options=options)
|
|
22
|
+
|
|
23
|
+
def update_screen(self, image, session, header):
|
|
24
|
+
image = cv2.resize(image, self.size)
|
|
25
|
+
|
|
26
|
+
super().update_screen(image, session, header)
|
|
27
|
+
|
|
28
|
+
self.matrix.SetImage(Image.fromarray(image).convert("RGB"))
|
|
29
|
+
|
|
30
|
+
return self
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from blueness import module
|
|
5
|
+
from bluer_options.logger import crash_report
|
|
6
|
+
from bluer_options.host import is_mac
|
|
7
|
+
from bluer_objects import file
|
|
8
|
+
from bluer_objects.graphics import add_signature
|
|
9
|
+
from bluer_objects.graphics.screen import get_size
|
|
10
|
+
|
|
11
|
+
from bluer_sbc import NAME
|
|
12
|
+
from bluer_sbc import env
|
|
13
|
+
from bluer_sbc.hardware.hat.prototype import Prototype_Hat
|
|
14
|
+
from bluer_sbc.host import signature
|
|
15
|
+
from bluer_sbc.logger import logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
NAME = module.name(__file__, NAME)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Display(Prototype_Hat):
|
|
22
|
+
def __init__(self):
|
|
23
|
+
super().__init__()
|
|
24
|
+
|
|
25
|
+
self.canvas = None
|
|
26
|
+
self.canvas_size = (640, 480)
|
|
27
|
+
|
|
28
|
+
self.title = " | ".join(signature())
|
|
29
|
+
|
|
30
|
+
self.created = False
|
|
31
|
+
|
|
32
|
+
self.sign_images = True
|
|
33
|
+
self.interpolation = cv2.INTER_LINEAR
|
|
34
|
+
|
|
35
|
+
def create(self):
|
|
36
|
+
if self.created:
|
|
37
|
+
return
|
|
38
|
+
self.created = True
|
|
39
|
+
|
|
40
|
+
logger.info(f"{NAME}.create()")
|
|
41
|
+
|
|
42
|
+
if env.BLUER_SBC_DISPLAY_FULLSCREEN and not is_mac():
|
|
43
|
+
# https://stackoverflow.com/a/34337534
|
|
44
|
+
cv2.namedWindow(self.title, cv2.WND_PROP_FULLSCREEN)
|
|
45
|
+
cv2.setWindowProperty(
|
|
46
|
+
self.title, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
screen_height, screen_width = get_size()
|
|
50
|
+
self.canvas_size = (
|
|
51
|
+
screen_width,
|
|
52
|
+
screen_height,
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
cv2.namedWindow(self.title, cv2.WINDOW_NORMAL)
|
|
56
|
+
cv2.resizeWindow(
|
|
57
|
+
self.title,
|
|
58
|
+
self.canvas_size[0],
|
|
59
|
+
self.canvas_size[1],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def save(self, filename: str = "") -> str:
|
|
63
|
+
if self.canvas is None:
|
|
64
|
+
return ""
|
|
65
|
+
|
|
66
|
+
if not filename:
|
|
67
|
+
filename = file.auxiliary("display", "jpg")
|
|
68
|
+
|
|
69
|
+
return filename if file.save_image(filename, self.canvas) else ""
|
|
70
|
+
|
|
71
|
+
def update_gui(self):
|
|
72
|
+
try:
|
|
73
|
+
if len(self.canvas.shape) == 2:
|
|
74
|
+
self.canvas = np.stack(3 * [self.canvas], axis=2)
|
|
75
|
+
|
|
76
|
+
cv2.imshow(
|
|
77
|
+
self.title,
|
|
78
|
+
cv2.cvtColor(
|
|
79
|
+
cv2.resize(
|
|
80
|
+
self.canvas,
|
|
81
|
+
dsize=self.canvas_size,
|
|
82
|
+
interpolation=self.interpolation,
|
|
83
|
+
),
|
|
84
|
+
cv2.COLOR_BGR2RGB,
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
crash_report(e)
|
|
89
|
+
|
|
90
|
+
def update_screen(self, image, session, header):
|
|
91
|
+
super().update_screen(image, session, header)
|
|
92
|
+
|
|
93
|
+
self.canvas = np.copy(image)
|
|
94
|
+
|
|
95
|
+
if self.sign_images:
|
|
96
|
+
self.canvas = add_signature(
|
|
97
|
+
self.canvas,
|
|
98
|
+
header=header,
|
|
99
|
+
footer=[" | ".join(signature())],
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
self.create()
|
|
103
|
+
|
|
104
|
+
self.update_gui()
|
|
105
|
+
|
|
106
|
+
key = cv2.waitKey(1)
|
|
107
|
+
if key not in [-1, 255]:
|
|
108
|
+
key = chr(key).lower()
|
|
109
|
+
logger.info(f"{NAME}.update_screen(): key: '{key}'")
|
|
110
|
+
self.key_buffer.append(key)
|
|
111
|
+
|
|
112
|
+
return self
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from PIL import Image
|
|
2
|
+
from PIL import ImageDraw
|
|
3
|
+
from PIL import ImageFont
|
|
4
|
+
|
|
5
|
+
from bluer_options import string
|
|
6
|
+
|
|
7
|
+
from bluer_sbc.hardware.screen import Screen
|
|
8
|
+
from bluer_sbc.logger import logger
|
|
9
|
+
|
|
10
|
+
BUTTON = 24
|
|
11
|
+
|
|
12
|
+
RST = None # on the PiOLED this pin isnt used
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Grove(Screen):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super(Grove, self).__init__()
|
|
18
|
+
|
|
19
|
+
from grove.grove_button import GroveButton
|
|
20
|
+
|
|
21
|
+
# https://wiki.seeedstudio.com/Grove-OLED_Display_0.96inch/
|
|
22
|
+
self.size = (64, 128)
|
|
23
|
+
|
|
24
|
+
self.button = GroveButton(BUTTON)
|
|
25
|
+
self.button.on_press = lambda t: grove_button_on_press(self, t)
|
|
26
|
+
self.button.on_release = lambda t: grove_button_on_release(self, t)
|
|
27
|
+
|
|
28
|
+
import Adafruit_SSD1306
|
|
29
|
+
|
|
30
|
+
# https://github.com/IcingTomato/Seeed_Python_SSD1315/blob/master/examples/stats.py
|
|
31
|
+
self.display = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
|
|
32
|
+
|
|
33
|
+
self.display.begin()
|
|
34
|
+
self.display.clear()
|
|
35
|
+
self.display.display()
|
|
36
|
+
|
|
37
|
+
self.image = Image.new(
|
|
38
|
+
"1",
|
|
39
|
+
(self.display.width, self.display.height),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.draw = ImageDraw.Draw(self.image)
|
|
43
|
+
|
|
44
|
+
# Draw a black filled box to clear the image.
|
|
45
|
+
self.draw.rectangle(
|
|
46
|
+
(0, 0, self.display.width, self.display.height),
|
|
47
|
+
outline=0,
|
|
48
|
+
fill=0,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
self.padding = -2
|
|
52
|
+
self.top = self.padding
|
|
53
|
+
self.bottom = self.display.height - self.padding
|
|
54
|
+
|
|
55
|
+
self.font = ImageFont.load_default()
|
|
56
|
+
|
|
57
|
+
self.line_count = 8
|
|
58
|
+
self.line_length = 21
|
|
59
|
+
|
|
60
|
+
def update_screen(self, image, session, header):
|
|
61
|
+
super().update_screen(image, session, header)
|
|
62
|
+
|
|
63
|
+
signature = (" | ".join(session.signature())).split(" | ")
|
|
64
|
+
|
|
65
|
+
self.draw.rectangle(
|
|
66
|
+
(0, 0, self.display.width, self.display.height),
|
|
67
|
+
outline=0,
|
|
68
|
+
fill=0,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
for row in range(min(len(signature), self.line_count)):
|
|
72
|
+
self.draw.text(
|
|
73
|
+
(0, self.top + 8 * row),
|
|
74
|
+
signature[row],
|
|
75
|
+
font=self.font,
|
|
76
|
+
fill=255,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
self.display.image(self.image)
|
|
80
|
+
self.display.display()
|
|
81
|
+
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def grove_button_on_press(screen: Screen, t):
|
|
86
|
+
logger.info("grove.button: pressed.")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def grove_button_on_release(screen: Screen, t):
|
|
90
|
+
logger.info(f"grove.button: released after {string.pretty_duration(t)}.")
|
|
91
|
+
|
|
92
|
+
if t > 60:
|
|
93
|
+
logger.info("long press, ignored.")
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
if t > 5:
|
|
97
|
+
key = "s"
|
|
98
|
+
elif t > 3:
|
|
99
|
+
key = "u"
|
|
100
|
+
else:
|
|
101
|
+
key = " "
|
|
102
|
+
|
|
103
|
+
screen.key_buffer.append(key)
|
|
104
|
+
logger.info(f"{screen.__class__.__name__}: '{key}'")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from bluer_sbc import fullname
|
|
4
|
+
from bluer_sbc.logger import logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Hardware:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
logger.info(f"{self.__class__.__name__}.init().")
|
|
10
|
+
|
|
11
|
+
self.key_buffer = []
|
|
12
|
+
self.animated = False
|
|
13
|
+
|
|
14
|
+
def animate(self):
|
|
15
|
+
if self.buffer is None:
|
|
16
|
+
return self
|
|
17
|
+
if not self.animated:
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
y = random.randint(0, self.buffer.shape[0] - 1)
|
|
21
|
+
x = random.randint(0, self.buffer.shape[1] - 1)
|
|
22
|
+
|
|
23
|
+
self.buffer[y, x] = 255 - self.buffer[y, x]
|
|
24
|
+
|
|
25
|
+
self.animated = False
|
|
26
|
+
self.update_screen(self.buffer, None, [], [])
|
|
27
|
+
self.animated = True
|
|
28
|
+
|
|
29
|
+
def clock(self):
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
def pressed(self, keys):
|
|
33
|
+
output = bool([key for key in keys if key in self.key_buffer])
|
|
34
|
+
|
|
35
|
+
self.key_buffer = [key for key in self.key_buffer if key not in keys]
|
|
36
|
+
|
|
37
|
+
return output
|
|
38
|
+
|
|
39
|
+
def pulse(self, pin=None, frequency=None):
|
|
40
|
+
"""
|
|
41
|
+
pulse pin.
|
|
42
|
+
:param pin: "data" / "incoming" / "loop" / "outputs"
|
|
43
|
+
:param frequency: frequency
|
|
44
|
+
:return: self
|
|
45
|
+
"""
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def release(self):
|
|
49
|
+
logger.info(f"{self.__class__.__name__}.release()")
|
|
50
|
+
|
|
51
|
+
def signature(self):
|
|
52
|
+
return [
|
|
53
|
+
fullname(),
|
|
54
|
+
f"hardware:{self.__class__.__name__}",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
def update_screen(self, image, session, header):
|
|
58
|
+
return self
|
|
File without changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from blueness import module
|
|
5
|
+
from bluer_options import string
|
|
6
|
+
|
|
7
|
+
from bluer_sbc import NAME
|
|
8
|
+
from bluer_sbc.hardware.hat.prototype import Prototype_Hat
|
|
9
|
+
from bluer_sbc.logger import logger
|
|
10
|
+
|
|
11
|
+
NAME = module.name(__file__, NAME)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
parser = argparse.ArgumentParser(NAME)
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"task",
|
|
17
|
+
type=str,
|
|
18
|
+
default="",
|
|
19
|
+
help="input/output",
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"--outputs",
|
|
23
|
+
type=str,
|
|
24
|
+
default="",
|
|
25
|
+
)
|
|
26
|
+
args = parser.parse_args()
|
|
27
|
+
|
|
28
|
+
hardware = Prototype_Hat()
|
|
29
|
+
|
|
30
|
+
success = False
|
|
31
|
+
if args.task == "input":
|
|
32
|
+
logger.info("loop started (Ctrl+C to stop)")
|
|
33
|
+
# https://stackoverflow.com/a/18994932/10917551
|
|
34
|
+
try:
|
|
35
|
+
while True:
|
|
36
|
+
logger.info(
|
|
37
|
+
"inputs: {}".format(
|
|
38
|
+
", ".join([str(hardware.input(pin)) for pin in hardware.input_pins])
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
time.sleep(0.1)
|
|
42
|
+
except KeyboardInterrupt:
|
|
43
|
+
logger.info("Ctrl+C, stopping.")
|
|
44
|
+
finally:
|
|
45
|
+
hardware.release()
|
|
46
|
+
success = True
|
|
47
|
+
elif args.task == "output":
|
|
48
|
+
outputs = args.outputs + len(hardware.output_pins) * "1"
|
|
49
|
+
for index, pin in enumerate(hardware.output_pins):
|
|
50
|
+
hardware.output(pin, outputs[index] == "1")
|
|
51
|
+
hardware.release()
|
|
52
|
+
success = True
|
|
53
|
+
elif args.task == "validate":
|
|
54
|
+
logger.info("loop started (Ctrl+C to stop)")
|
|
55
|
+
value = True
|
|
56
|
+
try:
|
|
57
|
+
while True:
|
|
58
|
+
activity = False
|
|
59
|
+
for pin, pin_name in zip(
|
|
60
|
+
[
|
|
61
|
+
hardware.green_switch_pin,
|
|
62
|
+
hardware.red_switch_pin,
|
|
63
|
+
hardware.switch_pin,
|
|
64
|
+
hardware.trigger_pin,
|
|
65
|
+
],
|
|
66
|
+
"green_switch_pin,red_switch_pin,switch_pin,trigger_pin".split(","),
|
|
67
|
+
):
|
|
68
|
+
if hardware.activated(pin):
|
|
69
|
+
logger.info(
|
|
70
|
+
"{}: {} activated.".format(
|
|
71
|
+
string.timestamp(ms=True),
|
|
72
|
+
pin_name,
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
activity = True
|
|
76
|
+
|
|
77
|
+
for pin in hardware.output_pins:
|
|
78
|
+
hardware.output(pin, activity or value)
|
|
79
|
+
time.sleep(0.1)
|
|
80
|
+
|
|
81
|
+
value = not value
|
|
82
|
+
except KeyboardInterrupt:
|
|
83
|
+
logger.info("Ctrl+C, stopping.")
|
|
84
|
+
finally:
|
|
85
|
+
hardware.release()
|
|
86
|
+
success = True
|
|
87
|
+
else:
|
|
88
|
+
logger.error(f"-{NAME}: {args.task}: command not found.")
|
|
89
|
+
|
|
90
|
+
if not success:
|
|
91
|
+
logger.error(f"-{NAME}: {args.task}: failed.")
|