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)