diff --git a/src/mqtt_esp32/mqtt_esp32.ino b/src/mqtt_esp32/mqtt_esp32.ino
new file mode 100644
index 0000000000000000000000000000000000000000..06d73765579118095b16077ae5aaafc9c09cb2d8
--- /dev/null
+++ b/src/mqtt_esp32/mqtt_esp32.ino
@@ -0,0 +1,466 @@
+#include <WiFi.h>
+#include <PubSubClient.h>
+#include <HardwareSerial.h>
+
+#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
+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 = 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);
+
+// UART2
+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;
+    Serial.begin(115200);
+
+    // Set UART2 serial
+    SerialPort.begin(115200, SERIAL_8N1, 16, 17); // RX pin: 16, TX pin: 17
+
+    // Connecting to a WiFi network
+    WiFi.begin(ssid, password);
+    while (WiFi.status() != WL_CONNECTED) {
+        delay(500);
+        Serial.println("Connecting to WiFi..");
+    }
+    Serial.println("Connected to the Wi-Fi network");
+
+    // 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-";
+      client_id += String(WiFi.macAddress());
+      Serial.printf("The client %s connects to the public MQTT broker\n", client_id.c_str());
+      if (client.connect(client_id.c_str())) 
+      {
+          Serial.println("Public EMQX MQTT broker connected");
+      } else 
+      {
+          Serial.print("failed with state ");
+          Serial.print(client.state());
+          delay(2000);
+      }
+    }
+
+    // Publish and subscribe
+    client.publish(topic_send, "Hi, I'm ESP32 ^^");
+    client.subscribe(topic_img);
+    client.subscribe(topic_txt);
+}
+
+/**
+ *@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);
+  SerialPort.print((char)lenght);
+
+  // SEND DATA AND COMPUTE CRC
+  uint8_t crc = 0;
+  for(int i = 0; i < lenght; i++)
+  {
+    if(data[i] == 0x77 || data[i] == 0x10 || data[i] == 0xAA)
+    {
+      crc = crc ^ 0x10;
+      SerialPort.print((char)0x10);
+    }
+    crc = crc ^ data[i];
+    SerialPort.print((char)data[i]);
+  }
+
+  // SEND UART PACKET FOOTER
+  SerialPort.print((char)(crc ^ 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
+  static uint8_t buffer[BUFFER_SIZE]; // Receive buffer
+  static int buffer_index = 0; // Current buffer index
+  static int message_length = 0; // Length of the currently received message
+  static uint8_t expected_crc = 0; // Expected CRC
+  uint8_t received_crc = 0;
+  bool ctrl_byte = false;
+
+
+  while(SerialPort.available())
+  {
+    uint8_t received_byte = SerialPort.read();
+
+    switch(received_byte)
+    {
+      case 0x77:
+        // IF 0x10 just before
+        if(ctrl_byte)
+        {
+          buffer[buffer_index++] = received_byte;
+          ctrl_byte = false;
+          continue;
+        }
+
+        // UART HEADER
+        buffer_index = 0;
+        receiving_data = false;
+        break;
+      case 0xAA:
+        // IF 0x10 just before
+        if(ctrl_byte)
+        {
+          buffer[buffer_index++] = received_byte;
+          ctrl_byte = false;
+          continue;
+        }
+
+        // UART HEADER
+        receiving_data = false;
+        if(expected_crc == received_crc)
+        {
+          memcpy(received_buffer, buffer, message_length);
+          buffer[buffer_index] = 0;
+          return;
+        }
+        break;
+      case 0x10:
+        // IF 0x10 just before
+        if(!ctrl_byte)
+        {
+          ctrl_byte = true;
+          continue;
+        }
+
+        // DATA
+        buffer[buffer_index++] = received_byte;
+        ctrl_byte = false;
+        break;
+      default:
+          // DATA LEN
+          if(!receiving_data)
+          {
+            message_length = received_byte;
+            expected_crc = received_byte; // Expected CRC is initialized with the message length
+            receiving_data = true;
+            continue;
+          }
+          
+          // DATA
+          // GET MESSAGE CRC
+          if(buffer_index == message_length)
+          {
+            received_crc = received_byte;
+            continue;
+          }
+
+          // Store message bytes in the buffer
+          buffer[buffer_index++] = received_byte;
+          expected_crc ^= received_byte; // Calculate expected CRC
+        break;
+    }
+  }
+}
+
+/**
+ *@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;
+}
+
+/**
+ *@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++)
+  {
+    if(packet[i] == '\n')
+    {
+      cnt++;
+    }
+
+    if(cnt >= 3)
+    {
+      *length -= i - 1;
+      return packet + i + 1;
+    }
+  }
+
+  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];
+  char header[] = "PT;x,y;IMAGE;";
+  strcpy((char*)raw_data, header);
+  raw_data[3] = '0' + idx;
+  raw_data[5] = '0' + idy;
+
+  uint8_t send_len = 13;
+
+  // RGB DATA PROCESS
+  // HARDCODED
+  for(int x = (cluster_size_y - 1 - idy) * 10; x < (cluster_size_y - idy) * 10; x+=1)
+  {
+    for(int y = idx * 30; y < (idx+1) * 30; y+=3)
+    {
+        // Serial.println()
+      int i = d2_to_flat(x, y, 30 * cluster_size_y);
+
+      // COMPUTE COLOR IN 16 BITS
+      uint8_t r = data[i] & 0x1F;
+      uint8_t g = data[i+1] & 0x3F;
+      uint8_t b = data[i+2] & 0x1F ;
+      uint16_t rgb = r << 11 | g << 5 | b;
+
+      // SPLIT ON 8 BITS
+      uint8_t rgb_0 = rgb >> 8;
+      uint8_t rgb_1 = rgb & 0xFF;
+
+      // SEND RGB
+      raw_data[send_len++] = rgb_0;
+      raw_data[send_len++] = rgb_1;
+    }
+  }
+
+  // SEND UART PACKET HEADER
+  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
+  Serial.print("Message arrived in topic: ");
+  Serial.println(topic);
+
+  if(strcmp(topic, topic_txt) == 0)
+  {
+    char data[100];
+    char header[] = "PT;0,0;MOVING;16711680;0.1;";
+    memcpy(data, header, strlen(header) + 1);
+
+    // MATRIX ID
+    data[3] = '0' + cluster_size_x - 1;
+    data[5] = '0';
+
+    strncat(data, (char*)payload, length);
+    uint32_t len = strlen(data);
+
+    // SEND UART PACKET HEADER
+    SerialPort.print((char)0x77);
+    SerialPort.print((char)len);
+    uint8_t crc = 0;
+
+    for(int i = 0; i < len; i++)
+    {
+      if(data[i] == 0x77 || data[i] == 0x10 || data[i] == 0xAA)
+      {
+        crc = crc ^ 0x10;
+        SerialPort.print((char)0x10);
+      }
+      crc = crc ^ data[i];
+      SerialPort.print((char)data[i]);
+    }
+
+    // SEND UART PACKET FOOTER
+    SerialPort.print((char)(crc ^ len));
+    SerialPort.print((char)0xAA);
+
+    return;
+  }
+
+  // GET RAW DATA
+  uint8_t* raw_data = remove_header_from_ppm(payload, &length);
+  if(raw_data == NULL)
+  {
+    return;
+  }
+
+  // SEND UART PACKET FOR EACH MATRIX
+  for(int x_mat = 0; x_mat < cluster_size_x; x_mat++)
+  {
+    for(int y_mat = cluster_size_y - 1; y_mat >= 0; y_mat--)
+    {
+      // SEND UART PACKET
+      send_data_to_matrix(x_mat, y_mat, raw_data, IMG_DATA_SIZE);
+    }
+  }
+}
+
+/**
+ *@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();
+  receive_uart_data(rcv_buffer);  // READ UART BUFFER
+
+  // CHECK PRESENCE FROM HEPIALIGHT
+  if(strcmp(rcv_buffer, "ESP") == 0)
+  {
+    Serial.println(rcv_buffer);
+    rcv_buffer[0] = 0;  // CLEAR BUFFER
+    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';
+    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