ujjval r.
Published

RGB Matrix using ESP32s3 module

Using interactive RGB LEDs from Wurth to create a smart light.

IntermediateWork in progressOver 3 days134
RGB Matrix using ESP32s3 module

Things used in this project

Hardware components

ESP32S
Espressif ESP32S
×1
NextPCB  Custom PCB Board
NextPCB Custom PCB Board
×1
Grove - Vision AI Module
Seeed Studio Grove - Vision AI Module
×1

Software apps and online services

MicroPython
MicroPython

Hand tools and fabrication machines

Tweezers, SMD Soldering/Desoldering
Tweezers, SMD Soldering/Desoldering

Story

Read more

Custom parts and enclosures

3D model

3D model for enclosure

Sketchfab still processing.

Code

BLE_APP_CODE

Python
This is a BLE sample app code that is flashed to the device. It will work as a peripheral to which central can write/read the RGB color value characteristics.
import time
import bluetooth
from bluetooth import BLE

import asyncio
import ubinascii
import neopixel
import machine
from micropython import const
import esp32

import network

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
_IRQ_GATTS_READ_REQUEST = const(4)

_IRQ_GATTC_READ_RESULT = const(15)
_IRQ_GATTC_READ_DONE = const(16)
_IRQ_GATTC_WRITE_DONE = const(17)

nvs = esp32.NVS("RGBMAT")

try:
    status = nvs.get_i32("Lighton")
except OSError:
    print('Blob is not set, setting it now..')
    nvs.set_i32("Lighton", 1)
    nvs.set_i32("red" , 255)
    nvs.set_i32("blue" , 0)
    nvs.set_i32("green" , 0)
    nvs.commit()

ledcorner = machine.Pin(12)
ledfive = machine.Pin(45)
ledseven = machine.Pin(47)

neopixelcorner = neopixel.NeoPixel(ledcorner, 9)
neopixelfive = neopixel.NeoPixel(ledfive, 5)
neopixelseven = neopixel.NeoPixel(ledseven, 7)

class RGBMATRIX():
    def __init__(self, name):
        self.name = name
        self.BLE = bluetooth.BLE()
        self.BLE.active(True)
        self.connection = set()
        self.register_services()
        self.BLE.irq(self.ble_irq)
        self.advertise()
        
        self.redl = nvs.get_i32("red")
        self.bluel = nvs.get_i32("blue")
        self.greenl = nvs.get_i32("green")
        
        self.set_led_strip(self.redl.to_bytes(1), self.bluel.to_bytes(1), self.greenl.to_bytes(1))        
        
    def register_services(self):
        RGB_UUID = bluetooth.UUID('7bf20ec6-e66f-4a61-9aa4-8fa20097a0c4')
        
        RED_UUID = (bluetooth.UUID('7bf20ec6-e66f-4a61-9aa4-8fa20097a0c5'), bluetooth.FLAG_READ | bluetooth.FLAG_WRITE,)
        BLUE_UUID = (bluetooth.UUID('7bf20ec6-e66f-4a61-9aa4-8fa20097a0c6'), bluetooth.FLAG_READ | bluetooth.FLAG_WRITE,)
        GREEN_UUID = (bluetooth.UUID('7bf20ec6-e66f-4a61-9aa4-8fa20097a0c7'), bluetooth.FLAG_READ | bluetooth.FLAG_WRITE,)

        rgb_service = (RGB_UUID, (RED_UUID, BLUE_UUID, GREEN_UUID,),)
        services = (rgb_service,)
        ( (self.red, self.green, self.blue,), ) = self.BLE.gatts_register_services(services)
        
    def advertise(self):
        
        name = bytes(self.name, 'UTF-8')
        advdata = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) +  name        
        self.BLE.gap_advertise(100, advdata) 
         #get MAC address
        mac = self.BLE.config('mac')[1]
        print("device MAC address is: "+ubinascii.hexlify(mac).decode())
        
    def ble_irq(self, event, data):
        
        if event == _IRQ_CENTRAL_CONNECT:
            conn_handle, addr_type, addr = data
            self.connection.add(conn_handle)
            print('Central connected')
            
        elif event == _IRQ_CENTRAL_DISCONNECT:
            conn_handle, addr_type, addr = data
            self.connection.remove(conn_handle)
            self.advertise()
            print('Central disconnected.')
            
        elif event == _IRQ_GATTS_WRITE:
            conn_handle, attrb_handle = data
            #print('Gatts write')
            self.val_red = self.BLE.gatts_read(self.red)
            self.val_blue = self.BLE.gatts_read(self.blue)
            self.val_green = self.BLE.gatts_read(self.green)            
            self.set_led_strip(self.val_red, self.val_blue, self.val_green)
            
        elif event == _IRQ_GATTS_READ_REQUEST:
            conn_handle, attrb_handle = data
            #print('read request')
        
    def set_led_strip(self, red, blue, green):
        
        for i in range(9):
            neopixelcorner[i] = (int.from_bytes(red), int.from_bytes(blue), int.from_bytes(green))
    
        for i in range(5):
            neopixelfive[i] = (int.from_bytes(red), int.from_bytes(blue), int.from_bytes(green))
            
        for i in range(7):
            neopixelseven[i] = (int.from_bytes(red), int.from_bytes(blue), int.from_bytes(green))
            
        neopixelcorner.write()
        neopixelfive.write()
        neopixelseven.write()
        
        nvs.set_i32("red" , int.from_bytes(red))
        nvs.set_i32("blue" , int.from_bytes(blue))
        nvs.set_i32("green" , int.from_bytes(green))         
        
