I am developing my remote camera robot with flask on raspberry pi zero w. Where is mjpg-streamer used for video stream and picamera python lib used to take high resolution stills. And I having very weird issue. My problem is whether I am using raspistill or picamera python library to take still it failes to do it if I start my app from /etc/rc.local
script after power on automaticaly and it returning internal server error or file not found error. But when I starting my app manualy with the same command sudo python3 app.py
in terminal it takes images without problems. Is there any possibility that starting from /etc/rc.local somehow affects my app? Is there some difference? Maybe some libs does not get initialized properly. I realy have no idea why is it happening.
Here is my code:
from flask import Flask, render_template, Response, request, jsonify, send_file
import time # Import the Time library
import serial
import pigpio
from threading import Thread, Event
import subprocess
from readline import ReadLine
import picamera
import datetime
import logging
SERVO_V = 21
SERVO_H = 20
pi = pigpio.pi()
MOT_FWD_A = 9
MOT_FWD_B = 10
MOT_BWD_A = 8
MOT_BWD_B = 7
SERVO_V = 21
SERVO_H = 20
LED = 4
FRQ = 1000
DUTY = 64
CAMSTREAM_SCRIPT = '/home/pi/Desktop/UCTRONICS_Smart_Robot_Car_RaspberryPi-master/mjpg-streamer.sh'
serial_data = ""
EMPTY_DATA = "0.0"
prevGyro = EMPTY_DATA
prevMetres = EMPTY_DATA
prevBatt = EMPTY_DATA
pi.set_PWM_dutycycle(MOT_FWD_A, 0)
pi.set_PWM_dutycycle(MOT_FWD_B, 0)
pi.set_PWM_dutycycle(MOT_BWD_A, 0)
pi.set_PWM_dutycycle(MOT_BWD_B, 0)
pi.set_PWM_frequency(MOT_FWD_A, FRQ)
pi.set_PWM_frequency(MOT_FWD_B, FRQ)
pi.set_PWM_frequency(MOT_BWD_A, FRQ)
pi.set_PWM_frequency(MOT_BWD_B, FRQ)
pi.set_PWM_dutycycle(LED, 0)
pi.set_PWM_frequency(LED, 50)
ser = serial.Serial(
port='/dev/ttyS0',
baudrate = 9600
)
logging.basicConfig(filename="app.txt", filemode='a', format='%(name)s - %(levelname)s - %(message)s', level=logging.INFO)
def logInfo(info):
with open('rover.log','a') as log:
log.write(info)
print(info)
def setServoPosition(servoPin, newPosition):
pi.set_servo_pulsewidth(servoPin, newPosition)
print(newPosition)
def StopMotors():
pi.set_PWM_dutycycle(MOT_FWD_A, 0)
pi.set_PWM_dutycycle(MOT_FWD_B, 0)
pi.set_PWM_dutycycle(MOT_BWD_A, 0)
pi.set_PWM_dutycycle(MOT_BWD_B, 0)
# Turn both motors forwards AF = 1 AB = 0 BF = 1 BB = 0
def Forwards():
print(DUTY)
pi.set_PWM_dutycycle(MOT_FWD_A, DUTY)
pi.set_PWM_dutycycle(MOT_FWD_B, DUTY)
pi.set_PWM_dutycycle(MOT_BWD_A, 0)
pi.set_PWM_dutycycle(MOT_BWD_B, 0)
def Backwards():
pi.set_PWM_dutycycle(MOT_FWD_A, 0)
pi.set_PWM_dutycycle(MOT_FWD_B, 0)
pi.set_PWM_dutycycle(MOT_BWD_A, DUTY)
pi.set_PWM_dutycycle(MOT_BWD_B, DUTY)
# Turn right
def Right():
pi.set_PWM_dutycycle(MOT_FWD_A, 0)
pi.set_PWM_dutycycle(MOT_FWD_B, DUTY)
pi.set_PWM_dutycycle(MOT_BWD_A, DUTY)
pi.set_PWM_dutycycle(MOT_BWD_B, 0)
time.sleep(0.1)
StopMotors()
# Turn left
def Left():
pi.set_PWM_dutycycle(MOT_FWD_A, DUTY)
pi.set_PWM_dutycycle(MOT_FWD_B, 0)
pi.set_PWM_dutycycle(MOT_BWD_A, 0)
pi.set_PWM_dutycycle(MOT_BWD_B, DUTY)
time.sleep(0.1)
StopMotors()
app = Flask(__name__)
@app.route('/')
def execute():
return render_template('index.html')
serialEvent = Event()
@app.route("/info", methods=['GET'])
def telemetry():
global prevBatt
global prevGyro
global prevMetres
gyro = None
battery = None
metres = None
global serial_data
#serialEvent.set()
rl = ReadLine(ser)
data1 = rl.readline()
data2 = rl.readline()
data3 = rl.readline()
serial_data = data1+data2+data3
#print(serial_data)
data_str = str(serial_data, 'utf-8')
index_b = data_str.find('Bat')
if (index_b is not -1):
battery = data_str[index_b+4:index_b+9]
else:
battery = EMPTY_DATA
index_g = data_str.find('gyro')
if (index_g is not -1):
gyro = data_str[index_g+5:index_g+11]
else:
gyro = EMPTY_DATA
index_d = data_str.find('Distance')
if (index_d is not -1):
metres = data_str[index_d+9:index_d+15]
else:
metres = EMPTY_DATA
if (index_g is not -1):
prevGyro = gyro
else:
gyro = prevGyro
if (index_b is not -1):
prevBatt = battery
else:
battery = prevBatt
if (index_d is not -1):
prevMetres = metres
else:
metres = prevMetres
#print("GYRO: %s\n" % (gyro))
#print("BAT: %s\n" % (battery))
#print("MET: %s\n" % (metres))
return jsonify(voltage=battery,
gyro_x=gyro,
distance=metres)
@app.route("/control", methods=['GET'])
def remoteControl():
global DUTY
var =request.args.get('var')
val = int(request.args.get('val'))
print("var = %s \n val = %d" % (var, val))
if (var == "car"):
if (val == 1):
Forwards()
elif (val == 2):
Left()
elif (val == 3):
StopMotors()
elif (val == 4):
Right()
elif (val == 5):
Backwards()
elif (var == "servo"):
if (val < 1100):
setServoPosition(SERVO_V, 1100)
elif (val > 2400):
setServoPosition(SERVO_V, 2400)
else:
setServoPosition(SERVO_V, val)
elif (var == "servo1"):
if (val < 500):
setServoPosition(SERVO_H, 500)
elif (val > 2300):
setServoPosition(SERVO_H, 2300)
else:
setServoPosition(SERVO_H, val)
elif (var == "speed"):
if (val > 240):
DUTY = 240
elif (val < 0):
DUTY = 0
else:
DUTY = val
elif (var == "led"):
if (val > 255):
pi.set_PWM_dutycycle(LED,255)
elif (val < 0):
pi.set_PWM_dutycycle(LED, 0)
else:
pi.set_PWM_dutycycle(LED, val)
return('', 204)
def clientIsHere():
while(1):
cmd_out = subprocess.run(['hostapd_cli', 'all_sta'], stdout=subprocess.PIPE).stdout.decode('utf-8')
if (not len(cmd_out) > 27):
StopMotors()
time.sleep(0.5)
@app.route("/getlog", methods=['GET'])
def getLog():
return send_file("app.txt")
@app.route("/snapshot", methods=['GET'])
def snapshot():
filename = str(datetime.datetime.now()) + ".jpg"
logging.info("Filename would be " + filename)
subprocess.run([CAMSTREAM_SCRIPT, 'stop'])
logInfo("Trying to take img")
with picamera.PiCamera() as camera:
time.sleep(2)
try:
camera.resolution = (1920,1080)
camera.rotation = 90
camera.capture(filename)
except picamera.PiCameraError as e :
logging.info(e)
subprocess.run([CAMSTREAM_SCRIPT, 'start'])
print("Restarted stream. Sending " + "/home/pi/rov/" + filename + " to client")
logging.info("sending img filename = " + filename)
return send_file("/home/pi/rov/" + filename)
if __name__ == '__main__':
motorsThread = Thread(target=clientIsHere)
motorsThread.start()
app.run(debug=False, host='0.0.0.0', threaded=True)
Where is main problematic part is:
@app.route("/snapshot", methods=['GET'])
def snapshot():
filename = str(datetime.datetime.now()) + ".jpg"
logging.info("Filename would be " + filename)
subprocess.run([CAMSTREAM_SCRIPT, 'stop'])
#here I tried with different delays and tried with start_preview()- stop_preview() it doesnt't change anything
with picamera.PiCamera() as camera:
time.sleep(2)
try:
camera.resolution = (1920,1080)
camera.rotation = 90
camera.capture(filename)
except picamera.PiCameraError as e :
logging.info(e) # <-- It fails to take img but never get to this exception somehow
subprocess.run([CAMSTREAM_SCRIPT, 'start'])
print("Restarted stream. Sending " + "/home/pi/rov/" + filename + " to client")
logging.info("sending img filename = " + filename)
return send_file("/home/pi/rov/" + filename)
Html code for still request
document.getElementById('get-image').addEventListener('click', (event) => {
window.open(BASE_URL + '/snapshot')
});
Full html code: https://pastebin.com/aAwah71i
My /etc/rc.local is look like:
sudo /home/pi/Desktop/UCTRONICS_Smart_Robot_Car_RaspberryPi-master/mjpg-streamer.sh start &
pigpiod -s 10 &
python3 /home/pi/rov/app.py &
exit 0
Streamer bash script:
#!/bin/bash
# chmod +x mjpg-streamer.sh
# Crontab: @reboot /home/pi/mjpg-streamer/mjpg-streamer.sh start
# Crontab: @reboot /home/pi/mjpg-streamer/mjpg-streamer-experimental/mjpg-streamer.sh start
MJPG_STREAMER_BIN="/usr/local/bin/mjpg_streamer" # "$(dirname $0)/mjpg_streamer"
MJPG_STREAMER_WWW="/usr/local/share/mjpg-streamer/www"
MJPG_STREAMER_LOG_FILE="${0%.*}.log" # "$(dirname $0)/mjpg-streamer.log"
RUNNING_CHECK_INTERVAL="2" # how often to check to make sure the server is running (in seconds)
HANGING_CHECK_INTERVAL="3" # how often to check to make sure the server is not hanging (in seconds)
VIDEO_DEV="/dev/video0"
FRAME_RATE="15"
QUALITY="80"
RESOLUTION="960x544" # 160x120 176x144 320x240 352x288 424x240 432x240 640x360 640x480 800x448 800x600 960x544 1280x720 1920x1080 (QVGA, VGA, SVGA, WXGA) # lsusb -s 001:006 -v | egrep "Width|Height" # https://www.textfixer.com/tools/alphabetical-order.php # v4l2-ctl --list-formats-ext # Show Supported Video Formates
PORT="8080"
YUV="yes"
################INPUT_OPTIONS="-r ${RESOLUTION} -d ${VIDEO_DEV} -f ${FRAME_RATE} -q ${QUALITY} -pl 60hz"
INPUT_OPTIONS="-r ${RESOLUTION} -d ${VIDEO_DEV} -q ${QUALITY} -pl 60hz --every_frame 2 -rot 90" # Limit Framerate with "--every_frame ", ( mjpg_streamer --input "input_uvc.so --help" )
if [ "${YUV}" == "true" ]; then
INPUT_OPTIONS+=" -y"
fi
OUTPUT_OPTIONS="-p ${PORT} -w ${MJPG_STREAMER_WWW}"
# ==========================================================
function running() {
if ps aux | grep ${MJPG_STREAMER_BIN} | grep ${VIDEO_DEV} >/dev/null 2>&1; then
return 0
else
return 1
fi
}
function start() {
if running; then
echo "[$VIDEO_DEV] already started"
return 1
fi
export LD_LIBRARY_PATH="$(dirname $MJPG_STREAMER_BIN):."
echo "Starting: [$VIDEO_DEV] ${MJPG_STREAMER_BIN} -i \"input_uvc.so ${INPUT_OPTIONS}\" -o \"output_http.so ${OUTPUT_OPTIONS}\""
${MJPG_STREAMER_BIN} -i "input_uvc.so ${INPUT_OPTIONS}" -o "output_http.so ${OUTPUT_OPTIONS}" >> ${MJPG_STREAMER_LOG_FILE} 2>&1 &
sleep 1
if running; then
if [ "$1" != "nocheck" ]; then
check_running & > /dev/null 2>&1 # start the running checking task
check_hanging & > /dev/null 2>&1 # start the hanging checking task
fi
echo "[$VIDEO_DEV] started"
return 0
else
echo "[$VIDEO_DEV] failed to start"
return 1
fi
}
function stop() {
if ! running; then
echo "[$VIDEO_DEV] not running"
return 1
fi
own_pid=$$
if [ "$1" != "nocheck" ]; then
# stop the script running check task
ps aux | grep $0 | grep start | tr -s ' ' | cut -d ' ' -f 2 | grep -v ${own_pid} | xargs -r kill
sleep 0.5
fi
# stop the server
ps aux | grep ${MJPG_STREAMER_BIN} | grep ${VIDEO_DEV} | tr -s ' ' | cut -d ' ' -f 2 | grep -v ${own_pid} | xargs -r kill
echo "[$VIDEO_DEV] stopped"
return 0
}
function check_running() {
echo "[$VIDEO_DEV] starting running check task" >> ${MJPG_STREAMER_LOG_FILE}
while true; do
sleep ${RUNNING_CHECK_INTERVAL}
if ! running; then
echo "[$VIDEO_DEV] server stopped, starting" >> ${MJPG_STREAMER_LOG_FILE}
start nocheck
fi
done
}
function check_hanging() {
echo "[$VIDEO_DEV] starting hanging check task" >> ${MJPG_STREAMER_LOG_FILE}
while true; do
sleep ${HANGING_CHECK_INTERVAL}
# treat the "error grabbing frames" case
if tail -n2 ${MJPG_STREAMER_LOG_FILE} | grep -i "error grabbing frames" > /dev/null; then
echo "[$VIDEO_DEV] server is hanging, killing" >> ${MJPG_STREAMER_LOG_FILE}
stop nocheck
fi
done
}
function help() {
echo "Usage: $0 [start|stop|restart|status]"
return 0
}
if [ "$1" == "start" ]; then
start && exit 0 || exit -1
elif [ "$1" == "stop" ]; then
stop && exit 0 || exit -1
elif [ "$1" == "restart" ]; then
stop && sleep 1
start && exit 0 || exit -1
elif [ "$1" == "status" ]; then
if running; then
echo "[$VIDEO_DEV] running"
exit 0
else
echo "[$VIDEO_DEV] stopped"
exit 1
fi
else
help
fi