0

I've got a Raspberry Pi Zero W acting as a Flask python server that listens for webhooks, then runs a function to turn on a motor until the GPIO pins read a certain combination of inputs. I used to just run gunicorn using Forever, but that would really only last a day or two before needing to be reset again, so I've been trying to "productionize" it by using nginx and uwsgi instead.

The problem comes when uwsgi starts up, and it can't start the script, as GPIO access requires sudo. The actual GPIO stuff being performed is using [Adafruit_GPIO.SPI][1], which is based on RPi.GPIO.

I'm curious if we can get uwsgi to happily run the zwikau server and its GPIO calls without sudo or as root, or, failing that, simply get uwsgi to run the python code itself as root.

Code and config:

Jessie (8), with kernel version 4.14.18. RPi.GPIO is on the latest pip version (0.6.3).

the /etc/rc.local startup script is:

sudo /usr/local/bin/uwsgi --ini /home/pi/developer/zwikau/uwsgi_config.ini --uid www-data --gid www-data --daemonize /var/log/uwsgi.log
sudo chmod 666 /dev/spidev0.0
sudo chmod 666 /dev/spidev0.1

And here's the python code that gets called in response to a webhook call:

import time

# Import SPI library (for hardware SPI) and MCP3008 library.
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008


# Hardware SPI configuration:
SPI_PORT   = 0
SPI_DEVICE = 0
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))


print('Reading MCP3008 values, press Ctrl-C to quit...')
# Print nice channel column headers.
print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*range(8)))
print('-' * 57)
# Main program loop.
while True:
    # Read all the ADC channel values in a list.
    values = [0]*8
    for i in range(8):
        # The read_adc function will get the value of the specified channel (0-7).
        values[i] = mcp.read_adc(i)
    # Print the ADC values.
    print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*values))
    # Pause for half a second.
    time.sleep(0.5)

Here's /var/log/uwsgi.log from startup:

*** Starting uWSGI 2.0.15 (32bit) on [Fri Feb 16 04:27:58 2018] ***
compiled with version: 4.9.2 on 09 January 2018 08:20:54
os: Linux-4.9.35+ #1014 Fri Jun 30 14:34:49 BST 2017
nodename: zwikauPi
machine: armv6l
clock source: unix
detected number of CPU cores: 1
current working directory: /
detected binary path: /usr/local/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
setgid() to 33
setuid() to 33
chdir() to /home/pi/developer/zwikau
your processes number limit is 3405
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uwsgi socket 0 bound to UNIX address /var/sockets1/zwikau.sock fd 3
Python version: 2.7.9 (default, Sep 17 2016, 20:55:23)  [GCC 4.9.2]
Python main interpreter initialized at 0x1156fc8
python threads support enabled
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 143360 bytes (140 KB) for 2 cores
*** Operational MODE: threaded ***
Traceback (most recent call last):
  File "./server.py", line 2, in <module>
    from board import spin_to_index, spin_at_least
  File "./board.py", line 15, in <module>
    GPIO.setup(16, GPIO.OUT)
RuntimeError: No access to /dev/mem.  Try running as root!
unable to load app 0 (mountpoint='') (callable not found or import error)
*** no app loaded. going in full dynamic mode ***
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 636)
spawned uWSGI worker 1 (pid: 674, cores: 2)
1

I suggest you use a recent Raspbian (it would have been helpful to know which operating system and version you have installed) and a recent RPi.GPIO (ditto its version).

RPi.GPIO on Raspbian has not required root access to manipulate the GPIO for several years.

By the way there is other SPI software to read SPI devives, the Python spidev module would be a typical choice, it would do away with the Adafruit dependency.

  • Thanks for your answer. When I posted I was on Jessie (8), with kernel version 4.9.3; I upgraded to 4.14.18 and still the same problem. RPi.GPIO is on the latest pip version (0.6.3). I followed the instructions here to make sure that /dev/gpiomem is owned by the pi user I'm running the uwsgi server under. I've updated my q to ask how to make this run not as root, but under pi without using sudo. – Andrew Konoff Feb 16 '18 at 20:27
  • Turns out I was running the server not as pi but as www-data! Whoops. – Andrew Konoff Feb 16 '18 at 23:19
1

So I solved this quite simply: I remembered that I had followed these instructions to set up a uwsgi server that is run by a new www-data user. That user, unlike pi, both owned the whole directory in which all the code was, and didn't have permissions to access /dev/gpiomem. So when uwsgi started up and tried to run GPIO-accessing python code, it did so as www-data.

The fix was simple: if any process needs to access GPIO, just make sure that the user running it is added to the gpio usergroup:

sudo adduser newuser gpio

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.