Xenon: Intermittent writing to USB port on Raspberry Pi

xenon
Tags: #<Tag:0x00007fe21fcc6c68>

#1

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.


#2

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


#3

@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

#4

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.


#5

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 "&lt;stdin&gt;", line 2, in &lt;module&gt;

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.


#6

Hi, try to modify your python script like this:

port = "ttyACM0"
ser = serial.Serial("/dev/%s" % port, 9600, timeout=0.5)
while True:
    try:
        line = ser.readline()
    except serial.SerialException as e:
        #There is no new data from serial port
        break
    except TypeError as e:
        #Disconnect of USB->UART occured
        ser.close()
        break
    except KeyboardInterrupt:
        #Disconnect of USB->UART occured
        print "bye bye"
        ser.close()
        break
    else:
        #Some data was received
        print "line:" , line 

Please note I didn’t try this and the original thread can be found here

Also as suggested on this thread try to use Python 2.7 instead of Python 3.x
Hope that will help :smile: