diff --git a/src/mqtt_esp32/mqtt_esp32.ino b/src/mqtt_esp32/mqtt_esp32.ino index 4bf3e890736c7f28e1bf6abfbd795159f9b816cd..06d73765579118095b16077ae5aaafc9c09cb2d8 100644 --- a/src/mqtt_esp32/mqtt_esp32.ino +++ b/src/mqtt_esp32/mqtt_esp32.ino @@ -5,6 +5,7 @@ #define MATRIX_SIZE 10 // 10x10 #define IMG_DATA_SIZE MATRIX_SIZE * MATRIX_SIZE * 3 //DATA SIZE FOR ONE MATRIX = RGB * MATRIX_SIZE #define BUFFER_SIZE 128 +#define CLUSTER "cluster1" // WiFi const char *ssid = "uni-ete2"; // Enter your WiFi name @@ -13,10 +14,12 @@ const char *password = "uni-ete2-esp32"; // Enter WiFi password // MQTT Broker const char *mqtt_broker = "192.168.1.101"; const char *topic_send = "fromesp"; -const char *topic_img = "cluster2/image"; -const char *topic_txt = "cluster2/text"; +const char *topic_img = CLUSTER"/image"; +const char *topic_txt = CLUSTER"/text"; +const char *topic_size = CLUSTER"/size"; const int mqtt_port = 1883; +// WIFI & MQTT WiFiClient espClient; PubSubClient client(espClient); @@ -24,9 +27,23 @@ PubSubClient client(espClient); HardwareSerial SerialPort(2); // use UART2 char rcv_buffer[BUFFER_SIZE] = {0}; +// MATRIX CLUSTER SIZE uint8_t cluster_size_x = 1; uint8_t cluster_size_y = 1; +/** + *@brief + Setup all the peripherals of the ESP32 (UART2 & Software serial). + It will connect automatically to an Hardcoded wifi router, + then it will connect with the MQTT broker. + Finally it will publish a message to the topic, + and will subscribe to the needed sub-topic. + + *@params + void + *@return + void +*/ void setup() { // Set software serial baud to 115200; @@ -43,11 +60,10 @@ void setup() } Serial.println("Connected to the Wi-Fi network"); - //connecting to a mqtt broker + // Connecting to a mqtt broker client.setServer(mqtt_broker, mqtt_port); client.setCallback(callback); client.setBufferSize(2000); - while (!client.connected()) { String client_id = "esp32-client-"; @@ -70,8 +86,27 @@ void setup() client.subscribe(topic_txt); } -// UART2 FUNCTIONS, COMM WITH HEPIALIGHT -void sendMessageToHepialight(const char* data, uint8_t lenght) +/** + *@brief + Send an UART message in Pin TX 17 from UART2. + The message will follow the Hepialight2 custom UART format. + + Format : + + Header : [0x77] + Len : [0x01-0xFF] + Data[0] : [0x00-0xFF] + Data[n] : [0x00-0xFF] + Crc : [0x00-0xFF] = Data[0] ^ Data[1] ^ Data[n] ^ Len + Footer : [0xAA] + + *@params + data : data to send to the Hepialight2 + length : length of the data + *@return + void +*/ +void send_uart_data(const char* data, uint8_t lenght) { // SEND UART PACKET HEADER SerialPort.print((char)0x77); @@ -95,6 +130,26 @@ void sendMessageToHepialight(const char* data, uint8_t lenght) SerialPort.print((char)0xAA); } +/** + *@brief + Receive an UART message in Pin RX 17 from UART2. + The received message should follow the Hepialight2 custom UART format. + + Format : + + Header : [0x77] + Len : [0x01-0xFF] + Data[0] : [0x00-0xFF] + Data[n] : [0x00-0xFF] + Crc : [0x00-0xFF] = Data[0] ^ Data[1] ^ Data[n] ^ Len + Footer : [0xAA] + + *@params + received_buffer : Will fill the bufeer with + a null-terminated string with the received data + *@return + void +*/ void receive_uart_data(char* received_buffer) { static bool receiving_data = false; // Indicator for data reception @@ -181,12 +236,34 @@ void receive_uart_data(char* received_buffer) } } +/** + *@brief + Convert the 2d index to a 1d index. + + *@params + x : x index on a 2d space. + x : y index on a 2d space. + pitch : the lenght of the 2d space. + *@return + int : 1d array index +*/ int d2_to_flat(uint8_t x, uint8_t y, uint32_t pitch) { return (x * pitch) + y; } -uint8_t* mqtt_packet_to_raw(uint8_t* packet, unsigned int* length) +/** + *@brief + Remove the header from the ppm image received from + a mqtt message. + + *@params + packet : mqtt packet data that contains the ppm image. + lenght : length of the packet data. + *@return + uint8_t* : returns the mqtt packet pointer offset by the header. +*/ +uint8_t* remove_header_from_ppm(uint8_t* packet, unsigned int* length) { int cnt = 0; for(int i = 0; i < *length; i++) @@ -206,6 +283,24 @@ uint8_t* mqtt_packet_to_raw(uint8_t* packet, unsigned int* length) return NULL; } +/** + *@brief + Send the received ppm image to the Hepialight 2 by UART2. + The hepialight2 is adressed by his ID on a 2d space (Example : (0, 0)). + The 24 bits RGB data are refactored in 16 bits data (Hepialigt2 buffer size is limited). + + R : 8 bits -> 5 bits + G : 8 bits -> 6 bits + B : 8 bits -> 5 bits + + *@params + idx : Hepialight matrix ID-x. + idy : Hepialight matrix ID-y. + data : PPM image data (without PPM header). + lenght : length of the data. + *@return + void +*/ void send_data_to_matrix(uint8_t idx, uint8_t idy, uint8_t* data, uint32_t length) { uint8_t raw_data[220]; @@ -241,33 +336,21 @@ void send_data_to_matrix(uint8_t idx, uint8_t idy, uint8_t* data, uint32_t lengt } } - //Serial.println(send_len); - // SEND UART PACKET HEADER - SerialPort.print((char)0x77); - SerialPort.print((char)send_len); - uint8_t crc = 0; - - for(int i = 0; i < send_len; i++) - { - if(raw_data[i] == 0x77 || raw_data[i] == 0x10 || raw_data[i] == 0xAA) - { - crc = crc ^ 0x10; - SerialPort.print((char)0x10); - //Serial.println(0x10); - } - crc = crc ^ raw_data[i]; - SerialPort.print((char)raw_data[i]); - //Serial.println(raw_data[i]); - } - - // SEND UART PACKET FOOTER - SerialPort.print((char)(crc ^ send_len)); - SerialPort.print((char)0xAA); - - //Serial.println(crc ^ send_len); + send_uart_data((char*)raw_data, send_len); } +/** + *@brief + Callback function called when the ESP32 receive a mqtt packet. + Only when the ESP32 is subscribed to the right topic. + *@params + topic : The concerned topic. + payload : Received packet. + data : length of the received packet. + *@return + void +*/ void callback(char *topic, byte *payload, unsigned int length) { // TERMINAL SERIAL @@ -311,7 +394,7 @@ void callback(char *topic, byte *payload, unsigned int length) } // GET RAW DATA - uint8_t* raw_data = mqtt_packet_to_raw(payload, &length); + uint8_t* raw_data = remove_header_from_ppm(payload, &length); if(raw_data == NULL) { return; @@ -328,6 +411,22 @@ void callback(char *topic, byte *payload, unsigned int length) } } +/** + *@brief + Main loop. Handle the mqtt callback function. + When the ESP32 receive a "ESP" UART message, it will respond "OK". + When the ESP32 receive a "MATSIZE" message, + it will update the hepialight2 cluster size + and will publish to the /size subtopic to signal the broker. + + *@params + idx : Hepialight matrix ID-x + idy : Hepialight matrix ID-y + data : PPM image data (without PPM header) + lenght : length of the data. + *@return + void +*/ void loop() { client.loop(); @@ -338,17 +437,30 @@ void loop() { Serial.println(rcv_buffer); rcv_buffer[0] = 0; // CLEAR BUFFER - sendMessageToHepialight("OK", 2); // SEND OK + send_uart_data("OK", 2); // SEND OK } // SET MATRIX SIZE if(strncmp(rcv_buffer, "MATSIZE", 7) == 0) { Serial.println(rcv_buffer); + + // GET CLUSTER SIZE cluster_size_x = (rcv_buffer[8] + 1) - '0'; cluster_size_y = (rcv_buffer[10] + 1) - '0'; - Serial.println(cluster_size_x); - Serial.println(cluster_size_y); rcv_buffer[0] = 0; // CLEAR BUFFER + + // PUBLISH NEW CLUSTER SIZE + char clust_size[20]; + char clust_y[10]; + itoa(cluster_size_x*10, clust_size, 10); + int i_0 = strlen(clust_size); + clust_size[i_0] = 'x'; + clust_size[i_0+1] = 0; + + itoa(cluster_size_y*10, clust_y, 10); + strcat(clust_size, clust_y); + + client.publish(topic_size, clust_size); } -} +} \ No newline at end of file diff --git a/src/mqtt_hepialight/BOOT.PY b/src/mqtt_hepialight/BOOT.PY deleted file mode 100644 index ac29550d8b38030f5f3c584c2ee9b028d5ebdbbb..0000000000000000000000000000000000000000 --- a/src/mqtt_hepialight/BOOT.PY +++ /dev/null @@ -1,6 +0,0 @@ -# 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/src/mqtt_hepialight/MAIN.PY b/src/mqtt_hepialight/MAIN.PY deleted file mode 100644 index 1ff179079d05afb56d99da95b3d62ad83b5f5a93..0000000000000000000000000000000000000000 --- a/src/mqtt_hepialight/MAIN.PY +++ /dev/null @@ -1,33 +0,0 @@ -def color16_to_32(color): - color = int(color) - r = ((color >> 11) & 0xFF) * 2**3 - g = ((color >> 5) & 0xFF) * 2**2 - b = (color & 0xFF) * 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(i, j, data[i * NBR_LIGNES + j]) - -def data_to_color16(data): - d_color16 = [] - - id_x = data[0] - id_y = data[1] - - #afficher_texte(f"{len(data)}") - - for i in range(2, len(data), 2): - d_color16.append(data[i] << 8 | data[i+1]) - - return list(map(color16_to_32, d_color16)) - -r_data = [] -while(True): - r_data = recevoir_msg(S) - if len(r_data) > 0: - break - -data32 = data_to_color16(r_data) -disp_img_data(data32) \ No newline at end of file diff --git a/src/mqtt_hepialight/USERLIB.PY b/src/mqtt_hepialight/USERLIB.PY deleted file mode 100644 index e5f31673722965015c79e2030bff3ea96b9f2d00..0000000000000000000000000000000000000000 --- a/src/mqtt_hepialight/USERLIB.PY +++ /dev/null @@ -1,249 +0,0 @@ -#!/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) \ No newline at end of file