[Python] [Windows] Communication série en Python à l'aide de DLL

Lorsqu'il s'agit d'incorporer, il est souvent débogué par communication série. Si vous créez votre propre appareil IoT, il n'y a pas de shell, donc un simple moniteur série entre en jeu.

Dans ce cas, la norme serait TeraTerm? Les macros sont également importantes.

Cependant, quand je veux traiter le résultat en série ou convertir le résultat en csv, je veux utiliser Python ou quelque chose (dans mon cas, c'est juste une question de familiarité, pas lequel est le meilleur). Je pense que l'utilisation de pyserial est probablement la voie royale.

Mais,

C'est étrange d'être pris en Java (quoi?). Dans un autre cas, je jouais avec python et le langage C, donc j'ai essayé de réaliser cette fois une communication série en l'appliquant. Créez la partie de communication série avec DLL et accédez-y à partir de ctypes. Cela suffit pour un échange de messages lent tel que intégré.

J'espère qu'il vous sera utile de savoir comment utiliser les contrôles spécifiques à Windows de Python.

Environnement, restrictions, etc.

Vérifiez ci-dessous. Puisqu'il s'agit d'un prototype, je voudrais vous pardonner.

--Confirmé sous Windows10 (64 bits) et Windows7 (64 bits). --Python est la série 2.7 32 bits, et DLL est également construit et utilisé avec x86.

Très facile sur les ctypes

Le tutoriel est ci-dessous http://starship.python.net/crew/theller/ctypes/tutorial.html

Je pense que les DLL utilisant Visual C ++ peuvent être codées de manière normale. D'un autre côté, le côté python a besoin d'une certaine ingéniosité, et il est nécessaire de définir des fonctions à l'aide de ctypes. Cependant, je pense qu'il est plus rapide de regarder le didacticiel ci-dessus et le code réel, je vais donc exposer le code immédiatement.

Côté DLL

Vous trouverez ci-dessous le code côté DLL.

#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);
}

Les quatre fonctions suivantes sont préparées. C'est extrêmement simple.

//Port ouvert, port="COMxx", Baud est bps. La valeur de retour est 0 pour le succès et négative pour l'échec
INT32 SERIAL_open(char *port, INT32 baud)

//Lisez les données. Lorsqu'il est appelé, len contient le nombre d'octets lus et char contient les données reçues.
//Avec un timeout de 500 msec*len=Cela peut être 0.
char * SERIAL_read(void *len)

//Écriture de données. La chaîne de texte est de len octets (comme vous pouvez le voir dans le code, le maximum est de 1024 octets)
INT32 SERIAL_write(char *text, INT32 len)

//Port fermé
void SERIAL_close(void)

Générez ce code en tant que DLL dans Visual Studio. Les points à noter dans ce cas sont les suivants.

Le code réel est censé être une communication série écrite selon le manuel utilisant l'API Win32 (plutôt, je suis désolé, c'est une version dégradée). .. Si vous construisez avec, vous pouvez créer une dll normalement. J'écrirai le côté Python comme si j'utilisais uniquement cette DLL.

Les sites suivants ont été très utiles lors de la création du programme. http://www.geocities.co.jp/SiliconValley-SanJose/5309/serial.html http://www.ys-labo.com/BCB/2007/070512%20RS232C%20zenpan.html

