简体   繁体   English

使用Python从Arduino读取串行数据

[英]Reading Serial Data from Arduino with Python

I'm working on a little project using the MaxSonar EZ1 ultrasonic range sensor and Arduino Diecimila. 我正在使用MaxSonar EZ1超声波测距传感器和Arduino Diecimila做一个小项目。

Using the MaxSonar playground code , I have Arduino writing the number of inches to serial every .5 seconds, along with a delimiter. 使用MaxSonar操场代码 ,我让Arduino每隔0.5秒写一个串行的英寸数以及一个定界符。 When monitoring the serial data, the output looks similar to: 监视串行数据时,输出看起来类似于:

5.13.15.12.123.39.345...

On the Python side, I have a basic Flask app with a /distance route that returns a JSON object with the serial value: 在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()

And index.html is a basic site with some JS that polls /distance every half second for a new reading. index.html是一个带有一些JS的基本站点,它每半秒轮询/ distance一次以获取新的读数。 With the value, I should be able to build an interesting UI that changes based on how close/far I am from the sonar. 有了这个值,我应该能够构建一个有趣的UI,该UI会根据我离声纳的距离/远近而变化。

$(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();
});

The Question 问题

The issue I'm running into is that there is no guarantee that when python reads from serial, that there will be a value. 我遇到的问题是,不能保证当python从串行读取时,会有一个值。 Often times, when it polls, I get either an empty value or a partial value, while other times it is spot on. 很多时候,当它轮询时,我要么得到一个空值,要么得到一个部分值,而其他时候却发现它。

How can I change my technique such that I am able to consistently poll the serial data and receive the last good reading from the Arduino serial output? 如何更改我的技术,以便能够一致地轮询串行数据并从Arduino串行输出接收最后的良好读数?

You want to set your serial reading to happen in the background and not on demand. 您希望将串行读取设置为在后台进行,而不是按需进行。 You can use threading and Queue . 您可以使用threadingQueue You add your serial values to the Queue once you determine you have a valid value, and then your socket call simply pulls from the Queue. 一旦确定了有效值,便可以将序列值添加到Queue中,然后您的套接字调用就从Queue中拉出。 It'll be something like this: 会是这样的:

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

You'll need to add a method to join the thread for a graceful exit, but that should get you going 您需要添加一种方法来加入线程以正常退出,但这应该可以助您一臂之力

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM