Skip to content
Antoine Lafitte edited this page Jul 17, 2024 · 11 revisions

Welcome to the bbk3-rythm wiki!

For now, I will explain what I have done and what I plan on doing on this project.

Introduction

Cube (hardware)

The 'BBK3' is a small cube-like keyboard. it is small enough to be held in one hand and it can act like a controller to use in games. It is composed of a Seeed XIAO nrf52840 sense and 5 switches, RGB lights and custom made key caps.

Cube (firmware)

Python and CircuitPython

At the beginning of the project, the cube was using python with CircuitPython to use different components of the board. I spent the first weeks trying to learn CircuitPython and coding on SoCs because it was a first for me.

Archive of the latest code in python :

"""
import time
import board
from digitalio import DigitalInOut, Pull
from adafruit_debouncer import Debouncer
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
import neopixel

print("- Deco Keypad -")
time.sleep(1)  # Sleep for a bit to avoid a race condition on some systems

#  ----- Keymap -----  #
# change as needed, e.g. capital A (Keycode.SHIFT, Keycode.A)
switch_a_output = Keycode.A
switch_b_output = Keycode.W
switch_c_output = Keycode.D
switch_d_output = Keycode.S
switch_e_output = Keycode.SPACE

#  ----- Keyboard setup -----  #
keyboard = Keyboard(usb_hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)  # We're in the US :)

# ----- Key setup ----- #
switch_a_in = DigitalInOut(board.D1)
switch_b_in = DigitalInOut(board.D2)
switch_c_in = DigitalInOut(board.D3)
switch_d_in = DigitalInOut(board.D4)
switch_e_in = DigitalInOut(board.D5)

switch_a_in.pull = Pull.UP
switch_b_in.pull = Pull.UP
switch_c_in.pull = Pull.UP
switch_d_in.pull = Pull.UP
switch_e_in.pull = Pull.UP

switch_a = Debouncer(switch_a_in)
switch_b = Debouncer(switch_b_in)
switch_c = Debouncer(switch_c_in)
switch_d = Debouncer(switch_d_in)
switch_e = Debouncer(switch_e_in)


# ----- NeoPixel setup ----- #
MAGENTA = 0xFF00FF
CYAN = 0x0088DD
WHITE = 0xCCCCCC
BLACK = 0x000000

pixel_pin = board.D0
pixels = neopixel.NeoPixel(pixel_pin, 5, brightness=0.25)
pixels.fill(BLACK)
time.sleep(0.3)
pixels.fill(WHITE)
time.sleep(0.3)
pixels.fill(BLACK)
time.sleep(0.3)
pixels[0] = MAGENTA
pixels[1] = CYAN
pixels[2] = MAGENTA
pixels[3] = CYAN
pixels[4] = MAGENTA



while True:
    switch_a.update()  # Debouncer checks for changes in switch state
    switch_b.update()
    switch_c.update()
    switch_d.update()
    switch_e.update()

    if switch_a.fell:
        keyboard.press(switch_a_output)
        pixels[0] = WHITE

    if switch_a.rose:
        keyboard.release(switch_a_output)
        pixels[0] = MAGENTA

    if switch_b.fell:
        keyboard.press(switch_b_output)
        pixels[1] = WHITE
    if switch_b.rose:
        keyboard.release(switch_b_output)
        pixels[1] = CYAN

    if switch_c.fell:
        keyboard.press(switch_c_output)
        pixels[2] = WHITE
    if switch_c.rose:
        keyboard.release(switch_c_output)
        pixels[2] = MAGENTA

    if switch_d.fell:
        keyboard.press(switch_d_output)
        pixels[3] = WHITE
    if switch_d.rose:
        keyboard.release(switch_d_output)
        pixels[3] = CYAN

    if switch_e.fell:
        keyboard.press(switch_e_output)
        pixels[4] = WHITE
    if switch_e.rose:
        keyboard.release(switch_e_output)
        pixels[4] = MAGENTA
"""
from _bleio import adapter
from board import IMU_PWR, IMU_SCL, IMU_SDA
from busio import I2C
from digitalio import DigitalInOut, Direction
from time import sleep

from adafruit_lsm6ds.lsm6ds3 import LSM6DS3

DEVICE_NAME = "XIAO nRF52840 Sense"
INTERVAL = 0.1
SENSITIVITY = 0.01

# Turn on IMU and wait 50 ms
imu_pwr = DigitalInOut(IMU_PWR)
imu_pwr.direction = Direction.OUTPUT
imu_pwr.value = True
sleep(0.05)

# Set up I2C bus and initialize IMU
i2c_bus = I2C(IMU_SCL, IMU_SDA)
sensor = LSM6DS3(i2c_bus)

class BTHomeAdvertisement:
    _ADV_FLAGS = [0x02, 0x01, 0x06]
    _ADV_SVC_DATA = [0x06, 0x16, 0xD2, 0xFC, 0x40, 0x22, 0x00]

    def _name2adv(self, local_name):
        adv_element = bytearray([len(local_name) + 1, 0x09])
        adv_element.extend(bytes(local_name, "utf-8"))
        return adv_element

    def __init__(self, local_name=None):
        if local_name:
            self.adv_local_name = self._name2adv(local_name)
        else:
            self.adv_local_name = self._name2adv(adapter.name)

    def adv_data(self, movement):
        adv_data = bytearray(self._ADV_FLAGS)
        adv_svc_data = bytearray(self._ADV_SVC_DATA)
        adv_svc_data[-1] = movement
        adv_data.extend(adv_svc_data)
        adv_data.extend(self.adv_local_name)
        return adv_data

bthome = BTHomeAdvertisement(DEVICE_NAME)

while True:
    gyro_x, gyro_y, gyro_z = sensor.gyro
    moving = gyro_x**2 + gyro_y**2 + gyro_z**2
    if moving > SENSITIVITY:
        print("Moving")
        print((gyro_x, gyro_y, gyro_z))
        adv_data = bthome.adv_data(1)
    else:
        adv_data = bthome.adv_data(0)
    adapter.start_advertising(
        adv_data, scan_response=None, connectable=False, interval=INTERVAL * 2
    )
    sleep(INTERVAL)
    adapter.stop_advertising()

C++ and Arduino

I then switched to arduino because it's in C++, a language I am a bit more familiar with (even though Python is easy to use). I mainly did the switch because i saw that there was a lot of documentation, examples and even forum posts about arduino, plus a lot of what i see online, even though it's not entirely made for my board, provide a great starting point for a direction to follow.

The code is not finished yet so I will update this page later.

WIP

Links used during this project

Python

https://wiki.seeedstudio.com/XIAO_BLE/
https://learn.adafruit.com/circuitpython-essentials/circuitpython-essentials
https://learn.adafruit.com/welcome-to-circuitpython/overview
https://docs.circuitpython.org/projects/lsm6dsox/en/latest/
https://github.com/koenvervloesem/BTHome-Inertial-Sensor-in-CircuitPython
https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html
https://learn.adafruit.com/custom-hid-devices-in-circuitpython?view=all
https://circuitpython-joystickxl.readthedocs.io/en/stable/intro.html

C++

A lot of the current code is from built from examples provided by the libraries downloaded or already on the board
https://github.com/adafruit/Adafruit_NeoPixel
https://github.com/adafruit/Adafruit_TinyUSB_Arduino
https://github.com/Seeed-Studio/Seeed_Arduino_LSM6DS3

Gamepad Tests

https://hardwaretester.com/gamepad

Unity

https://docs.unity3d.com/Packages/[email protected]/manual/Layouts.html
https://docs.unity3d.com/Packages/[email protected]/manual/HID.html
https://forum.unity.com/threads/new-input-system-custom-input-device-analog-reading-as-0-1-0-1-for-1-1.1116076/