Asynchrone Verarbeitung mit Arduino (asynchrone Verarbeitung von Verarbeitungsanforderungen von Linux)

In einem anderen Artikel schrieb ich [Korrigieren der Anzeige der 16x8-LED-Matrix]. Wenn Sie mit dieser LED-Matrix eine Anfrage zur Nachrichtenanzeige von Linux senden, erstellen wir ein Arduino, das die Nachricht scrollt und eine Antwort zurückgibt. Der Punkt dieses Artikels ist "Empfangen von Anfragen und asynchrones Anzeigen von Nachrichten in Arduino (genau genommen pseudoasynchron)".

Was zu verwenden.


Überprüfung der seriellen Linux <-> arduino USB-Verbindung

Kernel-Nachricht, wenn Arduino NANO mit Linux verbunden ist
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
Kernel-Nachricht, wenn Arduino NANO Every mit Linux verbunden ist
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&nbsp;Funktionsprüfung mit


$ screen /dev/ttyUSB0 115200
serial test
hogehoge
[hogehoge]

Versuchen Sie, den Text zu scrollen

Wenn Sie eine Anfrage von Linux an arduino übergeben, wird eine Antwort zurückgegeben, wenn der Inhalt ausgeführt wird. Implementieren Sie vorerst die folgenden Funktionen als Ausgangspunkt. Daten werden in JSON übergeben.

Funktion request response
Bildlaufanzeige {"textscr":"hello world !"} {"status":200, "msg":"..."}
Arduino Gesundheitscheck {"status":{}} {"status":200, "msg":"..."}

Unten finden Sie einen Auszug aus dem Hauptcode von 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());
    }
  }
}
Der gesamte Arduino-Code befindet sich hier

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

Unten ist der Linux-Seitencode Zuerst habe ich endlich die USB-Verbindung zwischen der Linux-Box und Arduino überprüft, aber hier verwende ich Ubuntu, die Testumgebung des Windows Linux-Subsystems.

test.py


import serial
import time
import json

s = serial.Serial()
s.port = "/dev/ttyS4" #Stellen Sie eine Verbindung zu COM4 Arduino her
s.baudrate = 115200
s.timeout = 1
s.dtr = False   #Verhindert, dass Arduino beim seriellen Verbinden zurückgesetzt wird
s.open()
time.sleep(1)   #Warten Sie auf Gefühle

s.reset_input_buffer()  #Reinigen des Empfangspuffers der seriellen Schnittstelle

def request(data):
    print("request:", data)
    s.write(json.dumps(data).encode())  # encode()Muss eine binäre Zeichenfolge sein
    while True: #Warten Sie, bis die Antwort zurückkommt
        msg = s.readline().decode()
        if(len(msg) > 0):
            print("response:", msg)
            break

request({"textscr" : {"msg":"Hello World !!!"}})
request({"status" : {}})

Ausführungsergebnis led.gif Es ist richtig, dass die Antwort der Anfrage "textscr" {"status": 200, "msg": Hello World !!! "} lautet, aber es scheint, dass das erste Zeichen zu diesem Zeitpunkt verschüttet wurde. Berücksichtigen Sie die Fehlerbehandlung. Ist es?


Verwalten der Verarbeitungsblockierung während der Textscrollanzeige

Wie Sie dem Ausführungsergebnis entnehmen können, wird die Antwort erst zurückgegeben, wenn die Textscr-Anzeige abgeschlossen ist und ein anderer Befehl während des Wartens nicht akzeptiert wird. Es ist etwas schwierig zu handhaben. Der Textscroll auf der Arduino-Seite verwendet eine for-Schleife, um 100 ms auf die Anzeige von Zeichen zu warten und diese dann horizontal um 1 Punkt zu verschieben. Jedes Mal, wenn der Text um 1 Punkt verschoben wird, wird der Vorgang an loop () zurückgegeben und der nächste Schritt wird gescrollt. Ermöglicht den Empfang von Befehlen. Wenn während des Bildlaufs ein neuer Text gesendet wird, wird die vorhandene Anzeigeausführung verworfen und überschrieben.

Arduino-modifizierte Version Der gesamte Code befindet sich hier
#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;
      }
    }
  }
}

Auszug unten.

test2.ino


