PyQt 開發採用串口通訊的上位機軟件(2)

本博客的全部原創文章採用創做公用版協議。要求署名、非商業用途和保持一致。要求署名必須包含個人網名(geokai)以及文章來源(選擇博客首地址或者具體博文地址)。html

商業性使用須預先徵得本人贊成(發送Email到 geokai@126.com).vue

 

 

3.2 串口的操做之Pyserialpython

  Python有本身的專用Serial的庫,沒有仔細閱讀qt的代碼不太清楚pyqt的串口庫是否繼承這個庫。git

  說說爲何又忽然轉到了這個庫呢,由於上位機與下位機計劃採用ModBus做爲通訊協議。本覺得PyQt是徹底繼承C++ Qt的所有類,可是不行的是並不這樣。全部的QModbus開頭的類在pyqt文檔中都沒有找到。因此徹底以PyQt作所有上位機功能看來不太容易實現。搜索了一番發現Python有一個Modbus_tk這個庫,封裝了Modbus的相關功能,包括rtu,ascii協議模式,master和slave節點模式。而且這個庫直接繼承了Pyserial庫。OK既然這樣那串口的操做直接使用pyserial庫會更簡單和高效。api

  Pyserial的官方文檔地址https://pythonhosted.org/pyserial/框架

  3.2.1 列出可用串口dom

    import serial.tools.list_ports #相應的函數在這個包下面,直接import serial是沒法使用這個工具包的函數

    com_list = serial.tools.list_ports.comports() # 返回一個serial.tools.list_ports.ListPortInfo的列表對象,每一個元素均含有串口的多個屬性工具

    for port in com_list:oop

      print(port.device) #返回端口號如COM3

      print(port.describe) #返回設備名字

      print(port.location) #返回設備在計算機上的位置

    

    得到這些比較重要的信息後就能夠打開端口了

  3.2.2 打開串口

    import serial

    ser = serial.Serial(com_list[0].device, baudrate=19200, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False) #採用直接給參數的方法配置建立一個Serial實例對象,執行完後會直接打開端口

    

    ser = serial.Serial()

    ser.baudrate=19200

    ser.port=com_list[0].device

    ser.open() #這種方式能夠先配置後打開

    ser.close()

  

  3.2.3串口的異常狀態判斷

    以前採用QSerialPort()類時,有一個error()方法能夠判斷串口的狀態。可是PySerial彷佛沒有這麼完善的功能。我能力不足沒辦法把這個功能實現,這裏介紹一個本身想的能有基本功能的方法。

    串口正常打開後,哪怕拔掉設備,使用ser.is_open判斷串口狀態始終是True。可是經過獲取串口信息則獲得一個空的對象。基於這樣的想法從新寫一下打開串口並經過串口發送數據的代碼。

    

    ser =serial.Serial()
    ser.baudrate=19200
    ser.port='COM3'
    ser.timeout=1

    try:
      ser.open()
      print('Opened')
    except serial.SerialException:
      print('error')
 
    time.sleep(5) #暫停一下,拔掉串口設備
 
    if ser.is_open and len(list(lp.grep(ser.port))) !=0: #
      print('able to send')
      
      for i in range(3):
        try:
          ser.write(b'aaaaaaaaaaaaaaaaaaaa')
          print('send ok')
          break
        except serial.SerialException:
          print('re-try')
          ser.close()
          ser.open()
        if i==2:
          ser.close()
          print('port error')
    else:
      ser.close()
      print('port error')

    

 

4.通訊部分

  這一部分主要實現上位機軟件與下位機的通訊實現。

  4.1上位機modbus master

    上位機的modbus實現採用python現有的modbus_tk庫來實現,貼上基本代碼

import modbus_tk
import modbus_tk.defines as cst
import serial
from modbus_tk import modbus_rtu
import time 

PORT = 'COM3'

def main(master):

  try:
    #Connect to the slave
     master.set_timeout(1)
    master.set_verbose(True)
    x = master.execute(1, cst.READ_INPUT_REGISTERS, 4,1)  #讀取下位機的寄存器值,地址爲4,,長度爲1,採用cst.READ_INPUT_REGISTERS  與   cst.READ_HOLDING_REGISTERS方法效果是一致的
    if len(x)>0:
      print(x[0]) #此處是獲取的值,注意符號,本例中下位機也使用16位無符號數

    
  except :
    print('unknown' error)

if __name__ == "__main__":
  master = modbus_rtu.RtuMaster(
    serial.Serial(port=PORT, baudrate=19200, bytesize=8, parity='N', stopbits=1, xonxoff=0))
  while True:
    main(master)
    tme.sleep(1)
 

  4.2下位機modbus部分基於Arduino

    採用arduino的modbus庫,其中設置一個地址爲4的寄存器。

    

/**
* Modbus slave example 2:
* The purpose of this example is to link the Arduino digital and analog
* pins to an external device.
*
* Recommended Modbus Master: QModbus
* http://qmodbus.sourceforge.net/
*
* Editado al español por LuxARTS
*/

//Incluye la librería del protocolo Modbus
#include <ModbusRtu.h>
#include <stdlib.h>
#define ID 1

