2023-05-22 21:41:27 +02:00
|
|
|
from rgbmatrix import RGBMatrix, RGBMatrixOptions
|
2023-05-29 20:34:21 +02:00
|
|
|
from Point2D import interpolate_point_pairs, mirror_points, generate_image_from_point_array, generate_point_array_from_image, pair_points
|
2023-06-05 21:44:27 +02:00
|
|
|
from State import StateSingleton
|
2023-05-29 20:09:29 +02:00
|
|
|
|
2023-05-29 20:23:33 +02:00
|
|
|
import time
|
|
|
|
import random
|
2023-05-29 20:09:29 +02:00
|
|
|
from PIL import Image
|
2023-05-22 21:41:27 +02:00
|
|
|
import paho.mqtt.client as mqtt
|
|
|
|
import time
|
2023-05-23 21:51:06 +02:00
|
|
|
import threading
|
|
|
|
|
|
|
|
# Configuration for the matrix screens
|
|
|
|
options = RGBMatrixOptions()
|
|
|
|
options.rows = 32
|
|
|
|
options.cols = 64
|
|
|
|
options.chain_length = 2
|
|
|
|
options.parallel = 1
|
2023-07-02 08:50:38 +02:00
|
|
|
options.hardware_mapping = 'regular'
|
2023-05-23 21:51:06 +02:00
|
|
|
matrix = RGBMatrix(options=options)
|
2023-06-05 21:44:27 +02:00
|
|
|
prootState = StateSingleton()
|
|
|
|
prootState.set_matrix(matrix)
|
2023-05-23 21:51:06 +02:00
|
|
|
|
|
|
|
|
2023-06-05 21:44:27 +02:00
|
|
|
def generate_eye_frames(emote_eye_png):
|
|
|
|
eye_frames = []
|
|
|
|
points_left_eye_open = generate_point_array_from_image(Image.open("faces/eyeLeftOpen.png"))
|
|
|
|
if emote_eye_png != "faces/eyeLeftOpen.png":
|
|
|
|
left_eye_pairs = pair_points(points_left_eye_open, generate_point_array_from_image(Image.open(emote_eye_png)))
|
|
|
|
for i in range(11):
|
|
|
|
eye_frames.append(interpolate_point_pairs(left_eye_pairs, i/10))
|
|
|
|
else:
|
|
|
|
for i in range(11):
|
|
|
|
eye_frames.append(points_left_eye_open)
|
|
|
|
return eye_frames
|
|
|
|
|
|
|
|
|
|
|
|
def generate_mouth_frames(emote_mouth_png):
|
|
|
|
mouth_frames = []
|
|
|
|
points_left_mouth = generate_point_array_from_image(Image.open("faces/mouthLeft.png"))
|
|
|
|
if emote_mouth_png != "faces/mouthLeft.png":
|
|
|
|
left_mouth_pairs = pair_points(points_left_mouth, generate_point_array_from_image(Image.open(emote_mouth_png)))
|
|
|
|
for i in range(11):
|
|
|
|
mouth_frames.append(interpolate_point_pairs(left_mouth_pairs, i/10))
|
|
|
|
else:
|
|
|
|
for i in range(11):
|
|
|
|
mouth_frames.append(points_left_mouth)
|
|
|
|
return mouth_frames
|
|
|
|
|
|
|
|
|
|
|
|
def generate_nose_frames(emote_nose_png):
|
|
|
|
nose_frames = []
|
|
|
|
points_left_nose = generate_point_array_from_image(Image.open("faces/noseLeft.png"))
|
|
|
|
if emote_nose_png != "faces/noseLeft.png":
|
|
|
|
left_nose_pairs = pair_points(points_left_nose, generate_point_array_from_image(Image.open(emote_nose_png)))
|
|
|
|
for i in range(11):
|
|
|
|
nose_frames.append(interpolate_point_pairs(left_nose_pairs, i/10))
|
|
|
|
else:
|
|
|
|
for i in range(11):
|
|
|
|
nose_frames.append(points_left_nose)
|
|
|
|
return nose_frames
|
|
|
|
|
|
|
|
|
2023-07-04 19:09:27 +02:00
|
|
|
def generate_face_frames_canvases(emote_eye_png, emote_mouth_png, emote_nose_png):
|
2023-06-05 21:44:27 +02:00
|
|
|
eye_frames = generate_eye_frames(emote_eye_png)
|
|
|
|
mouth_frames = generate_mouth_frames(emote_mouth_png)
|
|
|
|
nose_frames = generate_nose_frames(emote_nose_png)
|
2023-07-04 19:09:27 +02:00
|
|
|
face_frames_canvases = []
|
2023-06-05 21:44:27 +02:00
|
|
|
|
|
|
|
for frame_number in range(11):
|
|
|
|
eyes = eye_frames[frame_number] + mirror_points(eye_frames[frame_number])
|
|
|
|
mouth = mouth_frames[frame_number] + mirror_points(mouth_frames[frame_number])
|
|
|
|
nose = nose_frames[frame_number] + mirror_points(nose_frames[frame_number])
|
|
|
|
face = eyes + mouth + nose
|
|
|
|
|
|
|
|
face_image = generate_image_from_point_array(face, 128, 32)
|
|
|
|
|
|
|
|
offscreen_canvas = matrix.CreateFrameCanvas()
|
|
|
|
offscreen_canvas.SetImage(face_image, unsafe=False)
|
|
|
|
|
2023-07-04 19:09:27 +02:00
|
|
|
face_frames_canvases.append(offscreen_canvas)
|
2023-06-05 21:44:27 +02:00
|
|
|
|
2023-07-04 19:09:27 +02:00
|
|
|
return face_frames_canvases
|
2023-06-05 21:44:27 +02:00
|
|
|
|
2023-07-02 09:27:43 +02:00
|
|
|
# Function that pre-computes all the transition frames
|
2023-06-05 21:44:27 +02:00
|
|
|
def animate():
|
|
|
|
blink_animation_FrameCanvases = []
|
|
|
|
angry_animation_FrameCanvases = []
|
2023-06-09 20:35:32 +02:00
|
|
|
stun_animation_FrameCanvases = []
|
2023-09-30 14:09:05 +02:00
|
|
|
love_animation_FrameCanvases = []
|
2023-06-05 21:44:27 +02:00
|
|
|
|
|
|
|
for emote_FrameCanvasses, emote_eye_png, emote_mouth_png, emote_nose_png in [
|
|
|
|
(blink_animation_FrameCanvases, "faces/eyeLeftClosed.png", "faces/mouthLeft.png", "faces/noseLeft.png"),
|
2023-06-09 20:35:32 +02:00
|
|
|
(angry_animation_FrameCanvases, "faces/eyeLeftAngry.png", "faces/mouthLeft.png", "faces/noseLeft.png"),
|
2023-09-30 14:09:05 +02:00
|
|
|
(stun_animation_FrameCanvases, "faces/eyeLeftStunned.png", "faces/mouthLeftSad.png", "faces/noseLeft.png"),
|
|
|
|
(love_animation_FrameCanvases, "faces/eyeLeftLove.png", "faces/mouthLeft.png", "faces/noseLeft.png")
|
2023-06-05 21:44:27 +02:00
|
|
|
]:
|
2023-07-04 19:09:27 +02:00
|
|
|
|
|
|
|
print("start generating ten face frames for " + emote_eye_png)
|
2023-10-02 08:02:30 +02:00
|
|
|
startT = round(time.time()*1000)
|
2023-07-04 19:09:27 +02:00
|
|
|
|
2023-06-05 22:07:26 +02:00
|
|
|
print("generating face with features: " + emote_eye_png +" "+ emote_mouth_png +" "+ emote_nose_png)
|
2023-07-04 19:09:27 +02:00
|
|
|
face_frames_canvases = generate_face_frames_canvases(emote_eye_png, emote_mouth_png, emote_nose_png)
|
|
|
|
emote_FrameCanvasses.extend(face_frames_canvases)
|
|
|
|
|
2023-10-02 08:02:30 +02:00
|
|
|
endT = round(time.time()*1000)
|
2023-07-04 19:09:27 +02:00
|
|
|
print("generating ten face frames took: " + str(endT - startT) + " ms")
|
2023-06-05 21:44:27 +02:00
|
|
|
|
|
|
|
state = StateSingleton()
|
|
|
|
state.set_blink_animation_frames(blink_animation_FrameCanvases)
|
|
|
|
state.set_angry_animation_frames(angry_animation_FrameCanvases)
|
2023-06-09 20:35:32 +02:00
|
|
|
state.set_stun_animation_frames(stun_animation_FrameCanvases)
|
2023-09-30 14:09:05 +02:00
|
|
|
state.set_love_animation_frames(love_animation_FrameCanvases)
|
2023-06-05 21:44:27 +02:00
|
|
|
|
2023-06-05 22:14:17 +02:00
|
|
|
state.set_animations_ready()
|
2023-06-05 21:44:27 +02:00
|
|
|
|
|
|
|
animate()
|
|
|
|
|
2023-05-23 21:51:06 +02:00
|
|
|
|
2023-05-22 21:41:27 +02:00
|
|
|
|
2023-07-02 09:27:43 +02:00
|
|
|
# The interupt time is responsible for the (roughly) 100 hz frame rate
|
2023-05-29 20:23:33 +02:00
|
|
|
def interrupt_timer():
|
2023-06-05 21:44:27 +02:00
|
|
|
proot_state = StateSingleton()
|
2023-05-29 20:23:33 +02:00
|
|
|
|
|
|
|
while True:
|
2023-06-05 22:11:15 +02:00
|
|
|
if proot_state.get_animations_ready():
|
|
|
|
proot_state.update()
|
|
|
|
time.sleep(0.01)
|
2023-05-29 20:23:33 +02:00
|
|
|
|
|
|
|
|
2023-07-02 09:27:43 +02:00
|
|
|
# Function responsible for the blinking behaviour when Idle
|
2023-05-29 20:23:33 +02:00
|
|
|
def random_blinks():
|
|
|
|
while True:
|
|
|
|
time.sleep(random.randint(3, 5))
|
|
|
|
|
2023-06-05 21:44:27 +02:00
|
|
|
proot_state = StateSingleton()
|
2023-05-29 20:23:33 +02:00
|
|
|
|
2023-06-05 21:44:27 +02:00
|
|
|
if proot_state.get_animations_ready():
|
2023-06-09 20:46:43 +02:00
|
|
|
if proot_state.current_expression == proot_state.states[0]:
|
|
|
|
proot_state.set_desired_expression(1)
|
2023-07-02 09:27:43 +02:00
|
|
|
time.sleep(0.25)
|
2023-06-09 20:46:43 +02:00
|
|
|
proot_state.set_desired_expression(0)
|
2023-06-05 21:44:27 +02:00
|
|
|
|
2023-05-29 20:23:33 +02:00
|
|
|
|
|
|
|
|
2023-05-23 22:24:51 +02:00
|
|
|
# Create and start screen update interrupts
|
2023-05-29 20:24:34 +02:00
|
|
|
screen_update_thread = threading.Thread(target=interrupt_timer)
|
2023-05-23 21:51:06 +02:00
|
|
|
screen_update_thread.start()
|
2023-05-23 19:49:26 +02:00
|
|
|
|
2023-05-23 22:24:51 +02:00
|
|
|
# Create and start random blinks interrupts
|
2023-05-29 20:24:34 +02:00
|
|
|
screen_update_thread = threading.Thread(target=random_blinks)
|
2023-05-23 22:24:51 +02:00
|
|
|
screen_update_thread.start()
|
|
|
|
|
2023-05-23 19:49:26 +02:00
|
|
|
|
2023-05-23 19:45:50 +02:00
|
|
|
|
2023-06-05 21:44:27 +02:00
|
|
|
## Load the object from disk
|
|
|
|
#with open('my_object.pickle', 'rb') as file:
|
|
|
|
# interpolated_faces = pickle.load(file)
|
2023-05-31 19:58:10 +02:00
|
|
|
#
|
2023-06-05 21:44:27 +02:00
|
|
|
#for interpolated_face_image in interpolated_faces:
|
|
|
|
# offscreen_interpolated_canvas = matrix.CreateFrameCanvas()
|
2023-05-31 19:58:10 +02:00
|
|
|
# offscreen_interpolated_canvas.SetImage(interpolated_face_image, unsafe=False)
|
2023-06-05 21:44:27 +02:00
|
|
|
# blink_animation_FrameCanvases.append(offscreen_interpolated_canvas)
|
2023-05-31 19:58:10 +02:00
|
|
|
#
|
2023-06-05 21:44:27 +02:00
|
|
|
#
|
|
|
|
## Store the object to disk
|
|
|
|
#with open('my_object.pickle', 'wb') as file:
|
|
|
|
# pickle.dump(interpolated_faces, file)
|
2023-05-31 19:58:10 +02:00
|
|
|
|
|
|
|
|
2023-05-22 21:41:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# functions called by the MQTT listener
|
|
|
|
def on_connect(client, userdata, flags, response_code):
|
|
|
|
print("Connected to MQTT broker with result code " + str(response_code))
|
|
|
|
client.subscribe("test")
|
|
|
|
|
|
|
|
|
|
|
|
def on_message(client, userdata, message):
|
|
|
|
print("Received message '" + str(message.payload) + "' on topic '"
|
|
|
|
+ message.topic + "' with QoS " + str(message.qos))
|
2023-05-31 18:56:15 +02:00
|
|
|
bean_number = str(message.payload)[12:13]
|
|
|
|
bean_state = str(message.payload)[6:10]
|
2023-06-05 21:44:27 +02:00
|
|
|
|
2023-05-31 18:56:15 +02:00
|
|
|
print("pin number: " + bean_number + " pin state: " + bean_state)
|
2023-06-05 21:44:27 +02:00
|
|
|
|
|
|
|
proot_state = StateSingleton()
|
|
|
|
if not proot_state.get_animations_ready():
|
|
|
|
print("animation not yet ready.")
|
|
|
|
return
|
|
|
|
if bean_state == "rose":
|
|
|
|
proot_state.set_desired_expression(0)
|
|
|
|
elif bean_state == "fell":
|
2023-06-05 22:26:27 +02:00
|
|
|
proot_state.set_desired_expression(int(bean_number))
|
2023-06-05 21:44:27 +02:00
|
|
|
|
2023-05-22 21:41:27 +02:00
|
|
|
|
|
|
|
# MQTT broker configuration
|
2023-07-01 22:19:30 +02:00
|
|
|
broker_address = "localhost" # Replace with your MQTT broker's address
|
2023-05-22 21:41:27 +02:00
|
|
|
broker_port = 1883
|
|
|
|
broker_keepalive = 60
|
|
|
|
|
|
|
|
client = mqtt.Client()
|
|
|
|
client.on_connect = on_connect
|
|
|
|
client.on_message = on_message
|
|
|
|
|
|
|
|
client.connect(broker_address, broker_port, broker_keepalive)
|
|
|
|
|
|
|
|
client.loop_start()
|
|
|
|
|
|
|
|
|
|
|
|
while True:
|
2023-05-23 20:58:43 +02:00
|
|
|
# this sleep sets the time between finishing one screen update and the next starting
|
2023-05-23 21:51:06 +02:00
|
|
|
pass
|
2023-05-23 20:58:43 +02:00
|
|
|
|
|
|
|
|