Trilobot Doesn't Work Properly With Flask

I am trying to create a Flask web interface to control the Trilobot. Unfortunately, the motors are inconsistent when we put the commands into a Flask script (see below). The motors won’t stop when we use the stop command (they keep going but slowly) and the two motors go at different speeds (pulling the car to the right) when we tell it to go straight.

Without Flask, the same commands seem to work, though there’s a slight pull to the right. Here’s our script. Is there a trick to getting it to work?

from flask import Flask, render_template
from trilobot import Trilobot
from time import sleep
import datetime
tbot = Trilobot()
app = Flask(__name__)
@app.route("/")
def hello():
    now = datetime.datetime.now()
    timeString = now.strftime("%Y-%m-%d %H:%M")
    templateData = {
      'title' : 'HELLO!',
      'time': timeString
      }
    return render_template('index.html', **templateData)
@app.route("/<direction>")
def action(direction):
    now = datetime.datetime.now()
    timeString = now.strftime("%Y-%m-%d %H:%M")
    templateData = {
        'title' : 'Moving ' + direction,
        'direction' : direction,
        'time' : timeString
        }
    if direction == "forward":
        print("going forward")
        tbot.forward(1.0)
        sleep(1)
    elif direction == "right":
        print("going right")
        tbot.turn_right(0.7)
        sleep(1)
    elif direction == "left":
        print("going left")
        tbot.turn_left(0.7)
        sleep(1)
    elif direction == "stop":
        print("stopping")
        tbot.stop()
        sleep(1)
    return render_template('index.html', **templateData)
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80, debug=True)

I have run into the same issue, but I see there has been no reply here for 2 years.

Flask spawns different threads when you run the app. My suspicion is that the trilobot control is not thread safe (nor is Flask, for that matter). So I have written a Flask app with the Trilobot simple-controller running in a separate process from the Flask app, and that is working.

Here is the Flask application. One has to make sure the controller process is not initialized more than once in a thread. Hence the lock as well as the global flag robot_initialized. The controller process uses the Python multiprocessing library, which actually allows concurrency (after all, the RPi has four processors): the threading library cannot.

from flask import Flask, Response, render_template, request, jsonify
import threading
from multiprocessing import Process, Queue
import json
import os
import signal
from trilobot import *


app = Flask(__name__)
SPEED = 0.7
robot_initialized = False
robot_lock = threading.Lock()
robot_process = None
command_que = None

def initialize_robot(speed):
    # Lock to only allow a single thread to run this code at one time
    with robot_lock:
        global robot_initialized
        # Only initialize the processs to control Trilobot once in a thread
        if not robot_initialized:
            global robot_process
            global command_que
            # queue to push commands to the control process
            command_que = Queue()
            # Initialize and start the process
            robot_process = Process(target=dispatch_command, args=(command_que, 
                                                                   SPEED))
            robot_process.start()
            robot_initialized = True

def dispatch_command(que, speed):
    tbot = Trilobot()
    while True:
        # If there is a command in the queue, dequeue and execute it
        if not que.empty():
            command = que.get()
            if command == "exit":
                tbot.stop()
                break
            elif command == "forward":
                tbot.forward(speed)
            elif command == "reverse":
                tbot.backward(speed)
            elif command == "left":
                tbot.turn_left(speed)
            elif command == "right":
                tbot.turn_right(speed)
            else:
                tbot.stop()

# Route to load the webpage
@app.route('/')
def home():
    return render_template('index.html')

# Route for remote controls (you can adapt this as needed)
@app.route('/controls')
def remote_controls():
    initialize_robot(SPEED)
    command = request.args.get("command")
    if command == "exit":
        # push "exit" command
        command_que.put("exit")
        # wait for control process to exit
        robot_process.join()
        status = {'status': 'exit'}
        os.kill(os.getpid(), signal.SIGINT)
    else:
        command_que.put(command)
        status = {'status': command}
    return jsonify(json.dumps(status))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

The whole project can be found here; including the HTML and JavaScript; GitHub - karencfisher/trilobot-web-remote