//Crear instancia
Modbus slave(ID, 0, 0); //ID del nodo. 0 para el master, 1-247 para esclavo
//Puerto serie (0 = TX: 1 - RX: 0)
//Protocolo serie. 0 para RS-232 + USB (default), cualquier pin mayor a 1 para RS-485
boolean led;
int8_t state = 0;
unsigned long tempus;

uint16_t au16data[9]; //La tabla de registros que se desea compartir por la red

/*********************************************************
Configuración del programa
*********************************************************/
void setup() {
io_setup(); //configura las entradas y salidas

slave.begin(19200); //Abre la comunicación como esclavo
tempus = millis() + 100; //Guarda el tiempo actual + 100ms
digitalWrite(13, HIGH ); //Prende el led del pin 13 (el de la placa)
au16data[4] = 0x8000;
}

/*********************************************************
Inicio del programa
*********************************************************/
void loop() {
//Comprueba el buffer de entrada
state = slave.poll( au16data, 9 ); //Parámetros: Tabla de registros para el intercambio de info
// Tamaño de la tabla de registros
//Devuelve 0 si no hay pedido de datos
//Devuelve 1 al 4 si hubo error de comunicación
//Devuelve mas de 4 si se procesó correctamente el pedido

if (state > 4) { //Si es mayor a 4 = el pedido fué correcto
tempus = millis() + 50; //Tiempo actual + 50ms
digitalWrite(13, HIGH);//Prende el led
}
if (millis() > tempus) digitalWrite(13, LOW );//Apaga el led 50ms después

//Actualiza los pines de Arduino con la tabla de Modbus
io_poll();

}

/**
* pin maping:
* 2 - digital input
* 3 - digital input
* 4 - digital input
* 5 - digital input
* 6 - digital output
* 7 - digital output
* 8 - digital output
* 9 - digital output
* 10 - analog output
* 11 - analog output
* 14 - analog input
* 15 - analog input
*
* pin 13 reservado para ver el estado de la comunicación
*/
void io_setup() {
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(13, OUTPUT);

digitalWrite(6, LOW );
digitalWrite(7, LOW );
digitalWrite(8, LOW );
digitalWrite(9, LOW );
digitalWrite(13, HIGH ); //Led del pin 13 de la placa
analogWrite(10, 0 ); //PWM 0%
analogWrite(11, 0 ); //PWM 0%
}


/*********************************************************
Enlaza la tabla de registros con los pines
*********************************************************/
void io_poll() {
long temp;
// digital inputs -> au16data[0]
// Lee las entradas digitales y las guarda en bits de la primera variable del vector
// (es lo mismo que hacer una máscara)
bitWrite( au16data[0], 0, digitalRead( 2 )); //Lee el pin 2 de Arduino y lo guarda en el bit 0 de la variable au16data[0]
bitWrite( au16data[0], 1, digitalRead( 3 ));
bitWrite( au16data[0], 2, digitalRead( 4 ));
bitWrite( au16data[0], 3, digitalRead( 5 ));

// digital outputs -> au16data[1]
// Lee los bits de la segunda variable y los pone en las salidas digitales
digitalWrite( 6, bitRead( au16data[1], 0 )); //Lee el bit 0 de la variable au16data[1] y lo pone en el pin 6 de Arduino
digitalWrite( 7, bitRead( au16data[1], 1 ));
digitalWrite( 8, bitRead( au16data[1], 2 ));
digitalWrite( 9, bitRead( au16data[1], 3 ));

// Cambia el valor del PWM
analogWrite( 10, au16data[2] ); //El valor de au16data[2] se escribe en la salida de PWM del pin 10 de Arduino. (siendo 0=0% y 255=100%)
analogWrite( 11, au16data[3] );


temp=au16data[4];
// Serial.write(temp>>16);
// Serial.write(temp>>8);
// Serial.write(temp);

// Lee las entradas analógicas (ADC)

temp=(long)temp+(long)(random(-3,4));
//temp=-1;
// Serial.write(temp>>24);
// Serial.write(temp>>16);
// Serial.write(temp>>8);
// Serial.write(temp);


temp=temp<0?0:temp;
temp=temp>65535?65535:temp;


//temp=temp<0?0:temp;
//temp=temp>65535?65535:temp;
au16data[4] = temp;
//au16data[4] = byte(random(65530,65535));//analogRead( 0 ); //El valor anaalógico leido en el pin A0 se guarda en au16data[4]. (siendo 0=0v y 1023=5v)
au16data[5] = 0xffff;

// Diagnóstico de la comunicación (para debug)
au16data[6] = slave.getInCnt(); //Devuelve cuantos mensajes se recibieron
au16data[7] = slave.getOutCnt(); //Devuelve cuantos mensajes se transmitieron
au16data[8] = slave.getErrCnt(); //Devuelve cuantos errores hubieron
}

   

    

#至此基本的框架已經搭出來了,上位機與下位機的通信也打通了。硬件上的問題已經基本解決

後面開始專一的GUI的編寫。

  

      

做者:geokai

出處: https://www.cnblogs.com/geokai/

本文版權歸做者geokai和博客園全部,歡迎轉載和商用,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利.

若有問題, 可郵件(geokai@126.com)諮詢.

相關文章
相關標籤/搜索