Xenon: Intermittent writing to USB port on Raspberry Pi

I am seeing a very odd behavior with my Xenon -> Raspberry Pi, and I honestly am not sure where the issue is. I am trying to write to the Pi over the USB port. The problem - it will work and then fail. For example, it worked last night and this morning - same code - does not. If you see a Raspberry Pi flying out the window, it may have been mine…

In my Xenon, in setup() I have the following:

Serial.begin(9600);
Mesh.subscribe("meter-data", writeDataToUSB);

writeDataToUSB is a simple function to write data to the Serial port.

// -----------------------------------------------------------------------------
void writeDataToUSB(const char *event, const char *data)
// -----------------------------------------------------------------------------
{

    Serial.println(data);

    String deviceID;
    String data_copy = String(data);
    deviceID = data_copy.substring(11, data_copy.indexOf('",') - 1);
    Mesh.publish("publish-response", deviceID);
}

I test this by connecting the Xenon to my comp and can follow the serial output to see data is coming out. particle serial monitor --follow

However on the Pi side -

  • I have opened the Serial interface for data.
  • Run dmesg | grep tty which returns the port:
[    0.000000] Kernel command line: coherent_pool=1M 8250.nr_uarts=1 bcm2708_fb.fbwidth=1920 bcm2708_fb.fbheight=1080 bcm2708_fb.fbswap=1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  console=tty1 root=PARTUUID=d9b3f436-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles cgroup_enable=memory cgroup_memory=1
[    0.000301] console [tty1] enabled
[    0.812585] 3f201000.serial: ttyAMA0 at MMIO 0x3f201000 (irq = 81, base_baud = 0) is a PL011 rev2
[    0.814150] 3f215040.serial: ttyS0 at MMIO 0x0 (irq = 53, base_baud = 31250000) is a 16550
[    4.867127] cdc_acm 1-1.5:1.0: ttyACM0: USB ACM device

I then open a Python3 terminal, can see the port is open, but then get empty results.

import serial
port = "ttyACM0"
ser = serial.Serial("/dev/%s" % port, 9600, timeout=0.5)
while True:
    line = ser.readline()
    print(line)
    print(line.decode())

Returns:


b''

b''

b''

Last night I was able to open the same port and get the following:

Any ideas? This one has me totally flabbergasted.

@emile You shouldn’t be Mesh.publishing from within an event handler. Have you tried commenting out the lines after Serial.println(data);?

@armor

Ah, is there a better way to trigger the Publish after I get an event?

I just figured out what is happening. If I reboot the Pi, then the USB port is ttyACM0. However if I make a deploy to the gateway (changes or not), then the Pi thinks its a new device, and the port goes to ttyACM1.

[    5.009947] cdc_acm 1-1.5:1.0: ttyACM0: USB ACM device
[  256.339004] cdc_acm 1-1.5:1.0: ttyACM1: USB ACM device

If I deploy again it even shows:

[    5.009947] cdc_acm 1-1.5:1.0: ttyACM0: USB ACM device
[  256.339004] cdc_acm 1-1.5:1.0: ttyACM1: USB ACM device
[  354.123296] cdc_acm 1-1.5:1.0: ttyACM1: USB ACM device

Ah, is there a better way to trigger the Publish after I get an event?

Yes, the model is you set a global variable (could be a bool or int) and then act on that in the loop() and reset the flag.

1 Like

If anyone hits this in the future, I wanted to add some more information on what I’ve found. I’ve finally nailed it down.

What is happening-

  • A Xenon is writing to my Raspberry Pi over the USB port, /dev/ttyACM0
  • While I have that port open, I am reading the data that is written by the Xenon
port = "ttyACM0"
ser = serial.Serial("/dev/%s" % port, 9600, timeout=0.5)
while True:
    line = ser.readline()
  • If I deploy an update to the Xenon writing to the Pi while the Pi is reading, the Port is closed in the Python script
Traceback (most recent call last):

File "<stdin>", line 2, in <module>

File "/usr/lib/python3/dist-packages/serial/serialposix.py", line 509, in read

raise SerialException('read failed: {}'.format(e))

serial.serialutil.SerialException: read failed: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
  • Also a new port is created by Raspbian/Linux as /dev/ttyACM1

Problems

After deploying the update to the Gateway Xenon, 2 things happen

  • I’m no longer reading the right port (and this behavior would continue the more times I deploy)
  • The edge Xenons are then all trying to reconnect to the Gateway at the same time. This gets hairy with many devices taking a while to reconnect.

Some Solutions

  • I want to limit the updates to the Gateway Xenon application. So I’m going to simplify it as much as possible so it just writes messages to the USB port. That should help.
  • However there will be times when I need to deploy to the gateway, and handle the devices that do not reconnect. That is where a reset function will come in handy. If the device is online and hasn’t connected in X minutes, System.reset()

Like in the MarcoPolo code:

bool particleConnect = false;
unsigned long LostTime = 0;
unsigned long ResetTimeout = 600000;  //10 min = 600000


  if (particleConnect) {
    if (!Particle.connected()){
      if (millis() - LostTime > ResetTimeout) {
          System.reset();
      }
    }
  }

Unresolved Problem
I still need to sort out a deployment process so I don’t keep creating multiple ports.

What I’m thinking right now, I’ll write a deployment script that will -

  • Send a request into the Python script
  • Closes the port in the python script - ser.close()
  • Deploy the gateway application to the Xenon
  • Send a request to the Python script
  • Reopen the port in the python script ser.open()

However I’m definitely open to other ideas. This seems like a complicated way to handle a simple situation.

1 Like

@emile you may be able to install the particle CLI or grab the code that’s used to identify a serial port as a Particle device. That way no matter what it comes up in the /dev/ folder you can always get to it.

1 Like

Nailed it. In Python3:

https://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.list_ports

import serial
for port in serial.tools.list_ports.comports():
    print(port.manufacturer). # This should be 'Particle'
    if port.manufacturer == 'Particle':
        ....
2 Likes

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.