Côté Python (communication série à l'aide de ctypes)

C'est du côté Python. Le code de la classe qui effectue la communication série à l'aide de la DLL ci-dessus est indiqué ci-dessous. Au fait, c'est le code lorsque le nom de fichier de la DLL est serial_if.dll.

#!/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();

Interface avec DLL

Décrivez les fonctions de la DLL série. Le commentaire contient la description de la gestion de la DLL.

Cette fois, nous avons adopté la description qui utilise DLL en utilisant windll. À ce moment, définissez d'abord le nom de la DLL à utiliser comme suit.

dll = windll.serial_if
# windll.(Nom du fichier DLL)Spécifiez la DLL correspondante avec. L'exemple ci-dessus est en série_if.Signifie dll

Ensuite, définissez les arguments et la valeur de retour de chaque fonction exportée par la DLL à l'aide de la description de ctypes. Je vais l'écrire en comparaison avec la fonction C ci-dessous.


# 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);
#Aucune définition requise s'il n'y a pas d'arguments ou de valeurs de retour

Si vous êtes une personne qui a fait à la fois C et Python, vous pouvez comprendre la méthode d'écriture de base en comparant ce qui précède. Tutoriel sera utile pour traiter les types détaillés.

Après cela, vous pouvez appeler la fonction définie normalement. Veuillez noter ce qui suit. Soyez particulièrement prudent lorsque vous obtenez une valeur de C avec un pointeur.

La classe SerialDriver ci-dessus appelle une fonction DLL pour effectuer une communication série. Ceux qui utilisent cette classe sont implémentés de manière à ne pas avoir besoin de connaître les ctypes.

En ce qui concerne la valeur, le site suivant de stackoverflow a été utile. http://stackoverflow.com/questions/2330587/how-to-convert-ctypes-c-long-to-pythons-int

Exemple d'application de communication côté Python

Voici un programme qui utilise la classe SerialDriver pour effectuer une communication série réelle et enregistre les données reçues en série. En passant, cela est basé sur l'hypothèse que la classe SerialDriver a été écrite avec le nom de fichier serial_lib.py (comme vous pouvez le voir en important le code ci-dessous).

#!/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

De plus, j'ai écrit ce fichier en tant que serial_test.py. Telle est la prémisse de l'explication suivante.

Comme vous pouvez le voir dans le code, il comprend les fonctions suivantes.

Traitement principal --serial_test --serial_read Traitement de la réception série

Comme vous pouvez le voir à partir du code DLL, le résultat de la réception peut être de 0 octet car il expire dans 500 ms. Par conséquent, le processus de poursuite de la lecture jusqu'à ce qu'un mot-clé spécifique soit reçu est effectué ici (le mécanisme est donc tel qu'il ne peut pas être supprimé à moins que ce mot-clé ne vienne. Aucune contre-mesure n'a été prise pour ce w)

Le processus principal (fonction serial_test) est traité selon le flux suivant.

  1. Ouvrez le fichier journal. Le nom du fichier est "Time.txt" de sorte qu'il s'agira d'un fichier différent même s'il est exécuté plusieurs fois.
  2. Ouvrez le port série.
  3. Attendez de recevoir la chaîne "teraterm command1".
  4. Une fois reçu, écrivez son contenu dans le fichier journal.
  5. Envoyez "réponse python 1".
  6. Attendez de recevoir la chaîne "teraterm command2".
  7. Une fois reçu, écrivez son contenu dans le fichier journal.
  8. Envoyez "réponse python 2".
  9. Fermez le fichier journal et le port série.

En passant, s'il s'agit de COM10 ou supérieur, il est inutile dans ce qui précède et vous devez le décrire comme \\. \ COM10 "(spécifications de type MS comme ci-dessous) https://support.microsoft.com/ja-jp/help/115831/howto-specify-serial-ports-larger-than-com9

Comportement du partenaire de communication (macro TeraTerm)

L'autre partie de la fonction serial_test ci-dessus a été réalisée expérimentalement avec la macro TeraTerm suivante.

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

Le contenu est comme indiqué ci-dessous.

On a l'impression de communiquer les uns avec les autres.

Méthode d'exécution, etc.

Le test a été exécuté comme suit.

--Mettez la DLL, la classe SerialDeriver et l'application de test qui l'utilise (serial_test.py dans l'exemple d'environnement de l'auteur) dans le même dossier.

En appliquant cela, il est possible d'effectuer une communication série et une analyse de données avec Python. Vous pourrez vérifier la fonction en utilisant la série et la convertir en CSV. Il devrait être possible de prendre en charge l'apprentissage automatique avec des données de capteur en série.

Licence

Je l'ai utilisé ci-dessous. Merci d'avoir fourni le merveilleux logiciel.

c'est tout.

Recommended Posts

[Python] [Windows] Communication série en Python à l'aide de DLL
Fonctionnement de la souris à l'aide de l'API Windows en Python
Communication série avec Python
injection de dll en python
Câblage Communication Pi-SPI avec Python
Introduction à la communication série [Python]
Installation de Python en 2 lignes @Windows
Traduit à l'aide de googletrans en Python
Utilisation du mode Python dans le traitement
Réglage du mode octet de l'application de communication série TWE-Lite (envoi en Python)
La communication I2C est effectuée en contrôlant la communication série avec python (à l'aide d'un périphérique USBGPIO8)
Contrôle de la communication série avec communication python et SPI (à l'aide d'un périphérique USBGPIO8)
[Python] Afficher plusieurs fenêtres avec Tkinter
Programmation GUI en Python avec Appjar
Précautions lors de l'utilisation de Pit avec Python
Mettez MeCab dans "Windows 10; Python3.5 (64 bits)"
Essayez d'utiliser LevelDB avec Python (plyvel)
Windows10: Installation de la bibliothèque MeCab pour python
Utilisation de variables globales dans les fonctions python
Obtenir, publier un mémo de communication en Python
Voyons voir l'utilisation de l'entrée en python
Puissance totale en Python (en utilisant functools)
Reconnaissance de caractères manuscrits à l'aide de KNN en Python
Essayez d'utiliser LeapMotion avec Python
Recherche de priorité de profondeur à l'aide de la pile en Python
Lors de l'utilisation d'expressions régulières en Python
Création d'interface graphique en python avec tkinter 2
Remarques sur l'utilisation d'OpenCV avec Windows10 Python 3.8.3.
Notes utilisant cChardet et python3-chardet dans Python 3.3.1.
Essayez d'utiliser l'API Wunderlist en Python
Création d'interface graphique en python à l'aide de tkinter partie 1
Obtenir l'équilibre Suica en Python (en utilisant libpafe)
Pratique d'utilisation de ceci en Python (mauvais)
Hachez lentement les mots de passe en utilisant bcrypt en Python
Essayez d'utiliser l'API Kraken avec Python
[50 comptes] Transmission de clé à l'aide de Python pour Windows
Vérifier et recevoir le port série en Python (vérification du port)
Communication de socket en utilisant le serveur de socket avec python maintenant
[FX] Hit oanda-API avec Python en utilisant Docker
Tweet à l'aide de l'API Twitter en Python
J'ai essayé d'utiliser l'optimisation bayésienne de Python
[Python] [Windows] Faites une capture d'écran avec Python
Connectez-vous à Slack à l'aide de requêtes en Python
Obtenez des données Youtube en Python à l'aide de l'API Youtube Data
Création de scicit-learn dans un environnement Windows 10 à l'aide de Pycharm
Utilisation des constantes physiques dans Python scipy.constants ~ constant e ~
Scraping de sites Web à l'aide de JavaScript en Python
Développement de slack bot avec python en utilisant chat.postMessage
Ecrire un module python dans fortran en utilisant f2py
Dessinez une structure arborescente en Python 3 à l'aide de graphviz
Remarques sur l'utilisation de python (pydev) avec eclipse
Classification des maladies par Random Forest en utilisant Python
Téléchargez des fichiers dans n'importe quel format en utilisant Python
Exécution de tâches parallèles à l'aide de concurrent.futures en Python
Caractères Python déformés dans l'environnement Windows + Git Bash
Python en optimisation
CURL en Python
Métaprogrammation avec Python