OSError: [Errno 13] Permission denied: '/dev/ttyACM0' - using pyserial from Python to Arduino
Asked Answered
O

3

24

Environment

  • Linux Mint 17.1
  • Python 2.7
  • pyserial 2.7
  • Arduino UNO rv3

Desired Behaviour

I'm trying to send three values from a Python application to Arduino.

It works when doing the following from terminal:

$ python
$ import serial
$ import struct
$ ser = serial.Serial('/dev/ttyACM0', 9600)
$ ser.write(struct.pack('>3B', 255, 0, 0))

Current Behaviour

It doesn't work when using the same code in a Python file ie:

import serial
import struct
ser = serial.Serial('/dev/ttyACM0', 9600)
ser.write(struct.pack('>3B', red_value, green_value, blue_value))

Error Message

$ sudo tail -100 /var/log/apache2/error.log
OSError: [Errno 13] Permission denied: '/dev/ttyACM0'

Troubleshooting

Permissions

Application file:

$ ls -l
-rwxr-xr-x 1 myname mygroupname 114146 Jan  9 19:16 my_application.py

ttyACM0:

ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 Jan  9 20:12 /dev/ttyACM0

Groups

Groups the owner is a member of:

$ groups
mygroupname adm dialout cdrom sudo dip plugdev lpadmin sambashare

Due to various suggestions on the internet I also added the owner to the tty group via System Settings > Users and Groups. This had no effect.

Serial Ports Available

$ dmesg | grep tty
[    0.000000] console [tty0] enabled
[ 3390.614686] cdc_acm 3-2:1.0: ttyACM0: USB ACM device

Update

I can force it to work under the following conditions:

01. Permissions for world must be set to rw ie:

sudo chmod 666 /dev/ttyACM0

02. Arduino IDE serial monitor needs to be open.

However these conditions are not sustainable as:

  • Permissions are reset each time the USB is connected.
  • The Arduino IDE serial monitor shouldn't need to be open.
Oatis answered 9/1, 2015 at 10:1 Comment(0)
O
56

The following fleshes out some of the ideas in the first answer (I tried to add this content to that answer and accept it, but the edits were rejected). I'm not an expert in the area, so please just use this information to support your own research.

You can do one of the following:

01. Alter the permissions on /dev/ttyACM0 so that world has read and write priviliges (something you may not want to do) - although you may find they reset each time the device is plugged in eg:

sudo chmod 666 /dev/ttyACM0  

02. Create a rule in /etc/udev/rules.d that will set the permissions of the device (a restart will be required):

# navigate to rules.d directory
cd /etc/udev/rules.d
#create a new rule file
sudo touch my-newrule.rules
# open the file
sudo vim my-newrule.rules
# add the following
KERNEL=="ttyACM0", MODE="0666"

This also sets permissions for world to read and write, which you may not want to do.

For more information about this approach, see these answers:

https://unix.stackexchange.com/a/48596/92486

https://mcmap.net/q/582041/-how-can-i-programmatically-set-permissions-on-my-char-device

03. The third option, which is the option I implemented, adds the Apache user to the dialout group so that if the script is being run by Apache, then it can access the device.

a) Find the location of your Apache config file, then search for the User setting within that file:

# open file in editor
sudo vim /etc/apache2/apache2.conf
# search for User setting
/User

You may find something like:

# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}

b) Quit vim and search for APACHE_RUN_USER in /etc/apache2/envvars (if the above scenario applies):

# open file in editor
sudo vim /etc/apache2/envvars
# search for APACHE_RUN_USER
/APACHE_RUN_USER

You may find something like:

export APACHE_RUN_USER=www-data

c) Add the User www-data to the dialout group:

sudo usermod -a -G dialout www-data

d) Restart.

As the Apache user has been added to the dialout group, the script should now be able to access the device.

Further Reading

How to find the location of the Apache config file:

https://mcmap.net/q/73773/-lost-httpd-conf-file-located-apache-closed

Oatis answered 11/1, 2015 at 10:48 Comment(1)
Thank you for the pointers! Under Ubuntu, what ended up working for me was askubuntu.com/a/133244/230541Shena
O
3

The permissions on the file make no difference to the user that the program runs as

When you are logged in interactively you do have permission to use the /dev/ttyACM0

When your script is running (presumably as the apache user) it does not have permission

You need to alter the permissions on the /dev/ttyACM0

See the 2nd answer here How can I programmatically set permissions on my char device for an example of altering udev permissions so the file has the correct permissions

Octillion answered 9/1, 2015 at 14:8 Comment(4)
Tried first suggestion from second answer, sudo vim /etc/udev/udev.conf, added default_mode="0666", shut down computer and restarted, permissions not effected: ls -l /dev/ttyACM0 returns: crw-rw---- 1 root dialout 166, 0 Jan 10 00:20 /dev/ttyACM0. Am looking at second suggestion from second answer now.Oatis
This solution worked: unix.stackexchange.com/a/48596/92486 . sudo vim /etc/udev/rules.d/my-newrule.rules, add KERNEL=="ttyACM0", MODE="0666". This gives read/write permissions to world. I don't understand, however, when the owner was a member of dialout why they didn't have read/write permissions in the first place.Oatis
If you are running the script via apache then it runs as the apache user. Look for the User setting in the httpd.conf. This user has to be a member of dialout in order for it to work with 660 permissionsOctillion
Thanks for the clarification, I have added findings to the answer (in the process of being reviewed) and can confirm the application is now running as expected.Oatis
C
0

Based on the accepted answer, I was able to just add the following to my setup.sh script

printf "KERNEL==\"ttyACM0\", MODE=\"0666\"" | sudo tee /etc/udev/rules.d/si-ct.rules
Corinthian answered 23/12, 2021 at 21:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.