Programando arduino en openbsd

2022/04/24

En un artículo previo utilicé Arduino-Makefile en debian para crear y cargar programas arduino. Me pregunto… ¿ahora que estoy utilizando OpenBSD (7.1, -current) cambiará mucho como hacerlo?

TL;DR:

Instalar paquetes:

$ doas pkg_add arduino-makefile picocom

Agregar al usuario al grupo dialer:

$ doas usermod -G dialer jmpc

Utilizar como Makefile:

# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile

BOARD_TAG    = mega
BOARD_SUB    = atmega2560
MONITOR_PORT = /dev/ttyU0
AVRDUDE_CONF = /usr/local/share/examples/avrdude/avrdude.conf

USER_LIB_PATH = libraries
# ARDUINO_LIBS  = <...> # no se están incluyendo librerías

include /usr/local/share/arduino-makefile/Arduino.mk

Conectarse a la placa a través del dispositivo /dev/ttyU0.

Instalación

Comienzo revisando los paquetes disponibles para arduino:

$ pkg_info -Q arduino
arduino-1.8.10v0
arduino-adafruit-gfx-1.7.0
arduino-adafruit-ra8875-1.3.5
arduino-esp32-2.0.1
arduino-esp8266-3.0.2p0
arduino-makefile-1.6.0p2
makeesparduino-6.5.0p1

¡Suerte!, al parecer ya está empaquetado Arduino-Makefile. Se revisa la descripción de ambos paquetes:

$ pkg_info -d arduino arduino-makefile
Information for https://cdn.openbsd.org/pub/OpenBSD/snapshots/packages/amd64/arduino-1.8.10v0.tgz

Description:
Arduino is an open-source electronics prototyping platform based on
flexible, easy-to-use hardware and software. It's intended for artists,
designers, hobbyists, and anyone interested in creating interactive
objects or environments.

Arduino can sense the environment by receiving input from a variety of
sensors and can affect its surroundings by controlling lights, motors,
and other actuators. The microcontroller on the board is programmed
using the Arduino programming language (based on Wiring) and the Arduino
development environment (based on Processing). Arduino projects can be
stand-alone or they can communicate with software running on a computer
(e.g. Flash, Processing, MaxMSP).

This package contains the libraries, header files and tools to develop
Arduino sketches. It does not include the Arduino IDE frontend.

Maintainer: The OpenBSD ports mailing-list <ports@openbsd.org>

WWW: https://www.arduino.cc/


Information for https://cdn.openbsd.org/pub/OpenBSD/snapshots/packages/amd64/arduino-makefile-1.6.0p2.tgz

Description:
This is a very simple Makefile which knows how to build Arduino
sketches. It defines entire workflows for compiling code, flashing
it to Arduino and even communicating through Serial monitor. You
don't need to change anything in the Arduino sketches.

To use, create a Makefile and add:

	include /usr/local/share/arduino-makefile/Arduino.mk

Then compile with `gmake` and upload with `gmake upload`.

Maintainer: The OpenBSD ports mailing-list <ports@openbsd.org>

WWW: https://github.com/sudar/Arduino-Makefile

Confirmamos que es lo que estamos buscando. También vemos en la descripción indicaciones de como usar Arduino-Makefile:

Se revisan las dependencias de los paquetes:

@name arduino-1.8.10v0
@depend devel/avr/binutils:avr-binutils-*:avr-binutils-2.30
@depend devel/avr/gcc:avr-gcc-*:avr-gcc-8.5.0p0
@depend devel/avr/libc:avr-libc-*:avr-libc-2.0.0
@depend devel/avrdude:avrdude-*:avrdude-6.3
@name arduino-makefile-1.6.0p2
@depend devel/arduino:arduino-*:arduino-1.8.10v0
@depend devel/gmake:gmake-*:gmake-4.3
@depend devel/py-serial,python3:py3-serial-*:py3-serial-3.4p2
@depend devel/teensyloader:teensyloader-*:teensyloader-2.2
@depend lang/python/3.9,-main:python->=3.9,<3.10:python-3.9.12

Aquí vemos que arduino-makefile depende de arduino, así que vamos a instalar directamente el primero:

$ doas pkg_add arduino-makefile

Revisando el contenido de los paquetes, vemos ambos traen ejemplos, entre ellos el Hello World! del mundo arduino: Blink.

$ pkg_info -L arduino arduino-makefile | grep /Blink/
/usr/local/share/examples/arduino/01.Basics/Blink/Blink.ino
/usr/local/share/examples/arduino/01.Basics/Blink/Blink.txt
/usr/local/share/examples/arduino-makefile/Blink/Blink.ino
/usr/local/share/examples/arduino-makefile/Blink/Makefile

Revisando la diferencia en los archivos, aparte de los comentarios en un caso se utiliza el valor del pin 13 y en otro la constante LED_BUILTIN.

Si bien el archivo /usr/local/share/examples/arduino-makefile/Blink/Makefile parecería que podría sernos útil, este incluye una ruta relativa a Arduino.mk que no aplicaría a nuestro caso, ya que vamos a copiar solo el ejemplo y tampoco aplicaría el valor de la variable BOARD_TAG, ya que la placa que voy a estar utilizando como ejemplo es la atmega2560. Por lo anterior copio el ejemplo del paquete arduino y creo un Makefile a partir del que tenía en el post anterior con debian con los siguientes cambios:

$ cp -a /usr/local/share/examples/arduino/01.Basics/Blink .
$ cd Blink
$ cat > Makefile <<'END'
# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile

BOARD_TAG    = mega
BOARD_SUB    = atmega2560
# MONITOR_PORT = /dev/ttyACM0 # el dispositivo no existe en OpenBSD
AVRDUDE_CONF = /usr/local/share/examples/avrdude/avrdude.conf

USER_LIB_PATH = libraries
# ARDUINO_LIBS  = <...> # no se están incluyendo librerías

include /usr/local/share/arduino-makefile/Arduino.mk
END

Como paso siguiente compilamos con gmake y vemos que errores se obtienen:

$ gmake
-------------------------
Arduino.mk Configuration:
- [AUTODETECTED]       CURRENT_OS = OPENBSD
- [USER]               ARDUINO_DIR = /usr/local/share/arduino
- [COMPUTED]           ARDMK_DIR = /usr/local/share/arduino-makefile (relative to Common.mk)
- [DEFAULT]            ARDUINO_VERSION = 100
- [DEFAULT]            ARCHITECTURE =
- [DEFAULT]            ARDMK_VENDOR = arduino
- [DEFAULT]            ARDUINO_SKETCHBOOK =
- [USER]               AVR_TOOLS_DIR = /usr/local
- [COMPUTED]           ARDUINO_LIB_PATH = /usr/local/share/arduino/libraries (from ARDUINO_DIR)
- [USER]               ALTERNATE_CORE_PATH = /usr/local/share/arduino
- [COMPUTED]           ARDUINO_VAR_PATH = /usr/local/share/arduino/variants (from ALTERNATE_CORE_PATH)
- [COMPUTED]           BOARDS_TXT = /usr/local/share/arduino/boards.txt (from ALTERNATE_CORE_PATH)
- [USER]               USER_LIB_PATH = libraries
- [DEFAULT]            PRE_BUILD_HOOK = pre-build-hook.sh
- [USER]               BOARD_SUB = atmega2560
- [USER]               BOARD_TAG = mega
- [COMPUTED]           CORE = arduino (from build.core)
- [COMPUTED]           VARIANT = mega (from build.variant)
- [COMPUTED]           OBJDIR = build-mega-atmega2560 (from BOARD_TAG)
- [COMPUTED]           ARDUINO_CORE_PATH = /usr/local/share/arduino/cores/arduino (from ALTERNATE_CORE_PATH, BOARD_TAG and boards.txt)
- [ASSUMED]            MONITOR_BAUDRATE = 9600
- [DEFAULT]            OPTIMIZATION_LEVEL = s
- [DEFAULT]            MCU_FLAG_NAME = mmcu
- [DEFAULT]            CFLAGS_STD = -std=gnu11 -flto -fno-fat-lto-objects
- [DEFAULT]            CXXFLAGS_STD = -std=gnu++11 -fno-threadsafe-statics -flto
- [COMPUTED]           DEVICE_PATH = /dev/ttyU0 (from MONITOR_PORT)
- [DEFAULT]            FORCE_MONITOR_PORT =
- [AUTODETECTED]       Size utility: Basic (not AVR-aware)
- [COMPUTED]           BOOTLOADER_PARENT = /usr/local/share/arduino/hardware/arduino//bootloaders (from ARDUINO_DIR)
- [COMPUTED]           ARDMK_VERSION = 1.5
- [COMPUTED]           CC_VERSION = 8.5.0 (avr-gcc)
-------------------------
...
/usr/local/bin/avr-size build-mega-atmega2560/Blink_.hex
   text	   data	    bss	    dec	    hex	filename
      0	   1536	      0	   1536	    600	build-mega-atmega2560/Blink_.hex

De la salida anterior vemos que compiló sin problemas y nos da la pista que el dispositivo a usar para conectar la placa y subir el programa es /dev/ttyU0.

Comenzamos revisando el manual:

$ apropos tty
...
tty, cua(4) - general terminal interface
ucom(4) - USB tty support
...
$ man ucom
UCOM(4)                      Device Drivers Manual                     UCOM(4)

NAME
     ucom – USB tty support
...
FILES
     /dev/ttyU?    tty devices
     /dev/cuaU?    call out devices (see tty(4))

Ahora revisando los permisos del dispositivo:

$ ls -lh /dev/ttyU*
crw-rw---- 1 root dialer 66, 0 Apr 17 03:19 /dev/ttyU0
crw-rw---- 1 root dialer 66, 1 Apr 17 03:19 /dev/ttyU1
crw-rw---- 1 root dialer 66, 2 Apr 17 03:19 /dev/ttyU2
crw-rw---- 1 root dialer 66, 3 Apr 17 03:19 /dev/ttyU3

Por lo que tenemos que agregarnos al grupo dialer:

$ doas usermod -G dialer jmpc

y ahora tenemos que salir de la sesión y volver a entrar para que se cargue el grupo.

Confirmamos que tenemos permisos:

$ groups
jmpc ... dialer ...

Conectamos la placa a un puerto usb y corroboramos que el sistema la detectó:

$ dmesg
umodem0 at uhub0 port 2 configuration 1 interface 0 "Arduino (www.arduino.cc) product 0x0042" rev 1.10/0.01 addr 5
umodem0: data interface 1, has no CM over data, has break
umodem0: status change notification available
ucom0 at umodem0

Ahora intentamos hacer un upload:

$ gmake upload
...
mkdir -p build-mega-atmega2560
gmake reset
gmake[1]: Entering directory '/home/jmpc/tmp/Blink'
/usr/local/bin/ard-reset-arduino  /dev/ttyU0
gmake[1]: Leaving directory '/home/jmpc/tmp/Blink'
gmake do_upload
gmake[1]: Entering directory '/home/jmpc/tmp/Blink'
/usr/local/bin/avrdude -q -V -p atmega2560 -C /usr/local/share/examples/avrdude/avrdude.conf -D -c wiring -b 115200 -P /dev/ttyU0 \
		-U flash:w:build-mega-atmega2560/Blink_.hex:i

avrdude: AVR device initialized and ready to accept instructions
avrdude: Device signature = 0x1e9801 (probably m2560)
avrdude: reading input file "build-mega-atmega2560/Blink_.hex"
avrdude: writing flash (1536 bytes):
avrdude: 1536 bytes of flash written

avrdude: safemode: Fuses OK (E:FD, H:D8, L:FF)

avrdude done.  Thank you.

gmake[1]: Leaving directory '/home/jmpc/tmp/Blink'

Y podemos comprobar que el programa se cargó en la placa y el led está parpadeando.

Leer puerto serie - ASCIITable

Previamente ya vimos que podíamos cargar sin problemas un programa y ejecutarlo.

Ahora se intentará leer información enviada por la placa por el puerto serie.

Nuevamente se partirá de un ejemplo existente, esta vez ASCIITable:

$ cp -a /usr/local/share/examples/arduino/04.Communication/ASCIITable .
$ cd ASCIITable
$ cat > Makefile <<'END'
# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile

BOARD_TAG    = mega
BOARD_SUB    = atmega2560
MONITOR_PORT = /dev/ttyU0
AVRDUDE_CONF = /usr/local/share/examples/avrdude/avrdude.conf

USER_LIB_PATH = libraries
# ARDUINO_LIBS  = <...> # no se están incluyendo librerías

include /usr/local/share/arduino-makefile/Arduino.mk
END

Esta vez se explicitó el valor de MONITOR_PORT ya que sabemos a que corresponde. El resto de los valores fueron los utilizados (y explicados) previamente.

Se compila y carga el programa a la placa sin problemas:

$ gmake && gmake upload
...
avrdude: AVR device initialized and ready to accept instructions
avrdude: Device signature = 0x1e9801 (probably m2560)
avrdude: reading input file "build-mega-atmega2560/ASCIITable_.hex"
avrdude: writing flash (2308 bytes):
avrdude: 2308 bytes of flash written

avrdude: safemode: Fuses OK (E:FD, H:D8, L:FF)

avrdude done.  Thank you.

gmake[1]: Leaving directory '/home/jmpc/tmp/ASCIITable'

Luego para conectarnos instalamos el programa picocom:

$ doas pkg_add picocom

y luego nos conectamos utilizando:

$ picocom -b 9600 /dev/ttyU0
picocom v3.1

port is        : /dev/ttyU0
flowcontrol    : none
baudrate is    : 9600
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        :
omap is        :
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready
ASCII Table ~ Character Map
!, dec: 33, hex: 21, oct: 41, bin: 100001
", dec: 34, hex: 22, oct: 42, bin: 100010
#, dec: 35, hex: 23, oct: 43, bin: 100011

...
~, dec: 126, hex: 7E, oct: 176, bin: 1111110

Notas:

  1. La velocidad se obtiene de revisar el código (ASCIITable.ino) y corroborar la velocidad a la que se incializa la conexión serial (Serial.begin(9600);).
  2. En OpenBSD también existe el programa [cu] disponible en el sistema base en lugar de picocom.

Escritura en puerto serie - PhysicalPixel

Al revisar la documentación de cu (que no se utilizó previamente ya que tengo mayor experiencia utilizando picocom) se indica de utilizar los dispositivos /dev/cua?? y leer el manual de cua. En este se encuentra:

For hardware terminal ports, dial-out is supported through matching device nodes called calling units. For instance, the terminal called /dev/tty03 would have a matching calling unit called /dev/cua03.

De aquí que surja la duda de si se puede escribir en el puerto serie utilizando el dispositivo /dev/ttyU0 (lo que sería lo esperable) o debe utilizarse el /dev/cuaU0 (y no me imagino escribiendo en una terminal y recibiendo los datos en otra). Si bien la duda parece venida de una lectura superficial del manual probemoslo con el ejemplo PhysicalPixel:

$ cp -a /usr/local/share/examples/arduino/04.Communication/PhysicalPixel .
$ cd PhysicalPixel/
$ cat > Makefile <<'END'
# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile

BOARD_TAG    = mega
BOARD_SUB    = atmega2560
MONITOR_PORT = /dev/ttyU0
AVRDUDE_CONF = /usr/local/share/examples/avrdude/avrdude.conf

USER_LIB_PATH = libraries
# ARDUINO_LIBS  = <...> # no se están incluyendo librerías

include /usr/local/share/arduino-makefile/Arduino.mk
END
$ gmake
$ gmake upload

Luego nos conectamos a la placa e intentamos mandarle los carácteres L y H que apagan y prenden el led interno respectivamente:

$ picocom -b 9600 /dev/ttyU0

Y se corrobora que funciona como se esperaba.

Nota: en este ejemplo se incluye en el archivo layout.png que puede llevar a pensar que siempre hay que conectar un led a la placa. Como se indica en la página del ejemplo:

Many Arduino boards have a built-in LED connected to pin 13; if your board has no built-in LED, attach an external LED to pin 13.

Conclusión

  1. No hubo mayores problemas para instalar los programas necesarios en OpenBSD ya que estos estaban empaquetados.
  2. Leyendo la descripción del paquete y teniendo un poco de intuición se pudieron compilar y subir (upload) los programas Blink, ASCIITable y PhysicalPixel de arduino sin problemas.
  3. Se tuvo suerte en que el parche haya agregado por defecto el valor de MONITOR_PORT, lo que permitió encontr la documentación sobre los puertos ucom.
  4. Lo único no indicado en la documentación, pero trivial de solucionar era tener permisos para el dispositivo usb en el cual se conecta la placa.
  5. La página del manual de ucom y cu me confundió al mencionar el caso de los call-out devices o dial-out respectivamente, pero luego de probar se pudo enviar sin problemas informacion mediante el dispositivo /dev/ttyU0.

En resumen, ¡igual de fácil que en linux!

Apéndice - Cálculo de MONITOR_PORT

¿Como se calculó el MONITOR_PORT a /dev/ttyU0?

Suponiendo que el dispositivo es específico de OpenBSD o los BSD en general, ya que en linux es /dev/ttyACM0, se busca en los parches del port y se encuentra en patch-Common_mk:

+        ifndef MONITOR_PORT
+            MONITOR_PORT = /dev/ttyU0
+        endif

Apéndice - Uso de cu

Buscando en internet por el uso de arduino en OpenBSD encuentro los posts:

En estos se indica el uso de cu pero utilizan el dispositivo /dev/cuaU0 en lugar de /dev/ttyU0:

$ cu -l /dev/cuaU0 -s 9600

Volviendo a probar los programas Blink, ASCIITable y PhysicalPixel utilizando /dev/cuaU0 en lugar de /dev/ttyU0, los programas funcionaron igual.