Multiple boards upload_port works but crashes 2nd board

Noob here……I have to disconnect the 2nd and 3rd board to program the 1st board.

I have three boards that I am trying to integrate data from for a robotics project. My platformio.ini file is:

; PlatformIO Project Configuration File
[env:joystick]
platform = teensy
board = teensy35
framework = arduino
upload_port = /dev/potentiometer_control
;
[env:left_speed]
platform = teensy
board = teensy31
framework = arduino
upload_port = /dev/odom_left
;
[env:transmission]
platform = teensy
board = teensy31
framework = arduino
upload_port = /dev/transmission_control
; end

If I do not disconnect the boards I am NOT programming they will crash. In other words, compiling and uploading to one board conflicts with the other boards and my work around is to disconnect all boards except the specific one I am programming. As an example, I use the following pio command: $ sudo platformio run -t upload -e left_speed

My udev rules are:
SUBSYSTEM==“tty”, ATTRS{idVendor}==“16c0” , ATTRS{idProduct}==“0483” , ATTRS{serial}==“5879620”, SYMLINK+=“odom_left”
SUBSYSTEM==“tty”, ATTRS{idVendor}==“16c0”, ATTRS{idProduct}==“0483”, ATTRS{serial}==“5022550”, SYMLINK+=“transmission_control”
SUBSYSTEM==“tty”, ATTRS{idVendor}==“16c0” , ATTRS{idProduct}==“0483” , ATTRS{serial}==“5588560”, SYMLINK+=“potentiometer_control”

Can someone offer advice on how to avoid causing programming one board to corrupt the 2nd board?

Can you please share the log where you upload for the environment joystick but PlatformIO chooses to use upload port /dev/odom_left instead?

Thanks for trying to help. I’m hoping by “log” you mean the screen results which are below.

My environment is an RPi with three Teensy’s connected via USB. I disconnected Teensy3.5/joystick and Teensy3.2/transmission.

  1. I put blink.cpp as the only file in my /src folder and run $ sudo platformio run -t upload -e left_speed.
  2. I then plugged Teensy3.5/joystick back into the RPi. At this point Teensy3.2/left_speed is blinking away. With Teensy3.2/left_speed and Teensy3.5/joystick plugged in I run $ sudo platformio run -t upload -e joystick. The results are both are:

Teensy3.2/left_speed is stopped blinking
Teensy3.5/joystick has not taken the version of blink.cpp I compiled. Pressing the reset button has no effect.

For both boards I use this recovery process:

  • Plug USB into Windows 10 machine at which point I get USB not recognized.
  • I go ahead and upload blink from the Arduino IDE. The port defaults to COM3.
  • I press reboot on the Teensy and the program initializes and now the board appears on COM8. (i.e. back to a healthy state.)
ubuntu@ubiquityrobot:~$ sudo platformio run -t upload -e joystick
[sudo] password for ubuntu:
/usr/local/lib/python2.7/dist-packages/requests/__init__.py:83: RequestsDependencyWarning: Old version of cryptography ([1, 2, 3]) may cause slowdown.
  warnings.warn(warning, RequestsDependencyWarning)
