Tom Egan

Fix the Blinking Red Light of Doom

If you are using a Linux PC to upload a firmware application to an Arduino compatible but not an Arduino brand device sometimes you will get the oddest errors:

***failed; ***failed; avrdude: Error: butterfly programmer uses avr_write_page() but does not provide a cmd() method. *** page 83 (addresses 0x0f54 - 0x0fd3) failed to write # | 100% 0.57s avrdude: 4052 bytes of flash written avrdude: verifying flash memory against .pio/build/feather32u4/firmware.hex: avrdude: load data flash data from input file .pio/build/feather32u4/firmware.hex: avrdude: input file .pio/build/feather32u4/firmware.hex contains 4052 bytes avrdude: reading on-chip flash data: Reading | avrdude: error: programmer did not respond to command: set addr #avrdude: error: programmer did not respond to command: set addr ##avrdude: error: programmer did not respond to command: set addr #avrdude: error: programmer did not respond to command: set addr ##avrdude: error: programmer did not respond to command: set addr #avrdude: error: programmer did not respond to command: set addr ##avrdude: error: programmer did not respond to command: set addr #avrdude: error: programmer did not respond to command: set addr ##avrdude: error: programmer did not respond to command: set addr ## | 100% 0.05s avrdude: verifying ... avrdude: verification error, first mismatch at byte 0x0000 0x45 != 0x0c avrdude: verification error; content mismatch avrdude: safemode: Verify error - unable to read lfuse properly. Programmer may not be reliable. avrdude: safemode: Verify error - unable to read lfuse properly. Programmer may not be reliable. avrdude: safemode: Sorry, reading back fuses was unreliable. I have given up and exited programming mode avrdude: error: programmer did not respond to command: leave prog mode avrdude: error: programmer did not respond to command: exit bootloader avrdude done. Thank you. *** [upload] Error 1
On Adafruit brand boards this will be accompanied by odd LED pulsing even after double tapping the reset button to put the board in bootloader mode. The fix is fairly simple: as root, create a file /etc/udev/rules.d/99-third-party-arduino.rules and into it enter:
# Arduino compatible boards from Adafruit ATTRS{idVendor}=="239a", ATTRS{idProduct}=="*", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
With the file in place run in a terminal:
sudo udevadm control --reload-rules sudo udevadm trigger

That's it… but why does this happen? This happens when a board support package for the Arduino IDE either does not include udev rules, the Arduino IDE does not properly install the udev rules or you are using something like Platformio to program the Arduino compatible board. udev is the Linux subsystem that is responsible for figuring out what to do when a USB device is connected to a Linux PC (among other things). Unless otherwise instructed it looks at the devices “capabilities” and makes a guess. Most Arduino compatible boards indicate they are serial port devices. udev, dutifully connects them as serial ports and wakes up any service that has asked to be notified of a serial port being added to the PC e.g. Modem Manager. This is makes connecting a serial modem “plug and play” but Modem Manager doesn’t know what to do with an Arduino and instead of doing nothing, it may continually try to reset the device with AT commands, meaning it is trying to write to the Arduino same as avrdude (the tool used by the Arduino IDE and Platformio to upload a firmware application onto your Arduino) two programs writing to the same device at the same time is a recipe for disaster and the cause of the error messages at the start of this post.

One solution is to uninstall Modem Manager, and this is the solution suggested by Adafruit. However, some devices including some lab equipment actually emulate a dial up modem so this is not a solution for everyone. Additionally, uninstalling Modem Manager can be tricky. Fortunately, we can instruct udev not to wake up Modem Manager when a serial device with certain characteristics is connected and this is what the file above does. The rules file above instructs udev that when a device with the vendor id 239a i.e. Adafruit is connected, it should connect it as a serial port writable by everyone and not inform Modem Manager of the new device. The rules file above only does this for boards by Adafruit but you can add additional lines for other vendors by determining the vendor id using lsusb

[tegan@tegan-lpt rules.d]$ lsusb Bus 002 Device 007: ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter Bus 002 Device 006: ID 0424:5807 Standard Microsystems Corp. Hub Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 003: ID 1bcf:2b96 Sunplus Innovation Technology Inc. Integrated_Webcam_HD Bus 001 Device 088: ID 239a:800c Adafruit Feather 32u4 Bus 001 Device 028: ID 0bda:4014 Realtek Semiconductor Corp. Integrated_Webcam_HD Bus 001 Device 030: ID 05ac:020b Apple, Inc. Pro Keyboard [Mitsumi, A1048/US layout] Bus 001 Device 029: ID 17ef:6019 Lenovo USB2807 Hub Bus 001 Device 027: ID 05e3:0610 Genesys Logic, Inc. 4-port hub Bus 001 Device 025: ID 0424:2807 Standard Microsystems Corp. Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
then copy the line starting with ATTRS and ending with ENV{ID_MM_PORT_IGNORE}="1", paste in a new line, and edit the vendor id accordingly. If the vendor makes a wide range of products you may want to limit this change to specific devices by also specifying the board product id. Determine the board product id using lsusb as above e.g. 800c and for the Adafruit Feather 32u4 the rules file would look like:
# Adafruit Feater 32u4 proto board ATTRS{idVendor}=="239a", ATTRS{idProduct}=="800c", MODE:="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"