MicroPython bluetooth remote control LED between Raspberry Pi Pico W and ESP32-C3, using aioble library.

Last post introduced "MicroPython bluetooth example using aioble library, temp_client.py and temp_sensor.py". This post modified it to remote control LED via bluetooth between Raspberry Pi Pico W and ESP32-C3-DevKitM-1, achieve the same function as in "BLE examples run on Nano 33 BLE (as Peripheral) and Uno R4 WiFi (as Central), using ArduinoBLE in Arduino framework".


Basically, the bluetooth/aioble parts on both boards are same, but code in separated is caused by different configurare of onboard LED.

Code:

mpy_aioble_peripheral_led_esp32c3.py
"""
MicroPython v1.21 exercise run on Espressif ESP32-C3-DevKitM-1 using aioble lib.
Play the role as BLE Peripheral of LED.

As the role in ArduinoBLE examples>Peripheral>LED

- Setup as BLE Peripheral of LED, wait connection from Central device.
- Turn ON/OFF onboard LED according to received command:
  0 - Turn OFF LED
  1 - Turn ON LED
"""

import sys
import machine
import os
import time
import neopixel

sys.path.append("")

from micropython import const

import uasyncio as asyncio
import aioble
import bluetooth

import random
import struct

# On Espressif ESP32-C3-DevKitM-1:
# and the onboard RGB LED is connected to GPIO8
np = neopixel.NeoPixel(machine.Pin(8), 1)

# To match with UUIDs in ArduinoBLE side,
# they are re-assigned following ArduinoBLE.
_ENV_LED_UUID = bluetooth.UUID('19b10000-e8f2-537e-4f6c-d104768a1214')
_ENV_LED_CONTROL_UUID = bluetooth.UUID('19b10001-e8f2-537e-4f6c-d104768a1214')

# I don't what appearance should be used, so I choice unknown
# key="0" value="Unknown" description="None"
_ADV_APPEARANCE_UNKNOWN = const(0)

# How frequently to send advertising beacons.
_ADV_INTERVAL_MS = 250_000

# Register GATT server.
led_service = aioble.Service(_ENV_LED_UUID)
led_characteristic = aioble.Characteristic(
    led_service, _ENV_LED_CONTROL_UUID, write=True, read=True, notify=True, capture=True
)
aioble.register_services(led_service)

# Serially wait for connections. Don't advertise while a central is
# connected.
async def peripheral_task():
    while True:
        async with await aioble.advertise(
            _ADV_INTERVAL_MS,
            name="LED",
            services=[_ENV_LED_UUID],
            appearance=_ADV_APPEARANCE_UNKNOWN,
        ) as connection:
            print("Connection from", connection.device)
            
            #await connection.disconnected()
            while connection.is_connected():
                await asyncio.sleep_ms(500)
                
            print("Dis-connected")
         
async def read_task():
    while True:
        connection, data = await led_characteristic.written()
        print(connection)
        print(type(data), data)
        print(type(data[0]), data[0])
        
        if data[0]==0:
            ledoff()
        else:
            ledon()
        
        await asyncio.sleep(1)

def ledon():
    np[0] = (10, 10, 10)
    np.write()
    
def ledoff():
    np[0] = (0, 0, 0)
    np.write()

print("=========================================================")
print("mpy_aioble_peripheral_led_esp32c3.py")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("---------------------------------------------------------")
print(aioble.__name__, aioble.__version__)
print("=========================================================")
print()

ledoff()
time.sleep(0.5)
for l in range(3):
    ledon()
    time.sleep(0.2)
    ledoff()
    time.sleep(0.2)
    
# Run the tasks.
async def main():
    t1 = asyncio.create_task(peripheral_task())
    t2 = asyncio.create_task(read_task())
    await asyncio.gather(t1, t2)
    
asyncio.run(main())


mpy_aioble_central_ledControl_picoW.py
"""
MicroPython v1.21 exercise run on Raspberry Pi Pico W using aioble lib.
Play the role as BLE Central of LedCentrol.

As the role in ArduinoBLE examples>Central>LedControl

- Connect to BLE Peripheral of LED
- Read GPIO9 to send commmand to Peripheral.
  0 - Turn OFF LED
  1 - Turn ON LED
"""

import sys
import machine
import os
import time

sys.path.append("")

from micropython import const

import uasyncio as asyncio
import aioble
import bluetooth

import random
import struct

# GP9 as button input
button = machine.Pin(9, machine.Pin.IN, machine.Pin.PULL_UP)

# To match with UUIDs in ArduinoBLE side,
# they are re-assigned following ArduinoBLE.
_ENV_LED_UUID = bluetooth.UUID('19b10000-e8f2-537e-4f6c-d104768a1214')
_ENV_LED_CONTROL_UUID = bluetooth.UUID('19b10001-e8f2-537e-4f6c-d104768a1214')

async def find_led_peripheral():
    # Scan for 5 seconds, in active mode, with very low interval/window (to
    # maximise detection rate).
    async with aioble.scan(5000, interval_us=30000, window_us=30000, active=True) as scanner:
        async for result in scanner:
            # See if it matches our name and the environmental sensing service.
            if result.name() == "LED" and _ENV_LED_UUID in result.services():
                return result.device
    return None

# Helper to encode the led characteristic encoding (sint16, LED state).
def _encode_led(to_encode):
    return struct.pack("<h", int(to_encode))

async def main():
    device = await find_led_peripheral()
    if not device:
        print("LED Peripheral not found")
        return

    try:
        print("Connecting to", device)
        connection = await device.connect()
    except asyncio.TimeoutError:
        print("Timeout during connection")
        return

    async with connection:
        try:
            led_service = await connection.service(_ENV_LED_UUID)
            led_characteristic = await led_service.characteristic(_ENV_LED_CONTROL_UUID)
        except asyncio.TimeoutError:
            print("Timeout discovering services/characteristics")
            return
        
        print("connected", connection)
        
        # assume button released after connected
        lastButton = 1
        await led_characteristic.write(_encode_led(0x00))

        while True: 
            nowButton = button.value()
            if nowButton != lastButton:
                lastButton = nowButton
            
                if nowButton:
                    await led_characteristic.write(_encode_led(0x00))
                    led.off()
                else:
                    await led_characteristic.write(_encode_led(0x01))
                    led.on()
                            
            await asyncio.sleep_ms(100)

print("=========================================================")
print("mpy_aioble_central_ledControl_picoW.py")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("---------------------------------------------------------")
print(aioble.__name__, aioble.__version__)
print("=========================================================")
print()

"""
Unlike the original Raspberry Pi Pico,
the on-board LED on Pico W is not connected to a pin on RP2040,
but instead to a GPIO pin on the wireless chip.

MicroPython has been modified accordingly.
This means that we can now do:
"""
led = machine.Pin("LED", machine.Pin.OUT)
led.off()
time.sleep(0.5)
for l in range(3):
    led.on()
    time.sleep(0.2)
    led.off()
    time.sleep(0.2)

asyncio.run(main())


mpy_aioble_peripheral_led_picoW.py
"""
MicroPython v1.21 exercise run on Raspberry Pi Pico W using aioble lib.
Play the role as BLE Peripheral of LED.

As the role in ArduinoBLE examples>Peripheral>LED

- Setup as BLE Peripheral of LED, wait connection from Central device.
- Turn ON/OFF onboard LED according to received command:
  0 - Turn OFF LED
  1 - Turn ON LED
"""

import sys
import machine
import os
import time

sys.path.append("")

from micropython import const

import uasyncio as asyncio
import aioble
import bluetooth

import random
import struct

# To match with UUIDs in ArduinoBLE side,
# they are re-assigned following ArduinoBLE.
_ENV_LED_UUID = bluetooth.UUID('19b10000-e8f2-537e-4f6c-d104768a1214')
_ENV_LED_CONTROL_UUID = bluetooth.UUID('19b10001-e8f2-537e-4f6c-d104768a1214')

# I don't what appearance should be used, so I choice unknown
# key="0" value="Unknown" description="None"
_ADV_APPEARANCE_UNKNOWN = const(0)

# How frequently to send advertising beacons.
_ADV_INTERVAL_MS = 250_000

# Register GATT server.
led_service = aioble.Service(_ENV_LED_UUID)
led_characteristic = aioble.Characteristic(
    led_service, _ENV_LED_CONTROL_UUID, write=True, read=True, notify=True, capture=True
)
aioble.register_services(led_service)

# Serially wait for connections. Don't advertise while a central is
# connected.
async def peripheral_task():
    while True:
        async with await aioble.advertise(
            _ADV_INTERVAL_MS,
            name="LED",
            services=[_ENV_LED_UUID],
            appearance=_ADV_APPEARANCE_UNKNOWN,
        ) as connection:
            print("Connection from", connection.device)
            
            #await connection.disconnected()
            while connection.is_connected():
                await asyncio.sleep_ms(500)
                
            print("Dis-connected")
         
async def read_task():
    while True:
        connection, data = await led_characteristic.written()
        print(connection)
        print(type(data), data)
        print(type(data[0]), data[0])
        
        if data[0]==0:
            led.off()
        else:
            led.on()
        
        await asyncio.sleep(1)

print("=========================================================")
print("mpy_aioble_peripheral_led_picoW.py")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("---------------------------------------------------------")
print(aioble.__name__, aioble.__version__)
print("=========================================================")
print()
"""
Unlike the original Raspberry Pi Pico,
the on-board LED on Pico W is not connected to a pin on RP2040,
but instead to a GPIO pin on the wireless chip.

MicroPython has been modified accordingly.
This means that we can now do:
"""
led = machine.Pin("LED", machine.Pin.OUT)
led.off()
time.sleep(0.5)
for l in range(3):
    led.on()
    time.sleep(0.2)
    led.off()
    time.sleep(0.2)
    
# Run the tasks.
async def main():
    t1 = asyncio.create_task(peripheral_task())
    t2 = asyncio.create_task(read_task())
    await asyncio.gather(t1, t2)
    
asyncio.run(main())


mpy_aioble_central_ledControl_esp32c3.py
"""
MicroPython v1.21 exercise run on Espressif ESP32-C3-DevKitM-1 using aioble lib.
Play the role as BLE Central of LedCentrol.

As the role in ArduinoBLE examples>Central>LedControl

- Connect to BLE Peripheral of LED
- Read GPIO9 (BOOT Button of ESP32-C3-DevKitM-1) to send commmand to Peripheral.
  0 - Turn OFF LED
  1 - Turn ON LED
"""

import sys
import machine
import os
import time
import neopixel

sys.path.append("")

from micropython import const

import uasyncio as asyncio
import aioble
import bluetooth

import random
import struct

# On Espressif ESP32-C3-DevKitM-1:
# The onboard BOOT Button is connected to GPIO9
# and the onboard RGB LED (NeoPixel) is connected to GPIO8

button_BOOT = machine.Pin(9, machine.Pin.IN, machine.Pin.PULL_UP)
np = neopixel.NeoPixel(machine.Pin(8), 1)

# To match with UUIDs in ArduinoBLE side,
# they are re-assigned following ArduinoBLE.
_ENV_LED_UUID = bluetooth.UUID('19b10000-e8f2-537e-4f6c-d104768a1214')
_ENV_LED_CONTROL_UUID = bluetooth.UUID('19b10001-e8f2-537e-4f6c-d104768a1214')

async def find_led_peripheral():
    # Scan for 5 seconds, in active mode, with very low interval/window (to
    # maximise detection rate).
    async with aioble.scan(5000, interval_us=30000, window_us=30000, active=True) as scanner:
        async for result in scanner:
            # See if it matches our name and the environmental sensing service.
            if result.name() == "LED" and _ENV_LED_UUID in result.services():
                return result.device
    return None

# Helper to encode the led characteristic encoding (sint16, LED state).
def _encode_led(to_encode):
    return struct.pack("<h", int(to_encode))

async def main():
    device = await find_led_peripheral()
    if not device:
        print("LED Peripheral not found")
        return

    try:
        print("Connecting to", device)
        connection = await device.connect()
    except asyncio.TimeoutError:
        print("Timeout during connection")
        return

    async with connection:
        try:
            led_service = await connection.service(_ENV_LED_UUID)
            led_characteristic = await led_service.characteristic(_ENV_LED_CONTROL_UUID)
        except asyncio.TimeoutError:
            print("Timeout discovering services/characteristics")
            return
        
        print("connected", connection)
        
        # assume button released after connected
        lastButton = 1
        await led_characteristic.write(_encode_led(0x00))

        while True:
            nowButton = button_BOOT.value()
            if nowButton != lastButton:
                lastButton = nowButton

                if nowButton:
                    await led_characteristic.write(_encode_led(0x00))
                    ledoff()
                else:
                    await led_characteristic.write(_encode_led(0x01))
                    ledon()
                
            await asyncio.sleep_ms(100)

def ledon():
    np[0] = (10, 10, 10)
    np.write()
    
def ledoff():
    np[0] = (0, 0, 0)
    np.write()

print("=========================================================")
print("mpy_aioble_central_ledControl_esp32c3.py")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("---------------------------------------------------------")
print(aioble.__name__, aioble.__version__)
print("=========================================================")
print()

ledoff()
time.sleep(0.5)
for l in range(3):
    ledon()
    time.sleep(0.2)
    ledoff()
    time.sleep(0.2)

asyncio.run(main())


next:
~ How this code communicate with Arduino (using ArduinoBLE lib) - BLE communication between MicroPython aioble and ArduinoBLE.

Comments

Popular posts from this blog

MicroPython/ESP32-C3 + 1.8" 128x160 TFT ST7735 SPI, using boochow/MicroPython-ST7735 library.

CameraWebServe: ESP32-S3 (arduino-esp32) + OV5640 camera module