diff --git a/hepialight/boot.py b/hepialight/boot.py new file mode 100644 index 0000000000000000000000000000000000000000..1fee5942580e9a3147d64599c312b874ac40ce53 --- /dev/null +++ b/hepialight/boot.py @@ -0,0 +1,6 @@ +# boot.py -- run on boot-up +# can run arbitrary Python, but best to keep it minimal + +import mbed +from userlib import * +mbed.main('main.py') # main script to run after this one \ No newline at end of file diff --git a/hepialight/main.py b/hepialight/main.py new file mode 100644 index 0000000000000000000000000000000000000000..4384b2ffd2d7fe6426f2e13e0698a1fc2b6845c1 --- /dev/null +++ b/hepialight/main.py @@ -0,0 +1,180 @@ +id = [0,0] +matsize = [0, 0] + + +# Display and images functions +def color16_to_32(color): + color = int(color) + r = ((color >> 11) & 0x1F) * 2**3 + g = ((color >> 5) & 0x3F) * 2**2 + b = (color & 0x1F) * 2**3 + return r << 16 | g << 8 | b + +def disp_img_data(data): + for i in range(NBR_LIGNES): + for j in range(NBR_COLONNES): + allumer_led(j, NBR_LIGNES-1-i, data[i * NBR_COLONNES + j]) + +def data_to_color16(data): + d_color16 = [] + + for i in range(0, len(data), 2): + d_color16.append(data[i] << 8 | data[i+1]) + + return list(map(color16_to_32, d_color16)) + +def display_id(): + afficher_texte("{}, {}".format(id[0], id[1]), speed=0.01) + +def msg_to_str(msg): + return str(msg)[2:-1] + +# Used by the send_xy functions +def send_passthrough_str(msg, x, y): + x = int(x) + y = int(y) + + if x > id[0]: + d = E + elif x < id[0]: + d = O + elif y > id[1]: + d = N + else: + d = S + + envoyer_msg(d, "PT;{},{};{}".format(x, y, msg)) + +# Not meant to be used by the user +def send_passthrough_bytes(msg, x, y): + x = int(x) + y = int(y) + + if x > id[0]: + d = E + elif x < id[0]: + d = O + elif y > id[1]: + d = N + else: + d = S + + envoyer_msg(d, msg) + +# Send commands to a specific matrix +def send_xy(x, y, msg): + send_passthrough_str(msg, x, y) + +def send_text_xy(x, y, text, color=ROUGE, speed=0.1): + send_xy(x, y, "TEXT;{};{};{}".format(color, speed, text)) + +def send_moving_xy(x, y, text, color=ROUGE, speed=0.1): + send_xy(x, y, "MOVING;{};{};{}".format(color, speed, text)) + + +# Check if there is a connected matrix in the d direction +def check_presence(d): + envoyer_msg(d, "PING") + + for x in range(10): + time.sleep(0.1) + ret = recevoir_msg(d) + + if ret == b"PONG": return True + + return False + +# Update the ID of the current matrix, and update the id of the neighbours +def update_id(new_id): + global id + + id[0] = int(new_id[0]) + id[1] = int(new_id[1]) + + # The matrices which are in the x = 0 pos, update the id North and East. + # The others only broadcast East. + if (id[0] == 0): + envoyer_msg(N, "SET_ID:{},{}".format(id[0], id[1] + 1)) + + envoyer_msg(E, "SET_ID:{},{}".format(id[0] + 1, id[1])) + + if not check_presence(N) and not check_presence(E): + send_xy(0, 0, "MATSIZE;{},{}".format(id[0], id[1])) + + display_id() + + + +# Handle the commands received via UART +def handle_receive(msg, msg_bytes, d): + + print("Received from {} : {}".format(d, msg)) + + if msg.startswith("SET_ID"): + tmp_id = msg[7:].split(",") + update_id(tmp_id) + + elif msg == "PING": + envoyer_msg(d, "PONG") + + elif msg == "DISP_ID": + display_id() + + elif msg.startswith("PT;"): + msgs = msg.split(";") + m = msgs[2:] + m = ";".join(m) + x, y = msgs[1].split(",") + + x = int(x) + y = int(y) + + if x == id[0] and y == id[1]: + handle_receive(m, msg_bytes[7:], d) + return + + print("sending passthrough to {},{}: {}".format(x,y,m)) + send_passthrough_bytes(msg_bytes, x, y) + + elif msg.startswith("MATSIZE"): + x, y = msg.split(";")[-1].split(",") + print("Matsize : {} {}".format(x, y)) + matsize = [int(x), int(y)] + + elif msg.startswith("TEXT"): + msgs = msg.split(";") + color = int(msgs[1]) + speed = float(msgs[2]) + afficher_texte(msgs[3], color, speed) + + elif msg.startswith("MOVING"): + msgs = msg.split(";") + color = int(msgs[1]) + speed = float(msgs[2]) + text = msgs[3] + + time.sleep(speed * 11) + send_moving_xy(id[0] - 1, id[1], text, color=color, speed=speed) + afficher_texte(text, color, speed) + + elif msg.startswith("IMAGE"): + print("IMAGE LEN : {}".format(len(msg_bytes[6:]))) + img = data_to_color16(msg_bytes[6:]) + disp_img_data(img) + + elif msg == "OK": + update_id(id) + + +# Main +afficher_texte("Ready", VERT, speed=0.01) + +while True: + + for d in [N, S, E, O]: + msg_bytes = recevoir_msg(d) + + if msg_bytes == b"": continue + + msg = msg_to_str(msg_bytes) + handle_receive(msg, msg_bytes, d) \ No newline at end of file diff --git a/hepialight/userlib.py b/hepialight/userlib.py new file mode 100644 index 0000000000000000000000000000000000000000..decb87c3be779a7bd02b060ad962d9a5f5355ed1 --- /dev/null +++ b/hepialight/userlib.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import time +from Hepialight import touch, screen, accel, uart + +# Constantes +NBR_LIGNES = 10 +NBR_COLONNES = 10 + +# Couleurs +ROUGE = 0xFF0000 +VERT = 0x00FF00 +BLEU = 0x0000FF + +_accel = accel() +_seuil_accel = 0.3 + +def delai(delai_en_sec): + time.sleep(delai_en_sec) + +def _colorToInt(hexColor): + return int(hexColor[1:], 16) + +_COLORS = { + "R": _colorToInt("#A2142F"), + "B": _colorToInt("#0072BD"), + "O": _colorToInt("#D95319"), + "Y": _colorToInt("#EDB120"), + "P": _colorToInt("#7E2F8E"), + "G": _colorToInt("#77AC30"), + "C": _colorToInt("#4DBEEE"), + ".": _colorToInt("#000000") +} + +# Coordonnées +N = 0 +S = 1 +E = 2 +O = 3 + + +# use https://goo.gl/gGYfJV to add new chars +_TEXT_DICT = { + ' ': [], + 'A': [14, 17, 17, 31, 17, 17, 17], + 'B': [15, 17, 17, 31, 17, 17, 15], + 'C': [14, 17, 1, 1, 1, 17, 14], + 'D': [15, 17, 17, 17, 17, 17, 15], + 'E': [31, 1, 1, 15, 1, 1, 31], + 'F': [31, 1, 1, 15, 1, 1, 1], + 'G': [14, 17, 1, 1, 25, 17, 30], + 'H': [17, 17, 17, 31, 17, 17, 17], + 'I': [31, 4, 4, 4, 4, 4, 31], + 'J': [31, 16, 16, 16, 16, 17, 14], + 'K': [17, 17, 9, 7, 9, 17, 17], + 'L': [1, 1, 1, 1, 1, 1, 31], + 'M': [17, 27, 27, 21, 21, 17, 17], + 'N': [17, 19, 21, 21, 25, 17, 17], + 'O': [14, 17, 17, 17, 17, 17, 14], + 'P': [15, 17, 17, 17, 15, 1, 1], + 'Q': [14, 17, 17, 17, 17, 21, 14, 4, 8], + 'R': [15, 17, 17, 15, 9, 17, 17], + 'S': [30, 1, 1, 14, 16, 16, 15], + 'T': [31, 4, 4, 4, 4, 4, 4], + 'U': [17, 17, 17, 17, 17, 17, 14], + 'V': [17, 17, 17, 17, 10, 10, 4], + 'W': [17, 17, 21, 21, 21, 10, 10], + 'X': [17, 17, 10, 4, 10, 17, 17], + 'Y': [17, 10, 4, 4, 4, 4, 4], + 'Z': [31, 16, 8, 4, 2, 1, 31], + 'a': [0, 0, 14, 16, 30, 17, 30], + 'b': [1, 1, 13, 19, 17, 17, 15], + 'c': [0, 0, 14, 17, 1, 17, 14], + 'd': [16, 16, 30, 17, 17, 17, 14], + 'e': [0, 0, 14, 17, 31, 1, 30], + 'f': [24, 4, 30, 4, 4, 4, 31], + 'g': [0, 0, 30, 17, 17, 25, 22, 16, 14], + 'h': [1, 1, 15, 17, 17, 17, 17], + 'i': [4, 0, 7, 4, 4, 4, 31], + 'j': [16, 0, 28, 16, 16, 16, 16, 17, 14], + 'k': [1, 1, 9, 9, 7, 9, 17], + 'l': [7, 4, 4, 4, 4, 4, 31], + 'm': [0, 0, 21, 31, 21, 21, 21], + 'n': [0, 0, 13, 19, 17, 17, 17], + 'o': [0, 0, 14, 17, 17, 17, 14], + 'p': [0, 0, 13, 19, 17, 17, 15, 1, 1], + 'q': [0, 0, 30, 17, 17, 25, 22, 16, 16], + 'r': [0, 0, 27, 6, 2, 2, 15], + 's': [0, 0, 30, 1, 14, 16, 15], + 't': [0, 4, 31, 4, 4, 4, 24], + 'u': [0, 0, 17, 17, 17, 25, 22], + 'v': [0, 0, 17, 17, 10, 10, 4], + 'w': [0, 0, 17, 21, 21, 10, 10], + 'x': [0, 0, 17, 17, 14, 17, 17], + 'y': [0, 0, 17, 17, 10, 10, 4, 4, 3], + 'z': [0, 0, 31, 8, 4, 2, 31], + '0': [14, 25, 21, 21, 21, 19, 14], + '1': [4, 7, 4, 4, 4, 4, 31], + '2': [14, 17, 16, 14, 1, 1, 31], + '3': [14, 17, 16, 14, 16, 17, 14], + '4': [8, 12, 10, 9, 31, 8, 8], + '5': [31, 1, 1, 15, 16, 16, 15], + '6': [12, 2, 1, 15, 17, 17, 14], + '7': [31, 16, 8, 4, 2, 2, 2], + '8': [14, 17, 17, 14, 17, 17, 14], + '9': [14, 17, 17, 30, 16, 8, 6], + '.':[0, 0, 0, 0, 0, 4, 4], + ',':[0, 0, 0, 0, 0, 4, 4, 2], + ';':[0, 0, 4, 4, 0, 4, 4, 2], + '?':[14, 17, 16, 12, 0, 4, 4], + '!':[4, 4, 4, 4, 0, 4, 4], + '-':[0, 0, 0, 0, 14], + '_':[0, 0, 0, 0, 0, 0, 31], + '*':[0, 0, 10, 4, 10], + '+':[0, 0, 4, 4, 31, 4, 4], + '/':[16, 16, 8, 4, 4, 2, 2], + '\\':[2, 2, 4, 8, 8, 16, 16], + '<':[0, 0, 8, 4, 2, 4, 8], + '>':[0, 0, 2, 4, 8, 4, 2], + '#':[10, 10, 31, 10, 31, 10, 10], + '=':[0, 0, 0, 31, 0, 31], + "'":[8, 8, 4], + "%":[11, 11, 4, 2, 2, 13, 13, 0, 0], + "&":[6, 1, 1, 6, 5, 9, 22, 0, 0], + "@":[14, 17, 29, 27, 31, 1, 30, 0, 0], + "$":[4, 30, 5, 14, 20, 15, 4, 0, 0], +} + +def afficher_texte(text, color=ROUGE, speed=0.1): + eteindre_tout() + + width = 5 + height = 9 + h_offset = 9 + spacewidth = 2 + + def printColumn(i, xpos, text): + if i < h_offset or i >= h_offset + len(text) * (width + spacewidth): + char = ' ' + col = 0 + else: + i -= h_offset + char = text[i // (width + spacewidth)] + col = i % (width + spacewidth) + if char not in _TEXT_DICT: + char = ' ' + colbit = 1 << col + charMap = _TEXT_DICT[char] + for line in range(height): + colored = len(charMap)>line and charMap[line] & colbit + screen.set_led((xpos, height - 1 - line), color if colored else 0) + + for step in range(h_offset + len(text) * (width + spacewidth) + 1): + for i in range(NBR_COLONNES): + printColumn(step + i, i, text) + delai(speed) + +def afficher_grille(grille): + arr = [_COLORS[c] for c in grille if c in _COLORS] + for i in range(NBR_LIGNES): + for j in range(NBR_COLONNES): + screen.set_led((j, NBR_LIGNES-1-i), arr[i * NBR_COLONNES + j]) + + +def allumer_tout(couleur): + for i in range(NBR_LIGNES): + for j in range(NBR_COLONNES): + screen.set_led((i, j), couleur) + +def eteindre_tout(): + allumer_tout(0) + +def allumer_ligne(num_ligne, couleur): + for i in range(NBR_COLONNES): + screen.set_led((i, num_ligne), couleur) + +def allumer_colonne(num_colonne, couleur): + for j in range(NBR_LIGNES): + screen.set_led((num_colonne, j), couleur) + +def allumer_led(pos_x, pos_y, couleur): + screen.set_led((int(pos_x), int(pos_y)), couleur) + +def eteindre_led(pos_x, pos_y): + screen.set_led((pos_x, pos_y), 0) + +def penche_gauche(): + return _accel.get_axis()[0] > _seuil_accel + +def accel_vertical(): + return _accel.get_axis()[1] + +def accel_horizontal(): + return _accel.get_axis()[0] + +def penche_droite(): + return _accel.get_axis()[0] < -_seuil_accel + +def penche_avant(): + return _accel.get_axis()[1] > _seuil_accel + +def penche_arriere(): + return _accel.get_axis()[1] < -_seuil_accel + +def touche_bas_gauche(): + return touch.read(0) + +def touche_bas_droite(): + return touch.read(1) + +def touche_haut_gauche(): + return touch.read(2) + +def touche_haut_droite(): + return touch.read(3) + +def round(x): + return int(x+0.5) + +def ceil(x): + return x if int(x) == x else int(x+1) + +def floor(x): + return int(x+0.5) + +def abs(x): + return x if x>=0 else -x + +def max(x, y): + return x if x>y else y + +def min(x, y): + return x if x<y else y + +def envoyer_msg(ID, data): + '''Envoi une valeur vers une des carte voisines + :ID: quelle carte + :data: la valeur + ''' + + uart.send_to(ID, data) + + +def recevoir_msg(ID): + '''Détecte si le pavé tactile supérieur droit est pressé + :returns: True si pressé, False sinon + ''' + + return uart.recv(ID)