0

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

1 Answer 1

1

There are dozens of posts (on this and other sites) explaining why rc.local is unsuitable for starting tasks on a modern systemd distribution. Rather than reposting you should search them yourself.

There are many factors to consider, and unfortunately there seems to be no single guide which covers them all.

You could start with You could start with rc-local and explore an alternative systemd

This is not a Pi problem, but common to all modern Linux distributions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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