[Python] [Windows] Serielle Kommunikation in Python über DLL

Wenn es um das Einbetten geht, wird es häufig durch serielle Kommunikation getestet. Wenn Sie Ihr eigenes IoT-Gerät herstellen, gibt es keine Shell, sodass ein einfacher serieller Monitor ins Spiel kommt.

Wäre in diesem Fall der Standard TeraTerm? Makros sind ebenfalls erheblich.

Wenn ich jedoch das serielle Ergebnis verarbeiten oder das Ergebnis in CSV konvertieren möchte, möchte ich Python oder etwas anderes verwenden (in meinem Fall ist es nur eine Frage der Vertrautheit, nicht welches besser ist). Ich denke, dass die Verwendung von pyserial wahrscheinlich der Königsweg ist.

Aber,

Es fühlt sich seltsam an, in Java gefangen zu sein (was?). In einem anderen Fall habe ich mit Python und C gespielt, also habe ich diesmal versucht, die serielle Kommunikation durch Anwenden zu realisieren. Erstellen Sie den seriellen Kommunikationsteil mit DLL und greifen Sie über ctypes darauf zu. Dies ist ausreichend für einen langsamen Nachrichtenaustausch, z. B. integriert.

Ich hoffe, es wird hilfreich sein, wie man Windows-spezifische Steuerelemente von Python verwendet.

Umwelt, Einschränkungen usw.

Überprüfen Sie unten. Da es sich um einen Prototyp handelt, möchte ich Ihnen vergeben.

Sehr einfach über ctypes

Das Tutorial ist unten http://starship.python.net/crew/theller/ctypes/tutorial.html

Ich denke, dass DLLs mit Visual C ++ auf normale Weise codiert werden können. Auf der anderen Seite benötigt die Python-Seite etwas Einfallsreichtum, und es ist notwendig, Funktionen mithilfe von ctypes zu definieren. Ich denke jedoch, dass es schneller ist, sich das obige Tutorial und den tatsächlichen Code anzusehen, sodass ich den Code sofort verfügbar machen werde.

DLL-Seite

Unten ist der Code auf der DLL-Seite.

#include "stdafx.h"
#include "stdio.h"
#include "windows.h"

#define DLL_API __declspec(dllexport)
#define WCHAR_PORT_NUMBER_BUF_LENGTH (16 * 2)
#define SERIAL_BUFFER_SIZE (1024)

extern "C" DLL_API INT32 __stdcall SERIAL_open(char *port, INT32 baud);
extern "C" DLL_API char * __stdcall SERIAL_read(void *len);
extern "C" DLL_API INT32 __stdcall SERIAL_write(char *text, INT32 len);
extern "C" DLL_API void __stdcall SERIAL_close(void);

static HANDLE SERIAL_handle = NULL;
static char SERIAL_recv[SERIAL_BUFFER_SIZE + 1];

DLL_API INT32 __stdcall SERIAL_open(char *port, INT32 baud)
{
	TCHAR tcPort[WCHAR_PORT_NUMBER_BUF_LENGTH];
	DCB comDcb;
	BOOL success;
	INT32 ret = 0;
	COMMTIMEOUTS comTimeouts;

	memset(tcPort, 0, 32);
	MultiByteToWideChar(CP_OEMCP,
		MB_PRECOMPOSED,
		port,
		strlen(port),
		tcPort,
		WCHAR_PORT_NUMBER_BUF_LENGTH / 2);

	SERIAL_handle = CreateFile(tcPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,
		OPEN_EXISTING, 0, NULL);
	if (SERIAL_handle == INVALID_HANDLE_VALUE) {
		return -1;
	}

	success = SetupComm(SERIAL_handle, SERIAL_BUFFER_SIZE, SERIAL_BUFFER_SIZE);
	if (success == FALSE)
	{
		ret = -2;
		goto error;
	}

	memset(&comDcb, 0, sizeof(DCB));
	comDcb.DCBlength = sizeof(DCB);
	comDcb.BaudRate = baud;
	comDcb.fParity = FALSE;
	comDcb.Parity = NOPARITY;
	comDcb.ByteSize = 8;
	comDcb.StopBits = ONESTOPBIT;
	success = SetCommState(SERIAL_handle, &comDcb);
	if (success == FALSE) {
		ret = -3;
		goto error;
	}

	memset(&comTimeouts, 0, sizeof(COMMTIMEOUTS));
	comTimeouts.ReadIntervalTimeout = 500;
	comTimeouts.ReadTotalTimeoutMultiplier = 0;
	comTimeouts.ReadTotalTimeoutConstant = 500;
	comTimeouts.WriteTotalTimeoutMultiplier = 0;
	comTimeouts.WriteTotalTimeoutConstant = 500;
	success = SetCommTimeouts(SERIAL_handle, &comTimeouts);
	if (success == FALSE)
	{
		ret = -4;
		goto error;
	}

	success = PurgeComm(SERIAL_handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
	if (success == FALSE)
	{
		ret = -5;
		goto error;
	}

	return 0;

error:
	CloseHandle(SERIAL_handle);
	return ret;
}

DLL_API char * __stdcall SERIAL_read(void *len)
{
	BOOL  success;
	DWORD recvLen = 0;
	INT32 *lenToPython = (INT32 *)len;

	success = ReadFile(SERIAL_handle, SERIAL_recv, SERIAL_BUFFER_SIZE, &recvLen, NULL);
	if (success == FALSE || recvLen == 0) {
		*lenToPython = 0;
		return "";
	}

	*lenToPython = (INT32)recvLen;
	SERIAL_recv[recvLen] = '\0';
	return SERIAL_recv;
}

DLL_API INT32 __stdcall SERIAL_write(char *text, INT32 len)
{
	BOOL  success;
	DWORD dummy = 0;

	success = WriteFile(SERIAL_handle, text, strlen(text), &dummy, NULL);
	if (success == TRUE)
	{
		return 0;
	}
	else {
		return -1;
	}
}

DLL_API void __stdcall SERIAL_close(void)
{
	if (SERIAL_handle == NULL) {
		return;
	}
	CloseHandle(SERIAL_handle);
}

Die folgenden vier Funktionen sind vorbereitet. Es ist sehr einfach.

//Hafen offen, Hafen="COMxx"Baud ist bps. Der Rückgabewert ist 0 für Erfolg und negativ für Fehler
INT32 SERIAL_open(char *port, INT32 baud)

//Daten lesen. Beim Aufruf enthält len die Anzahl der gelesenen Bytes und char die empfangenen Daten.
//Mit einer Zeitüberschreitung von 500 ms*len=Es kann 0 sein.
char * SERIAL_read(void *len)

//Daten schreiben. Die Textzeichenfolge ist len Bytes (wie Sie dem Code entnehmen können, beträgt das Maximum 1024 Bytes)
INT32 SERIAL_write(char *text, INT32 len)

//Hafen geschlossen
void SERIAL_close(void)

Erstellen Sie diesen Code als DLL in Visual Studio. In diesem Fall sind folgende Punkte zu beachten.

Der eigentliche Code soll serielle Kommunikation gemäß dem Lehrbuch unter Verwendung der Win32-API sein (es tut mir eher leid, dass es sich um eine verschlechterte Version handelt). .. Wenn Sie mit erstellen, können Sie normal eine DLL erstellen. Ich werde die Python-Seite so schreiben, als würde ich nur diese DLL verwenden.

Die folgenden Websites waren beim Erstellen des Programms sehr hilfreich. http://www.geocities.co.jp/SiliconValley-SanJose/5309/serial.html http://www.ys-labo.com/BCB/2007/070512%20RS232C%20zenpan.html

Python-Seite (serielle Kommunikation mit ctypes)

Es ist die Python-Seite. Der Code der Klasse, die die serielle Kommunikation unter Verwendung der obigen DLL durchführt, wird unten gezeigt. Übrigens ist es der Code, wenn der Dateiname der DLL serial_if.dll lautet.

#!/usr/bin/env python

from ctypes import *

'''
Describe serial DLL's functions.
'''

dll = windll.serial_if
dll.SERIAL_open.argtype = (c_char_p, c_int)
dll.SERIAL_open.restype = c_int
dll.SERIAL_read.argtype = c_void_p
dll.SERIAL_read.restype = c_char_p
dll.SERIAL_write.argtype = (c_char_p, c_int)
dll.SERIAL_write.restype = c_int

'''
Serial Device Driver
'''

class SerialDriver:
    def open(self, port, baud):
        int_ret = dll.SERIAL_open(port, baud);
        if int_ret == 0:
            return True
        else:
            return False

    def read(self):
         read_len = c_int(0)
         text = dll.SERIAL_read(byref(read_len))
         return text, read_len.value

    def write(self, text):
        write_ret = dll.SERIAL_write(text, len(text))
        if write_ret == 0:
            return True
        else:
            return False

    def close(self):
        dll.SERIAL_close();

Schnittstelle mit DLL

Beschreiben der Funktionen der seriellen DLL. In den Kommentar wird die Beschreibung für den Umgang mit der DLL geschrieben.

Dieses Mal haben wir die Beschreibung übernommen, die DLL mit windll verwendet. Legen Sie zu diesem Zeitpunkt zunächst den Namen der zu verwendenden DLL wie folgt fest.

dll = windll.serial_if
# windll.(DLL-Dateiname)Geben Sie die entsprechende DLL mit an. Das obige Beispiel ist seriell_if.Bedeutet dll

Definieren Sie als Nächstes die Argumente und den Rückgabewert jeder von der DLL exportierten Funktion anhand der Beschreibung der ctypes. Ich werde es im Vergleich mit der C-Funktion unten schreiben.


# INT32 SERIAL_open(char *port, INT32 baud);
dll.SERIAL_open.argtype = (c_char_p, c_int)
dll.SERIAL_open.restype = c_int

# char * SERIAL_read(void *len);
dll.SERIAL_read.argtype = c_void_p
dll.SERIAL_read.restype = c_char_p

# INT32 SERIAL_write(char *text, INT32 len);
dll.SERIAL_write.argtype = (c_char_p, c_int)
dll.SERIAL_write.restype = c_int

# void SERIAL_close(void);
#Keine Definition erforderlich, wenn keine Argumente oder Rückgabewerte vorhanden sind

Wenn Sie eine Person sind, die sowohl C als auch Python ausgeführt hat, können Sie die grundlegende Schreibmethode verstehen, indem Sie die oben genannten vergleichen. Tutorial ist hilfreich für den Umgang mit detaillierten Typen.

Danach können Sie die definierte Funktion normal aufrufen. Bitte beachten Sie Folgendes. Seien Sie besonders vorsichtig, wenn Sie mit einem Zeiger einen Wert von C erhalten.

Die obige SerialDriver-Klasse ruft eine DLL-Funktion auf, um eine serielle Kommunikation durchzuführen. Diejenigen, die diese Klasse verwenden, sind so implementiert, dass sie die c-Typen nicht kennen müssen.

In Bezug auf den Wert war die folgende Site des Stapelüberlaufs hilfreich. http://stackoverflow.com/questions/2330587/how-to-convert-ctypes-c-long-to-pythons-int

Beispiel einer Python-seitigen Kommunikations-App

Das folgende Programm verwendet die SerialDriver-Klasse, um die eigentliche serielle Kommunikation durchzuführen und die seriell empfangenen Daten zu protokollieren. Dies basiert übrigens auf der Annahme, dass die SerialDriver-Klasse mit dem Dateinamen serial_lib.py geschrieben wurde (wie Sie anhand des Imports im folgenden Code sehen können).

#!/usr/bin/env python

from serial_lib import SerialDriver
from datetime import datetime
import sys
import time

def serial_read(serial, keyword):
    text = ""
    text_len = 0
    while text.find(keyword) < 0:
        read_text, read_len = serial.read()
        if read_len > 0:
            text_len += read_len
            text += read_text
    return text

def serial_test():

    filename = datetime.now().strftime('%Y%m%d%H%M%S') + ".txt"
    f = open(filename, "w")

    serial = SerialDriver()
    ret = serial.open("COM3", 115200)
    print "python:SERIAL_open=" , ret
    if ret == False:
        sys.exit()

    text = serial_read(serial,"teraterm command1")
    print text
    f.write(text)

    text = "python response1\r\n"
    serial.write(text)

    text = serial_read(serial,"teraterm command2")
    print text
    f.write(text)

    text = "python response2\r\n"
    serial.write(text)

    f.close()
    serial.close()

if __name__ == "__main__":

    serial_test()
    print "python: complete"

#EOF

Außerdem habe ich diese Datei als serial_test.py geschrieben. Dies ist die Voraussetzung für die folgende Erklärung.

Wie Sie dem Code entnehmen können, besteht er aus den folgenden Funktionen.

--serial_test Hauptverarbeitung --serial_read Serielle Empfangsverarbeitung

Wie Sie anhand des DLL-Codes sehen können, kann das Empfangsergebnis 0 Byte betragen, da das Zeitlimit in 500 ms abläuft. Daher wird hier der Vorgang des weiteren Lesens ausgeführt, bis ein bestimmtes Schlüsselwort empfangen wurde (der Mechanismus ist also so, dass er nur entfernt werden kann, wenn dieses Schlüsselwort kommt. Für dieses w wurden keine Gegenmaßnahmen ergriffen).

Der Hauptprozess (serial_test-Funktion) wird gemäß dem folgenden Ablauf verarbeitet.

  1. Öffnen Sie die Protokolldatei. Der Dateiname lautet "Time.txt", sodass es sich um eine andere Datei handelt, auch wenn sie mehrmals ausgeführt wird.
  2. Öffnen Sie die serielle Schnittstelle.
  3. Warten Sie, bis Sie die Zeichenfolge "teraterm command1" erhalten.
  4. Schreiben Sie den Inhalt nach dem Empfang in die Protokolldatei.
  5. Senden Sie "Python-Antwort 1".
  6. Warten Sie, bis Sie die Zeichenfolge "teraterm command2" erhalten.
  7. Schreiben Sie den Inhalt nach dem Empfang in die Protokolldatei.
  8. Senden Sie "Python-Antwort 2".
  9. Schließen Sie die Protokolldatei und die serielle Schnittstelle.

Abgesehen davon, wenn es COM10 oder höher ist, ist es oben unbrauchbar und Sie müssen es als \\. \ COM10 "beschreiben (MS-ähnliche Spezifikationen wie unten gezeigt) https://support.microsoft.com/ja-jp/help/115831/howto-specify-serial-ports-larger-than-com9

Verhalten des Kommunikationspartners (TeraTerm-Makro)

Die andere Partei der obigen Funktion serial_test wurde experimentell mit dem folgenden TeraTerm-Makro ausgeführt.

timeout=30
sendln 'teraterm command1'
wait 'python response1'
sendln 'teraterm command2'

Der Inhalt ist wie unten angegeben.

Es fühlt sich an, als würde man miteinander kommunizieren.

Ausführungsmethode usw.

Der Test wurde wie folgt durchgeführt.

Auf diese Weise ist es möglich, eine serielle Kommunikation und Datenanalyse mit Python durchzuführen. Sie können die Funktion mit serial überprüfen und in CSV konvertieren. Es sollte möglich sein, maschinelles Lernen mit seriellen Sensordaten durchzuführen.

Lizenz

Ich habe es unten benutzt. Vielen Dank für die Bereitstellung der wunderbaren Software.

das ist alles.

Recommended Posts

[Python] [Windows] Serielle Kommunikation in Python über DLL
Mausbedienung mit Windows-API in Python
Serielle Kommunikation mit Python
DLL-Injektion in Python
WiringPi-SPI-Kommunikation mit Python
Einführung in die serielle Kommunikation [Python]
Python-Installation in 2 Zeilen @Windows
Übersetzt mit Googletrans in Python
Verwenden des Python-Modus in der Verarbeitung
TWE-Lite App für die serielle Kommunikation Byte-Modus-Einstellung (in Python senden)
Die I2C-Kommunikation erfolgt durch Steuerung der seriellen Kommunikation mit Python (über ein USBGPIO8-Gerät).
Serielle Kommunikationssteuerung mit Python- und SPI-Kommunikation (mit USBGPIO8-Gerät)
[Python] Mit Tkinter mehrere Fenster anzeigen
GUI-Programmierung in Python mit Appjar
Vorsichtsmaßnahmen bei der Verwendung von Pit mit Python
Setzen Sie MeCab in "Windows 10; Python3.5 (64bit)"
Versuchen Sie es mit LevelDB mit Python (plyvel)
Windows 10: Installieren der MeCab-Bibliothek für Python
Verwendung globaler Variablen in Python-Funktionen
Holen Sie sich ein Kommunikationsmemo in Python
Mal sehen, wie man Eingaben in Python verwendet
Gesamtleistung in Python (mit Funktools)
Handschriftliche Zeichenerkennung mit KNN in Python
Versuchen Sie es mit LeapMotion mit Python
Suche nach Tiefenpriorität mit Stack in Python
Bei Verwendung regulärer Ausdrücke in Python
GUI-Erstellung in Python mit tkinter 2
Hinweise zur Verwendung von OpenCV mit Windows 10 Python 3.8.3.
Hinweise zur Verwendung von cChardet und python3-chardet in Python 3.3.1.
Versuchen Sie es mit der Wunderlist-API in Python
GUI-Erstellung in Python mit tkinter Teil 1
Holen Sie sich Suica Balance in Python (mit libpafe)
Übung, dies in Python zu verwenden (schlecht)
Hash-Passwörter langsam mit bcrypt in Python
Versuchen Sie, die Kraken-API mit Python zu verwenden
[50 zählt] Schlüsselübertragung mit Python für Windows
Überprüfen und empfangen Sie die serielle Schnittstelle in Python (Portprüfung)
Socket-Kommunikation über Socket-Server mit Python jetzt
[FX] Hit oanda-API mit Python mit Docker
Tweet mit der Twitter-API in Python
Ich habe versucht, die Bayes'sche Optimierung von Python zu verwenden
[Python] [Windows] Machen Sie eine Bildschirmaufnahme mit Python
Melden Sie sich mit Anforderungen in Python bei Slack an
Holen Sie sich Youtube-Daten in Python mithilfe der Youtube-Daten-API
Erstellen von Scicit-Learn in einer Windows 10-Umgebung mit Pycharm
Verwenden physikalischer Konstanten in Python scipy.constants ~ Konstante e ~
Scraping von Websites mit JavaScript in Python
Entwicklung eines Slack Bot mit Python mit chat.postMessage
Schreiben Sie mit f2py ein Python-Modul in fortran
Zeichnen Sie mit graphviz eine Baumstruktur in Python 3
Hinweise zur Verwendung von Python (Pydev) mit Eclipse
Krankheitsklassifizierung durch Random Forest mit Python
Laden Sie Dateien in jedem Format mit Python herunter
Parallele Taskausführung mit concurrent.futures in Python
Verstümmelte Python-Zeichen in der Windows + Git Bash-Umgebung
Python in der Optimierung
CURL in Python
Metaprogrammierung mit Python