I am attempting to use the Arduino CLI within a Docker container on a Mac host. My setup allows for communication over serial, but I'm encountering issues getting the Arduino CLI to function correctly.
Due to the limitation on Mac that prevents passing a serial device directly to a Docker container (refer to this open issue from 2016), I'm using socat
to bridge the serial connection over TCP and back to serial within the container.
The Docker command I'm using includes the --add-host=host.docker.internal:host-gateway
option to forward all TCP traffic to the container from the host:
docker run -it --rm \
-v "$HOME:$HOME" \
-e "HOME=$HOME" \
-w "$PWD" \
-e "DISPLAY=host.docker.internal:0" \
--add-host=host.docker.internal:host-gateway \
--user root \
openmodelica-screen bash
On the Mac host, I'm connecting the Arduino on /dev/cu.usbmodem14201 to TCP port 9000:
socat TCP-LISTEN:9000,reuseaddr,fork FILE:/dev/cu.usbmodem14201,raw
Within the Linux container, I'm creating a serial port from TCP port 9000, which I can find with ls /dev/pts
, usually /dev/pts/2:
socat tcp:host.docker.internal:9000,reuseaddr pty,raw,echo=0 &
I have a simple Arduino program that returns a temperature reading upon receiving a command. I can successfully communicate with it using picocom:
picocom -b 9600 /dev/pts/2
However, I noticed picocom sends each key press instantly without waiting for a carriage return.
When I attempt to upload a sketch using the Arduino CLI, I encounter an error:
./arduino-cli upload -p /dev/pts/2 --fqbn arduino:avr:leonardo MyFirstSketch
Error:
Connecting to programmer: .
Found programmer: Id = "Invalid"; type = l
Software Version = I.n; Hardware Version = v.a
avrdude: error: buffered memory access not supported. Maybe it isn't
a butterfly/AVR109 but a AVR910 device?
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.
avrdude: error: programmer did not respond to command: leave prog mode
avrdude: error: programmer did not respond to command: exit bootloader
Failed uploading: uploading error: exit status 1
I suspect the issue lies in the serial to TCP conversion and back. Could anyone provide tips or tricks for using socat in this context to ensure compatibility with the Arduino programmer? Any advice on resolving this error would be greatly appreciated.
I wanted to share my solution for the above question with the caveat that it may not be completely satisfying because it involves a physical step with pressing the reset button to the Arduino Leonardo before being able to program it.
On the mac host, brew install ser2net
and create file /usr/local/etc/ser2net/ser2net.yaml with this content:
%YAML 1.1
---
define: &confver 1.0
connection: &id001
accepter: tcp,9000
connector: serialdev,/dev/cu.usbmodem14201,57600n81,xonxoff,rtscts
options:
kickolduser: true
Then, to serve the Arduino on port 9000, start the server with this line, where -n
says not to send to the background (I am running in screen
):
/usr/local/Cellar/ser2net/4.6.1/sbin/ser2net -c /usr/local/etc/ser2net/ser2net.yaml -n
avrdude
)Inside the Linux container, I am running this in screen
to listen to port 9000 and turn that into a virtual serial port /dev/ttyS0
:
socat -d -d pty,link=/dev/ttyS0,rawer,b57600 tcp:host.docker.internal:9000
I used arduino-cli
to compile the binaries to the MyFirstSketch folder:
arduino-cli compile --fqbn arduino:avr:leonardo --export-binaries MyFirstSketch -v -v
Then, I used avrdude
to send the binary to the Arduino. arduino-cli
uses avrdude
internally, so it is sometimes easier to use avrdude
directly to be able to specify additional options. On the Arduino Leonardo, I found it necessary to press reset on the board first before this command.
avrdude -v -V -patmega32u4 -cavr109 "-Pnet:host.docker.internal:9000" -b57600 -D "-Uflash:w:MyFirstSketch/build/arduino.avr.leonardo/MyFirstSketch.hex:i"
and you should see this satifying message at the end:
Writing | ################################################## | 100% 1.01s
avrdude: 7024 bytes of flash written
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as D8
avrdude: safemode: efuse reads as CB
avrdude: safemode: Fuses OK (E:CB, H:D8, L:FF)
avrdude done. Thank you.
I wanted to share my solution, even if it is less than ideal, because it took me a lot of work to get to this point, and I think your project may benefit by some of the concepts and tools mentioned here.
I wasn't able to get avrdude to work with the virtual device /dev/ttyS0 that I set up earlier on socat, but /dev/ttyS0 works with serial programs like picocom.
To set this up properly, I would need to find a better way to use/edit the linux kernel used by the docker container, and that proved difficult.
To a programmer, it's not satifying to have a step that requires a human button press. The reset signal could be sent to the Arduino from the Mac by sending stty -f /dev/cu.usbmodem14201 1200
to the Arduino, but stty -F /dev/ttyS0 1200
did not work inside the Linux container.
One way around this is to use an RFC2217 compatible telnet signal, but this requires a compliant server and client. Ser2net is compliant with RFC2217, but it is hard to find a client that is. There is a client called ttynvt that is RFC2217 compliant, but it also required doing stuff with the Linux kernel.
With RFC2217, you can control/change the baud rate of the virtual serial device, and this is necessary to program the Arduino Leonardo. I would suggest looking into other Arduino board types or other microcontrollers if you are looking to program an Arduino inside of a docker container and using a Mac host.