# Nim on Arduino
There has been a few interesting projects over the past few years that have tried to bring alternative languages to the embedded world. The espruino and micropython are two interesting project that allow you to run programs written in JavaScript and python on a micro-controller. However they have one large drawback, they only support their own boards and therefore can only run them on a limited number of micro-controllers. These are two interesting new programming languages designed for low level system programming making them ideally suited for micro-controllers - rust and nim.
This post I will look at nim in an attempt to get it running on an Arduino UNO.
# Hello World in nim
After installing the latest version of nim (0.12.0 at the time of writing) it was trivial to get an example program up and running:
# example.nim
# This is a comment
echo("What's your name? ")
var name: string = readLine(stdin)
echo("Hi, ", name, "!")
nim compile --run example.nim
There are quite a few tutorials on how to program in nim in this post I will skip to the more interesting parts.
# Programming AVR without arduino
Before we start to look at how to compile and upload a nim program to and AVR chip we first need to see how this works without the Arduino SDK. Fortunately this process is quite easy and the example below where adapted from Balau's blog on the subject. I recommend reading his blog post for more details about the process.
# led.c
#include <avr/io.h>
#include <util/delay.h>
#define BLINK_DELAY_MS 1000
int main (void)
{
/* set pin 5 of PORTB for output*/
DDRB |= _BV(DDB5);
while(1) {
/* set pin 5 high to turn led on */
PORTB |= _BV(PORTB5);
_delay_ms(BLINK_DELAY_MS);
/* set pin 5 low to turn led off */
PORTB &= ~_BV(PORTB5);
_delay_ms(BLINK_DELAY_MS);
}
}
Then to compile and upload to the Arduino UNO simply run the following
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led.o led.c
avr-gcc -mmcu=atmega328p led.o -o led
avr-objcopy -O ihex -R .eeprom led led.hex
# Change /dev/ttyACM0 to the serial port of your arduino
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:led.hex
And that is it, the on board LED should now be slowly blinking away.
# Compile and example nim program for AVR
The only working example I could find of how to compile a nim program for AVR was from this GitHub issue.
So let us try it out:
# hello.nim
echo "Hello, world!"
# panicoverride.nim
proc printf(frmt: cstring) {.varargs, importc, header: "<stdio.h>", cdecl.}
proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.}
{.push stack_trace: off, profiler:off.}
proc rawoutput(s: string) =
printf("%s\n", s)
proc panic(s: string) =
rawoutput(s)
exit(1)
{.pop.}
The above files can be converted to c with the following, note that I needed to
add the --gc:none
from the example from the GitHub issue.
nim c -c --gc:none --cpu:avr --os:standalone --deadCodeElim:on hello.nim
This will give you a directory named nimcache
with two c file inside, these
can be compiled and uploaded to the Arduino UNO using the commands from our
previous step, note that I added the include path to the nim libraries.
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -I/usr/lib/nim -c -o nimcache/hello.o nimcache/hello.c
avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -I/usr/lib/nim -c -o nimcache/system.o nimcache/system.c
avr-gcc -mmcu=atmega328p -I/usr/lib/nim nimcache/hello.o nimcache/system.o -o nimcache/hello
avr-objcopy -O ihex -R .eeprom nimcache/hello nimcache/hello.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:nimcache/hello.hex
And the led stops blinking - progress, but nim is able to directly compile to
AVR, allowing us to skip the avr-gcc
steps. In order to do this we need to
specify a few options via the nim.cfg. I found these options by using the
--parallelBuild:1 --verbosity:2
flags to see how nim was compiling the
program.
First I noticed it was using gcc
not gcc-avr
. This was fixed by adding the
following
# nim.cfg
avr.standalone.gcc.path = "/usr/bin"
avr.standalone.gcc.exe = "avr-gcc"
avr.standalone.gcc.linkerexe = "avr-gcc"
I then noticed some of the flags where missing from the compiler and linker. This was fixed by adding the following to the config
# nim.cfg
passC = "-Os"
passC = "-DF_CPU=16000000UL"
passC = "-mmcu=atmega328p"
passL = "-mmcu=atmega328p"
Finally I added a couple more options for convenience
# nim.cfg
cpu = "avr"
gc = "none"
define = "release"
deadCodeElim = "on"
The os flag is the only one I could not get to work in the config and needs to be passed in on the command line. You can now compile and upload the program with
nim c --os:standalone hello.nim
avr-objcopy -O ihex -R .eeprom hello hello.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:hello.hex
# Blink in nim
Now its time to get nim to blink the led. We will only need the nim.cfg
and
panicoverride.nim
files from the previous steps. Then we need to create a
small c library to talk to the Arduino that we can wrap with nim. This is just
the c example above split into separate functions.
# led.c
#include <avr/io.h>
#include <util/delay.h>
void led_setup(void) {
DDRB |= _BV(DDB5);
}
void led_on() {
PORTB |= _BV(PORTB5);
}
void led_off() {
PORTB &= ~_BV(PORTB5);
}
void delay(int ms) {
// Not the best way to do this, but it does not matter for this example
for (int i = 0; i < ms; i++) {
_delay_ms(1);
}
}
And now for the nim version of blink.
# blink.nim
{.compile: "led.c".}
proc led_setup(): void {.importc.}
proc led_on(): void {.importc.}
proc led_off(): void {.importc.}
proc delay(ms: int): void {.importc.}
when isMainModule:
led_setup();
while true:
led_on();
delay(1000);
led_off();
delay(1000);
Finally the steps to compile and upload it, these are basically the same as
above. Note that we led.c is compiled for us due to the {.compile: "led.c".}
line in blink.nim.
nim c --os:standalone blink.nim
avr-objcopy -O ihex -R .eeprom blink blink.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:blink.hex
# Conclusion
Overall the process was quite straight forward with the main issue being lack of documentation specific to the AVR architecture. Although it is a good starting point to trying out nim on an Arduino we are still missing the Arduino libraries. It would be a lot of work for any real project to be written in it without more work on supporting libraries or wrapping the Arduino libraries.