Processing joystick (platform: teensy; board: teensy35; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/usr/local/lib/python2.7/dist-packages/requests/__init__.py:83: RequestsDependencyWarning: Old version of cryptography ([1, 2, 3]) may cause slowdown.
warnings.warn(warning, RequestsDependencyWarning)
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/teensy/teensy35.html
PLATFORM: Teensy > Teensy 3.5
HARDWARE: MK64FX512 120MHz 255.99KB RAM (512KB Flash)
DEBUG: CURRENT(jlink) EXTERNAL(jlink)
Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF MODES: FINDER(chain) COMPATIBILITY(soft)
Collected 94 compatible libraries
Scanning dependencies...
No dependencies
Compiling .pioenvs/joystick/src/test1_blink.cpp.o
Linking .pioenvs/joystick/firmware.elf
Checking size .pioenvs/joystick/firmware.elf
Building .pioenvs/joystick/firmware.hex
Memory Usage -> http://bit.ly/pio-memory-usage
DATA:    [          ]   0.9% (used 2340 bytes from 262136 bytes)
PROGRAM: [          ]   2.0% (used 10504 bytes from 524288 bytes)
Configuring upload protocol...
AVAILABLE: jlink, teensy-cli, teensy-gui
CURRENT: upload_protocol = teensy-cli
Rebooting...
Uploading .pioenvs/joystick/firmware.hex
Teensy Loader, Command Line, Version 2.1
Read ".pioenvs/joystick/firmware.hex": 10508 bytes, 2.0% usage
Soft reboot performed
Waiting for Teensy device...
(hint: press the reset button)
Found HalfKay Bootloader
Read ".pioenvs/joystick/firmware.hex": 10508 bytes, 2.0% usage
Programming...........
Booting
============================================================================ [SUCCESS] Took 7.14 seconds ============================================================================

===================================================================================== [SUMMARY] =====================================================================================
Environment joystick            [SUCCESS]
Environment left_speed          [SKIP]
Environment transmission        [SKIP]
============================================================================ [SUCCESS] Took 7.14 seconds ============================================================================
ubuntu@ubiquityrobot:~$

I think the underlying issue is the Teensy loader doesn’t support a use case with multiple boards connected (it’s mentioned one or two times on the github, not sure about the teensy forum), and has no way of know which board you want to program if multiple boards are present, so the first board it encounters in a reset/ready to program state gets programmed.

I have both Teensy 3.5 and 3.6s, so gave it a try.

With a udev rule like so…

SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789]?", ATTRS{serial}=="2268990", SYMLINK+="teensy35"
SUBSYSTEM=="tty", ATTRS{idVendor}=="16c0" , ATTRS{idProduct}=="04[789]?" , ATTRS{serial}=="2257750", SYMLINK+="teensy36"

platformio.ini

[env:teensy35]
platform = teensy
board = teensy35
framework = arduino
upload_port = /dev/teensy35
monitor_speed = 57600

[env:teensy36]
platform = teensy
board = teensy36
framework = arduino
upload_port = /dev/teensy36
monitor_speed = 57600

And the following program. It basically a couple of things. It first let me check the device serial numbers. After that, if the program detects it’s running on the teensy3.5 serial, it blinks five times, or blinks six times if on the teensy 3.6. Additionally, it checks for a preprocessor macro - TEENSY35 to see if the program was mean for the 3.5 (alternately, TEENSY36 would have been defined). The result is teensy 3.5 will blink slower than the teensy 3.6. This lets me test if the code went on the right board… should get five slow flashes from teensy 3.5, and six faster flashes from 3.6.

#include <Arduino.h>
#include "TeensyMAC.h" //https://github.com/FrankBoesing/TeensyMAC

void setup()
{
  pinMode(LED_BUILTIN,OUTPUT);
  digitalWrite(LED_BUILTIN,LOW);

  Serial.begin(57600);
}

void loop()
{
  int model = 0;
  uint32_t serial = teensySerial();
  uint64_t mac = teensyMAC();

  if (serial == 2268990)
  {
    model = 5;
  }
  else /*if (serial == 2257750)*/
  {
    model = 6;
  }

  for (int i = 0; i < model; i++)
  {
    digitalWrite(LED_BUILTIN, HIGH);
#ifdef TEENSY35
    delay(200);
#else
    delay(100);
#endif    
    digitalWrite(LED_BUILTIN, LOW);
#ifdef TEENSY35
    delay(300);
#else
    delay(200);
#endif    
  }

  Serial.println("Reading Teensy HWIDs...");
  Serial.printf("Serial: %u\n", serial);
  Serial.printf("MAC: 0x%012llX\n", mac);
  Serial.println();

  delay(1000);
}

With both boards plugged in, and trying to upload to both, the 3.6 board would get the 3.5 board program, I would get a warning about the code being for the wrong teensy version, and would it stop blinking. The 3.5 board seemed to get the correct program, which could just be luck, the order they were plugged in, or how fast it resets.

Thank you very much for spending time on this. I think I will pursue “turning off” the USB ports, upload firmware and then turning them back on again. Reference: link

1 Like

Nice! :slight_smile: Another option would be a USB hub that has individual power switches… then that becomes platform independent also. But not so handy as it’s not automatable via a script, so there is that also.

Thanks again pfeerick. With the idea that someone reading this might save some time….

