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

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

24. June 2022

Weather display for LilyGO TTGO T5-4.7″ E-Paper ESP32 deployed using Arduino IDE 2.0

LilyGO TTGO T5-4.7″ E-Paper ESP32 is nice display which integrates ESP32, USB-C, Li-Po and 18650 accumulator support in one board. The display driver is GDEH0213B72.

One interesting use-case for the board is the Weather display.

There are several steps to get the Weather display working. Let’s walk through them.

Drivers

Linux users may skip this section since the modern kernel supports CH34x drivers.

macOS users may encounter the following error when flashing:

Failed to write to target RAM (result was ...)

It’s necessary to install the driver from https://github.com/WCHSoftGroup/ch34xser_macos.

Windows 8, 10 users may need to install https://www.wch.cn/download/CH343SER_EXE.html. Windows 11 should install the driver automatically.

Arduino IDE setup

Download Arduino IDE 2.0.

Add ESP32 boards support. Click File, click Preferences, select Settings tab. Enter the following URL to Additional boards manager URLs:

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

Click Ok. Click Tools, select Boards: …, click Boards Manager… . It will open the left pane with a list of boards. Type esp32 to Filter your search field. Find esp32 by Espressif Systems, click Install.

Preparing code

Open a terminal and clone LilyGo-EPD47 library to Arduino/libraries:

Linux:

cd ~/Arduino/libraries
git clone git@github.com:Xinyuan-LilyGO/LilyGo-EPD47.git --depth 1

macOS:

cd ~/Documents/Arduino/libraries
git clone git@github.com:Xinyuan-LilyGO/LilyGo-EPD47.git --depth 1

Make a clone LilyGo-EPD-4-7-OWM-Weather-Display to the directory with Arduino projects. The folder name with the project should match the name of the source code file OWM_EPD47_epaper_v2.5 to avoid the unnecessary step of moving the file.

Linux:

cd ~/Arduino
git clone git@github.com:Xinyuan-LilyGO/LilyGo-EPD-4-7-OWM-Weather-Display.git OWM_EPD47_epaper_v2.5

macOS:

cd ~/Documents/Arduino/
git clone git@github.com:Xinyuan-LilyGO/LilyGo-EPD-4-7-OWM-Weather-Display.git OWM_EPD47_epaper_v2.5

Open Arduino IDE 2.0, click File, select Sketchbook, click OWM_EPD47_epaper_v2.5.

The project requires ArduinoJson to build. Click Tools, click Manage libraries… . The pane with Library Manager should open, type ArduinoJson to Filter your search field. Find ArduinoJson by Benoit Blanchon, click Install.

Try to build the project.

It might fail with the following error:

.../Arduino/libraries/LilyGo-EPD47/src/rmt_pulse.c:9:24: fatal error: hal/rmt_ll.h: No such file or directory
compilation terminated.
Multiple libraries were found for "WiFiClient.h"
  Used: .../.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/WiFi
  Not used: .../arduino-1.8.13/libraries/WiFi
exit status 1

Open the file ~/Arduino/libraries/LilyGo-EPD47/src/rmt_pulse.c and comment out line 9:

/* #include <hal/rmt_ll.h> */

The project should be buildable now.

Configuring local parameters

Open file owm_credentials.h and configure ssid, password, apikey, City, Country.

The project is acquiring data from openweathermap.org. Create new free account in order to get apikey.

The project implementations contain support for power saving, so if you’re flashing in the early morning/late night, you might be surprised that nothing is on display. To change power-saving options open file OWM_EPD47_epaper_v2.5.ino and change WakeupHour to a value that suits your need.

Flashing

Connect the module. Select the board from the dropdown in the toolbar. Click Port (/dev/ttyACMx on Linux), filter for ESP32 Wrover module and click Ok.

Click the Upload arrow.

If the flashing is successful, you may enjoy the new Weather display. Congratulations!

3D printed enclosure

There are several versions of files for 3D printing. You can find them in the discussion at GitHub – LilyGo-EPD47. The picture in this article is based on model thing:4782302 printed on Original Prusa MINI+ with PET-G. The model has a few cosmetic limitations:

  • It’s not possible to keep the display standing while USB-C is connected.
  • Buttons are not completely reachable.
  • The display must be attached by a tape or other method to the stand to avoid detaching from the case.

Notes

Double-check whether the battery holder is present when ordering the board with the display from the e-shop. Even when it’s displayed on the picture, it does not mean that the battery holder or battery is part of the delivery.

When connecting by USB-C to USB-C cable, the device should light up at least a red led. If nothing is visible, rotate the USB-C connected to the board by 180 degrees or use USB-A to USB-C cable.

13. June 2018

Android Studio NDK could not start mips64el-linux-android-strip

If you’re using Android Studio to build C++ application with NDK you might encounter following error during build:

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:transformNativeLibsWithStripDebugSymbolForDebug'.
...
Caused by: java.io.IOException: Cannot run program ".../Android/Sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64/bin/mips64el-linux-android-strip": 
error=2, No such file or directory

The problem is caused by the upgrade of NDK in Android Studio to NDK v17. This version has a different structure of tools. It’s not compatible with Android Experimental Plugin v0.11.

The directory which should contain binary of tools contains just file NOTICE-MIPS64:

This mips64el-linux-android-4.9 directory exists to make the NDK compatible with the Android
SDK's Gradle plugin, version 3.0.1 and earlier, which expects the NDK
to have a MIPS64 toolchain directory.

The solution is to download NDK v16 from NDK older releases. Extract it and replace former ndk-bundle directory.

Commands for macOS or Linux users:

cd ~/Android/Sdk
unzip ~/Downloads/android-ndk-r16b-linux-x86_64.zip
mv ndk-bundle ndk-bundle-v17
mv android-ndk-r16b ndk-bundle

If you’re Windows user the location of NDK is typically in your profile AppData\Local\Android. Do not forget to unblock the ZIP after downloading (right click, Properties, Unblock, Ok)

Commands for Windows users:

cd ~\AppData\Local\Android\Sdk
unzip ~\Downloads\android-ndk-r16b-windows-x86_64.zip
mv ndk-bundle ndk-bundle-v17
mv android-ndk-r16b ndk-bundle

Start Android Studio. It should prompt you to update NDK. Do not confirm this request, it will replace NDK with v17. You should see the following screen in SDK Manager:

You can find related sample source code at GitHub in sdl2-android-example – branch gradle-4-using-android-experimental-plugin repository. Further articles about SDL2 and Android are available under the tag SDL2.

Since Google is moving away from Gradle Android Experimental plugin you might consider using Gradle 5 + CMake. The migration is relatively easy and it could save you the trouble of being stuck with old NDK. You need to create CMakeLists.txt instead of build.gradle for C/C++ parts of the code. Example of migration is at GitHub – sdl2-android-example – PR#6.

10. May 2018

SDL2_gfx for Android – Graphic primitives

In previous article, we were talking about drawing pictures in JPEG format. Let’s look how to draw some graphic primitives like a line. SDL2_gfx is small library which has support for graphic primitives and some surface functions.

The initial steps to add SDL2_gfx to Android project is the same like in case of SDL2_jpeg.

Register library in settings.gradle:

include ':SDL2_gfx'

The library should be stored in SDL2_gfx directory with build.gradle.

Now update our C application.

Here is simple example which draws a line:

thickLineColor(renderer, 0, 300, 300, 300, 20, 0xFF00FFFF);

You can find the source code at GitHub in sdl2-android-example repository. Further articles about SDL2 and android are available under the tag SDL2.

Special thanks for this extension of SDL2 Android example goes to jojomickymack who suggested how to add support for SDL2_gfx to Android project. Thank you.