[记录]python使用serial模块实现实时WebConsole

Posted wsjhk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[记录]python使用serial模块实现实时WebConsole相关的知识,希望对你有一定的参考价值。

###tornado+websocket+多进程实现:

1.index.html

<!DOCTYPE HTML>
<html>
  <head>
    <style>
      body { margin: 0px; padding: 20px; }
      #received { width: 500px; height: 400px; border: 1px solid #dedede; overflow-y:scroll;}
      #sent { width: 500px; } 
    </style>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script type="text/javascript" src="static/main.js"></script>
  </head>
  <body>
  <h1>Websockets serial console</h1>

  <p>Data received from serial port</p>
  <div id="received">
  </div>
  <button id="clear">Clear</button>
  
  <p>Send data to serial port</p>
  <form id="sent">
  	<input type="text" id="cmd_value">
  	<button id="cmd_send">Send</button>
  </form>
  </body>
</html>

2.main.js

$(document).ready(function(){

var received = $(\'#received\');


var socket = new WebSocket("ws://localhost:8080/ws");
 
socket.onopen = function(){  
  console.log("connected"); 
}; 

socket.onmessage = function (message) {
  console.log("receiving: " + message.data);
  received.append(message.data);
  received.append($(\'<br/>\'));
};

socket.onclose = function(){
  console.log("disconnected"); 
};

var sendMessage = function(message) {
  console.log("sending:" + message.data);
  socket.send(message.data);
};


// GUI Stuff


// send a command to the serial port
$("#cmd_send").click(function(ev){
  ev.preventDefault();
  var cmd = $(\'#cmd_value\').val();
  sendMessage({ \'data\' : cmd});
  $(\'#cmd_value\').val("");
});

$(\'#clear\').click(function(){
  received.empty();
});


});

3.serialworker.py

import serial
import time
import multiprocessing

## Change this to match your local settings
SERIAL_PORT = \'/dev/ttyACM0\'
SERIAL_BAUDRATE = 115200

class SerialProcess(multiprocessing.Process):
 
    def __init__(self, input_queue, output_queue):
        multiprocessing.Process.__init__(self)
        self.input_queue = input_queue
        self.output_queue = output_queue
        self.sp = serial.Serial(SERIAL_PORT, SERIAL_BAUDRATE, timeout=1)
 
    def close(self):
        self.sp.close()
 
    def writeSerial(self, data):
        self.sp.write(data)
        # time.sleep(1)
        
    def readSerial(self):
        return self.sp.readline().replace("\\n", "")
 
    def run(self):
 
    	self.sp.flushInput()
 
        while True:
            # look for incoming tornado request
            if not self.input_queue.empty():
                data = self.input_queue.get()
 
                # send it to the serial device
                self.writeSerial(data)
                print "writing to serial: " + data
 
            # look for incoming serial data
            if (self.sp.inWaiting() > 0):
            	data = self.readSerial()
                print "reading from serial: " + data
                # send it back to tornado
            	self.output_queue.put(data)

4.server.py

import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.gen
from tornado.options import define, options
import os
import time
import multiprocessing
import serialworker
import json
 
define("port", default=8080, help="run on the given port", type=int)
 
clients = [] 

input_queue = multiprocessing.Queue()
output_queue = multiprocessing.Queue()



class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(\'index.html\')

class StaticFileHandler(tornado.web.RequestHandler):
	def get(self):
		self.render(\'main.js\')
 
class WebSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        print \'new connection\'
        clients.append(self)
        self.write_message("connected")
 
    def on_message(self, message):
        print \'tornado received from client: %s\' % json.dumps(message)
        #self.write_message(\'ack\')
        input_queue.put(message)
 
    def on_close(self):
        print \'connection closed\'
        clients.remove(self)


## check the queue for pending messages, and rely that to all connected clients
def checkQueue():
	if not output_queue.empty():
		message = output_queue.get()
		for c in clients:
			c.write_message(message)


if __name__ == \'__main__\':
	## start the serial worker in background (as a deamon)
	sp = serialworker.SerialProcess(input_queue, output_queue)
	sp.daemon = True
	sp.start()
	tornado.options.parse_command_line()
	app = tornado.web.Application(
	    handlers=[
	        (r"/", IndexHandler),
	        (r"/static/(.*)", tornado.web.StaticFileHandler, {\'path\':  \'./\'}),
	        (r"/ws", WebSocketHandler)
	    ]
	)
	httpServer = tornado.httpserver.HTTPServer(app)
	httpServer.listen(options.port)
	print "Listening on port:", options.port

	mainLoop = tornado.ioloop.IOLoop.instance()
	## adjust the scheduler_interval according to the frames sent by the serial port
	scheduler_interval = 100
	scheduler = tornado.ioloop.PeriodicCallback(checkQueue, scheduler_interval, io_loop = mainLoop)
	scheduler.start()
	mainLoop.start()

5.扩展:每个页面用户登录时生成一个唯一的key,用户绑定该用户该会话的操作,在进行操作时,把这个key也传给server端的input队列,然后在serialworker的run循环中对data数据进行处理,在self.output_queue.put(data)操作是又带上这个key,然后返回,server中checkQueue方法执行是只发给这个key的会话,然后前端显示结果。这样来实现每个会话信息互不干扰。如果不用key做标记,那么所有会话操作的信息,所有会话之间都能看到。同时,serial是否可以和os.openpty()结合,实现serial操作新建的伪终端来实现shell终端操作模式。linux的pty创建是成对出现的,一个主,一个从,执行命令的时一个写,另一个读。一般是配合subprocess来指定STDOUT=slave,这样执行命令之后把结果写到slave,然后从master读取后再前端显示。pty创建简单代码如下:

 

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
import os,sys,time
import serial
 
# 主 pty, 从 tty
master, slave = os.openpty()
 
print master
print slave
 
# 显示终端名
slave_name = os.ttyname(slave)
print master
print slave_name
 
ser = serial.Serial(slave_name, rtscts=True, dsrdtr=True)
print "Open port successful! the port information:"
print ser
print "\\n"
 
while ser.isOpen(): #the return is True or Faulse
    print "please write the msg(exit to break)"
    msg = raw_input(">") #add a break reason:::kill the child thread
    if msg == \'exit\':
        print "\\n"
        print "Please waiting to close the connection......"
        time.sleep(1)
        break;
    msg = msg + \'\\r\' + \'\\n\' #AT form define #data=ser.write(msg)
    os.write(master, msg)
    sys.stdout.flush() #flush the buffer
    print "\\n"
    print ("waiting to recv the data...")
    time.sleep(2)
    msg_recv = os.read(slave, 128)
    print ("\\n")
    print ("\\tOK! The recv msg is %s"%(msg_recv))

  

  

创建伪终端通信简图:

 

以上是关于[记录]python使用serial模块实现实时WebConsole的主要内容,如果未能解决你的问题,请参考以下文章

[ Python入门教程 ] Python中日志记录模块logging使用实例

python serial模块使用,是pyserial而非serial

NRF51822 如何使用RTT 实时终端调试(翻译教程)

如何从 Python 日志记录模块获得非阻塞/实时行为? (输出到 PyQt QTextBrowser)

python re模块记录

一文教会你用Python实现最有效的剪切板实时监控