I have a usb hub with three Teensy’s connected. In order to program them remotely and avoid having to physically disconnect or turn off power to the device I use unbind on the non-targeted devices. Once programming is complete I use bind to re-enable the devices that I used unbind on. The format of the command is $ echo -n “{device path}” > /sys/bus/usb/drivers/usb/unbind
The easiest way for me to get the {device path} was to use $ grep 16c0 /sys/bus/usb/devices/*/idVendor The results were:
/sys/bus/usb/devices/1-1.3.1.1/idVendor:16c0 ; joystick
/sys/bus/usb/devices/1-1.3.1.2/idVendor:16c0 ; left speed
/sys/bus/usb/devices/1-1.3.1.3/idVendor:16c0 ; transmission

“16c0” is the idVendor for the Teensy devices. I then disconnected and reconnected the devices to match up the {device path} with the correct USB port and device.

If I run $ ls /dev/ttyACM* I will see all Teensy’s connected.

I can then run for example $ echo -n “1-1.3.1.1” > /sys/bus/usb/drivers/usb/unbind which unbinds the port. I tried multiple ways to run without root access to no avail. So the full set of commands for me was

  1. $ sudo -i
  2. $ echo -n “1-1.3.1.1” > /sys/bus/usb/drivers/usb/unbind ; joystick
  3. $ echo -n “1-1.3.1.3” > /sys/bus/usb/drivers/usb/unbind ; transmission
  4. $ exit
  5. Move code to /src folder (e.g. Make any desired changes to blink.cpp)
  6. $ sudo platformio run -t upload -e left_speed
  7. $ sudo -i
  8. $ echo -n “1-1.3.1.1” > /sys/bus/usb/drivers/usb/bind ; joystick
  9. $ echo -n “1-1.3.1.3” > /sys/bus/usb/drivers/usb/bind ; transmission
  10. $ exit
1 Like

Nice! Thanks for the update! :slight_smile: Didn’t realise you could disable/enable devices that easily! :slight_smile:

To make that a bit shorter, could do something like this at the start… to give a user in the plugdev group (substitute if you don’t have a plugdev group) permissions to write to the bind/unbind virtual file without needing sudo. Would bring it down to simply a pair of sudo commands you’d need to run once after restart, unless you also added to your /etc/rc.local or some other startup file, etc.

sudo chown root:plugdev /sys/bus/usb/drivers/usb/{bind,unbind}
sudo chmod g+w /sys/bus/usb/drivers/usb/{bind,unbind}

Doing it that way brings it into the realms of something that you could use an extra_script for also… as now platformIO could run a script at the start of a compile that disables the other two devices…

Did you need sudo for platformio as well? Maybe related to the user/device permissions also?

Much appreciated. Slight tweak for me. I needed to add another “usb” to match my file structure, but now I am a few steps more productive…

sudo chown root:plugdev /sys/bus/usb/drivers/usb/{bind,unbind}
sudo chmod g+w /sys/bus/usb/drivers/usb/{bind,unbind}

Re: “…sudo for platformio…”, yes, without sudo I get a long list of python errors.

1 Like

My bad… it was wrong… I forgot to edit that when I re-ran it after consolidating the commands and found breakage :wink: Fixed!

Ouch! Perhaps you installed PlatformIO as the sudo user? What are the permissions on the /dev/ttys for the Teensys, and the symlinks? If you want to past that error log in at some point, we might be able to work around that.

Back using pio cli. My permissions appear to be:

ubuntu@ubiquityrobot:~$ ls -l /dev/ttyA*
crw-rw---- 1 root dialout 166, 0 Feb 11 2016 /dev/ttyACM0
crw-rw---- 1 root dialout 166, 1 Feb 11 2016 /dev/ttyACM1
crw-rw---- 1 root dialout 166, 2 Feb 11 2016 /dev/ttyACM2
crw-rw---- 1 root dialout 166, 3 Feb 11 2016 /dev/ttyACM3
crw-rw---- 1 root dialout 204, 64 May 3 09:36 /dev/ttyAMA0
ubuntu@ubiquityrobot:~$

That seems right… ubuntu/debian uses dialout as the group, and arch/manjaro uses plugdev, not sure about other distrubutions. Just use dialout where you used plugdev before, and it should be ok. :wink: