Dans un autre article, j'ai écrit [Corriger l'affichage de la matrice LED 16x8]. En utilisant cette matrice de LED, si vous envoyez une demande d'affichage de message depuis Linux, nous allons créer un Arduino qui fait défiler le message et renvoie une réponse. Le but de cet article est "Recevoir des requêtes et afficher des messages dans Arduino de manière asynchrone (à proprement parler, pseudo-asynchrone)".
Quoi utiliser.
usb 1-12: new full-speed USB device number 3 using xhci_hcd
usb 1-12: New USB device found, idVendor=0403, idProduct=6001
usb 1-12: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-12: Product: FT232R USB UART
usb 1-12: Manufacturer: FTDI
usb 1-12: SerialNumber: ALxxxxxx
usbcore: registered new interface driver usbserial_generic
usbserial: USB Serial support registered for generic
usbcore: registered new interface driver ftdi_sio
usbserial: USB Serial support registered for FTDI USB Serial Device
ftdi_sio 1-12:1.0: FTDI USB Serial Device converter detected
usb 1-12: Detected FT232RL
usb 1-12: FTDI USB Serial Device converter now attached to ttyUSB0
usb 1-6: new full-speed USB device number 4 using xhci_hcd
usb 1-6: New USB device found, idVendor=2341, idProduct=0058
usb 1-6: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-6: Product: Arduino Nano Every
usb 1-6: Manufacturer: Arduino LLC
usb 1-6: SerialNumber: FAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
cdc_acm 1-6:1.0: ttyACM0: USB ACM device
usbcore: registered new interface driver cdc_acm
cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
serial_test.ino
#include <Wire.h>
void setup()
{
Wire.begin();
Serial.begin(115200);
Serial.println("serial test");
}
void loop()
{
String str;
if(Serial.available()>0) {
str = Serial.readString();
Serial.println("[" + str + "]");
}
}
screen Contrôle de fonctionnement avec
$ screen /dev/ttyUSB0 115200
serial test
hogehoge
[hogehoge]
Lorsque vous transmettez une requête de Linux à arduino, il renverra une réponse lorsque le contenu est exécuté. Pour le moment, implémentez les fonctions suivantes comme point de départ. Les données sont transmises en JSON.
une fonction | request | response |
---|---|---|
Affichage du texte par défilement | {"textscr":"hello world !"} | {"status":200, "msg":"..."} |
Vérification de l'état d'Arduino | {"status":{}} | {"status":200, "msg":"..."} |
Voici un extrait du code principal d'Arduino
test.ino
int sendResponse(int status, String msg) {
StaticJsonDocument<100> response;
response["status"] = status;
response["msg"] = msg;
serializeJson(response, Serial);
Serial.println();
}
int ExecCmd(String cmd, JsonVariant value) {
if(cmd == "status") {
sendResponse(100, "alive");
} else if(cmd == "textscr") {
JsonObject obj2 = value.as<JsonObject>();
String msg = obj2["msg"];
int size = obj2["size"];
if(size < 1 || size > 2) {
size = 1;
}
matrix.setTextSize(size);
matrix.setTextWrap(false);
matrix.setTextColor(LED_ON);
int l = msg.length();
for(int16_t x = 7; x >= -6*l; x--) {
matrix.clear();
matrix.setCursor(x,0);
matrix.print(msg);
matrix.writeDisplay();
delay(100);
}
sendResponse(200, "msg:" + msg + ",size:" + (String)size);
} else { // "status" : 404
sendResponse(404, "command not found:" + cmd);
}
}
StaticJsonDocument<200> request;
void loop() {
if(Serial.available() > 0) {
DeserializationError error = deserializeJson(request, Serial);
if (error) {
sendResponse(500, error.c_str());
return;
}
JsonObject obj = request.as<JsonObject>();
for (JsonPair p : obj) {
ExecCmd((String)p.key().c_str(), p.value());
}
}
}
test.ino
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>
#include <ArduinoJson.h>
#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif
class aitendo_KLED1608K33D_8x16matrix : public Adafruit_LEDBackpack, public Adafruit_GFX {
public:
aitendo_KLED1608K33D_8x16matrix(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
private:
};
aitendo_KLED1608K33D_8x16matrix::aitendo_KLED1608K33D_8x16matrix(void) : Adafruit_GFX(16, 8) {
}
void aitendo_KLED1608K33D_8x16matrix::drawPixel(int16_t x, int16_t y, uint16_t color) {
if ((y < 0) || (x < 0)) return;
if ((getRotation() % 2 == 0) && ((x >= 16) || (y >= 8))) return;
if ((getRotation() % 2 == 1) && ((y >= 16) || (x >= 8))) return;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 0:
if (x >= 8) {
x -= 8;
y += 8;
}
break;
case 1:
y = 16 - y - 1;
if(y >= 8) {
y -= 8;
x += 8;
}
_swap_int16_t(x, y);
break;
case 2:
x = 16 - x - 1;
y = 8 - y - 1;
if (x >= 8) {
x -= 8;
y += 8;
}
break;
case 3:
x = 8 - x - 1;
if(y >= 8) {
y -= 8;
x += 8;
}
_swap_int16_t(x, y);
break;
}
if (color) {
displaybuffer[x] |= 1 << y;
} else {
displaybuffer[x] &= ~(1 << y);
}
}
aitendo_KLED1608K33D_8x16matrix matrix = aitendo_KLED1608K33D_8x16matrix();
void setup() {
Serial.begin(115200);
Serial.println("16x8 LED Matrix");
matrix.begin(0x70);
matrix.setBrightness(5);
matrix.setRotation(0);
matrix.clear();
matrix.writeDisplay();
}
int sendResponse(int status, String msg) {
StaticJsonDocument<100> response;
response["status"] = status;
response["msg"] = msg;
serializeJson(response, Serial);
Serial.println();
}
int ExecCmd(String cmd, JsonVariant value) {
if(cmd == "status") {
sendResponse(100, "alive");
} else if(cmd == "textscr") {
JsonObject obj2 = value.as<JsonObject>();
String msg = obj2["msg"];
int size = obj2["size"];
if(size < 1 || size > 2) {
size = 1;
}
matrix.setTextSize(size);
matrix.setTextWrap(false);
matrix.setTextColor(LED_ON);
int l = msg.length();
for(int16_t x = 7; x >= -6*l; x--) {
matrix.clear();
matrix.setCursor(x,0);
matrix.print(msg);
matrix.writeDisplay();
delay(100);
}
sendResponse(200, "msg:" + msg + ",size:" + (String)size);
} else { // "status" : 404
sendResponse(404, "command not found:" + cmd);
}
}
StaticJsonDocument<200> request;
void loop() {
if(Serial.available() > 0) {
DeserializationError error = deserializeJson(request, Serial);
if (error) {
sendResponse(500, error.c_str());
return;
}
JsonObject obj = request.as<JsonObject>();
for (JsonPair p : obj) {
ExecCmd((String)p.key().c_str(), p.value());
}
}
}
Voici le code côté Linux Tout d'abord, j'ai finalement vérifié la connexion USB entre la box Linux et Arduino, mais ici j'utilise Ubuntu, qui est l'environnement de test du sous-système Windows Linux.
test.py
import serial
import time
import json
s = serial.Serial()
s.port = "/dev/ttyS4" #Connectez-vous à COM4 arduino
s.baudrate = 115200
s.timeout = 1
s.dtr = False #Empêche arduino d'être réinitialisé lors de la connexion en série
s.open()
time.sleep(1) #Attendez les sentiments
s.reset_input_buffer() #Nettoyage du tampon de réception du port série
def request(data):
print("request:", data)
s.write(json.dumps(data).encode()) # encode()Doit être une chaîne binaire dans
while True: #Attendez que la réponse revienne
msg = s.readline().decode()
if(len(msg) > 0):
print("response:", msg)
break
request({"textscr" : {"msg":"Hello World !!!"}})
request({"status" : {}})
Résultat d'exécution Il est correct que la réponse à la requête "textcr" soit {"status": 200, "msg": Hello World !!! "}, mais il semble que le premier caractère a été renversé à ce moment. Pensez à la gestion des erreurs. C'est ça?
Comme vous pouvez le voir dans le résultat de l'exécution, la réponse n'est pas renvoyée tant que l'affichage des textes n'est pas terminé, et une autre commande n'est pas acceptée en attendant. C'est un peu difficile à gérer. Le défilement de texte du côté Arduino utilise une boucle for pour attendre 100 ms que le caractère soit affiché, puis le décale horizontalement de 1 point, de sorte que chaque fois que le texte est décalé d'un point, le processus est renvoyé en boucle () et l'étape suivante est en cours de défilement. Vous permet de recevoir des commandes. De plus, si un nouveau textcr est envoyé pendant le défilement, l'exécution d'affichage existante sera supprimée et écrasée.
<détails>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>
#include <ArduinoJson.h>
#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif
class aitendo_KLED1608K33D_8x16matrix : public Adafruit_LEDBackpack, public Adafruit_GFX {
public:
aitendo_KLED1608K33D_8x16matrix(void);
void drawPixel(int16_t x, int16_t y, uint16_t color);
private:
};
aitendo_KLED1608K33D_8x16matrix::aitendo_KLED1608K33D_8x16matrix(void) : Adafruit_GFX(16, 8) {
}
void aitendo_KLED1608K33D_8x16matrix::drawPixel(int16_t x, int16_t y, uint16_t color) {
if ((y < 0) || (x < 0)) return;
if ((getRotation() % 2 == 0) && ((x >= 16) || (y >= 8))) return;
if ((getRotation() % 2 == 1) && ((y >= 16) || (x >= 8))) return;
// check rotation, move pixel around if necessary
switch (getRotation()) {
case 0:
if (x >= 8) {
x -= 8;
y += 8;
}
break;
case 1:
y = 16 - y - 1;
if(y >= 8) {
y -= 8;
x += 8;
}
_swap_int16_t(x, y);
break;
case 2:
x = 16 - x - 1;
y = 8 - y - 1;
if (x >= 8) {
x -= 8;
y += 8;
}
break;
case 3:
x = 8 - x - 1;
if(y >= 8) {
y -= 8;
x += 8;
}
_swap_int16_t(x, y);
break;
}
if (color) {
displaybuffer[x] |= 1 << y;
} else {
displaybuffer[x] &= ~(1 << y);
}
}
aitendo_KLED1608K33D_8x16matrix matrix = aitendo_KLED1608K33D_8x16matrix();
#define DR_UNRELATED 0
#define DR_STOP 1
#define DR_OVERRIDE 2
#define DR_NEW 10
#define DR_CONTINUE 11
typedef struct {
const char *cmd;
int (*drawfunc)(int, JsonVariant);
} Cmds;
Cmds *drawing;
void setup() {
drawing = NULL;
Serial.begin(115200);
Serial.println("16x8 LED Matrix");
matrix.begin(0x70);
matrix.setBrightness(5);
matrix.setRotation(0);
matrix.clear();
matrix.writeDisplay();
}
int sendResponse(int status, String msg) {
StaticJsonDocument<100> response;
response["status"] = status;
response["msg"] = msg;
serializeJson(response, Serial);
Serial.println();
}
int cmdStatus(int stat, JsonVariant value) {
sendResponse((drawing ? 102 : 100), (drawing ? "drawing" : "free time"));
return(DR_UNRELATED);
}
int cmdTextscr(int stat, JsonVariant value) {
static String sMsg;
static int16_t l, x;
if(stat == DR_NEW) {
JsonObject obj = value.as<JsonObject>();
String msg = obj["msg"];
sMsg = msg;
l = msg.length();
x = 7;
matrix.setTextSize(1);
matrix.setTextWrap(false);
matrix.setTextColor(LED_ON);
}
if(x >= -6*l) {
matrix.clear();
matrix.setCursor(x,0);
matrix.print(sMsg);
matrix.writeDisplay();
delay(100);
x--;
} else {
//sendResponse(200, "finish textscr msg:" + msg);
return(DR_STOP);
}
if(stat == DR_NEW) {
sendResponse(200, "textscr msg:" + sMsg);
return(DR_OVERRIDE);
}
return(DR_UNRELATED);
}
Cmds cmds[] = {
{"status", cmdStatus},
{"textscr", cmdTextscr},
{"", NULL},
};
StaticJsonDocument<200> request;
JsonVariant JVNULL = JsonVariant();
void loop() {
if(Serial.available() > 0) {
DeserializationError error = deserializeJson(request, Serial);
if (error) {
sendResponse(500, error.c_str());
return;
}
JsonObject obj = request.as<JsonObject>();
for (JsonPair p : obj) {
String cmd = (String)p.key().c_str();
int i;
for(i = 0; cmds[i].cmd != ""; i++) {
if((String)cmds[i].cmd == cmd) {
int r = (*cmds[i].drawfunc)(DR_NEW, p.value());
switch(r) {
case DR_OVERRIDE:
drawing = &cmds[i];
break;
case DR_STOP:
drawing = NULL;
break;
}
break;
}
}
if(cmds[i].cmd == "") {
sendResponse(404, "command not found:" + cmd);
}
}
} else {
if(drawing) {
int r = drawing->drawfunc(DR_CONTINUE, JVNULL);
switch(r) {
case DR_STOP:
drawing = NULL;
break;
}
}
}
}
Extrait ci-dessous.
test2.ino
#define DR_UNRELATED 0 //Renvoyé si le traitement cmdxxxx n'écrase pas les autres requêtes en cours d'exécution
#define DR_STOP 1 //Renvoyé lorsque le traitement de cmdxxxx est terminé
#define DR_OVERRIDE 2 //Renvoyé lorsque le traitement cmdxxxx écrase une autre demande en cours d'exécution
#define DR_NEW 10 //Passer à cmdxxxx lorsqu'une nouvelle demande arrive
#define DR_CONTINUE 11 //Passé lors de l'appel de cmdxxxx lors d'un traitement ultérieur
int cmdStatus(int stat, JsonVariant value) {
...
}
int cmdTextscr(int stat, JsonVariant value) {
...
}
typedef struct {
const char *cmd;
int (*drawfunc)(int, JsonVariant);
} Cmds;
Cmds *drawing;
Cmds cmds[] = {
{"status", cmdStatus},
{"textscr", cmdTextscr},
{"", NULL},
};
void loop() {
if(Serial.available() > 0) {
...
for (JsonPair p : obj) {
String cmd = (String)p.key().c_str(); //Stocker la chaîne de demande reçue dans cmd
int i;
for(i = 0; cmds[i].cmd != ""; i++) { //cmds qui correspondent à cmd[]Boucle pour trouver et appeler des membres
if((String)cmds[i].cmd == cmd) {
int r = (*cmds[i].drawfunc)(DR_NEW, p.value());
switch(r) {
case DR_OVERRIDE:
drawing = &cmds[i]; //Souvenez-vous de la fonction que vous venez d'appeler car elle continuera à être traitée.
break;
case DR_STOP:
drawing = NULL; //La fonction que je viens d'appeler est terminée, il n'y a donc pas de fonction pour continuer le traitement
break;
}
break;
}
}
...
}
} else {
if(drawing) { //S'il y a une fonction de demande en cours d'exécution, appelez cette fonction
int r = drawing->drawfunc(DR_CONTINUE, JVNULL);
...
}
}
}
Désormais, du côté Linux, vous n'avez pas à attendre après l'envoi d'une requête, et vous pouvez envoyer une requête à tout moment.
test2.py
import serial
import time
import datetime
import json
s = serial.Serial()
s.port = "/dev/ttyS4"
s.baudrate = 115200
s.timeout = 1
s.dtr = False #Empêche arduino d'être réinitialisé lors de la connexion en série
s.open()
time.sleep(1) #Attendez les sentiments
s.reset_input_buffer() #Nettoyage du tampon de réception du port série
def request(data):
print(datetime.datetime.now().strftime('%H:%M:%S'), "request:", data)
s.write(json.dumps(data).encode()) # encode()Doit être une chaîne binaire dans
while True: #Attendez que la réponse revienne
msg = s.readline().decode()
if(len(msg) > 0):
print(datetime.datetime.now().strftime('%H:%M:%S' ), "response:", msg)
break
request({"textscr" : {"msg":"Hello World !!!"}})
time.sleep(3)
request({"status" : {}})
time.sleep(1)
request({"textscr" : {"msg":"\\(^_^)/"}})
time.sleep(6)
request({"status" : {}})
test2.résultat d'exécution py
ubuntu:~$ python3 test.py
00:25:39 request: {'textscr': {'msg': 'Hello World !!!'}}
00:25:39 response: {"status":200,"msg":"textscr msg:Hello World !!!"}
00:25:42 request: {'status': {}}
00:25:42 response: {"status":102,"msg":"drawing"}
00:25:43 request: {'textscr': {'msg': '\\(^_^)/'}}
00:25:43 response: {"status":200,"msg":"textscr msg:\\(^_^)/"}
00:25:49 request: {'status': {}}
00:25:49 response: {"status":100,"msg":"free time"}
Le temps est ajouté avant la demande et la réponse pour une compréhension facile. Il renverra une réponse dès que vous commencerez le défilement du texte, et si vous envoyez une autre requête textcr tout en défilant encore, il exécutera la nouvelle requête. Parfait. fin.
Recommended Posts