14. December 2023

How to connect USB and PS/2 Keyboards to ESP32 with Rust no_std and std

Have you ever wondered how to connect a PC keyboard to your ESP32 for innovative projects? Whether it’s an old PS/2 keyboard or a modern USB one, this article will show you how to integrate these input devices with your ESP32, using the versatile and powerful Rust programming language. The first part of the article describes Rust no_std solution. Rust std for USB solution is at the end of the article.

Preparing the ESP32-C3

First, let’s tackle the wiring for the ESP32-C3. Detailed in bjoernQ’s GitHub repository, “ps2keyboard-esp32c3”, this setup requires three resistors and is perfectly illustrated in the “Circuit” section. For a reliable and efficient connection, consider the wire-wrap technique recommended by Andreas Spiess in his enlightening video, “#243 Better than Dupont Wires: Wire Wrapping for our Projects”.

For the PS/2 connector, refer to the detailed pinout available in the Wikipedia article, “PS/2 port”, particularly the pinout diagram for the female connector (PS/2 port pinout diagram). Please, keep in mind that the pinout is for the female connector from the front side. For the male connector or the back side of the female connector you need to flip the image.

For USB keyboart you’ll need a USB keyboards support a COMBO PS/2 support (still available in many USB keyboards, allowing them to function as PS/2 devices). You can connect these USB keyboards directly by following the schematics from the Instructables project, “USB to PS/2 Convertor” with “ps2keyboard-esp32c3 schematics”,. Specifically, use this wiring diagram (USB to PS/2 wiring diagram) to integrate into the ESP32-C3 setup. No code changes are needed for this part.

Flashing the ESP32-C3

After setting up the hardware, it’s time to program the ESP32-C3. The software part is handled using the pc-keyboard crate, developed by thejpster’s. Flash your ESP32-C3 with the cargo espflash --release command, and watch as keyboard inputs appear on your console.

Interesting Project Ideas

Now that you have your keyboard connected to the ESP32. Here are some project ideas to get you started:

  • Smart Home Controller: Use the keyboard to control lights, temperature, or other IoT devices in your home.
  • Custom Game Controller: Create a gaming experience by mapping keyboard keys to game controls.
  • Educational Tool: Teach programming or robotics, using the keyboard as an input device for experiments and projects.
  • Artistic Installations: Incorporate the keyboard into interactive art projects or music synthesizers.

Rust std + ESP-IDF with USB support

The text above describes Rust no_std solution. If you’re using Rust std on top of ESP-IDF, you can use direct USB support on ESP32-S3 with USB HID for mouse and keyboard Board Support Package (BSP).

9. February 2023

How to call Rust functions from Python, Ruby and Zig

Rust language is great for creating libraries and drivers which can be consumed by other languages like Python, Ruby or Zig.

Let’s consider following Rust application Cargo.toml:

[package]
name = "rustlib"
version = "0.1.0"
edition = "2021"

[lib]
name = "rust_lib"
crate-type = ["dylib"]

File src/lib.rs:

#[no_mangle]
pub extern "C" fn add(left: usize, right: usize) -> usize {
    left + right
}

The next step is to build the library:

cargo build --release

The output library is stored in ./target/release/librust_lib.so

Python

Invoking the add function from Python is very easy:

import ctypes
lib = ctypes.CDLL("./target/release/librust_lib.so")
lib.add(1,2)

Ruby

In case of Ruby we will need ffi gem.
Installation on OpenSuse:

sudo zypper install ruby3.1-rubygem-ffi

Ruby code:

require 'ffi'

module MyLib
  extend FFI::Library
  ffi_lib './target/release/librust_lib.so'
  attach_function :add, [ :int, :int ], :int
end

puts MyLib.add 1, 2

Zig

Zig will require little bit more stuff. We need to generate C headers from Rust, which then can be loaded to Zig. Install cbindgen for the conversion:

cargo install cbindgen

Generate header file from the library.

cbindgen --lang c --output rustlib.h

Create Zig application in zig2rust:

const std = @import("std");
const rustlib = @cImport(@cInclude("rustlib.h"));

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const result = rustlib.add(1,2);
    try stdout.print("Result is {d}.\n", .{result});
}

Compile Zig application:

zig build-exe zig2rust.zig -I. -l target/release/librust_lib.so

Run the application:

./zig2rust

21. December 2022

Rust Bare Metal application for ESP32, desktop, Android and iOS

Rust language and tooling are very powerful. It makes it easy to build for different platforms and architectures and still have performance close to C language.

It’s possible to write the same application for ESP32 with Xtensa architecture or ESP32-C3 RISC-V architecture. The portability of Rust code is high. Changing the target and adding a few wrapper functions makes it possible to build the same application for the desktop or web browser with WebAssembly. Once the WebAssembly is ready, it’s possible to turn the application into Progressive Web App (PWA) which can be installed on Android or iOS.

ESP32 Spooky Maze game is example of application which can work on ESP32 and also on mobile device. The shared core code contains the main part of the implementation, and each platform has its tiny wrapper.

The example Rust application uses Embedded Graphics to draw images. When the app is running on real HW, it’s transferred to the display via SPI using ESP-HAL. The desktop version is using SDL2 to interact with Linux, macOS, or Windows. The web version is using WASM, and framebuffer is serialized to HTML5 canvas. PWA application for Android also supports access to Accelerometer, so it can simulate similar behavior like IMU on ESP32-C3-DevKit-RUST or ESP32-S3-BOX where user can tilt the device to move the character in the maze.

Web version of the application with PWA support: Spooky (full-screen mode)

Embedded version of the application: ESP32-S3-BOX, M5CORE-FIRE (M5Stack) and many more.

Thanks to cloud development IDEs, it’s possible to build the application on GitPod.io or GitHub Codespaces and run it even without real HW using Wokwi simulator.

31. October 2022

openSUSE Rust Bevy application failed with “Unable to find a GPU”

Bevy is data-driven engine written in Rust. The repository of Bevy contains several examples which can be started by a command like:

git clone --depth 1 https://github.com/bevyengine/bevy.git
cd bevy
cargo run --example alien_cake_addict

Users of openSUSe might experience following error:

hread 'main' panicked at 'Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/latest/docs/linux_dependencies.md', crates/bevy_render/src/renderer/mod.rs:121:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The problem is caused by missing Vulkan libraries in the system. This can be confirmed by running vkcube which may result in the following error:

vkEnumerateInstanceExtensionProperties failed to find the VK_KHR_surface extension.

Do you have a compatible Vulkan installable client driver (ICD) installed?
Please look at the Getting Started guide for additional information.

Check which graphic card is installed in the system:

sudo lspci -nnk | grep -A3 VGA

Which might result in a message like this:

00:02.0 VGA compatible controller [0300]: Intel Corporation CometLake-U GT2

With this knowledge, it’s clear that library Vulkan Intel is missing and can be installed simply by:

sudo zupper install libvulkan_intel

Application vkcube should work after this change.

30. August 2022

Unable to flash ESP32 (S2, S3, C3) with these USB-C cables

USB-C is becoming more and more popular. A straightforward assumption is that you can replace the old cable with a USB-A connector with USB-C, and everything should be the same. This assumption is wrong.

Some cables do not work well with ESP32, S2, S3, and C3. Here you can find information about cables which does not work and it’s not possible to flash dev boards with the chip with espflash, idf.py or esptool.py.

A typical error message looks like this:

Serial port: /dev/tty.usbserial-110
Connecting...

Unable to connect, retrying with extra delay...
Unable to connect, retrying with default delay...
Unable to connect, retrying with extra delay...
Error: espflash::connection_failed

  × Error while connecting to device
  ╰─▶ Failed to connect to the device
  help: Ensure that the device is connected and the reset and boot pins are not being held down

USB-C to micro-USB connected to boards ESP32-S2-USB-OTG and ESP32-S3-USB-OTG, length 60 cm, marks on cable “AWM 80 C 30V VW-1 USB 2.0 CABLE”:

  • directly connected to mac M1 2020 – does not work
  • workaround #1: press and hold boot button on the board, start flash command, release boot button after flashing process starts
  • workaround #2: connect mac M1 to monitor/hub with USB-C and connect the cable there
  • workaround #3: use computer with Windows or Linux, the cable seems to work correctly with these OSes