#define DR_UNRELATED 0  //Wird zurückgegeben, wenn die cmdxxxx-Verarbeitung andere laufende Anforderungen nicht überschreibt
#define DR_STOP 1       //Wird zurückgegeben, wenn die Verarbeitung von cmdxxxx abgeschlossen ist
#define DR_OVERRIDE 2   //Wird zurückgegeben, wenn die cmdxxxx-Verarbeitung eine andere laufende Anforderung überschreibt
#define DR_NEW 10       //Übergeben Sie es an cmdxxxx, wenn eine neue Anforderung eingeht
#define DR_CONTINUE 11  //Bestanden beim Aufruf von cmdxxxx in der nachfolgenden Verarbeitung

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(); //Speichern Sie die empfangene Anforderungszeichenfolge in cmd
      int i;
      for(i = 0; cmds[i].cmd != ""; i++) { //cmds, die mit cmd übereinstimmen[]Schleife, um Mitglieder zu finden und anzurufen
        if((String)cmds[i].cmd == cmd) {
          int r = (*cmds[i].drawfunc)(DR_NEW, p.value());
          switch(r) {
            case DR_OVERRIDE:
              drawing = &cmds[i]; //Denken Sie an die Funktion, die Sie gerade aufgerufen haben, da sie weiterhin verarbeitet wird.
              break;
            case DR_STOP:
              drawing = NULL; //Die Funktion, die ich gerade aufgerufen habe, ist abgeschlossen, sodass es keine Funktion gibt, mit der die Verarbeitung fortgesetzt werden kann
              break;
          }
          break;
        }
      }
      ...
    }

  } else {
    if(drawing) { //Wenn eine Anforderungsfunktion ausgeführt wird, rufen Sie diese Funktion auf
      int r = drawing->drawfunc(DR_CONTINUE, JVNULL);
      ...
    }
  }
}
  1. Bereiten Sie die LED-Anzeigefunktion cmdxxxx für jeden Anforderungsbefehl vor (derzeit nur zwei, "textscr" und "status"), und fügen Sie sie zusammen mit dem Befehlsnamen in cmds [] ein, einer Liste von Cmds-Strukturen.
  2. Wenn Sie beispielsweise die Anforderung "textscr" unter Linux senden, wird cmdTextscr aus loop () mit stat = DR_NEW aufgerufen, um den Bildlauf zu starten.
  3. cmdTextscr zeigt Text an, wartet 100 ms und gibt DR_OVERRIDE oder DR_UNRELATED zurück, wenn das Ende des Bildlaufs noch nicht erreicht ist. Bildlaufanzeige Gibt DR_STOP zurück, wenn es bis zum Ende abgeschlossen ist.
  4. Die loop () -Seite, die die Antwort erhalten hat, erinnert sich an draw = & cmds [1], da draw = NULL ist, wenn DR_STOP, und ausgeführt wird, wenn DR_OVERRIDE.
  5. loop () wiederholt das oben Gesagte in einer for-Schleife. Wenn draw! = NULL ist, wird der ausgeführte Prozess aufgerufen. Wenn eine neue Anforderung eingeht, wird die Ausführung wiederholt.

Auf der Linux-Seite müssen Sie jetzt nicht mehr nach dem Senden einer Anfrage warten, sondern können jederzeit eine Anfrage senden.

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   #Verhindert, dass Arduino beim seriellen Verbinden zurückgesetzt wird
s.open()
time.sleep(1)   #Warten Sie auf Gefühle

s.reset_input_buffer()  #Reinigen des Empfangspuffers der seriellen Schnittstelle

def request(data):
    print(datetime.datetime.now().strftime('%H:%M:%S'), "request:", data)
    s.write(json.dumps(data).encode())  # encode()Muss eine binäre Zeichenfolge sein
    while True: #Warten Sie, bis die Antwort zurückkommt
        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.py Ausführungsergebnis


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"}

Die Zeit wird vor der Anfrage und Antwort hinzugefügt, um das Verständnis zu erleichtern. Sobald Sie mit dem Scrollen beginnen, wird eine Antwort zurückgegeben und eine neue Anforderung ausgeführt, wenn Sie während des Bildlaufs eine weitere Textanforderung senden. Perfekt. Ende.

Recommended Posts

Asynchrone Verarbeitung mit Arduino (asynchrone Verarbeitung von Verarbeitungsanforderungen von Linux)
Parallele Verarbeitung mit Parallel von Scikit-Learn
Grundlagen der binärisierten Bildverarbeitung durch Python
Zeichnen mit Matrix-Reinventor von Python Image Processing-
Beispiel für eine effiziente Datenverarbeitung mit PANDAS
Bildverarbeitung von Grund auf mit Python (5) Fourier-Transformation
Bildverarbeitung von Grund auf mit Python (4) Konturextraktion
Sichern Sie mit rsync von QNAP auf Linux
Lerne Nim mit Python (ab Anfang des Jahres).
Öffnen Sie die Chrome-Version von LINE über die Befehlszeile [Linux].
[Kapitel 5] Einführung in Python mit 100 Klopfen Sprachverarbeitung
[Kapitel 6] Einführung in Scicit-Learn mit 100 Klopfen Sprachverarbeitung
[Kapitel 3] Einführung in Python mit 100 Klopfen Sprachverarbeitung
[Kapitel 2] Einführung in Python mit 100 Klopfen Sprachverarbeitung
Asynchrone Verarbeitung von Python ~ Asynchron vollständig verstehen und warten ~
Versuchen Sie, Anfragen von iPhone mit Burp Suite zu manipulieren
Lernen Sie die asynchrone Verarbeitung / Collouts von Python im Vergleich zu Node.js
[Grundlagen der Datenwissenschaft] Sammeln von Daten aus RSS mit Python
Lernen Sie mit "Google Colaboratory" ganz einfach 100 Sprachverarbeitungsklopfen 2020.
[Kapitel 4] Einführung in Python mit 100 Klopfen Sprachverarbeitung
ODBC-Zugriff auf SQL Server von Linux mit Python