Web server help and script

hi ive been trying to get a script to work with webserver here is the code i start the web server with sudo python3 app.py

 import subprocess
from flask import Flask, render_template, request
import os # Import os for path checking

app = Flask(__name__)

# --- Pololu Maestro Configuration ---
# IMPORTANT:
# 1. Verify this path matches where you unzipped and compiled UscCmd on your Pi.
#    You can double-check with: ls /home/neil/Desktop/maestro-linux/UscCmd
USCCMD_PATH = "/home/neil/Desktop/maestro-linux/UscCmd"

# 2. This MUST match your Maestro's actual device serial number.
#    You can find it by running: sudo /home/neil/Desktop/maestro-linux/UscCmd --list
#    It's usually the number after '#'. Your specific ID is "00467001".
MAESTRO_DEVICE_ID = "00467001"

# --- HTML Template Content (for templates/index.html) ---
# You'll need to create a directory named 'templates' in the same folder as app.py
# and create a file named 'index.html' inside it.
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Maestro Control</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; }
        .container { max-width: 600px; margin: auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { text-align: center; color: #333; }
        .button-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 15px; margin-top: 30px; }
        button {
            width: 100%;
            padding: 15px 20px;
            font-size: 1.1em;
            color: white;
            background-color: #007bff;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.2s ease-in-out, transform 0.1s ease-in-out;
        }
        button:hover { background-color: #0056b3; transform: translateY(-2px); }
        button:active { background-color: #004085; transform: translateY(0); }
        .status-message {
            margin-top: 25px;
            padding: 15px;
            border-radius: 5px;
            background-color: #e2e3e5;
            color: #333;
            border: 1px solid #d6d8db;
            word-wrap: break-word;
            white-space: pre-wrap; /* Preserve whitespace and line breaks */
        }
        .status-success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
        .status-error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Maestro Control Interface</h1>
        <div class="button-grid">
            <button onclick="triggerSequence('0')">Sequence 1 (Script 0)</button>
            <button onclick="triggerSequence('1')">Sequence 2 (Script 1 - Empty)</button>
            </div>
        <div id="status" class="status-message">Ready.</div>
    </div>

    <script>
        async function triggerSequence(sequenceId) {
            const statusDiv = document.getElementById('status');
            statusDiv.className = 'status-message'; // Reset class
            statusDiv.innerText = 'Sending command...';

            try {
                const response = await fetch('/trigger', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: `sequence_id=${sequenceId}`
                });

                const data = await response.text();

                if (response.ok) {
                    statusDiv.className = 'status-message status-success';
                    statusDiv.innerText = `Success: ${data}`;
                } else {
                    statusDiv.className = 'status-message status-error';
                    statusDiv.innerText = `Error: ${data}`;
                }
            } catch (error) {
                statusDiv.className = 'status-message status-error';
                statusDiv.innerText = `Network or fetch error: ${error.message}`;
                console.error('Fetch error:', error);
            }
        }
    </script>
</body>
</html>
"""
# --- End of HTML Template Content ---

# Create templates directory and index.html file if they don't exist
def create_html_template():
    template_dir = os.path.join(os.path.dirname(__file__), 'templates')
    os.makedirs(template_dir, exist_ok=True)
    html_file_path = os.path.join(template_dir, 'index.html')
    if not os.path.exists(html_file_path):
        with open(html_file_path, 'w') as f:
            f.write(HTML_CONTENT)
        print(f"Created {html_file_path}")

def run_usc_command(command_args):
    """
    Executes a UscCmd command and returns its output.
    This function includes the --device flag to ensure the correct Maestro is targeted.
    """
    # Check if UscCmd executable exists
    if not os.path.exists(USCCMD_PATH) or not os.access(USCCMD_PATH, os.X_OK):
        return f"Error: UscCmd not found or not executable at {USCCMD_PATH}. Please check the path and permissions.", False

    # Construct the full command
    cmd = [USCCMD_PATH, "--device", MAESTRO_DEVICE_ID] + command_args
    print(f"Executing Maestro command: {' '.join(cmd)}")

    try:
        # It's good practice to stop any currently running script before starting a new one.
        # This prevents potential conflicts if a script was left running.
        # Ensure --stop also uses the --device flag.
        stop_cmd = [USCCMD_PATH, "--device", MAESTRO_DEVICE_ID, "--stop"]
        subprocess.run(stop_cmd, check=True, capture_output=True, text=True)
        print("Stopped any active Maestro script.")

        # Execute the main command (e.g., --sub 0)
        result = subprocess.run(cmd, check=True, capture_output=True, text=True)
        print("Maestro command output:")
        print(result.stdout)
        if result.stderr:
            print("Maestro command error output (stderr):")
            print(result.stderr)

        # Check for error indicators in stdout (UscCmd often prints errors to stdout)
        if "ERROR" in result.stdout.upper() or "FAILED" in result.stdout.upper():
            return f"Maestro reported errors: {result.stdout}", False
        
        return result.stdout.strip(), True

    except subprocess.CalledProcessError as e:
        # This handles cases where UscCmd returns a non-zero exit code
        print(f"ERROR executing Maestro command (CalledProcessError): {e}")
        print(f"Stdout: {e.stdout}")
        print(f"Stderr: {e.stderr}")
        return f"Error executing command: {e.stderr or e.stdout}", False
    except Exception as e:
        # Catch any other unexpected errors
        print(f"An unexpected error occurred: {e}")
        return f"An unexpected error occurred: {e}", False


@app.route('/')
def index():
    # Ensure the HTML template is created when the app starts or is accessed
    create_html_template()
    return render_template('index.html')

@app.route('/trigger', methods=['POST'])
def trigger_sequence():
    sequence_id = request.form.get('sequence_id')
    if sequence_id is None:
        return "Error: Missing sequence_id", 400

    print(f"Received web request: Triggering Maestro Sequence (Subroutine) {sequence_id}")

    # Map sequence_id to the appropriate Maestro subroutine call
    if sequence_id == '0':
        output, success = run_usc_command(["--sub", "0"])
    elif sequence_id == '1':
        # Subroutine 1 is currently empty in your Maestro script, but the command is valid.
        output, success = run_usc_command(["--sub", "1"])
    # You can add more 'elif' blocks here for _sub2, _sub3, etc.
    # elif sequence_id == '2':
    #     output, success = run_usc_command(["--sub", "2"])
    else:
        return "Invalid sequence ID", 400

    if success:
        return output, 200
    else:
        # For HTTP 500, the Flask client might not show the full message automatically
        # but it helps in debugging. The JS will also catch this.
        return output, 500

if __name__ == '__main__':
    # When running the Flask app on a Raspberry Pi that accesses USB devices,
    # you often need root privileges, or you need to set up udev rules.
    # Running with 'sudo python3 app.py' is the common quick start method.
    # For a more permanent solution, consider a udev rule:
    # 1. Find vendor/product ID: lsusb (e.g., 1ffb:0089 for Pololu Micro Maestro)
    # 2. Create /etc/udev/rules.d/50-pololu.rules with content:
    #    SUBSYSTEM=="usb", ATTR{idVendor}=="1ffb", ATTR{idProduct}=="0089", MODE="0666"
    # 3. sudo udevadm control --reload-rules && sudo udevadm trigger
    # 4. Unplug/replug Maestro. Then you might not need sudo for app.py
    app.run(host='0.0.0.0', port=5000, debug=False)

this is the script i have on the micro maestro board # Subroutine 0: Moves Servo 0 through a range

_sub0:
  6000 0 servo     # Move servo 0 to center (quarter-microseconds)
  1000 delay       # Wait 1 second (milliseconds)
  4000 0 servo     # Move servo 0 to position 4000
  1000 delay       # Wait 1 second
  8000 0 servo     # Move servo 0 to position 8000
  1000 delay       # Wait 1 second
  6000 0 servo     # Move servo 0 back to center
  return           # Return from this subroutine

but i cant get any movement from servos they work ok when in command centre on pc with sliders and sequences

i can run this command with ssh /home/neil/Desktop/maestro-linux/UscCmd --servo 0,8000
and servo moves any help thanks

Hello.

I only briefly looked at your Python code, but I noticed some problems with your script that I think are worth addressing first. The syntax you are using (i.e. _sub0:) is for setting up a GOTO label named _sub0, which is different from a subroutine. To set up a subroutine named _sub0, you should use sub _sub0

Also, since you are using the “Restart script at subroutine” command (i.e. the --sub # option in usccmd), you should not end your subroutine with a RETURN command or it will cause errors (since there is nowhere to return to). If you want the subroutine to run once and stop, you can end it with a QUIT command instead; otherwise, if you want it to repeat endlessly you can put it inside of a BEGIN/REPEAT loop.

So, for example, your script should look something like this:

sub _sub0 #define subroutine
  6000 0 servo     # Move servo 0 to center (quarter-microseconds)
  1000 delay       # Wait 1 second (milliseconds)
  4000 0 servo     # Move servo 0 to position 4000
  1000 delay       # Wait 1 second
  8000 0 servo     # Move servo 0 to position 8000
  1000 delay       # Wait 1 second
  6000 0 servo     # Move servo 0 back to center
  quit           # stop the script

Can you try making that change and see if it helps?

Brandon

thank you brandon for the help its now moving servos
i tried the quit at the end but for some reason it just played all the sub0 sub1 sub2
one after the other
then stopping on the last sequence no matter where id put quit so i had to leave
it with begin return repeat
i get no error light and each sub x when called plays once until i press the button again ive cleaned up the python code and was thinking of posting it here on the forum thanks for the help

I’ve cleaned up the code more and quit now works and now runs the 1 sequence that’s called for in the script don’t know what I changed but it all works

1 Like