def main():
    BLE = RGBMATRIX("ESP32S3")
    #BLE.set_color(255)
    
if __name__ == "__main__":
    main()

ML-Gesture detection demo code

Python
This code is for Micropython. It receives the code value for detected gesture from the ML model that runs on Arduino Grove Vision AI module
# 7C:DF:A1:07:BE:A6
# Receiving the espnow packate and changing the LED colors to RED | GREEN | BLUE
# depending on the Gesture detection Punch || Five

import time
import machine, network
import espnow
import ubinascii
import neopixel

receiver = network.WLAN(network.WLAN.IF_STA)
receiver.active(True)

mac_bytes = receiver.config('mac')
mac_address = ubinascii.hexlify(mac_bytes, ':').decode().upper()
print(mac_address)

espnow = espnow.ESPNow()
espnow.active(True)

sender = b'\x7C\xDF\xA1\x07\xBE\xA6'
espnow.add_peer(sender)

ledcorner = machine.Pin(12)
ledfive = machine.Pin(45)
ledseven = machine.Pin(47)

neopixelcorner = neopixel.NeoPixel(ledcorner, 9)
neopixelfive = neopixel.NeoPixel(ledfive, 5)
neopixelseven = neopixel.NeoPixel(ledseven, 7)

red = 255
green = 0
blue = 0

def set_brightness(red):
    for i in range(9):
        neopixelcorner[i] = (red, green, blue)
    for i in range(5):
        neopixelfive[i] = (red, green, blue)
    for i in range(7):
        neopixelseven[i] = (red, green, blue)
        
    neopixelcorner.write()
    neopixelfive.write()
    neopixelseven.write()
    
set_brightness(red)

while(1):  
    
    mac, msg = espnow.recv()
    if(msg):
        print('msg received')
        if(msg == b'\xf0'):
            red = red + 20
        else:
            red = red - 20
    
    print(msg)
    if (red >= 255):
        red = 255
    if (red <= 0):
        red = 0
        
    set_brightness(red)    
        
    time.sleep(1)

ML-ARDUINO-CODE

Arduino
Arduino code for ML demo
#include <Wire.h>
#include "Seeed_Arduino_GroveAI.h"
#include "WiFi.h"
#include <esp_now.h>

GroveAI ai(Wire);
uint8_t state = 0;

uint8_t receiver_addr[] = {0x10, 0x20, 0xBA, 0x59, 0x7A, 0xC8};

typedef struct predictions  {
  uint8_t id;
  uint8_t confidence;
} predictions;

predictions myresults;

uint8_t ie_results = 0x00; 

esp_now_peer_info_t peerInfo;

//send callback
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
  // put your setup code here, to run once:
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  //send callback
  esp_now_register_send_cb(esp_now_send_cb_t(OnDataSent));
  
  memcpy(peerInfo.peer_addr, receiver_addr, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  if(esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }

  Wire.begin(8, 7);    //sda,scl
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.println(WiFi.macAddress());
  

  Serial.println("Initializing....");
  if(ai.begin(ALGO_OBJECT_DETECTION, MODEL_EXT_INDEX_1)) {
    Serial.println("Version: 0x");
    Serial.println(ai.version(), HEX);
    Serial.println("ID: 0x");
    Serial.println(ai.id(), HEX);
    Serial.println("Algorithm: ");
    Serial.println(ai.algo());
    Serial.println("Model: ");
    Serial.println(ai.model());
    Serial.println("Confidence:");
    Serial.println(ai.confidence());
    state=1;
    
    } else {
      Serial.println("Algorithm initialization failed.");
      }
}

void loop() {
  // put your main code here, to run repeatedly:
  if (state == 1) {
        uint32_t tick = millis();
        if (ai.invoke()) { // Start invocation
            while (ai.state() != CMD_STATE_IDLE) { // Wait for invocation to finish
                delay(20);
            }

            uint8_t len = ai.get_result_len(); // Get the number of detected objects
            if (len > 0) {
                Serial.print("Time consumed: ");
                Serial.println(millis() - tick);
                Serial.print("Number of detected objects: ");
                Serial.println(len);

                for (int i = 0; i < len; i++) {
                    object_detection_t data; // Prepare to receive data
                    ai.get_result(i, (uint8_t *)&data, sizeof(object_detection_t)); // Get result
                    printDetectionResult(data);
                    if (data.target == 0) {
                      ie_results = 0xF0;
                      Serial.println("Five Detected");
                    }
                    else {
                      ie_results = 0xFE;
                      Serial.println("Punch Detected");
                    }
                    myresults.confidence = data.confidence;
                }                
                Serial.println("Sending espnow data");    

                esp_err_t result = esp_now_send(receiver_addr, (uint8_t *) &ie_results, sizeof(ie_results));
                
                if (result == ESP_OK) {
                  Serial.println("Sent with success");
                }
                else {
                  Serial.println("Error sending the data");
                }
              }
                
            else {
                Serial.println("No objects detected.");
            }
        } 
        else {
            delay(1000);
            Serial.println("Invocation failed.");
        }
    } 
  else {
        state = 0; // Reset state
    }
}

void printDetectionResult(const object_detection_t &data) {
    Serial.println("Result: Detected");
    Serial.print("Object: ");
    Serial.print(data.target);
    Serial.print("\tX: ");
    Serial.print(data.x);
    Serial.print("\tY: ");
    Serial.print(data.y);
    Serial.print("\tWidth: ");
    Serial.print(data.w);
    Serial.print("\tHeight: ");
    Serial.print(data.h);
    Serial.print("\tConfidence: ");
    Serial.println(data.confidence);
}

Mirophone-I2S-MicroPython

Python
Code to collect samples from Microphone onboard
from machine import ADC, I2C, Pin, I2S, freq, SPI, SDCard
import time
import os
from time import sleep
import neopixel, machine
import uos

#======= USER CONFIGURATION =======
RECORD_TIME_IN_SECONDS = 30
SAMPLE_RATE_IN_HZ = 22050

#======= USER CONFIGURATION =======
WAV_SAMPLE_SIZE_IN_BITS = 16
WAV_SAMPLE_SIZE_IN_BYTES = WAV_SAMPLE_SIZE_IN_BITS // 8
MIC_SAMPLE_BUFFER_SIZE_IN_BYTES = 4096
SDCARD_SAMPLE_BUFFER_SIZE_IN_BYTES = MIC_SAMPLE_BUFFER_SIZE_IN_BYTES // 2

