2013-09-11 3 views
1

Я работаю над небольшим проектом, используя MaxSonar EZ1 ultrasonic range sensor и Arduino Diecimila.Чтение последовательных данных из Arduino с Python

Используя MaxSonar playground code, у меня есть Arduino, записывающий количество дюймов в серию каждые 0,5 секунды вместе с разделителем. При мониторинге последовательных данных, вывод похож на:

5.13.15.12.123.39.345... 

На стороне Python, у меня есть основное приложение фляги с/расстоянием маршрута, который возвращает объект JSON с порядковым значением:

from flask import Flask 
from flask import render_template 
import serial 
import json 
import random 

app = Flask(__name__, 
      static_folder="public", 
      template_folder="templates") 

port = "/dev/tty.usbserial-A6004amR" 
ser = serial.Serial(port,9600) 

@app.route("/") 
def index(): 
    return render_template('index.html') 

@app.route("/distance") 
def distance(): 
    distance = read_distance_from_serial() 
    return json.dumps({'distance': distance}) 

def read_distance_from_serial(): 
    x = ser.read(); 
    a = ''; 
    while x is not '.': 
    a += x; 
    x = ser.read() 

    print(a) 

    return a 
    # return random.randint(1, 100) 

if __name__ == "__main__": 
    app.debug = True 
    app.run() 

И index.html - это базовый сайт с некоторыми JS, который опроса/расстояния каждые полсекунды для нового чтения. Со значением я должен уметь создавать интересный пользовательский интерфейс, который изменяется в зависимости от того, насколько близко/далеко от сонара.

$(document).ready(function() { 

    window.GO = function() { 

    this.frequency = 500; // .5 seconds 

    this.init = function() { 
     window.setInterval(this.update_distance, 500); 
    } 

    this.update_distance = function() { 
     $.get('/distance', function(response) { 
     var d = response.distance; 
     $('#container').animate({"width": d + "%"}); 
     }, 'json') 
    } 
    } 

    go = new GO(); 
    go.init(); 
}); 

Вопрос

Проблема Я бегу в том, что нет никакой гарантии, что, когда питон читает из последовательного, что будет иметь значение. Часто, когда он опроса, я получаю либо пустое значение, либо частичное значение, в то время как в других случаях это происходит.

Как я могу изменить свою технику таким образом, чтобы я мог последовательно отслеживать последовательные данные и получать последние хорошие показания с серийного выхода Arduino?

ответ

1

Вы хотите, чтобы ваше серийное чтение происходило в фоновом режиме, а не по требованию. Вы можете использовать threading и Queue. Вы добавляете свои порядковые значения в очередь, как только вы определяете, что у вас есть допустимое значение, а затем ваш вызов сокета просто вытягивается из очереди. Это будет что-то вроде этого:

from flask import Flask 
from flask import render_template 
import serial 
import json 
import random 

import threading, Queue 

import logging 
logging.basicConfig(filename=__file__.replace('.py','.log'),level=logging.DEBUG,format='%(asctime)s [%(name)s.%(funcName)s] %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', filemode='a') 

class maxSonarSerialThread(threading.Thread): 
    def __init__(self, dataQ, errQ, port=None, baudrate=None): 
    self.logger = logging.getLogger('sonarSerialThread') 
    self.logger.debug('initializing') 
    threading.Thread.__init__(self) 
    self.ser = serial.Serial() 
    self.ser.timeout = 1 
    if port is None: 
     self.ser.port = "/dev/tty.usbserial-A6004amR" 
    else: 
     self.ser.port = port 
    if baudrate is None: 
     self.baudrate = 115200 
    else: 
     self.baudrate = baudrate 
    #self.ser.flushInput() 
    self.readCount = 0 
    self.sleepDurSec = 5 
    self.waitMaxSec = self.sleepDurSec * self.ser.baudrate/10 
    self.dataQ = dataQ 
    self.errQ = errQ 
    self.keepAlive = True 
    self.stoprequest = threading.Event() 
    self.setDaemon(True) 
    self.dat = None 
    self.inputStarted = False 
    self.ver = ver 

    def run(self): 
    self.logger.debug('running') 
    dataIn = False 
    while not self.stoprequest.isSet(): 
     if not self.isOpen(): 
     self.connectForStream() 

     while self.keepAlive: 
     dat = self.ser.readline() 
     //some data validation goes here before adding to Queue... 
     self.dataQ.put(dat) 
     if not self.inputStarted: 
      self.logger.debug('reading') 
     self.inputStarted = True 
     self.dat.close() 
     self.close() 
     self.join_fin() 

    def join_fin(self): 
    self.logger.debug('stopping') 
    self.stoprequest.set() 

    def connectForStream(self, debug=True): 
    '''Attempt to connect to the serial port and fail after waitMaxSec seconds''' 
    self.logger.debug('connecting') 
    if not self.isOpen(): 
     self.logger.debug('not open, trying to open') 
     try: 
     self.open() 
     except serial.serialutil.SerialException: 
     self.logger.debug('Unable to use port ' + str(self.ser.port) + ', please verify and try again') 
     return 
    while self.readline() == '' and self.readCount < self.waitMaxSec and self.keepAlive: 
     self.logger.debug('reading initial') 
     self.readCount += self.sleepDurSec 
     if not self.readCount % (self.ser.baudrate/100): 
      self.logger.debug("Verifying MaxSonar data..") 
      //some sanity check 

    if self.readCount >= self.waitMaxSec: 
     self.logger.debug('Unable to read from MaxSonar...') 
     self.close() 
     return False 
    else: 
     self.logger.debug('MaxSonar data is streaming...') 

    return True 

    def isOpen(self): 
    self.logger.debug('Open? ' + str(self.ser.isOpen())) 
    return self.ser.isOpen() 

    def open(self): 
    self.ser.open() 

    def stopDataAquisition(self): 
    self.logger.debug('Falsifying keepAlive') 
    self.keepAlive = False 

    def close(self): 
    self.logger.debug('closing') 
    self.stopDataAquisition() 
    self.ser.close() 

    def write(self, msg): 
    self.ser.write(msg) 

    def readline(self): 
    return self.ser.readline() 


app = Flask(__name__, 
      static_folder="public", 
      template_folder="templates") 

port = "/dev/tty.usbserial-A6004amR" 
dataQ = Queue.Queue() 
errQ = Queue.Queue() 
ser = maxSonarSerialThread(dataQ, errQ, port=port, ver=self.hwVersion) 
ser.daemon = True 
ser.start() 

@app.route("/") 
def index(): 
    return render_template('index.html') 

@app.route("/distance") 
def distance(): 
    distance = read_distance_from_serial() 
    return json.dumps({'distance': distance}) 

def read_distance_from_serial(): 
    a = dataQ.get() 
    print str(a) 
    return a 

Вам нужно добавить метод, чтобы присоединиться нить для изящного выхода, но это должно получить ты

+0

не будет фоновую нить лицо такой же, проблема потенциально не синхронизирована с тем, когда arduino пишет в серийный номер? – doremi

+0

Фоновая нить постоянно читается из сокета, а это значит, что вы получите все, что Arduino вкладывает, а не что-либо в каком-либо буфере в момент его опроса. Вместо этого вы проверяете Очередь действительных значений. Если очередь Queue пуста, когда вы запрашиваете значение, то вы опережаете Arduino. Или вы знаете, что у него были проблемы. Вы также можете войти в серийный поток, чтобы узнать, что происходит. Независимо от того, ваш сервер будет иметь доступ к всем данным Arduino между запросами для следующего, а не произвольным портом. – wwwslinger

+0

И если вы хотите контролировать некоторую частоту дискретизации, просто добавьте счет на основе собственной частоты дискретизации и добавьте только к dataQ каждые 1000 читает, например. Вы также можете прочитать в конец dataQ для вывода, чтобы его очистить и таким образом контролировать выборку. У вас просто гораздо больше контроля, если вы не читаете сериал по требованию. – wwwslinger

Смежные вопросы