Running Cayenne-ser.sh Script on Pi Boot

I’ve been using Cayenne to host sensor data being fed from an arduino to my dashboard via Serial connection to a RPi 3B+. All works very well, but my question is for running the connection script automatically on reboot. On first run I VNC into the Pi, open the terminal and manually start the script and run it in the background. I would like for the connection script to run automatically if the Pi needs to reboot.

I’ve tried creating a unit file in systemd

[Unit]
Description=Connect to Cayenne
Required=network.target

[Service]
Type=simple
ExecStart=/home/pi/Desktop/Cayenne-MQTT-Arduino-master/extras/scripts/cayenne-ser.sh -c /dev/ttyACM0
Restart=always

[Install]
WantedBy=multi-user.target

I ran

sudo chmod 644 /etc/systemd/system/cayenne_connection.service

It successfully runs with the systmctl start and stop commands, but fails to run on reboot after running the systemctl enable command. Any help you could offer would be much appreciated.

@adam any idea about this?

Take a look at this. Looks like you can create a udev rule that will run the script when you plug in the device (or when the device is started). If I had to guess your service is starting before some required parts of the OS are ready.

1 Like

Thanks for taking the time to reply. I’ve checked out the link you offered. Do I have to do all three of the things suggested in answer (create a udev rule, modify the system service, and create the executable file?

I’ve never created a udev rule before so I don’t just want to dive in and make a mistake. On another note, when I reboot, if I unplug and then replug the arduino into the Pi’s USB port the connection is reestablished. Will your suggestion remove this need?

This should make it work, let me know if it doesn’t.

Create a new file /etc/udev/rules.d/95-cayenne_connection.rules with contents of KERNEL=="ttyUSB0", ENV{SYSTEMD_WANTS}="cayenne_connection.service" replace ttyUSB0 with your serial port.

Change /etc/systemd/system/cayenne_connection.service to

[Unit]
Description=Connect to Cayenne
Required=network.target

[Service]
ExecStart=/path/to/Cayenne/serial.sh

Replace ExecStart=/path/to/Cayenne/serial.sh with your correct path

1 Like

14%20PM

This was the prompt I received after making the changes you suggested above. Was I correct in assuming that you wanted me to remove the [Install] section of the unit file? This was unsuccessful upon reboot and when I run

sudo systemctl is-enabled cayenne_connection.service

It returns “static”

Yep, as per #3 you should be fine as the udev rule should handle start/stop. Let me know how it works! I haven’t done much with udev so if it doesn’t I’ll fire up a pi and give it a try.

No joy. Upon reboot the arduino does not come back online. I ran

sudo systemctl status cayenne_connection.service -l

and it returned that is was running however not connected on dashboard. If I stop and then start the service manually in the terminal it pops right back up. Here is the prompt from the status check

Does it also start working if you disconnect and reconnect your USB device?

It is no longer responding to the unplugging now that we’ve added the changes. The only thing that gets the service running is to stop it and restart it.

Just for the heck of it I threw the command path into a shell script and substituted that into the ExecStart but to no avail.

I’ve been looking through the udev rule documentation. Granted I am not super familiar with it, but am I wrong in thinking the rule waits for something “new” to be plugged in before taking action? Any luck on your pi?

Oddly, the service was responding to the unplugging and replugging of the USB prior to adding the udev rule. Since eliminating the Install portion of the systemd service, the only way to get it running after reboot is to stop the service and then start it again.

Leave everything the way you have it. The problem is that the service is starting before something that is required. If you add sleep 30 (might be able to reduce that 30 seconds to something smaller, I chose 30 to be safe) to line 2 of cayenne-ser.sh it will work. I changed my service file to the following and it still won’t connect on a reboot

[Unit]
Description=Connect to Cayenne
Requires=network-online.target
After=network.target network-online.target

[Service]
ExecStart=/home/pi/cayenne/Cayenne-MQTT-Arduino/extras/scripts/cayenne-ser.sh -c /dev/ttyACM0

So to recap, the working solution is:
Create the udev rule
sudo nano /etc/udev/rules.d/95-cayenne_connection.rules
Add the line
KERNEL=="ttyACM0", ENV{SYSTEMD_WANTS}="cayenne_connection.service"
Change KERNEL=="ttyACM0" to your serial port if needed

Create the service file
sudo nano /etc/systemd/system/cayenne_connection.service
Add the following

[Unit]
Description=Connect to Cayenne
Requires=network-online.target
After=network.target network-online.target

[Service]
ExecStart=/home/pi/cayenne/Cayenne-MQTT-Arduino/extras/scripts/cayenne-ser.sh -c /dev/ttyACM0

Change ExecStart=/home/pi/cayenne/Cayenne-MQTT-Arduino/extras/scripts/cayenne-ser.sh to the path of your script

Change cayenne-ser.sh
nano cayenne-ser.sh
Add a new line at line 2 and insert sleep 30

1 Like

By the way, I also saw that the script claims that it’s connected and sending data on a reboot, even though it’s not. I’m not sure what is not started when the script runs, maybe @jburhenn would know?

I’m not sure what would require that delay. I did notice there is a SYSTEMD_READY udev option mentioned for items that show up uninitialized. Maybe you could try setting that to 0 and see if that has any effect.
https://www.freedesktop.org/software/systemd/man/systemd.device.html

I guess you could also try adding After=dev-ttyACM0.device to the service file, though I kind of doubt that will help. I’m not sure what else to try there, but I suspect you are right and there is some other dependency it needs that isn’t available when it starts up.

Made the required changes to udev rule, service file, and cayenne-ser.sh. I am including a screenshot of where I entered the sleep 30 you suggested to make sure I am putting it in the right place because I am still not getting the connection upon reboot.20%20AMservice%20file udev%20rule

Yes, that’s correct.

  • Can you post the contents of /etc/udev/rules.d/95-cayenne_connection.rules and /etc/systemd/system/cayenne_connection.service?
  • Also try increasing the 30 to something larger like 60.
  • Is your Pi wired or wireless?

Also, remember that you are adding 30 seconds to your boot up time so you won’t see the device come up right away. I’m using a pi zero wirelessly and it takes about 2 minutes for it to completely boot up and the device to show up as online

@jburhenn I tried SYSTEMD_READY=0 and got the same result. The service says it’s connected but the device on the dashboard is not communicating and shows as offline. From the output of sudo systemctl status cayenne_connection.service it looks like it’s not waiting on the network to be ready before starting the service. But, why does it not work when it retries and is able to resolve the MQTT server?

pi@raspberrypi:~ $ sudo systemctl status cayenne_connection.service
● cayenne_connection.service - Connect to Cayenne
   Loaded: loaded (/etc/systemd/system/cayenne_connection.service; static; vendor preset: enabled)
   Active: active (running) since Fri 2018-08-31 15:36:38 UTC; 47s ago
 Main PID: 389 (cayenne-ser.sh)
   CGroup: /system.slice/cayenne_connection.service
           ├─389 /bin/bash /home/pi/cayenne/Cayenne-MQTT-Arduino/extras/scripts/cayenne-ser.sh -c /dev/ttyACM0
           └─573 socat -d -d FILE:/dev/ttyACM0,raw,echo=0,clocal=1,cs8,nonblock=1,b9600 TCP:mqtt.mydevices.com:1883,nodelay

Aug 31 15:36:42 raspberrypi cayenne-ser.sh[389]: Connecting: FILE:/dev/ttyACM0,raw,echo=0,clocal=1,cs8,nonblock=1,b9600 <-> TCP:mqtt.mydevices.com:1883,nodelay
Aug 31 15:36:42 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:42 socat[449] N opening character device "/dev/ttyACM0" for reading and writing
Aug 31 15:36:42 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:42 socat[449] E getaddrinfo("mqtt.mydevices.com", "NULL", {1,0,1,6}, {}): Temporary failure in name resolution
Aug 31 15:36:42 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:42 socat[449] N exit(1)
Aug 31 15:36:42 raspberrypi cayenne-ser.sh[389]: Reconnecting in 3s...
Aug 31 15:36:45 raspberrypi cayenne-ser.sh[389]: Connecting: FILE:/dev/ttyACM0,raw,echo=0,clocal=1,cs8,nonblock=1,b9600 <-> TCP:mqtt.mydevices.com:1883,nodelay
Aug 31 15:36:45 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:45 socat[573] N opening character device "/dev/ttyACM0" for reading and writing
Aug 31 15:36:45 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:45 socat[573] N opening connection to AF=2 52.200.177.238:1883
Aug 31 15:36:45 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:45 socat[573] N successfully connected from local address AF=2 10.0.0.152:59306
Aug 31 15:36:45 raspberrypi cayenne-ser.sh[389]: 2018/08/31 15:36:45 socat[573] N starting data transfer loop with FDs [5,5] and [6,6]

So to answer my question, it was IPv6. On my Pi Zero when the OS boots WLAN0 gets an IPv6 address of fe80::272e:1604:ee51:73a7/64 a couple seconds before it gets an IPv4 address which satisfies the Requires/After of the cayenne_connection service. Disable IPv6 by editing cmdline.txt with sudo nano /boot/cmdline.txt and add ipv6.disable=1 at the end of the line. I also noticed that networkd and networkd-wait-online were not enabled by default so you need to enable them with sudo systemctl enable systemd-networkd.service and sudo systemctl enable systemd-networkd-wait-online.service After that everything is working as it should even without the sleep 30 in the serial script.

EDIT:
Making this change will cause your Arduino to not reconnect if there is an internet connection issue while the script is already running. It will also wipe out any values stored in memory as it resets the Arduino.

This can also be fixed by changing the end of cayenne-ser.sh to reset the device every time the script tries to reconnect.

while [ 1 ]; do
    echo Connecting: "$FROM_ATTR <-> $TO_ATTR"

    socat $GEN_ATTR $FROM_ATTR $TO_ATTR

    detect_conflicts

    echo Resetting device $COMM_PORT...
    stty $COMM_STTY $COMM_PORT hupcl

    echo Reconnecting in 3s...
    sleep 3
done

Wow man thanks a ton! You really did a lot of legwork for me and I really appreciate you for it. Everything is working perfectly now even after removing the delay from the serial script. I am a middle school teacher helping really talented kids with some IoT projects. This is going to be really huge for us. Could you please give me a quick explanation about what the two lines we added to the end of the serial file do so that if one of the kids ask I can give them a simple explanation? Thanks again!!

echo Resetting device $COMM_PORT...
stty $COMM_STTY $COMM_PORT hupcl