XIAO RP2040 Mouse Ver 3.0

Over the last few months, We have been steadily improving the design of our XIAO RP2040-based mouse device. With this, ver 3.0 all the hardware bugs were finally eliminated, and we also placed the device into its first-ever enclosure.

Let us take a look at the design

The PCB and Schematic


The PCB is a very strange shape, with lots of cut-outs. This is to accommodate the big push buttons that will be mounted in the enclosure, as well as to fit nicely into the mounting area of the enclosure… This design took quite some time with a pair of callipers and CAD, but all went well, and the shape is perfectly accurate.


The schematic is also straight forward, with the only real changes begin to the rotary encoder. In ver 2.0, We connected the encoder to the MCP23008, but for some reason CircuitPython does not seem to like an encoder connected to an IO extender… That forced us to do some software hack to use the encoder… I have thus decided to change things around in ver 3.0 and move the encoder back to the native GPIO on the XIAO RP2040

It is also interesting to note that the circuit was initially designed for the XIAO ESP32S3, but due to issues with stock, as well as crazy prices on local parts, we made a quick turn-around and went back to the RP2040. The ESP32S3 was going to allow us to implement a wireless device, through using ESPNow protocol… That may still be done in future, but for now, I think we have done enough work on the mouse device for the time being…

Manufacturing the PCB and Assembly

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to who will do his/her best to resolve your issue as soon as possible.

Find out more here


Assembly was quite easy, I chose to use a stencil, because the IO Expander chip has a very tiny footprint, as well as a leadless package… The stencil definitely helps prevent excessive solder paste, as well as saves a lot of time on reworking later…


In the picture above, we can clearly see why I had to design the PCB with such an irregular shape.

Firmware and Coding

We are still using CircuitPython for the firmware on this device. It is not perfect, but it works, well sort of anyway. What does that mean? Well… As far as the mouse functions are concerned, clicking, scrolling, moving the pointer – all of that is works perfectly, and thus allows me to use the device for basic operations every day. Drag and Drop, as well as selecting and or highlighting text DOES NOT work. This seem to be an issue with the HID code in Circuitpython, meaning it doesn’t seem to be implemented. It is also way beyond my abilities to implement it myself…

Below is the code.py file, with the boot.py below that


import time
import board
import busio
from rainbowio import colorwheel
import neopixel
import rotaryio
import microcontroller
from digitalio import Direction
from adafruit_mcp230xx.mcp23008 import MCP23008
import digitalio
i2c = busio.I2C(board.SCL, board.SDA)
mcp = MCP23008(i2c)


from analogio import AnalogIn
import usb_hid
from adafruit_hid.mouse import Mouse
joyX = board.A0
joyY = board.A1
JoyBtn = board.D2

LeftBtn = 0
CenterBtn = 1
RightBtn = 2
UpBtn = 3
DownBtn = 4
EncoderBtn = 5


mouse = Mouse(usb_hid.devices)
xAxis = AnalogIn(joyX)
yAxis = AnalogIn(joyY)

# NEOPIXEL
pixel_pin = board.NEOPIXEL
num_pixels = 1
pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.1, auto_write=False)

leftbutton = mcp.get_pin(LeftBtn)
leftbutton.direction = digitalio.Direction.INPUT
leftbutton.pull = digitalio.Pull.UP

centerbutton = mcp.get_pin(CenterBtn)
centerbutton.direction = digitalio.Direction.INPUT
centerbutton.pull = digitalio.Pull.UP

maint_btn = digitalio.DigitalInOut(JoyBtn)
maint_btn.switch_to_input(pull=digitalio.Pull.UP)

rightbutton = mcp.get_pin(RightBtn)
rightbutton.direction = digitalio.Direction.INPUT
rightbutton.pull = digitalio.Pull.UP

enc_btn = mcp.get_pin(EncoderBtn)
enc_btn.direction = digitalio.Direction.INPUT
enc_btn.pull = digitalio.Pull.UP

scroll_up = mcp.get_pin(UpBtn)
scroll_up.direction = digitalio.Direction.INPUT
scroll_up.pull = digitalio.Pull.UP

scroll_down = mcp.get_pin(DownBtn)
scroll_down.direction = digitalio.Direction.INPUT
scroll_down.pull = digitalio.Pull.UP



mousewheel = rotaryio.IncrementalEncoder(board.D6, board.D7, 4)
last_position = mousewheel.position
print(mousewheel.position)

move_speed = 3
enc_down = 0

RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
BLACK = (0, 0, 0)


if move_speed == 0:
    in_min, in_max, out_min, out_max = (0, 65000, -20, 20)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 1:
    pixels.fill(GREEN)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -15, 15)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 2:
    pixels.fill(BLUE)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -10, 10)


filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 3:
    pixels.fill(PURPLE)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -8, 8)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 4:
    pixels.fill(CYAN)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -5, 5)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )


pixels.fill(BLACK)
pixels.show()
while True:
    # Set mouse accelleration ( speed)
    #print(mousewheel.position)
    if move_speed == 0:
        pixels.fill(BLACK)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -20, 20)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 1:
        pixels.fill(GREEN)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -15, 15)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 2:
        pixels.fill(BLUE)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -10, 10)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 3:
        pixels.fill(PURPLE)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -8, 8)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 4:
        pixels.fill(CYAN)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -5, 5)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )

    current_position = mousewheel.position
    position_change = current_position - last_position

    x_offset = filter_joystick_deadzone(xAxis.value) * -1  # Invert axis
    y_offset = filter_joystick_deadzone(yAxis.value)
    mouse.move(x_offset, y_offset, 0)

    if enc_btn.value and enc_down == 1:
        move_speed = move_speed + 1
        if move_speed > 4:
            move_speed = 0

        # print (move_speed)
        enc_down = 0

    if not enc_btn.value:
        enc_down = 1

    if leftbutton.value:
        mouse.release(Mouse.LEFT_BUTTON)
        # pixels.fill(BLACK)
        # pixels.show()
    else:
        mouse.press(Mouse.LEFT_BUTTON)
        pixels.fill(GREEN)
        pixels.show()

    if centerbutton.value:
        mouse.release(Mouse.MIDDLE_BUTTON)
    else:
        mouse.press(Mouse.MIDDLE_BUTTON)
        pixels.fill(YELLOW)
        pixels.show()

    # Center button on joystick
    if maint_btn.value:
        mouse.release(Mouse.LEFT_BUTTON)
    else:
        mouse.press(Mouse.LEFT_BUTTON)
        pixels.fill(GREEN)
        pixels.show()

    if rightbutton.value:
        mouse.release(Mouse.RIGHT_BUTTON)
        # pixels.fill(BLACK)
        # pixels.show()
    else:
        mouse.press(Mouse.RIGHT_BUTTON)
        pixels.fill(PURPLE)
        pixels.show()

    if not scroll_up.value:
        mouse.move(wheel=1)
        time.sleep(0.25)
        pixels.fill(BLUE)
        pixels.show()

    if not scroll_down.value:
        mouse.move(wheel=-1)
        time.sleep(0.25)
        pixels.fill(CYAN)
        pixels.show()

    if not scroll_up.value and not scroll_down.value:
        for x in range(4):
            pixels.fill(RED)
            pixels.show()
            time.sleep(0.5)
            pixels.fill(BLACK)
            pixels.show()
            time.sleep(0.5)
        microcontroller.reset()



    if position_change > 0:
        mouse.move(wheel=position_change)
        #print(current_position)
        #pixels.fill(BLUE)
        #pixels.show()
    elif position_change < 0:
        mouse.move(wheel=position_change)
        #print(current_position)
        #pixels.fill(CYAN)
        #pixels.show()
    last_position = current_position
    pixels.fill(BLACK)
    pixels.show()

boot.py

import storage
import board, digitalio
import time
from rainbowio import colorwheel
import neopixel
import busio
from digitalio import Direction
from adafruit_mcp230xx.mcp23008 import MCP23008
import digitalio
i2c = busio.I2C(board.SCL, board.SDA)
mcp = MCP23008(i2c)



#button = digitalio.DigitalInOut(board.D8)
#button.pull = digitalio.Pull.UP

button = mcp.get_pin(6)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.UP

Rstbutton = mcp.get_pin(7)
Rstbutton.direction = digitalio.Direction.INPUT
Rstbutton.pull = digitalio.Pull.UP

# NEOPIXEL
pixel_pin = board.NEOPIXEL
num_pixels = 1
pixels = neopixel.NeoPixel(pixel_pin,num_pixels,brightness=0.2,auto_write=False)

RED = (255, 0, 0)
YELLOW = (255,150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
BLACK = (0, 0, 0)

# Disable devices only if button is not pressed.
#usb_hid.enable((), boot_device=2)
if button.value:
   pixels.fill(GREEN)
   pixels.show()
   storage.disable_usb_drive()
   usb_cdc.disable()
else:
    pixels.fill(RED)
    pixels.show()
    usb_cdc.enable(console=True, data=False)
    storage.enable_usb_drive()


time.sleep(5)
# Write your code here :-)

Assembling the Mushroom House Controller – Part 2

A Collaboration with Maesai Prasisart School, Measai, Chiang Rai, Thailand


In part two of the project, we let the students assemble the Mushroom house controller that they helped to design. It is important to note that they have never done any of this before, and also that most of the components are SMD.

This made for some interesting moments…

The PCB Arrives

The PCB arrived from the factory, and after an initial inspection by myself, we took it to the school so that a specially selected group of students can try their hand at assembling it.


One of the first remarks by the students was that everything was so tiny… Having never seen SMD assembly, they wrongly assumed that they would be required to manually solder the components using traditional solder and a soldering iron… This feeling of “dismay” was greatly increased when we started laying out the small bags with components.

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to who will do his/her best to resolve your issue as soon as possible.

Find out more here

The Assembly



To make things easier for them, I had previously selected all components, and together with a label and component designators, placed each component into a separate anti-static plastic bag. This achieved two things – it shielded the students from having to handle reels of components, potentially resulting in a lot of wastage, and it make it almost impossible to place a wrong component in a wrong place, as each of the bags were clearly marked with the specific component designator of the component it contained.


Their feelings of “dismay” were quickly replaced with wonder as I used a stencil to apply solder paste to the PCB. There were also confusing present, as they could not understand how the “sticky” solder would melt and keep the component in place. They were also quite worried about placing the components onto the PCB – that was until they saw that there was a selection of fine tweezers set out to use for exactly this purpose…

They now became very excited and took turns to each place a few components onto the PCB. I took special care to keep the diodes, optic isolators and microcontroller well away from them, at least until I explained that these components were polarised, or had to be placed in a specific orientation onto the board.

After a bit of struggling with the diodes, as well as the microcontroller, all the SMD components were eventually correctly placed onto the PCB. I now took over and used a hotplate to reflow the PCB.


This process completely amazed them, or at least, most of them, as some took this opportunity to continue with the ever present interaction of students and mobile phones that are so common in SE Asia 🙂

The PCB was now reflowed, and after a short break to let things cool down, we continued with the soldering and assembly of the through-hole components.


The proceedings would not be complete without a group photo of the students and the PCB that they assembled.

Conclusion of part 2

With the PCB now assembled, I used my desktop CNC machine to cut acrylic plastic to form a protective shell. The PCB will soon be installed at the remote site shown in part 1, and while it will be inside a IP65 electrical enclosure, I still felt the need for a little bit of added protection.

The firmware development is complete, and we are currently busy bringing the students up to date with the exact operation thereof. Our goal is that they would at least try to create their own version of the firmware for use in the electronics lab, as well as a comparison between my version of the firmware and theirs.

From the smiles on their faces during the entire process, it was quite obvious that they really enjoyed this project.

Mushroom House Controller – Part 1

A Collaboration with Maesai Prasisart School, Maesai, Chiang Rai, Thailand


A short while ago, the local high school in my area and myself decided to collaborate on a real-world project regarding an IoT device. The device (prototype above) will be used as a controller in an Oyster Mushroom growing house, which in turn will be managed by the members of a local disabled persons association. This is very exciting for a few reasons…

I will get the opportunity to teach interested students how to solve real-world problems using electronics, as well we will do something for the community and thus contribute to making the lives of the local disabled people a bit easier.

In the long term, the data collected by the device can be used to teach other mushroom growers in the area about the optimum environment to help them ensure that they get the perfect harvest every time…

In this post, I will thus do things a little differently from my normal setup, and focus more on the collaboration, as well as teaching moments. I shall also include quite a lot of pictures. The usual schematics and PCB descriptions shall still be available, but only on the PCBWay Shared projects page ( link to be added in due course)

The initial idea is to produce two different PCB’s with basically the same function, for the time being designed by myself. One, used in this post, will be a 3.3v version, with battery backup, and the other will be a 5v version, with level converters ( I could not resist the potential teaching opportunity that these provided) and no battery backup. The 5v version, which I shall introduce in part 2 will be assembled by the students, while I shall assemble the 3.3v version.

Further to that, the goal is to break the PCB up into logical modules and teach the students about each module. Their task would be to then design a PCB of that module, and in the end, combine all the modules into a functional project.
As a further part to this, a special interest group shall then make use of the knowledge learnt during the development of the modules to design a third PCB including all of those modules, as well as any other that they find may be needed, assemble it and use it in the real world.

Gallery – At the remote site

Over here, Grade 12 students assist in the construction of a mushroom house.
The pictures do not need a lot of captions, as they are quite self-explanatory

The next day, in the classroom

We started with a prototype on a breadboard, with some basic firmware. This was followed by a simple PCB design (with frequent 3D views so that the students could see what was happening)


This was followed by a practical session where the students had the opportunity to try their hand at designing a part of the PCB. It is quite important that we understand that this is the very very first time in their lives that they were ever exposed to this. They were all extremely excited and some grasped the concepts quite fast, while others took a more cautious approach…

Manufacturing the PCB

Now it was up to me to finalise the initial design and get it manufactured.


I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to who will do his/her best to resolve your issue as soon as possible.

Find out more here

Conclusion – Part 1

The assembly of the first board was done by myself, and firmware development was started. Stay tuned for Part 2 where the students will get the opportunity to assemble a SMD PCB for the very first time in their lives…

Single Cell Lipo Charger

This single cell lipo charger was created to solve a problem I encountered during a recent project. I constantly needed to recharge 18650 cells, and while I could also use my 4-cell charger, that didn’t always turn out to be the most practical. I also intend to use this small charger as a building block for a future complete power solution, including a boost converter, more protection features and proper cell status indication…

I am currently moving towards building more projects that will be used outside, “in the wild” and thus need a reliable way to power those. True to my way of doing things, I want to build my own stuff as far as possible. That way, I learn more about the technology, and I am sure that everything meets my exact specifications.

What is on the PCB?

The charger is based on the MCP73832T, by Microchip, which contains quite a lot of useful features, in a small package as well at a relatively small price tag, and with very few other required supporting components.

  • Linear Charge Management Controller:
  • Integrated Pass Transistor
  • Integrated Current Sense
  • Reverse Discharge Protection
  • High Accuracy Preset Voltage Regulation: + 0.75%
  • Four Voltage Regulation Options:
  • 4.20V, 4.35V, 4.40V, 4.50V
  • Programmable Charge Current: 15 mA to 500 mA
  • Selectable Preconditioning:
  • 10%, 20%, 40%, or Disable
  • Selectable End-of-Charge Control:
  • 5%, 7.5%, 10%, or 20%
  • Charge Status Output
  • Tri-State Output – MCP73831
  • Open-Drain Output – MCP73832
  • Automatic Power-Down
  • Thermal Regulation
  • Temperature Range: -40°C to +85°C
  • Packaging:
  • 8-Lead, 2 mm x 3 mm DFN
  • 5-Lead, SOT-23

A proper screw-type connection terminal on the output, as well as a DC Barrel Jack on the input, completes the PCB. On future revisions, I will seriously consider having a voltage-limiting circuit on the input side, since the MCP73832T is only capable of accepting an input voltage of up to 5.5v DC.

This is not a problem to me, as I will be using the current version for my own personal use. I do however believe it is essential to ensure that no over-voltage conditions can accidently occur BEFORE I will give this to someone else to use.

The schematic

Manufacturing the PCB

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to who will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly and Testing

Assembly of this PCB was quite easy, providing that you have a stencil, it will not take you more than a few minutes.


The PCB’s really came out very nicely 🙂


I have also made provision for using a through-hole 18650 battery holder, just in case you are like me, and have a few lying around in a drawer, or could just not be bothered with using the SMD version…


The completed PCB is relatively small and compact, taking into consideration the size of the 18650 cell of course… The screw terminal on the output really helps to keep everything secure when using the module to power a project, and the DC barrel jack provides a good connection to charge it all back up again…
Now, If I just remembered to add some form of voltage limiting on the input, as well as include a boost converter, It would be the perfect little “power bank” project… For now though, let’s leave those features to the future, as this is already extremely useful as is.

AI-Thinker AiPi Eyes S1 – Something interesting…


Ai-Thinker is definitely in my opinion one of the more interesting Chinese Electronics companies out there. Most of us will be very familiar with their NodeMCU ESP8266 as well as ESP32-S modules as well as Dev boards.

They also produce the very popular Ra01 and RA02 Lora modules…

What is less known about them is that they have a very active R&D department. Over the last few months, more than 8 months to be accurate, I believe that there seems to have been quite a dramatic change happening. Many RISC-V-based chips and modules started appearing on the AI-Thinker website, and I have also been fortunate to get my hands on a few of them.

The latest of these, the AI-M61-32S, is another one of these. The chip seems to be based on the BL-616 or BL-618 from Bouffalo Labs ( No spelling error there)

Information outside of China, and in English seems to be very sparse at the moment, but it does seem like there is a lot of potential here…

Let us take a closer look at the AiPi-Eyes-S1

The board is a very neat, but dense double-layer SMD assembly, with interfaces for a touch-screen, camera, speakers and microphones as well as USB ports and a firmware flashing port.

The kit also comes with an impressive number of peripherals, including a beautiful touchscreen, a small camera, speakers and microphones.

What are my thoughts?

As stated above, I find the kit very well designed, in the sense that it looks good.
The screen has very good resolution, and there seem to be enough peripherals to test out something… Why am I saying something?

1) According to the schematic, the screen and camera share GPIO pins, and can thus not function together – a definite minus point for me there.
2) There are NO free GPIO pins, and those that may be, very difficult to access – think small connectors etc
3) The demo program seems to cover some of the basic functionality of the chip, but not too much…

Let’s take a look at software support

At the time of writing, the kit has been sitting on my desk for just over 2 months. English documentation, beyond a quick guide to get the demo software running, is limited, if nonexistent.

The Touch Screen API is downloadable, but will take quite a while to decipher – It will be worthwhile though.

I have also received news from AI-Thinker that there is limited Arduino IDE support for the AI-M61-32S available. I don’t know to what extent, and will definitely explore that further in the future.

Further than that, there are links and examples for setting up some sort of API/SDK from Bouffalo Labs on the Ai-Thinker Website. Support documents seem once again to be sparse as far as English is concerned.

In conclusion

I see potential in the future for this new chip ( the AI-M61-32S ) or let’s just call it the BL-616 or BL-618 already. Ai-Thinker repackaged it into the popular ESP32-S form factor, so they seem to be hoping that it could be used as a drop-in-replacement for that.

Being RISC-V based, it is however still extremely new, and with all the various RISC-V standards currently out there, it will surely take some time to mature.

The development of an Arduino core to support it, is definitely a step into the right direction, if and only if, compatibility with existing libraries etc can be assured. That will definitely allow Makers and other hobbyists to start using the chip

As far as the AiPi-Eyes-S1 kit is concerned, well…
Full marks for aesthetics – it really does look good. As far as practical use or being fit for a particular purpose goes – not so much.

The board was clearly designed as a showcase of possible uses for the AI-M61-32S only. It was clearly not meant for use as a development board.

So, would I buy one right now? a definite NO on that. In the future, yes, maybe, providing that:

1) Documentation and information are updated and made easily available.
Slapping the words ” open source ” onto something does not mean that you can skimp on documentation. It is “open source” because the schematics are freely available, datasheets are available, and libraries and IDE support are available free of charge. “open source” does not mean to give someone a space shuttle, and not include the manuals, or at least a link to where to buy the manuals etc…

Xiao RP2040 Joystick Mouse – revision 2.00

Revision 1.0 of the Project


Over the last few months, I have been using the initial revision of this project on almost a daily basis. It has come a long way since the initial concept was implemented on the breadboard.

Initial Concept on a Breadboard

While completely functional, and relatively easy to use, quite a few things started adding up – making me believe that it could be better…

That prompted me to start thinking about a hardware revision, adding some missing features, like a middle button, and “maybe” a display to the device, making it easier to visualise settings, etc…

Current Revision 2.0 ” Proof of concept ” prototype

My main limitations came from the Seeed Studio Xiao RP2040 Module. While super tiny and compact, the module only has access to 11 GPIO pins on the RP2040 chip. Most of these were already in use, connected to buttons etc.

I would thus have to find an I2C IO expander that will be supported by CircuitPython and have a suitably small footprint. That way, I could free up many of the valuable GPIO pins on the Xiao RP2040 for other purposes.

What did I use?

My initial goto chip was the MCP23017, with 16 GPIO pins. But after some more thinking, I settled on the MCP23008, which has only 8 GPIO lines. I2C bus breakout headers to allow for expansion, as well as access to all the unused GPIO pins on the XIAO RP2040, were also added.

The Rotary encoder was once again included, as it could later be used for selecting Menu options etc.

What is the current status of the project?

The revision 2.00 hardware works as expected, with a few issues.
CircuitPython has an issue with rotary encoders connected to IO expanders. I don’t understand why that would be the case, but wrote my basic routine to handle the encoder, which at this time, is only used for scrolling. ( I have still got to decide if a display would be needed)

As far as settings are concerned, I have only implemented a sort of “mouse speed” feature that determines how fast or slow ( for better accuracy ) the pointer moves. This is currently controlled by the encoder button, on a cycling loop, with different colours on the NeoPixel as visual feedback on the current speed selected.

USB connectivity at computer startup and/or resuming from a suspend operation is still a major problem. This means that you have to physically reset the device after every resume from suspend, or after starting your computer.
From what I can see in the CircuitPython documentation, it is possible to detect USB connectivity. That part works. From there, It seems that once USB connectivity is lost, CircuitPython goes into some sort of unknown state, and no further code is executed, thus making a software reset not executing…

I have an idea that it has got something to do with the HID Mouse mode or something ???? For now, I am happy to just hit a reset button to continue…

Another big issue is a suitable enclosure. Revision 2.00 PCB was not designed to be placed into an enclosure, mainly because I have so far been quite unsuccessful in finding a suitable one. My 3D design skills are also quite lacking, so designing something from scratch won’t do either. I have decided to sort out all the hardware and firmware issues first, find an enclosure and then modify the PCB layout to fit that.

Manufacturing the PCB

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to who will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly and Testing

Assembly is easy but does require a stencil due to the small size of some of the SMD components.

CircuitPython Coding – A work in progress

This is the current code, and it is a work in progress. It works, and could definitely be optimised quite a lot. I am not very familiar with Python but I believe I can help myself around it.

import time
import board
import busio
from rainbowio import colorwheel
import neopixel
import digitalio
import rotaryio
import microcontroller
from digitalio import Direction
from adafruit_mcp230xx.mcp23008 import MCP23008
import digitalio
i2c = busio.I2C(board.SCL, board.SDA)
mcp = MCP23008(i2c)

from analogio import AnalogIn
import usb_hid
from adafruit_hid.mouse import Mouse

mouse = Mouse(usb_hid.devices)
xAxis = AnalogIn(board.A2)
yAxis = AnalogIn(board.A1)

# NEOPIXEL
pixel_pin = board.NEOPIXEL
num_pixels = 1
pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.1, auto_write=False)

leftbutton = mcp.get_pin(3)
leftbutton.direction = digitalio.Direction.INPUT
leftbutton.pull = digitalio.Pull.UP

centerbutton = mcp.get_pin(4)
centerbutton.direction = digitalio.Direction.INPUT
centerbutton.pull = digitalio.Pull.UP

maint_btn = digitalio.DigitalInOut(board.D0)
maint_btn.switch_to_input(pull=digitalio.Pull.UP)

rightbutton = mcp.get_pin(5)
rightbutton.direction = digitalio.Direction.INPUT
rightbutton.pull = digitalio.Pull.UP

enc_btn = mcp.get_pin(2)
enc_btn.direction = digitalio.Direction.INPUT
enc_btn.pull = digitalio.Pull.UP

scroll_up = mcp.get_pin(6)
scroll_up.direction = digitalio.Direction.INPUT
scroll_up.pull = digitalio.Pull.UP

scroll_down = mcp.get_pin(7)
scroll_down.direction = digitalio.Direction.INPUT
scroll_down.pull = digitalio.Pull.UP

enc_a = mcp.get_pin(0)
enc_a.direction = digitalio.Direction.INPUT
enc_a.pull = digitalio.Pull.UP

enc_b = mcp.get_pin(1)
enc_b.direction = digitalio.Direction.INPUT
enc_b.pull = digitalio.Pull.UP

enc_a_pressed = False
enc_b_pressed = False

#mousewheel = rotaryio.IncrementalEncoder(enc_a, mcp.get_pin(1))
#last_position = mousewheel.position

move_speed = 3
enc_down = 0

RED = (255, 0, 0)
YELLOW = (255, 150, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
BLACK = (0, 0, 0)


if move_speed == 0:
    in_min, in_max, out_min, out_max = (0, 65000, -20, 20)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 1:
    pixels.fill(GREEN)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -15, 15)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 2:
    pixels.fill(BLUE)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -10, 10)


filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 3:
    pixels.fill(PURPLE)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -8, 8)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )
if move_speed == 4:
    pixels.fill(CYAN)
    pixels.show()
    in_min, in_max, out_min, out_max = (0, 65000, -5, 5)
    filter_joystick_deadzone = (
        lambda x: int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
        if abs(x - 32768) > 500
        else 0
    )


pixels.fill(BLACK)
pixels.show()
while True:
    # Set mouse accelleration ( speed)
    if move_speed == 0:
        pixels.fill(BLACK)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -20, 20)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 1:
        pixels.fill(GREEN)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -15, 15)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 2:
        pixels.fill(BLUE)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -10, 10)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 3:
        pixels.fill(PURPLE)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -8, 8)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )
    if move_speed == 4:
        pixels.fill(CYAN)
        pixels.show()
        in_min, in_max, out_min, out_max = (0, 65000, -5, 5)
        filter_joystick_deadzone = (
            lambda x: int(
                (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
            )
            if abs(x - 32768) > 500
            else 0
        )

    #current_position = mousewheel.position
    #position_change = current_position - last_position

    x_offset = filter_joystick_deadzone(xAxis.value) * -1  # Invert axis
    y_offset = filter_joystick_deadzone(yAxis.value) * -1
    mouse.move(x_offset, y_offset, 0)

    if enc_btn.value and enc_down == 1:
        move_speed = move_speed + 1
        if move_speed > 4:
            move_speed = 0

        # print (move_speed)
        enc_down = 0

    if not enc_btn.value:
        enc_down = 1

    if leftbutton.value:
        mouse.release(Mouse.LEFT_BUTTON)
        # pixels.fill(BLACK)
        # pixels.show()
    else:
        mouse.press(Mouse.LEFT_BUTTON)
        pixels.fill(GREEN)
        pixels.show()

    if centerbutton.value:
        mouse.release(Mouse.MIDDLE_BUTTON)
    else:
        mouse.press(Mouse.MIDDLE_BUTTON)
        pixels.fill(YELLOW)
        pixels.show()

    # Center button on joystick
    if maint_btn.value:
        mouse.release(Mouse.LEFT_BUTTON)
    else:
        mouse.press(Mouse.LEFT_BUTTON)
        pixels.fill(GREEN)
        pixels.show()

    if rightbutton.value:
        mouse.release(Mouse.RIGHT_BUTTON)
        # pixels.fill(BLACK)
        # pixels.show()
    else:
        mouse.press(Mouse.RIGHT_BUTTON)
        pixels.fill(PURPLE)
        pixels.show()

    if not scroll_up.value:
        mouse.move(wheel=1)
        time.sleep(0.25)
        pixels.fill(BLUE)
        pixels.show()

    if not scroll_down.value:
        mouse.move(wheel=-1)
        time.sleep(0.25)
        pixels.fill(CYAN)
        pixels.show()

    if not scroll_up.value and not scroll_down.value:
        for x in range(4):
            pixels.fill(RED)
            pixels.show()
            time.sleep(0.5)
            pixels.fill(BLACK)
            pixels.show()
            time.sleep(0.5)
        microcontroller.reset()

    if enc_a.value:
        enc_a_pressed = False
    else:
        if enc_b_pressed:
            enc_a_pressed = False
        else:
            enc_a_pressed = True

    if enc_b.value:
        enc_b_pressed = False
    else:
        if enc_a_pressed:
            enc_b_pressed = False
        else:
            enc_b_pressed = True

    if enc_a_pressed:
        mouse.move(wheel=1)
        time.sleep(0.25)
        enc_a_pressed = False
    if enc_b_pressed:
        mouse.move(wheel=-1)
        time.sleep(0.25)
        enc_b_pressed = False

    #if position_change > 0:
    #    mouse.move(wheel=position_change)
    #    # print(current_position)
    #    pixels.fill(BLUE)
    #    pixels.show()
    #elif position_change < 0:
    #    mouse.move(wheel=position_change)
    #    # print(current_position)
    #    pixels.fill(CYAN)
    #    pixels.show()
    #last_position = current_position
    pixels.fill(BLACK)
    pixels.show()

Conclusion

Okay, so this is where it is at at the moment. The code is not perfect, and the hardware is not perfect, but it works. I am using this device every day, and also making changes as needed. At the moment, there are some issues, but they do not prevent the actual use of the device.

If you are interested or would like to make modifications, feel free to do so.

MCP23008 Breakout

I designed this breakout to assist me during prototyping my next version of the “RP2040 Emergency Mouse“. In that project, I used the SEEED Studio Xiao RP2040, along with a few other components to create a simple but effective computer mouse-type device.

While the “mouse” works quite well, I have quite early on discovered that it could be better. More on that in a follow-up article, but let us just say that I needed more GPIO pins than that were available on the XIAO RP2040 and that the layout can be improved a bit – especially If I want to get it into an enclosure.

I am still quite neutral about CircuitPython and Micropython on microcontrollers, and Python in general, but since the above-mentioned project runs completely on CircuitPython, it made a lot of good sense to get more into it.

What is on the PCB?


I wanted something as small as possible, and that meant that I chose a QFN package for the MCP23008 IO expander chip. At only 4mm x 4mm, and not being bothered to try and find a DIP version, a breakout board became a much-needed necessity.

Address Selection Jumpers, Two I2C bus headers, and of course the all-important GPIO pins make up all of the user accessible interfacing. Note that the chip reset line is permanently tied to Vcc to make things a bit less cluttered, and easier to use while prototyping.

A decoupling capacitor, as well as pullup resistors on the I2c lines, were also included. Another note here, I did not provide my usual selection jumper to disable these on this particular board.

The Schematic


Manufacturing the PCB

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to that will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly and Testing

Due to the small size of the QFN package, I strongly recommend that you either have this assembled professionally, or at least consider buying a stencil for applying the solder paste to this board. Maybe those with excellent eyesight can do without that?


Assembly took only a few minutes, with the help of an extremely accurate stencil, followed by a few minutes on a hotplate, and manually soldering on the header pins.

Using the MCP23008 with CircuitPython

I2C devices are very easy to use with the Arduino IDE or similar, and as such, I will not be covering that here.

Circuitpython, however, is gaining popularity, and I am slowly starting to see what the hype is about myself…

So, to get started, you need a microcontroller running CircuitPython – See Adafruit for excellent tutorials. You will also need a few libraries from Adafruit
See this link

I will give a very simple example below, showing how to set a pin as an output, as well as an input with internal pullup resistors enabled. Note that the MCP23008 DOES NOT SUPPORT pull-down resistors internally. You need to add those by yourself externally.

# Initialising all the required libraries
import board
import busio
from digitalio import Direction
from adafruit_mcp230xx.mcp23008 import MCP23008
i2c = busio.I2C(board.SCL, board.SDA)
# Adding the MCP23008
mcp = MCP23008(i2c)
# This assumes that you are using the default address [ i.e. all address 
# pins are grounded]
#
#
#
# Defining two outputs on pins 0 and 1
pin0 = mcp.get_pin(0)
pin0.direction = Direction.OUTPUT
pin1 = mcp.get_pin(1)
pin1.direction = Direction.OUTPUT
#
#
# We can now control the pins by setting them to true or false, true being 
# high
pin0.value = True
pin1.value = True
#
# and switch them off again by using
pin0.value = False
pin1.value = False
#
#
# We can also use the pins as inputs.
# We will activate the internal pullup as well
#
# first , we need another library
import digitalio
pin2 = mcp.get_pin(2)
pin2.direction = digitalio.Direction.INPUT
pin2.pull = digitalio.Pull.UP
#
#
# Reading the pin value is now as easy as 
pin2.value
#
# This will return True if the pin is high ( its default state with pullups # activated, of False if pulled low, by for example a switch of button )

Conclusion

The breakout works as expected, and it is very easy to use with CircuitPython.
I can now continue with the actual integration and Software for the RP2040 Mouse Rev 2.0 project.

High Current P-Mos Driver

This is a modification of my existing P-MOS driver circuits, intended for use with higher current LED Lights, as well as any other applications requiring a higher current capable P-Channel Mosfet to switch a load.

What is on the PCB?

I have used the IRF4905 P-Channe Mosfet here as it can sink up to 74 Amps of current – A complete overkill in many situations. Datasheet. The Mosfet is configured in a high-side switching configuration, thus eliminating problems with ground connections.

To prevent unreliable switching, a transistor is used to switch the gate, which is normally pulled high to keep the device switched off.


I have also included various connection headers for connecting the load, Power supply, as-well-as active high control headers for controlling the driver from a microcontroller. This was especially important as the Gate voltage of the Mosfet is above the acceptable 3.3 volt for use with many of the modern microcontrollers in use today.

It is important o note that I did not yet bother to do very accurate gate current calculations. I do not need super fast switching, and on the bench, the 500mA switching capability of the S9013W transistor gave me satisfactory results.

What is my intended use for this driver?

This is a 12v Automotive Fog light. It is meant to be an aftermarket upgrade. It will also be a very nice focused working light in my workshop, as the lighting is not optimal.

My initial idea is using two of these, PWM controlled from an ESPHome-controlled device to provide me with focused, dimmable lighting for assembly and other operations where a bit of extra light will be needed.

The Fish-eye lens of the internal lamp provides a very focused beam, and from initial testing seems to be exactly what I want.

The problem came in that the LED module consumes quite a bit of current ( 5A for the center lamp, and 3A for the ring light). These currents are way above the capabilities of my existing LED COB driver circuit, thus this MOD.

The Schematic

Manufacturing the PCB

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to that will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly and Testing

This PCB is definitely quite easy to assemble, as there are only 16 SMD components on the board. These are all easily hand-solderable. The Mosfets and their respective heatsinks are through-hole components and thus super easy as well.

It is very important to note that we should NOT connect the heatsinks together. This is due to the fact that the Heatsink is connected to the DRAIN pin on the MOSFET. Connecting them together will thus short the various channels together.

For my testing procedure, I have connected the driver to the LED Fog light, as well as a 12v supply. Using an ESP8266 running ESPHome, the LED fog light was controlled with PWM. The current draw was 5A and 3A respectively. All of the MOSFETs remained cool to the touch, and the PCB tracks did not heat up as well.

Next steps

The next steps for this project would be to design a PCB that integrates this driver with the ESPHome control device, as well as design and build a suitable enclosure for the two lights and the control unit. This should ideally be mountable on the ceiling above my workbench. It would also be nice to design some sort of gimbal for each light, that can be controlled with stepper motors or servo’s to allow me to position the lights where I need them.

Xiao ESP32S3 Media Device Prototype

I use a lot of ESP32 modules in my projects, mainly because of the integrated WiFi and BT, but also due to their small size. There are however usually quite a few external components required, and sometimes, that can be a bit of a turn-off.

Having access to quite a few XIAO modules from SEEED studio, and with support for the ESP32S3 recently being added to ESPHome, my other goto for projects, I decided to create a simple prototype based on the SEEED XIAO ESP32S3. this module offers several attractive features, of which its small size is definitely a big plus. Add to that that it also has battery charging circuitry and quite a lot of flash memory, and it seemed like a winner…

Not that it doesn’t have issues of its own, the biggest being that you have to power it from 5v, and the absolutely super tiny flash and reset buttons.

Cost-wise, they are only slightly more expensive than the native ESP32 module, which, by the time you have added the external components, does not amount to any difference anymore.

I also wanted to test out some power-supply circuitry, so this seemed like a good project to combine those in as well.

What exactly is the project about?

It is a testbed for three(3) different things, with the first being the XIAO ESP32S3, with I2S sound as an ESPHome media device. That part, while working, is in my view not perfect yet, as ESPHome does not seem to be able to utilise the 8mb of PSRam available on the XIAO for use with the I2S Audio, yet anyway…

The second is having my, by now almost common, dc buck converter circuit directly on the PCB. I want to test that, in order to make sure that there are not too much noise being generated that could influence the ESP32S3, or, most importantly, the I2S module. A while ago, I built a custom I2S shield, and unfortunately, the power capabilities, or let’s be frank, the lack of sufficient power, turned that project into a big disappointment at any volume other than almost below 2%…

This will thus also serve as a way to revamp that idea, but with better power capabilities and more available current.

The third part is a direct result of the XIAO’s 5v power requirement. I reused my LM317G variable voltage circuit to provide power to the module. In my view, it would have been so much nicer if I could just supply 3v directly to the XIAO…

This also brings us to the battery charging capability. I would have loved to use that, but,
1) It seems that the battery voltage is internally stepped up to 5v and then back to 3.3v for the ESP32S3. comment anyone?
2) The available output (3.3v) does not provide a lot of current – surely not enough to drive an I2S module, does it? more comments, anyone?
3) I am unsure of any internal isolation in above mentioned charging circuit, thus, I was not willing to connect my own boost converter to the battery and use that to power external components…

So, until I get definite answers on these three, I won’t be bothering with powering the circuit from the battery… unless it is a big 12v one

So what is on the PCB?

3D Render of the PCB

As mentioned above, I had a few goals with this prototype. Let us take a quick look at the board and its components.

Starting bottom left, we have the DC input area. Here I chose to provide both a DC Barrel Jack, as well as a screw Terminal. Both can be used, but I believe the screw terminal will add a bit more flexibility in an actual installation inside an enclosure.

To the right of those, we have a 7-way header. This provides access to D7 to D10 on the XIAO, and, in my case, is labelled for use with the I2S module and a DHT11 sensor. ( you can of course use it for something else as well). The DHT11 header is on the far right in this picture.

At the centre-left, the components around U1 forms the DC-DC buck converter. This is set to supply 3.3v at a maximum current of 3A.

To the right of that, around TP1, is a jumper to that supplies the XIAO with 5v from U2 ( the LM317G ). It is important to note that you should leave this jumper disconnected at first power-up, and then adjust R9 while measuring between GND and TP1 to get a voltage of exactly 5v on TP1. This will prevent you from damaging the XIAO module. Once that is set, power down, and place a jumper on the center and right side pins. Make sure to NOT adjust R9 again with this jumper in place.

Center-right is the star of the board. The XIAO ESP32S3 Module ( marked U3)
Note the cutout for the USB-C connector at its top.

A reset button, marked RST, and two headers, one for I2C and another with additional GPIO pins completes the PCB.

Note that the XIAO ESP32S3 makes use of an external antenna on a short UFL connected cable. Make sure to attach this BEFORE you power up the board for the first time.

Manufacturing

Since I have made use of a SEEED Module, I sent this to SEEED Studio for manufacturing.

Seeed Studio’s Fusion service seamlessly marries convenience with full-feature capabilities in one simple platform. Whether you are prototyping or looking for a mass production partner or based on open source product customization requirements and other design manufacturing services, Seeed Studio Fusion service is catered to your needs starting with a simple online platform. https://www.seeedstudio.com/fusion.html

Assembly and Testing

I chose to assemble this project on my own. There was some issue with component availability, and since I have most in stock myself, decided to save time and do it myself.

Little did I know that I will be severely handicapped by a broken wrist a few weeks later. That little incident forced me to use my non-dominant hand, and resulted in an almost 3-hour-long assembly operation! All did however go well, and everything works as expected.

Testing went well, and after verifying all the voltages and connections, I uploaded some previously prepared ESPHome code to the board.

Due to the fact that this is still an early prototype, as well as some issues with ESPHome, I wont be releasing the firmware just yet. That will however happen in the near future.

“The Emergency Mouse” – A project born out of necessity

Imagine You are working on a project late on a Friday evening and suddenly your mouse stops working… You can not scroll, and the right-side button won’t respond to your clicks… At the same time, you have a project design that has got to get finished… and the shops are all closed already…

These were the circumstances that led to the birth of “The Emergency Mouse” – A project born out of necessity. How did I solve my problem?

Having access to a lot of electronic modules saved the day. As a maker, I always have various modules and gadgets lying around, and on this unfortunate evening, I remember that the RP2040 has USB HID support. Combine that with a simple Analog Joystick module, a rotary encoder and some push buttons, add about 30 minutes worth of browsing the internet, struggling along with a broken mouse – we have to give the old one credit, it had a very long and hard life, and I finally found some example code that did not just jiggle the mouse pointer or do something equally silly…

The only problem with all of that was that the code was for CircuitPython… I generally dislike using Python on a Microcontroller, as I believe it is better suited for the computer, but, I am warming up to the idea… slowly…

The initial fix – a mess of wires on a breadboard

I quickly grabbed a RaspBerry Pi Pico out of a box, plugged it into a breadboard, loaded Circuitpyth and fired up the example code I got on the internet… While promising, It did not exactly do what I wanted… so a few minutes later, after some coding, I had a moving pointer, controlled by the small thumb joystick module, and with the center button as a “right button”…

So far so good… I can work more easily, but still did not have scrolling… so lets hit the datasheets and documentation on the Adafruit Website (not sponsored) and add a rotary encoder… works well, add more buttons, etc etc…

Eventually it was all done, and about 1 hour has passed, but we were left with a huge ugly mess on a breadboard, and a lot of unused GPIO pins.. So this Pico must go… it can be used for something more useful later…

Then my eye fell on a SEEED Studio XIAO RP2040 module, almost begging to be used… This is smaller, more compact… lets try that …

Initial breadboard version, here shown with the SEEED Studio XIAO RP2040

What functions did this “mouse” have

After changing to the XIAO RP2040, things went very quick…

I added two buttons for scrolling up and down, simulating a mouse wheel,
but kept the encoder… which, while VERY awkward to use at this stage, definitely had potential in the long run…

I also added another button to take over the function of a right button, while the center button on the joystick became left…

Disaster averted, with only about 2 hours wasted, I returned to my project and managed to get it finished using the “improvised-mouse-on-the-breadboard” contraption…

That night, while lying in bed, trying to get to fall asleep, the possibilities of this “contraption-on-the-breadboard” would not let me go… I am fairly old-school, and during the late 80’s and early 90’s owned quite a few “roller-ball” mouse devices… these later became trackballs, and being excessively overpriced, was promptly removed from my environment – the old ones did not last very long, and the new ones were, as I said, overly expensive…

I did however never forget the ease of use that first “rollerball mouse” gave me all that years ago, using only my thumb to move it around etc etc…

This idea would have to be investigated, and turned into a PCB… with that, I finally drifted off to sleep…

The PCB design

The next morning came, and due to reasons unknown, as well as being lazy, I decided not to leave the house, and go buy a new mouse. lets try online… No, they are crazy – I am not paying that for a mouse!

All the time using the “contraption-on-the-breadboard”. So this thing started growing on me… lets design a PCB

The Initial PCB design

After a few hours spent on deciding on optimal layout, I came up with this…
It was still a bit unrefined, but definitely had potential… It lacked a dedicated center button, and those momentary push-buttons requires a lot of force to use… but as a prototype, why not…

Let’s get this manufactured.

For this build, since I used a SEEED Studio module, I decided to send it to SEEED for manufacturing… no need to get components from various places, as they should have all in stock…

Seeed Studio’s Fusion service seamlessly marries convenience with full-feature capabilities in one simple platform. Whether you are prototyping or looking for a mass production partner or based on open source product customization requirements and other design manufacturing services, Seeed Studio Fusion service is catered to your needs starting with a simple online platform. https://www.seeedstudio.com/fusion.html

The PCB arrives from the factory

During the entire time that it took for the PCB to be manufactured and assembled, I was still using this “homemade mouse” – I started calling it a mouse now… and it was still on the breadboard… I never did bother to buy a new mouse, yet..


The PCB Arrived today, and apart from a few small soldering issues, looked great… I still had to do a bit of assembly on my own, as there was an issue with the components I wanted being out of stock.. I have plenty in stock of my own, so opted to do manual assembly…


The completed PCB now only needed a joystick, and some firmware…


After adding a few button caps, and mounting everything to a piece of acrylic plate, I had a working prototype…

The Firmware

As mentioned above, the device runs on CircuitPyton. As Such, there are quite a lot of “examples” on the internet, showing you how to do many USB HID “mouse” like things, but generally being completely useless…

I have thus spent quite a lot of time up to now, writing and refining my own version of the firmware, that is actually useful and does actually work.

It has the following features:
X-Y axis control of the mouse pointer via a thumb joystick, with a left click function on the center joystick button, as well as a dedicated “left” button.

A dedicated “right” button
A “virtual center” button made up of simultaneously pressing left and right

Up and down scrolling either using the rotary encoder as a “mouse wheel” or via dedicated up and down pushbuttons.

A dedicated Reset button – this is necessary, as I can not seem to get the device to initialise correctly at computer bootup.

Various software functions, like changing the pointer acceleration by pressing the center button on the rotary encoder

and most importantly, hiding the Circuitpyton drive, only showing it when I actually need access to the code in this device…

Various statuses are indicated using the NeoPixel on the XIAO, making it easy to see in what state the device is operating.

As such, I shall NOT be releasing the firmware at this moment, as it is still far from being perfect. It works, but it can be way better…

Summary and next steps

Since its “birth” late on a Friday night, about 3 weeks ago, I have been using this device, in its various forms as my primary pointer device. It is growing on me more every day, and it is quite comfortable to use – If we ignore the fact that it is not in a suitable enclosure and that I am still making small changes to the firmware from time to time.

I am already planning the next revision, in which I shall replace the momentary push-buttons with proper microswitches, as well as try my hand at designing a proper enclosure.

If you are a 3D printing expert and want to collaborate with me on this, let’s talk…