【问题标题】:Threading with python, cherrypy and pyserial使用 python、cherrypy 和 pyserial 进行线程化
【发布时间】:2013-06-16 00:25:02
【问题描述】:

因此,我使用cherrypy 和pyserial 编写了一个Web 界面来与Arduino Uno 交互。它非常完整,我唯一缺少的,并且我一直试图弄清楚一天的事情是不断读取 Arduino 发送的数据,并自动显示一个包含 html 内消息的 div代码。我可以在控制台中显示它,但我无法返回实际的 html。其实用return是不行的,只能用print,不方便,因为我要的是html页面中的数据,而不是console。 我尝试了很多方法来做到这一点。

这是我的代码,非常简单。常量函数不断读取从 Arduino 发送的数据,并将其发送到控制台。我希望它像实时更新一样将其发送到 html 代码。我该怎么做?

# -*- coding: Utf-8 -*-

import cherrypy, arduino, time, threading

ser=arduino.Serial('COM4', 9600)

def constant():
    while True:
        m=''
        print('running')
    while True:
        print('sub_running')
        byte=(ser.read().encode('Utf-8'))
        if byte=='*':
            break
        m=m+byte
        time.sleep(1)
    print(m)
    time.sleep(1)

class website(object):
    def index(self):
        return '''
            <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script><script src="annexes/functions.js"></script>
            <link rel=stylesheet type=text/css media=screen href="/annexes/design.css">
            <form action="command" method="POST">
            <input type="submit" name="command" value="Turn the LED on" text="hey"/>
            <input type="submit" name="command" value="Turn the LED off"/>
            </form>
        '''    
    index.exposed=True

    def command(self, command):
        m=''
        if command=='Turn the LED on':
            ser.write('1')
        if command=='Turn the LED off':
            ser.write('0')
        self.index
    command.exposed=True

_thread = threading.Thread(target=constant)
_thread.setDaemon(True)
_thread.start()

cherrypy.quickstart(website(), config='config.conf')

【问题讨论】:

    标签: python arduino cherrypy pyserial python-multithreading


    【解决方案1】:

    要让它做你想做的事,你有两个选择:

    • 使生成的网页在每次显示时打印动态数据,并在客户端添加元刷新(或javascript刷新)或
    • 创建一个长轮询路由,每次有新数据进入时都会更新(请参阅此example

    但我认为您问题的核心与如何将线程函数中的值发送到您的网站类有关。您需要使用Queue.Queue: 该示例显示了如何使用cherrypy 发出长轮询请求。

    在下面的示例中,您将错过 this site 上可用的 jsonyield 装饰器,这是了解cherrypy 和长轮询的好读物。

    # -*- coding: utf-8 -*-
    
    import cherrypy, arduino, time, threading
    import Queue
    
    def serialWatcher(ser, queue):
        while True:
            m=''
            print('running')
        while True:
            print('sub_running')
            byte=(ser.read().encode('Utf-8'))
            if byte=='*':
                break
            m=m+byte
            time.sleep(1)
        queue.put(m)
        time.sleep(1)
    
    class website(object):
        def __init__(self, ser, queue):
            self.serial = ser
            self.queue = queue
    
        # it's often better to use decorators when you can
        @cherrypy.expose
        def index(self):
            return '''
                <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script><script src="annexes/functions.js"></script>
                <link rel=stylesheet type=text/css media=screen href="/annexes/design.css">
                <form action="command" method="POST">
                <input type="submit" name="command" value="Turn the LED on" text="hey"/>
                <input type="submit" name="command" value="Turn the LED off"/>
                </form>
            '''
    
        @cherrypy.expose
        def command(self, command):
            m=''
            if command=='Turn the LED on':
                self.serial.write('1')
            if command=='Turn the LED off':
                self.serial.write('0')
            return self.index() # I assume that's what you wanted to write
    
        @cherrypy.expose
        @json_yield
        def get_arduino_results(self):
            """you can get the result of this long polling request using a JS AJAX query"""
            while True:
                while not self.queue.empty():
                    yield self.queue.get()
    
    
    # always use the following, so you can also use the module you're writing as imports
    # in other modules
    if __name__ == "__main__":
        q = Queue.Queue()
        # Never use global variables, always pass them as parameters
        ser = arduino.Serial('COM4', 9600) 
    
        _thread = threading.Thread(target=serialWatcher, args=(ser, q))
        _thread.setDaemon(True)
        _thread.start()
    
        cherrypy.quickstart(website(ser, q), config='config.conf')
    
    but the same principle would work for a standard page, something like:
    
        @cherrypy.expose
        get_arduino_results(self):
            out = "<ul>"
            while not self.queue.empty():
                out += "<li>" + self.queue.get() + "</li>"
            return out + "</ul>
    

    使用Queue 的整个想法是,在您的arduino 观看线程和cherrypy 线程之间交换信息时,您不会冒线程问题的风险。队列是线程安全的,并且同步读/写,因此它们是有序发生的,并且一次一个线程。

    HTH

    【讨论】:

    • 嘿,非常感谢您的出色回答,但我似乎无法弄清楚如何“安装”我刚刚下载的 jsonyield 装饰器。怎么样?
    • 你可以把你下载的代码和你的代码放在一个名为jsonyield.py的文件中,你可以在代码的开头写from jsonyield import jsonyield。另一个(丑陋的)解决方案是将整个 jsonyield 函数复制粘贴到源文件的顶部(但不要忘记它也需要导入!)
    • 非常感谢,所以您的解决方案应该实时显示 arduino 发送的内容,对吗?当我点击一个按钮时,什么都没有发生。我的 arduino 正在循环同样的事情:每 2 秒,它会发送一条不同的消息。所以脚本应该实时更新网页不是吗?
    • 到目前为止,我只是在尝试分析和理解您的代码以创建一个 Web 应用程序,该应用程序将在每次从 arduino 获取数据时更新页面。然后我会尝试将它实现到这个模块。使用 arduino 数据实时更新网页的最低代码是多少?
    • 第一个解决方案(使用 longpolling 和 jsonyield)将在您每次调用 get_arduino_results 方法时返回每个更新。因此,在您的 javascript 代码中,如果您想拥有自加载页面以来的所有事件,您必须将这些事件存储在一个数组中,并使用您的 AJAX 调用更新该数组。我建议的另一种方法将仅返回自上次调用以来已排队的所有事件。因此,如果您想始终以这种方式显示所有事件,您应该找到另一种结构。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多