Created
March 25, 2015 16:58
Revisions
-
tuxite created this gist
Mar 25, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,115 @@ <!DOCTYPE HTML> <html> <head> <title>NMEA Serial Simulator</title> <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.2.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js"></script> <!-- Latest compiled and minified JavaScript --> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script> <script type="text/javascript" charset="utf-8"> $(document).ready(function () { var socket = io.connect('http://' + document.domain + ':' + location.port + "/nmea"); socket.on('connect', function () { socket.emit('client-connect', JSON.stringify("Hello")); console.log("Connected"); }); socket.on('nmea', function (message) { console.log("Received nmea", JSON.parse(message)); data = JSON.parse(message); $("#nmea-sentence").html(data.date + " " + data.string); }); }); </script> </head> <body> <div id="content" class="container"> <div class="page-header"> <h1>NMEA Serial Simulator <small>using Flask and pySerial</small></h1> </div> <h2>Wind Data</h2> <div id="nmea"> <form class="form-inline" method="POST" action='/nmea'> <div class="form-group"> <label for="direction">Direction</label> <input type="number" value="" id="direction" name="direction" class="form-control" step="0.1" min="0" max="359.9"> </div> <div class="form-group"> <label for="speed">Speed</label> <input type="number" value="" id="speed" name="speed" class="form-control" step="0.1" min="0"> </div> <button id="client-connect-btn" type="submit" class="btn btn-default"> <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> Update </button> </form> </div> <div id="data"> <h3>Sent via the serial interface</h3> <pre id="nmea-sentence"></pre> </div> <h2>Serial Data</h2> <div id="serial"> <form class="form-inline" method="POST" action='/serial'> <div class="form-group"> <label for="baudrate">Baud Rate</label> <select id="baudrate" name="baud" class="form-control"> <option>300</option> <option>600</option> <option>1200</option> <option>2400</option> <option>4800</option> <option selected>9600</option> <option>14400</option> <option>19200</option> <option>28800</option> <option>31250</option> <option>38400</option> <option>57600</option> <option>115200</option> </select> </div> <div class="form-group"> <label for="bits">Bits</label> <select id="bits" name="bits" class="form-control"> <option>5</option> <option>6</option> <option>7</option> <option selected>8</option> </select> </div> <div class="form-group"> <label for="parity">Parity</label> <select id="parity" name="parity" class="form-control"> <option value="N" selected>NONE</option> <option value="E">EVEN</option> <option value="O">ODD</option> <option value="M">MARK</option> <option value="S">SPACE</option> </select> </div> <div class="form-group"> <label for="stop_bit">Stop</label> <select id="stop_bit" name="stop" class="form-control"> <option selected>1</option> <option>1.5</option> <option>2</option> </select> </div> <button id="client-connect-btn" type="submit" class="btn btn-default"> <span class="glyphicon glyphicon-log-in" aria-hidden="true"></span> Connect </button> </form> </div> </div> </body> </html> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,207 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """A simple serial NMEA simulator using Flask and SocketIO.""" import serial import json import time import datetime import sys import operator import threading import signal from flask import Flask, render_template, request, make_response from flask.ext.socketio import SocketIO app = Flask(__name__) app.config["SECRET_KEY"] = "your-secret" app.config["SERVER_NAME"] = "127.0.0.1:5002" socketio = SocketIO(app) import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Set the port from the command line PORT = "/dev/ttyS0" STOPBITS = { "1": 1, "1.5": 1.5, "2": 2 } PARITY = ['N', 'E', 'O', 'M', 'S'] SPEED = 0 DIRECTION = 0 thread = None class SerialThread(threading.Thread): """Thread class for emitting nmea sentences through the serial interface. """ def __init__(self, baud=9600, bits="8", parity="N", stop_bit="1"): """Initialization of the serial parameters. Keyword arguments: baud -- the speed of the connection. Acceptable values are : 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 31250, 38400, 57600, 115200 bits -- the number of bits (integer: 5, 6, 7, 8) parity -- the parity of the connection. One character: N, E, O, M, S stop_bit -- the stop bit: 1, 1.5 or 2 """ threading.Thread.__init__(self) self.ser = None self.baud = baud self.bits = bits self.parity = parity self.stop_bit = stop_bit self._stop = threading.Event() def stop(self): """Set the stop status of the thread.""" self._stop.set() def stopped(self): """Return the status of the thread. Checked regularly in the loop to stop if needed. """ return self._stop.isSet() def run(self): """Loop of the thread. Connect to the serial port using the parameters. Then, create an infinite loop with regular check of the status of the thread. Create the NMEA sentence using the global variables SPEED and DIRECTION. Send it to the serial interface and emit a socket.io message with the datetime of the sentence for the clients. """ global socketio self.ser = serial.Serial(port=PORT, baudrate=self.baud, parity=self.parity, stopbits=self.stop_bit, timeout=10) while True: if self.stopped(): break nmea = "$WIWVM,{0},R,{1},N,A".format(DIRECTION, SPEED) crc = reduce(operator.xor, [ord(x) for x in nmea[1:]], 0) nmea += "*{0:02X}\r\n".format(crc) self.ser.write(nmea) date = datetime.datetime.utcnow().replace(microsecond=0) socketio.emit("nmea", json.dumps({"date": date.isoformat(), "string": nmea}), namespace="/nmea") time.sleep(1) self.ser.close() return True @app.route('/') def index(): """Return the main page.""" return render_template('index.html') @app.route('/nmea', methods=['POST',]) def update(): """Update the speed and the direction of the wind. Return nothing (204) if succeed. """ global SPEED, DIRECTION try: SPEED = float(request.form['speed']) DIRECTION = float(request.form['direction']) except ValueError: logger.warning("NMEA Bad values") return make_response(("Bad Values", 500)) logger.info("Wind updated: {0}kn {1}deg".format(SPEED, DIRECTION)) return make_response(("", 204)) @app.route('/serial', methods=['POST',]) def connect(): """Update the parameters of the serial connection. Close the existing one if any (terminate the associated thread). Then, create a new thread with these new parameters. Only one serial thread at a time, normally... Return nothing (204) if succeed. """ global thread try: baud = int(request.form['baud']) bits = int(request.form['bits']) except ValueError: logger.warning("Serial connection vad values") return make_response(("Bad Values", 500)) parity = request.form['parity'] if parity not in PARITY: logger.warning("Serial connection vad values") return make_response(("Bad Values", 500)) stop = request.form['stop'] if stop not in STOPBITS: logger.warning("Serial connection vad values") return make_response(("Bad Values", 500)) stop = STOPBITS[stop] logger.info("Serial parameters: {0},{1},{2},{3}".format(baud, bits, parity, stop)) if thread: thread.stop() thread = SerialThread(baud, bits, parity, stop) logger.info("Started the thread") thread.start() return make_response(("", 204)) def kill_thread(*args): """Properly exit the script.""" global thread if thread: logger.info("Stopping the thread") thread.stop() exit() @socketio.on('client-connect', namespace='/nmea') def namespace_test(message): """Log the connection of a client.""" logger.info("SocketIO OK: {0}".format(message)) if __name__ == "__main__": # Set the port from the command line if len(sys.argv) > 1: PORT = sys.argv[1] signal.signal(signal.SIGINT, kill_thread) signal.signal(signal.SIGTERM, kill_thread) socketio.run(app)