Text
Page: 1
A Beginner's Complete
Guide to Microcontroller
Programming with Ruby
hasumikin
Bangkok, Thailand
October 6th - 7th 2023
Page: 2
Part 1
Preparation
Part 2
Getting Started with Microcontroller
Part 3
Exploring PicoRuby Further
Part 4
PicoRuby Under the Hood
Page: 3
self.inspect
Hitoshi HASUMI
hasumikin (GitHub, ex-Twitter, Bluesky and Threads)
Creator of PicoRuby and PRK Firmware
Committer of CRuby's IRB and Reline
First prize of Fukuoka Ruby Award
(2020 and 2022 )
A final nominee of Ruby Prize 2021
Page: 4
Part 1
Preparation
Page: 5
Setup (minimal)
Raspberry Pi Pico
Or other RP2040-based controller
USB cable
Terminal emulator on laptop
RP2040
Page: 6
Raspberry Pi Pico
Raspberry Pi Pico: Microcontroller board
MCU: RP2040
Cortex-Mzero+ (dual)
264 KB RAM
2 MB flash ROM
It generally runs without an OS (bere metal)
ref) Raspberry Pi: Single-board computer
It generally needs an OS like Raspberry Pi OS or
Windows for Arm
Page: 7
Terminal emulator
Linux -> GTKTerm
Windows -> Tera Term
macOS -> PuTTY (I'm not sure)
Traditional CUI/TUI tools may have CR/LF
trouble
cu
screen
minicom
Page: 8
Let's begin 1/4
Download the latest
`R2P2-*.(zip|gz)`
from GitHub
then unzip it into
`R2P2-*.uf2`
github.com/picoruby/R2P2/releases
Page: 9
Let's begin 2/4
Connect Pi Pico and PC while
pressing the BOOTSEL button
You'll find "RPI-RP2" drive
in file manager
https://www.raspberrypi.org/documentation/rp2040/getting-started
Page: 10
Let's begin 3/4
Drag & drop `R2P2-*.uf2` into RPI-RP2 drive
Page: 11
Let's begin 4/4
Open a proper
serial port on
terminal emulator
Page: 12
R2P2 Shell should start [Demo]
Unix-like shell running on Raspberry Pi Pico
You can use some
commands like `pwd`, `cd`,
`ls`, `mkdir`
It apparently has a
filesystem
(written in Ruby!)
Page: 13
PicoIRB [Demo]
PicoRuby's IRB is running within the R2P2 shell
on Raspberry Pi Pico
Your Ruby snippet is going to be compiled into
mruby VM code and executed on the fly
It means PicoRuby contains an mruby compiler
which can run on a one-chip microcontroller (will be
mentioned later)
Page: 14
Part 2
Getting Started with
Microcontroller
Page: 15
GPIO (General Purpose Input/Output)
Fundamental digital I/O
Variety of uses:
Input: Detects on-off state of switch and button
Output: Makes a voltage
You can even implement a communication protocol
by controlling GPIO in milli/micro sec
Page: 16
GPIO --- Blinking LED [Demo]
irb> led = GPIO.new(25, GPIO::OUT)
irb> 3.times do
irb*
led.write 1
irb*
sleep 1
irb*
led.write 0
irb*
sleep 1
irb* end
GPIO25 internally connects to on-board LED
through a resistor
Page: 17
GPIO --- Blinking LED by discrete parts
Parts list:
LED (RED)
Resistor (1kΩ)
Page: 18
GPIO --- Blinking LED by discrete parts
irb> pin = GPIO.new(15, GPIO::OUT)
Page: 19
GPIO --- Blinking LED by discrete parts
GPIO15 ===> 1kΩ ===> LED ===> GND
<----- 1.5V -----><--- 1.8V ---->
<------------ 3.3V ------------->
RP2040's logic level: 3.3V
LED voltage drop: 1.8V
(according to LED's datasheet)
Current: (3.3V - 1.8V) / 1kΩ = 1.5mA
(calculated by Ohm's Law)
Page: 20
Study time: Electromagnetism | Physics
Ohm's Law
V = I * R ⇔ I = V / R ⇔ R = V / I
Kirchhoff's Circuit Laws
Current law: The algebraic sum of currents in a
network of conductors meeting at a point is zero
Voltage law: The directed sum of the potential
differences (voltages) around any closed loop is zero
Page: 21
ADC (Analog to Digital Converter)
ADC handles values from 0 to logic-level by
converting an analog voltage to a digital value
e.g. RP2040's ADC has 12 bits depth and
accordingly takes a raw value from 0 (0 V) to
4095 (3.3 V)
Typical usage:
Temperature sensor
Joystick
Page: 22
ADC --- Temperature [Demo]
irb>
irb>
irb>
irb>
irb*
irb*
irb*
irb*
require 'adc'
adc = ADC.new(:temperature)
adc.read_raw
while true
voltage = adc.read_voltage
puts 27 - (voltage - 0.706) / 0.001721
sleep 1
end
RP2040 has an in-chip temperature sensor that
connects to an ADC channel
Page: 23
ADC --- Temperature by discrete parts
Parts list:
Resistor
Rref: 10kΩ
Thermistor
10kΩ (at 25℃ = 298.15K)
B const: 3950
T0: 298.15 (kelvin)
Page: 24
ADC --- Temperature by discrete parts
Parts list:
Resistor
Rref: 10kΩ
Thermistor
10kΩ (at 25℃ = 298.15K)
B const: 3950
T0: 298.15 (kelvin)
Page: 25
ADC --- Temperature by discrete parts
irb> require 'adc'
irb> Rref = 10000.0
irb> B = 3950.0
irb> T0 = 298.15
irb> def kelvin_temp(rth)
irb*
temp_inverse = 1 / B * Math.log(rth / Rref) + (1 / T0)
irb*
1 / temp_inverse
irb* end
irb> rth = (3.3 / adc.read_voltage - 1) * Rref
irb> puts "#{kelvin_temp(rth) - 273.15} C"
=> 28.1234 C
Page: 26
Part 3
Exploring PicoRuby Further
Page: 27
PicoRuby applications
R2P2
Unix-like shell system written in PicoRuby
You may want to say an Operating System in Ruby
PRK Firmware
Keyboard firmware framework for DIY keyboard
You can write your keymap and keyboard's behavior
with Ruby
Page: 28
R2P2 (again)
IRB
Multiple-line editor
Built-in commands and executables (all written
in Ruby)
You can write your own external command
Page: 29
Executables in R2P2
# date
puts Time.now.to_s
# mkdir
Dir.mkdir(ARGV[0])
Page: 30
Write a Ruby script file [Demo]
$> vim hello.rb
Edit the file and save it.
puts "Hello World!"
Then run it.
$> ./hello.rb
Page: 31
Or just drag and drop [Demo]
Page: 32
GPIO and ADC work together [Demo]
require 'adc'
def calc_temp(volt)
27 - (volt - 0.706) / 0.001721
end
adc = ADC.new(:temperature)
led = GPIO.new(25, GPIO::OUT)
while true
temp = calc_temp(adc.read_voltage)
puts "temp: #{temp} C"
led.write(30 < temp ? 1 : 0)
sleep 1
end
Page: 33
R2P2 [Demo]
`/home/app.rb` automatically runs on start up
# You can stop by Ctrl-C
led = GPIO.new(25, GPIO::OUT)
while true
led.write 1
puts "Hello World!"
sleep 1
led.write 0
sleep 1
end
Page: 34
BTW,
R2P2 stands for
Ruby on Raspberry Pi Pico
Page: 35
[FYI] Serial communication protocols
SPI: High speed, full duplex. e.g. Acceleration
sensor, Color display, etc.
I2C: Low speed, Addressing network with fewer
wires. e.g. RTC, Temperature sensor and
Charactor display, etc.
UART: Buffered asyncronous communication.
e.g. Terminal emulator, Wireless module like BLE
and LTE/5G, etc.
Page: 36
[FYI] I2C and UART
Example of I2C (RTC)
and UART (USB serial)
Watch the demo video
in README.md
It's also an example of
how to build your own app
github.com/picoruby/rp2040-peripheral-demo
Page: 37
[FYI] I2C and UART
Parts list:
PCF8523 RTC module
FTDI USB to TTL Serial
Adapter Cable (3.3V)
Page: 38
PRK Firmware - Corne (CRKBD)
Page: 39
PRK Firmware - Meishi2 (4-keys pad)
require "consumer_key"
kbd = Keyboard.new
kbd.init_pins(
# row0, row1
[ 6, 7 ],
[ 28, 27 ] # col0, col1
)
kbd.add_layer :default, %i[ ZERO_RAISE KC_1 KC_2 KC_3 ]
kbd.define_mode_key :ZERO_RAISE, [ :KC_0, :raise, 200, 200 ]
%i[ ZERO_RAISE
kbd.add_layer :raise,
KC_AUDIO_VOL_UP
KC_AUDIO_VOL_DOWN
KC_AUDIO_MUTE ]
kbd.start!
Page: 40
Part 4
PicoRuby Under the Hood
Page: 41
mruby and PicoRuby
mruby
General purpose embedded Ruby implementation
written by Matz
PicoRuby (PicoRuby compiler + mruby/c VM)
Another implementation of murby targeting on one-
chip microcontroller (smaller foot print)
The VM code specifications are common to both
So the compilers are interchangeable
Page: 42
Small foot print
$ valgrind
--tool=massif
--stacks=yes
path/to/(mruby|picoruby)
-e 'puts "Hello World!"'
\
\
\
\
`massif.out.[pid]` file will be created. Then,
$ ms_print massif.out.1234 | less
Page: 43
Small foot print
--------------------------------------------------------------------------------
Command:
mruby -e 'puts "Hello World!"'
Massif arguments:
--stacks=yes
ms_print arguments: massif.out.18391
--------------------------------------------------------------------------------
KB
133.5^
#
|
#
|
#
|
#
|
#
|
#
|
#
|
#
|
@
:@:::@:#:
|
@:@@@::::@:::@:#::
|
::@::::::@::::::::@:@@@::::@:::@:#::
|
@:::::::::@@::::@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@@@:::@:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
|
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
| @
@ :: @:::::::::@ :: :@:::@::::::@:::::: :@:@@@::::@:::@:#::
0 +----------------------------------------------------------------------->Mi
0
1.281
# Note: Measured in 64 bit Ubuntu
Page: 44
Small foot print
--------------------------------------------------------------------------------
Command:
picoruby -e 'puts "Hello World!"'
Massif arguments:
--stacks=yes
ms_print arguments: massif.out.21752
--------------------------------------------------------------------------------
KB
9.820^
#
|
@:#:::
|
@:#:::::
|
@:#:::::
|
@:#:::::
|
@
@:#:::::
|
@
@:#:::::
|
@
: @:#:::::
|
@
::: @:#:::::
|
@
: ::: @:#:::::
|
@
::@::::@:#:::::@
|
@
::@::::@:#:::::@
|
@:
:::::@::::@:#:::::@
|
@:::
:: :::@::::@:#:::::@
|
@::
::: :::@::::@:#:::::@
|
@:: :
::: :::@::::@:#:::::@
|
@:: :
::: :::@::::@:#:::::@
|
:@:: :
:
:: :@:@: :
:@@: ::@::::: ::: :::@::::@:#:::::@
|
:@:: :::::::::::::::@:@:@:::::@ ::: @:: : :::: :::@::::@:#:::::@
|::::::@::@:: :::
::: :::::@:@:@:: ::@ : : @:: : :::: :::@::::@:#:::::@
0 +----------------------------------------------------------------------->ki
0
324.5
# Note: Measured in 64 bit Ubuntu
Page: 45
Small foot print
RAM consumption of `puts "Hello World!"`
mruby: 133.5 KB (on 64 bit)
PicoRuby: 9.82 KB (on 64 bit)
RP2040 (32 bit) has 264 KB RAM
Only small applications running with mruby works
Big apps like R2P2 and PRK Firmware should be
written in PicoRuby
Page: 46
PicoRuby ecosystem
Picogems
PRK Firmware is also a Picogem
Peripheral gems
picoruby-gpio
picoruby-adc
picoruby-i2c
picoruby-spi
picoruby-uart
Peripheral interface guide
https://github.com/mruby/microcontroller-peripheral-interface-guide
Page: 47
PicoRuby ecosystem
Build system forked from mruby
You can build your application in a similar way to
mruby
You can also write your gem and host it on your
GitHub
RP2040 is the only target as of now. So,
You can port PicoRuby (Picogems) to other
microcontrollers
Page: 48
Restrictions of PicoRuby
Minimum built-in classes and methods
Doesn't support some syntax like heredoc and
numbered parameters
No meta-programming features
No strict distinction between instance methods
and singleton methods
Some bugs (because I'm lazy ).
See github.com/picoruby/picoruby/issues
Page: 49
Conclusion
PicoRuby is a Ruby implementaiton targeting on
one-chip microcontroller
Essential peripheral libraries: GPIO, ADC, I2C,
SPI, and UART are ready
You can develop your microcontroller
application step by step using the R2P2 and IRB
You need only R2P2 to write small applications
Future work:
Bluetooth for "Raspberry Pi Pico W" (soon
)
Page: 50
RubyKaigi 2024
[ad]
In Okinawa
May 15th - 17th
1000+ attendees, tons of tech talks
All Japanese talks come with simultaneous interpretation into English
Various parties
https://098free.com/photos/14262/
Page: 51
That's all! Visit repos and stargaze
github.com/picoruby/picoruby
github.com/picoruby/R2P2
github.com/picoruby/prk_firmware
github.com/picoruby/rp2040-peripheral-demo