用Python从Arduino读取串行数据

时间:2013-09-11 23:28:11

标签: javascript python serialization serial-port arduino

我正在使用MaxSonar EZ1 ultrasonic range sensor和Arduino Diecimila开展一个小项目。

使用MaxSonar playground code,我让Arduino每隔0.5秒写一个英寸的串行数,以及一个分隔符。监视串行数据时,输出类似于:

5.13.15.12.123.39.345...

在Python方面,我有一个带有/ distance路径的基本Flask应用程序,它返回一个带有序列值的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();
});

问题

我遇到的问题是,当python从serial读取时,无法保证会有值。通常情况下,当它进行轮询时,我会得到一个空值或一个部分值,而有时它会被点亮。

如何更改我的技术,以便我能够一致地轮询串行数据并从Arduino串行输出接收最后一个好的读数?

1 个答案:

答案 0 :(得分:1)

您希望将序列读数设置为在后台进行,而不是按需进行。您可以使用threadingQueue。一旦确定具有有效值,就将序列值添加到队列中,然后套接字调用只是从队列中提取。它会是这样的:

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

你需要添加一个方法来加入线程才能正常退出,但这应该让你前进