NUM_SAMPLE_BYTES_TO_WRITE = RECORD_TIME_IN_SECONDS * SAMPLE_RATE_IN_HZ * WAV_SAMPLE_SIZE_IN_BYTES
NUM_SAMPLES_IN_DMA_BUFFER = 256
NUM_CHANNELS = 2


#sd = machine.SDCard(slot=2, cs=machine.Pin(40))

def snip_16_mono(samples_in, samples_out):
    num_samples = len(samples_in) // 4
    for i in range(num_samples):
        samples_out[2*i] = samples_in[4*i + 2]
        samples_out[2*i + 1] = samples_in[4*i + 3]
            
    return num_samples * 2

def create_wav_header(sampleRate, bitsPerSample, num_channels, num_samples):
    datasize = num_samples * num_channels * bitsPerSample // 8
    o = bytes("RIFF",'ascii')                                                   # (4byte) Marks file as RIFF
    o += (datasize + 36).to_bytes(4,'little')                                   # (4byte) File size in bytes excluding this and RIFF marker
    o += bytes("WAVE",'ascii')                                                  # (4byte) File type
    o += bytes("fmt ",'ascii')                                                  # (4byte) Format Chunk Marker
    o += (16).to_bytes(4,'little')                                              # (4byte) Length of above format data
    o += (1).to_bytes(2,'little')                                               # (2byte) Format type (1 - PCM)
    o += (num_channels).to_bytes(2,'little')                                    # (2byte)
    o += (sampleRate).to_bytes(4,'little')                                      # (4byte)
    o += (sampleRate * num_channels * bitsPerSample // 8).to_bytes(4,'little')  # (4byte)
    o += (num_channels * bitsPerSample // 8).to_bytes(2,'little')               # (2byte)
    o += (bitsPerSample).to_bytes(2,'little')                                   # (2byte)
    o += bytes("data",'ascii')                                                  # (4byte) Data Chunk Marker
    o += (datasize).to_bytes(4,'little')                                        # (4byte) Data size in bytes
    return o

bck_pin = Pin(36)
ws_pin = Pin(37)
sdin_pin = Pin(35)

audio_in = I2S(0,                                 # create I2S peripheral to read audio   # sample data from an INMP441
               mode=I2S.RX, # microphone module 
               sck=bck_pin,
               ws=ws_pin, sd=sdin_pin,
               bits=32,
               format=I2S.STEREO,
               rate=22050,
               ibuf=20000
               )

wav = open('sound/green_12.wav','wb')

# create header for WAV file and write to SD card
wav_header = create_wav_header(
    SAMPLE_RATE_IN_HZ, 
    WAV_SAMPLE_SIZE_IN_BITS, 
    NUM_CHANNELS, 
    SAMPLE_RATE_IN_HZ * RECORD_TIME_IN_SECONDS
)
num_bytes_written = wav.write(wav_header)

# allocate sample arrays
#   memoryview used to reduce heap allocation in while loop
mic_samples = bytearray(MIC_SAMPLE_BUFFER_SIZE_IN_BYTES)
mic_samples_mv = memoryview(mic_samples)
wav_samples = bytearray(SDCARD_SAMPLE_BUFFER_SIZE_IN_BYTES)
wav_samples_mv = memoryview(wav_samples)

num_sample_bytes_written_to_wav = 0

print('Starting')

while num_sample_bytes_written_to_wav < NUM_SAMPLE_BYTES_TO_WRITE:
    try:
        # try to read a block of samples from the I2S microphone
        # readinto() method returns 0 if no DMA buffer is full
        num_bytes_read_from_mic = audio_in.readinto(mic_samples_mv)
        if num_bytes_read_from_mic > 0:
            # snip upper 16-bits from each 32-bit microphone sample
            num_bytes_snipped = snip_16_mono(mic_samples_mv[:num_bytes_read_from_mic], wav_samples_mv)
            num_bytes_to_write = min(num_bytes_snipped, NUM_SAMPLE_BYTES_TO_WRITE - num_sample_bytes_written_to_wav)
            # write samples to WAV file
            num_bytes_written = wav.write(wav_samples_mv[:num_bytes_to_write])
            num_sample_bytes_written_to_wav += num_bytes_written
    except (KeyboardInterrupt, Exception) as e:
        print('caught exception {} {}'.format(type(e).__name__, e))
        break

ESP-NOW Arduino

Arduino
Arduino code for ESP-NOW
#include <Keypad.h>
#include "WiFi.h"
#include <esp_now.h>
#include <esp_wifi.h>

uint8_t val;
esp_err_t result;

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS]  {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

byte rawPins[ROWS] = {8, 7, 6, 5};
byte colPins[COLS] = {4, 3, 2, 1};

Keypad keypad = Keypad(makeKeymap(keys), rawPins, colPins, ROWS, COLS);

//espnow related defines
uint8_t receiver_addr[] = {0x10, 0x20, 0xBA, 0x59, 0x7A, 0xC8};
esp_now_peer_info_t peerInfo;

//send callback
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_MODE_STA);
  esp_wifi_set_protocol( WIFI_IF_STA , WIFI_PROTOCOL_LR);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  //send callback
  esp_now_register_send_cb(esp_now_send_cb_t(OnDataSent));
  
  memcpy(peerInfo.peer_addr, receiver_addr, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  if(esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  char key = keypad.getKey();
  if(key) {
    Serial.println("Key Pressed : ");
    Serial.println(key);
    val = (int)key;

    switch (val) {
    //2 pressed
      case 50:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
      
      case 51:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
      case 53:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 54:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 56:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 57:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 48:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 65:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 66:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 67:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
        case 68:
        result = esp_now_send(receiver_addr, (uint8_t *) &val, sizeof(val));
                
        if (result == ESP_OK) {
          Serial.println("Sent with success");
        }
        else {
          Serial.println("Error sending the data");
        }
        break;
    }
    Serial.println(val);
  }
}

ESP-NOW Python code

Python
Micropython code for ESP-NOW
import time
import machine, network
import espnow
import ubinascii
import neopixel

receiver = network.WLAN(network.WLAN.IF_STA)
receiver.active(True)
receiver.config(protocol=network.MODE_LR)

mac_bytes = receiver.config('mac')
mac_address = ubinascii.hexlify(mac_bytes, ':').decode().upper()
print(mac_address)

espnow = espnow.ESPNow()
espnow.active(True)

sender = b'\x7C\xDF\xA1\x07\xBE\xA6'
espnow.add_peer(sender)

ledcorner = machine.Pin(12)
ledfive = machine.Pin(45)
ledseven = machine.Pin(47)

neopixelcorner = neopixel.NeoPixel(ledcorner, 9)
neopixelfive = neopixel.NeoPixel(ledfive, 5)
neopixelseven = neopixel.NeoPixel(ledseven, 7)

red = 255
green = 0
blue = 0

def set_color(red, green, blue):
    for i in range(9):
        neopixelcorner[i] = (red, green, blue)
    for i in range(5):
        neopixelfive[i] = (red, green, blue)
    for i in range(7):
        neopixelseven[i] = (red, green, blue)
        
    neopixelcorner.write()
    neopixelfive.write()
    neopixelseven.write()
    
while(1):
    mac, msg = espnow.recv()
    if(msg):
        print('msg received')
        print(msg)
        if(msg == b'2'):
            set_color(255, 255, 255)
        if(msg == b'3'):
            set_color(255, 0, 0)
        if(msg == b'5'):
            set_color(255, 255, 0)
        if(msg == b'6'):
            set_color(0, 255, 0)
        if(msg == b'8'):
            set_color(0, 255, 255)
        if(msg == b'9'):
            set_color(0, 0, 255)
        if(msg == b'0'):
            set_color(0, 0, 0)
        if(msg == b'A'):
            set_color(255, 0, 255)
        if(msg == b'B'):
            set_color(255, 50, 50)

Credits

ujjval r.
12 projects • 22 followers

Comments