element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Light Up Your Life
  • Challenges & Projects
  • Design Challenges
  • Light Up Your Life
  • More
  • Cancel
Light Up Your Life
Forum RGBMATRIX enabling BLE to change light color and adding effects - Blog-5
  • News
  • Forum
  • Projects
  • DC
  • Leaderboard
  • Files
  • Members
  • More
  • Cancel
  • New
Join Light Up Your Life to participate - click to join for free!
Actions
  • Share
  • More
  • Cancel
Forum Thread Details
  • Replies 0 replies
  • Subscribers 50 subscribers
  • Views 43 views
  • Users 0 members are here
  • esp32
  • würth
  • Light Up Your Life
Related

RGBMATRIX enabling BLE to change light color and adding effects - Blog-5

embeddedguy
embeddedguy 5 days ago

Table of Contents

  • Using Micropython BLE(Bluetooth) module
    • Using NVS to store color values
  • Adding Effects to the LEDs

This is blog-5 of Light Up your life challenge. In this blog post, I am going to cover the progress that I have made so far. In the previous blogs I have discussed LED parameters, PCB schematic creation in KiCad and then testing the final PCB that I received from NextPCB. So far everything worked fine as expected and that is good news (relief) for me.

Post-4 https://community.element14.com/challenges-projects/design-challenges/light-up-your-life/f/forum/56299/rgb-matrix-testing-wireless-communication---blog-4
Post-3 https://community.element14.com/challenges-projects/design-challenges/light-up-your-life/f/forum/56232/rgb-matrix-testing-the-new-pcb
Post-2 https://community.element14.com/challenges-projects/design-challenges/light-up-your-life/f/forum/56186/design-idea 
Post-1 https://community.element14.com/challenges-projects/design-challenges/light-up-your-life/f/forum/56140/planning-for-the-light-up-your-life-kit

In the last blog post, I have just started to create something interesting. I started to use wireless communication such as the ESP-NOW protocol to change the color and control the LEDs. That worked as expected but needed some more secure and finer control. I will continue to work on ESP-NOW but before that I have BLE functionality enabled on this device.

With ESP-NOW, I was using switches for analog input to the ESP32 chip and based on the analog values the ADC value will detect a button press. This is fine. But there are some issues that needs to be covered. They are following.

First, ADCs can suffer from noise. That means that there can be a wrong Analog value that can be detected by the chip. To reduce that there are some methods suggested by Espressif to put some 1nF capacitors at the input pin. So that the pin value does not accidentally change to 0.

But there are other wireless technologies such as Bluetooth that can be more effective in such cases. For example, BLE can operate using Android applications and follows a standard procedure to connect, disconnect, write values for characteristics, etc. This will reduce the need for separate remote devices to control the LED lights but will need complex Android BLE application programming knowledge. 

One more protocol that I have seen in some smart home products is using IR LEDs to transmit data. This can be something like ESP RMT protocol that transmits IR pulses using remote. This will work only when transmitter and receiver devices are in line of sight for not more than 30 meters or similar. The advantage is that it is easy to control the lights using tiny remote and no need to manage BLE connections. Also like ESP-NOW there is not a chance of accidently sending a wrong value if keyboard is not working as expected. 

Using Micropython BLE(Bluetooth) module

BLE protocol defines GATT service and GATT characteristics. These characteristic values can be read or write. There are other functions of these characteristics as well such as notify and indicate. But for read and write one can change the individual color value. i.e. between 0-255. There are many other possibilities such as fine control of strip and adding effects etc. But for now let's consider a simple application to change the color of the LEDs.

For this part, I am using the official Bluetooth module of Micropython. This module provides the required functionality for Bluetooth to work. 


import time
import bluetooth
from bluetooth import BLE
import asyncio
import ubinascii
import neopixel
import machine
from micropython import const
import esp32

One need to register the event IDs for the callback functions. The following code snipet shows some of the common IDs such as connect, disconnect and characteristic read/write.

_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)

After this it is required to create a Bluetooth object and define connect, disconnect and IRQ callback functions. Remember not to forget some of these functions as they are mandatory to be defined for proper Bluetooth functionality. 

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()
        
        
    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')
        

Next it is required to define some Neopixel objects on different pins for different types of LEDs.

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)

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))

Now we are good to go. Only the final thing is to define the main() function, create object and run it continuously. 

def main():
    BLE = RGBMATRIX("ESP32S3")
    #BLE.set_color(255)
    
if __name__ == "__main__":
    main()

One done just run the code. You will see that the device named ESP32S3 will appear on your BLE scanner lists. Now one can connect to the device and write the characteristics value for RED, GREEN and BLUE colors. The following video shows the colors in action with BLE application for Windows. One can use any BLE application such as nRF connect on mobile phones.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

Using NVS to store color values

One most important thing to me is to store color values. I do not prefer that when there is power cut or user turns off the main switch and starts the LED light, there is either no color or there is specific prefixed color like white. I think that the LEDs should have the same color that was set previously. And for this reason, I am using NVS (non-volatile storage) to store color key-value pairs. In the begging the storage will be initialized once and values stored will be the color values for the LEDs. 

This feature is useful in case where there are frequent power cuts and user do not need to set colors each time power is back or any situation similar to that.

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()

In the __init__ function one can insert the following code. This will set the values of each color to previously set values. 

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))        

The update the LED colors as there is BLE Write operation to the characteristic. 

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)

In the set_led_strip() store the values

 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))     

Adding Effects to the LEDs

Effects are ways to make LEDs blink more decorative and catchier. Hence, there are some standard effects such as Rainbow, chasing effect, fading effect, etc.

Out of these I was able to create Rainbow effect on these LEDs. Basically, one can take inspiration from Internet for different effects that LEDs can create. Here I have created a Rainbow effect using a Wheel() function that creates R, G, B values for the each color and updates these colors on the strip. Using some for() loop and doing the mathamatics of colors one can generate different effects that look eye catchy. 

Following video shows the Rainbow effect on the LEDs.

(note: I still need to work on one of the SPI LEDs to create this effect but other SPI and normal LEDs work fine.)

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

import neopixel
import machine
import time
import esp32
from machine import PWM, Pin

class LIGHT_EFFECTS(object):
    
    def __init__(self, interface = 2, miso = 38, mosi = 14, sck=10, num_led = 7, firstbit=0):
        
        self.spi_num = interface
        self.sck_pin = Pin(sck)
        self.miso_pin = Pin(miso)
        self.mosi_pin = Pin(mosi)
        self.first_bit = firstbit
        self.leds = num_led
        self.spi = machine.SPI(self.spi_num, baudrate=8000000, polarity = 0, phase = 0, sck = self.sck_pin, mosi = self.mosi_pin, miso = self.miso_pin, firstbit=self.first_bit)
        self.start_frame = bytes([0x00, 0x00, 0x00, 0x00])
        
    def send_start(self):
        self.spi.write(self.start_frame)
    
    def wheel(self, pos):
        if(pos) < 85:
            return (pos * 3, 255 - pos * 3, 0)
        elif pos < 170:
            pos -= 85
            return (255 - pos * 3, 0, pos * 3)
        else:
            pos -= 170
            return (0, pos * 3, 255 - pos * 3)
        
    def rainbow_cycle(self, wait, numled, pin_num):
        neopixel_id = neopixel.NeoPixel(machine.Pin(pin_num), numled)
        for j in range(255):
            for i in range(numled):
                pixel_index = (i * 256 // numled) + j
                neopixel_id[i] = self.wheel(pixel_index & 255)
                neopixel_id.write()
                time.sleep(wait)
                
    def rainbow_cycle_spi(self, wait, numled, pin_num):
        frame = []
        
        self.spi = machine.SPI(self.spi_num, baudrate=8000000, polarity = 0, phase = 0, sck = self.sck_pin, mosi = self.mosi_pin, miso = self.miso_pin, firstbit=self.first_bit)

        #b, g, r = self.get_pixel(0x00, 0x00, 0xff)
        for j in range(255):            
            for i in range(self.leds):                
                pixel_index = (i * 256 // numled) + j
                r, g, b = self.wheel(pixel_index & 255)
                frame.append(0xFC)
                frame.append(b)
                frame.append(g)
                frame.append(r)
            self.spi.write(self.start_frame)
            self.spi.write(bytearray(frame))
            self.spi.write('b\x07')
            time.sleep(wait)
            frame = []
            
            
    def wheel2(self, pos):
        
        if(pos) < 1365:
            return (pos * 3, 4096 - pos * 3 , 0)
        
        elif pos < 2730:
            pos -= 85
            return (4096 - pos * 3 , 0, pos * 3)
        
        else:
            pos -= 2730 
            return (0, pos * 3, 4096 - pos * 3 )
            
    def rainbow_cycle_spi2(self, wait, numled):
        
        frame = []
        self.spi = machine.SPI(self.spi_num, baudrate=8000000, polarity = 0, phase = 0, sck = 40, mosi = 13, miso = 38, firstbit=0)
        
        for i in range(numled):
            for k in range(48):
                if((color >> i) & 0x1):
                    frame.append(0xFC)
                else:
                    frame.append(0xC0)
                    
            send = bytearray(frame)
            self.spi.write(send)
            time.sleep(wait)
            frame = []
        
                
eff  = LIGHT_EFFECTS()

while True:
    eff.rainbow_cycle(0.0000001, 9, 12)
    eff.rainbow_cycle(0.00001, 7, 47)
    eff.rainbow_cycle(0.000001, 5, 45)
    eff.rainbow_cycle_spi(0.01, 5, 45)
    #eff.rainbow_cycle_spi2(0.001, 3)

  • Sign in to reply
  • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube