Text
Page: 1
mruby/c
The smallest Ruby
implementation for
microcontrollers
HASUMI Hitoshi @hasumikin
May 6, 2019 in Warszawa
May 14, 2019 in Krakรณw
Page: 3
about me
HASUMI Hitoshi
@hasumikin
live in Matsue city,
a holy place of Ruby
Sake ๐ถ
Soba ๐
Coffee โ
Page: 7
message from Matz
# # video
# # src = images/video.mp4
Page: 8
agenda
terminology
about my IoT project
mruby and mruby/c
how does mruby/c work
actual source code of my project
development environment
Page: 10
terminology
mruby/c
tha language I will talk about today
I say mrubyc as /c is hard to pronounce
microcontroller
small computer contains CPU, memory and
programmable I/O peripherals
in this talk, microcontroller is distinguished
from single board computer like Raspberry
Pi
Page: 11
terminology
RTOS
Real-time OS. usually used for
microcontroller
task
almost equivalent to `Thread` in linux. we
say `task` in microcontroller world
Page: 12
terminology
ๆญๆฅ้
้ (Aasahi-shuzo)
shuzo means Sake brewery
one of the best Japanese Sake brewery
Asahi-beer(famous for SUPER DRY) has no
concern with Asahi-shuzo
Aasahi-shuzo and I make IoT system using
mruby/c
Page: 13
why microcontroller?
I never use single board computer like
Raspberry Pi for production
environment.
Page: 14
why microcontroller?
it starts immediately right after plugged
in
end users, brewery workers in my case,
can use it simply like home electical things
you can narrow security issue list
many a malware aims at Linux or Windows
platform as a target
you don't need to consider unnecessary
deamon
you don't need to do `apt upgrade`
Page: 15
why microcontroller?
low energy
rarely overheated
many choices of power supply
mass production
you can choose appropriate chipset
(number of GPIOs, memory size, etc.) for
your application
cost advantage for parts supply and
subcontractor manufacturing
Page: 16
which microcontroller?
Page: 17
which microcontroller?
CYPRESS PSoC5LP
32 bit Arm Cortex-M3 CPU
Flash: 256KB
SRAM: 64KB
64KB is the target size
of mruby/c
Page: 18
which microcontroller?
Espressif ESP-WROOM-32 (ESP32)
32 bit dual core LX6 CPU
Flash: 4MB
SRAM: 520KB
Page: 19
about my IoT project
Page: 20
about my IoT project
IoT system for Asahi-shuzo
delivered to actual brew work in
January 2018
devices post temperature of Sake
materials in brewing, surrounding
temperature and humidity to server
data is displayed on smartphone app
Page: 21
about my IoT project
Page: 22
about my IoT project
Page: 23
about my IoT project
what were difficult about mruby/c?
we can neither do step execution nor
look into appropriate memory address
of mruby/c's variables
so many troubles in IoT
hard to find why the application doesn't
work well
mruby/c was growing
bugs, lack of features, docs and examples
Page: 24
about my IoT project
so, was mruby/c bad?
Page: 25
about my IoT project
so, was mruby/c bad? - NO
IoT at work makes you hurry
you have to go back and forth between
dark 10โ storage cellar and humid 35โ
manufacturing room
brewery workers run around
you have to amend your firmware with your
small laptop in 10 minutes
you will thank Ruby's descriptiveness and
agility
Page: 26
today's demo
CO 2 concentration
400ppm : atmospheric
1000ppm : your programming speed
decreases
1500ppm : tomatoes ๐
may grow well
> 2000ppm : sleepy, headache
> 40000ppm : ๐
Page: 27
today's demo
my device keeps taking CO 2
concentration
I will prove that it is due to CO 2 if
someone slept while I speaking
Page: 28
so many troubles in IoT
Page: 29
so many troubles in IoT
peripheral equipments (โ)
circuit and wiring design
printed circuit board = PCB
soldering (โ)
C, mruby and mruby/c (โ)
network
โ...I will explain these topics today
Page: 30
peripheral equipments
Page: 31
peripheral equipments
it is very important to check the
following before writing application
code
equipment like sensor or communication
module works as its spec sheet
whether the equipment is not broken
(sometimes broken by soldering ๐ญ)
unless you will regret
so...
Page: 32
peripheral equipments
Raspberry Pi & CRuby are great for pre-
prototyping
use breadboard or make PCB for test like this
photo
Page: 33
peripheral equipments
ex) CRuby for serial communication test
# notice this is CRuby for RasPi
require 'rubyserial'
require 'timeout'
BAUDRATE = 9600 # match with your instrument
sp = Serial.new '/dev/serial0', BAUDRATE, 8
loop do
puts '[command]'
command = gets
sp.write command.sub("\n", "\r") # replace LF if needed
sleep 0.1
result = ''
begin
Timeout.timeout(10) do
loop do
line = sp.read(128)
break if line == '' && result != ''
result << line
sleep 0.1
puts '=> ' + result
rescue Timeout::Error
puts 'timeout!'
Page: 34
peripheral equipments
ex) CRuby for serial communication test
$ serial_communication_test.rb
[command]
# command
AT
=> OK
# response
[command]
# command
AT+CIMI
=> 123456789012 # response
[command]
# command
AT+XXX
=> error
# response
Page: 36
soldering
it may work even if you leave a pin
unsoldered on surface mounting
because the pin touches circuit's surface
then, it will not work one day
Page: 37
soldering
discovering this kind of bug is much more
difficult than software bug
my teacher said
"all the cause of failure, it is impatience"
Page: 39
what is mruby?
github.com/mruby/mruby
another implemantation of Ruby for
embedded usage
easily being called from C/C++
ngx_mruby is a popular one
good for command line tools as one-
binary executable
Page: 40
what is mruby/c?
Page: 41
what is mruby/c?
github.com/mrubyc/mrubyc
yet another implementation of mruby
`/c` symbolizes compact, concurrent
and capability
especially dedicated to one-chip
microcontroller
Page: 42
mruby and mruby/c
mruby
mruby/c
v1.0.0 in Jan 2014
v1.0 in Jan 2017
for general
for one-chip
embedded
microcontroller
software
RAM < 400KB
RAM < 40KB
sometimes mruby is still too big to run
on microcontroller
Page: 43
both mruby and mruby/c
bytecodes are compiled by `mrbc` and VM
executes the bytecode
Page: 44
bytecode
a kind of intermediate representation
virtual machine dynamically interprets the
bytecode and processes the program
Page: 45
mruby on microcontroller
RTOS (Real-Time OS) manages mruby
VMs. RTOS has features like multi tasking
Page: 46
mruby/c on microcontroller
mruby/c has its own mechanism to
manage the runtime: rrt0
Page: 47
mruby/c - virtual machine (VM)
much smaller than mruby's one
that's why mruby/c runs on smaller RAM
accordingly, mruby/c has less
functionality than mruby
Page: 49
how less? - for example
mruby/c doesn't have module, hence
there is no Kernel module
then you must wonder how can you
`#puts`?
in mruby/c, `#puts` is implemented in
Object class
Page: 50
how less? - for example
mruby/c doesn't have #send, #eval, nor
#method_missing
moreover, mruby/c neither have your
favorite features like TracePoint nor
RubyVM::AST ๐
Page: 51
how less? - actually
the full list of mruby/c's classes
Array, FalseClass, Fixnum, Float, Hash,
Math, Mutex, NilClass, Numeric,
Object, Proc, Range, String, Symbol,
TrueClass, VM
Page: 52
despite the fact,
no problem in practical use of
microcontroller
as far as IoT go, mruby/c is enough
Ruby as I expect
we can fully develop firmwares with
features of mruby/c
Page: 53
how does mruby/c work
Page: 54
how does mruby/c work
~/sample_project
โโโ main.c
โโโ mrblib
โ
โโโ task_1.rb
โ
โโโ task_2.rb
โโโ src
โโโ task_1.c
โโโ task_2.c
task_*.c are compliled code from
task_*.rb
Page: 55
how does mruby/c work
/* main.c */
#include "src/task_1.c"
#include "src/task_2.c"
// use 30KB RAM for VMs in this case
#define MEMORY_SIZE (1024*30)
static uint8_t memory_pool[MEMORY_SIZE];
int main(void) {
mrbc_init(memory_pool, MEMORY_SIZE);
mrbc_create_task(task_1, 0);
mrbc_create_task(task_2, 0);
mrbc_run(); // 2 tasks run concurrently!
return 0;
// we will not write `main loop` in main.c
}
Page: 56
how does mruby/c work
we can run easily multiple VMs with
concurrency due to rrt0
you might be disappointed to know you
have to write C
yes, we have to write main.c
don't worry, it's almost boilerplate code
Page: 57
how does mruby/c work
~/mrubyc $ tree src -P *.h
src
โโโ alloc.h
โโโ c_array.h
...
โโโ console.h
โโโ errorcode.h
โโโ global.h
โโโ hal_posix
โ
โโโ hal.h
โโโ hal_psoc5lp
โ
โโโ hal.h
โโโ hal_esp32
โ
โโโ hal.h
โโโ load.h
โโโ mrubyc.h
โโโ opcode.h
โโโ rrt0.h
โโโ static.h
โโโ symbol.h
โโโ value.h
โโโ vm.h
โโโ vm_config.h
Page: 58
how does mruby/c work
~/mrubyc $ tree src
src
โโโ alloc.h
โโโ c_array.h
...
โโโ console.h
โโโ errorcode.h
โโโ global.h
โโโ hal_posix
โ
โโโ hal.h
โโโ hal_psoc5lp
โ
โโโ hal.h
โโโ hal_esp32
โ
โโโ hal.h
โโโ load.h
โโโ mrubyc.h
โโโ opcode.h
โโโ rrt0.h
โโโ static.h
โโโ symbol.h
โโโ value.h
โโโ vm.h
โโโ vm_config.h
-P *.h
# hal for POSIX
# hal for PSoC5LP
# hal for ESP32
Page: 59
how does mruby/c work
~/mrubyc $ tree src -P *.h
src
โโโ alloc.h
โโโ c_array.h
...
โโโ console.h
โโโ errorcode.h
โโโ global.h
โโโ hal_posix
โ
โโโ hal.h
โโโ hal_psoc5lp
โ
โโโ hal.h
โโโ hal_esp32
โ
โโโ hal.h
โโโ load.h
โโโ mrubyc.h
โโโ opcode.h
โโโ rrt0.h
# runtime scheduler
โโโ static.h
โโโ symbol.h
โโโ value.h
โโโ vm.h
โโโ vm_config.h
Page: 60
how does mruby/c work
~/mrubyc $ tree src -P *.h
src
โโโ alloc.h
โโโ c_array.h
...
โโโ console.h
โโโ errorcode.h
โโโ global.h
โโโ hal_posix
โ
โโโ hal.h
โโโ hal_psoc5lp
โ
โโโ hal.h
โโโ hal_esp32
โ
โโโ hal.h
โโโ load.h
โโโ mrubyc.h
โโโ opcode.h
โโโ rrt0.h
โโโ static.h
โโโ symbol.h
โโโ value.h # this gives you hints about variable
โโโ vm.h
โโโ vm_config.h
Page: 61
how does mruby/c work
~/mrubyc $ tree src -P *.h
src
โโโ alloc.h
โโโ c_array.h
...
โโโ console.h
โโโ errorcode.h
โโโ global.h
โโโ hal_posix
โ
โโโ hal.h
โโโ hal_psoc5lp
โ
โโโ hal.h
โโโ hal_esp32
โ
โโโ hal.h
โโโ load.h
โโโ mrubyc.h
โโโ opcode.h
โโโ rrt0.h
โโโ static.h
โโโ symbol.h
โโโ value.h
โโโ vm.h
โโโ vm_config.h # edit this if needed
Page: 63
debugging
we can neither do step execution nor
look into memory to see variables when
we use mruby/c in general
we should prepare a way of debugging
before writing app code
let's go with old-fashioned 'print
debug'. it'll be almost enough
Page: 64
debugging
/* a part of main.c */
// create serial console with UART for debug print
static void c_debugprint(mrb_vm *vm, mrb_value *v, int argc){
int total, used, free, fragment;
mrbc_alloc_statistics(&total, &used, &free, &fragment);
console_printf(
"Memory total:%d, used:%d, free:%d, fragment:%d\n",
total, used, free, fragment);
unsigned char *key = GET_STRING_ARG(1);
unsigned char *value = GET_STRING_ARG(2);
console_printf("%s:%s\n", key, value);
}
int main(void) {
...
mrbc_define_method(0, mrbc_class_object, "debugprint", c_debugprint);
...
}
Page: 65
debugging
# mruby
pi = 3.14
debugprint('Pi', pi.to_s)
=> # print in serial console like 'PuTTY' connecting USB
Memory total:30000, used:20000, free:10000, fragment:3
Pi:3.14
Page: 66
actual source code
Page: 67
actual source code
github.com/hasumikin/co2-demo
Page: 68
actual source code
~/co2-demo
โโโ main.c
โโโ mrblib
โโโ
โ
โ
โโโ
loops
โโโ
โโโ
models
โโโ
โโโ
โโโ
master.rb
slave.rb
co2.rb
led.rb
thermistor.rb
Page: 69
actual source code
# loops/master.rb
$co2 = Co2.new
# Makes it global so that another task
# can use it
led = Led.new(19) # 19 is a pin number which LED connects
while true
co2 = $co2.concentrate
if co2 > 2000
# When CO2 reaches fatal level
5.times do
# Turning LED on and off
led.turn_on
sleep 0.1
led.turn_off
sleep 0.1
end
elsif co2 > 1500 # CO2 reaches warning level
led.turn_on
# Just keeps turn it on
sleep 1
else
# Safe level
led.turn_off
# Turns off
sleep 1
end
end
Page: 70
actual source code
how does Led#trun_on work?
Page: 71
actual source code
# models/led.rb
class Led
def initialize(pin)
@pin = pin
gpio_init_output(@pin)
turn_off
end
def turn_on
gpio_set_level(@pin, 1)
end
Page: 72
actual source code
/* a part of main.c */
#include "models/led.c"
static void c_gpio_init_output(mrb_vm *vm, mrb_value *v,
int argc) {
int pin = GET_INT_ARG(1);
gpio_set_direction(pin, GPIO_MODE_OUTPUT);
}
static void c_gpio_set_level(mrb_vm *vm, mrb_value *v,
int argc){
int pin = GET_INT_ARG(1);
int level = GET_INT_ARG(2);
gpio_set_level(pin, level);
}
int main(void){
...
mrbc_define_method(0, mrbc_class_object, "gpio_init_output",
c_gpio_init_output);
mrbc_define_method(0, mrbc_class_object, "gpio_set_level",
c_gpio_set_level);
...
}
Page: 73
actual source code
/* a part of main.c */
#include "models/co2.c"
static void c_get_co2(struct VM *vm, mrbc_value v[], int argc){
uint8_t command[] = { // Command to take CO2
0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79
};
uart_write_bytes(uart_num, (const char*)command, 9);
// โWrite then โRead data
uint8_t data[10];
int length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, data, length, 10);
int i;
mrb_value array = mrbc_array_new( vm, 9 ); // mrubyc's variable
for( i = 0; i < 9; i++ ) {
mrb_value value = mrb_fixnum_value(data[i]);
mrbc_array_set( &array, i, &value ); // Adding a value to array
}
SET_RETURN(array); // Returning the array to mruby
}
int main(void){
...
mrbc_define_method(0, mrbc_class_object, "get_co2", c_get_co2);
...
}
Page: 74
actual source code
# models/co2.rb
class Co2
def concentrate
res = get_co2
# checks if the sensor works
if res[0] == 255 && res[1] == 134
res[2] * 256 + res[3]
else
0
end
end
end
Page: 75
actual source code
by the way,
C function can return String instead of
mruby/c Array
`mrbc_array_new` will allocate larger
memory than `mrbc_string_new`
so, you can use String instead of Array if
memory becomes short
Page: 76
actual source code
# loops/slave.rb
while true
co2 = $co2.concentrate
temperature = $thermistor.temperature
if co2 > 0
data = "co2=#{co2}&temperature=#{temperature}"
puts "DATASEND:#{data}"
sleep 300
else
sleep 3
end
end
Page: 77
development environment
Page: 78
development environment
PSoC Creator for PSoC5LP
Page: 79
development environment
the env depends on microcontroller
IDE is your env if you use PSoC5LP
you can code mruby on any text editor
IDE is almost mandatory to configure hardware
terminal based work is the one if you use
ESP32
Page: 80
dev tools for mruby/c
Page: 81
dev tools for mruby/c
mrubyc-utils
mrubyc-test
mrubyc-debugger
Page: 82
mrubyc-utils
github.com/hasumikin/mrubyc-utils
one-binary tool made with mruby
helps to install boilerplate of application
shows mruby/c's classes and methods
Page: 83
mrubyc-utils
your_project $ mrubyc-utils --help
Usage: mrubyc-utils COMMAND [ARGS]
install
update
checkout
-t | --tag
tag
classes
methods
-c | --class
compile
-w | --watch
-v | --version
-h | --help
Install mruby/c repo into your local and setup templates.
Please run this command at the top directory
of your firmware project.
Update mruby/c repo to the newest **master** branch.
Checkout specified tag or commit of mruby/c repo.
[required] You can specify anything that
`git checkout` will accept.
Show all tags of mruby/c repogitory that you installed.
Show all the classes that are defined in
mruby/c's virtual machine.
Show all the methods that are available
in a class of mruby/c.
[required] You have to specify class name
Compile your mruby source into C byte code.
This command is for PSoC Creator project. Use make command instead
if your project is dedicated to ESP32 or POSIX
[optional] Monitoring loop runs and will
compile mruby source every time you touched it.
Show version.
Show usage. (this message)
Dependencies:
git
mrbc (mruby compiler)
Page: 84
mrubyc-utils
your_project $ mrubyc-utils classes
- Array
- FalseClass
- Fixnum
- Float
- Hash
- Math
- Mutex
- NilClass
- Numeric
- Object
- Proc
- Range
- String
- Symbol
- TrueClass
- VM
Page: 85
mrubyc-utils
your_project $ mrubyc-utils methods --class=array
Array
- +
- inspect
- <<
- join
- []
- last
- []=
- length
- at
- max
- clear
- min
- collect
- minmax
- collect!
- new
- count
- pop
- delete_at
- push
- dup
- shift
- each
- size
- each_index
- to_s
- each_with_index
- unshift
- empty?
< Object
- first
- !
- index
...
Page: 86
mrubyc-test
github.com/mrubyc/mrubyc-test
unit testing framework
RubyGem implemented with CRuby
instead of mruby
supports stub and mock
official tool of mruby/c dev team
Page: 87
mrubyc-test
gathers information of test cases by CRuby
metaprogramming power
generates stub and mock methods
makes all-in-one script: test.rb
Page: 88
mrubyc-debugger
github.com/hasumikin/mrubyc-
debugger
RubyGem
debugger for infinite loop
Page: 89
(anime gif DEMO)
github.com/hasumikin/
mrubyc-debugger
Page: 91
summary
mruby/c is the smallest implementation
of Ruby
Page: 92
summary
mruby/c is the smallest implementation
of Ruby
we can write firmwares for
microcontrollers with mruby/c
Page: 93
summary
mruby/c is the smallest implementation
of Ruby
we can write firmwares for
microcontrollers with mruby/c
it has a short history though, it's ready
for production with the Rubyish
ecosystem like testing tool
Page: 95
(added after conference)
Page: 96
(added after conference)
the ventilation facility
of Browar Lubicz is pretty good ๐
Page: 98
conclusion
You should refresh air