Daten aus einer serieller Schnittstelle auslesen/auswerten

Hallo,
ich bin noch recht neu in der Programmierung, ich möchte aber versuchen Daten die ich über eine serielle Schnittstelle empfange auszulesen und einen bestimmten Wert an den seriellen Monitor (oder später auf eine SD-Karte) auszugeben.
Bei den Daten die ich empfange handelt es sich um 163 Bytes die immer mit "0x56 0xAB 0x56 0x02" beginnen. Ich benötige aber nur die Werte an Stelle 118, 119 und 120. Diese würde ich gerne am seriellen Monitor ausgeben.

Mit folgendem Code werden alle Werte ausgeben, natürlich ohne Ende.
Gibt es eine einfache Möglichkeit nur die gewünschten Werte auszugeben und dann fünf Minuten zu warten um die Werte erneut auszugeben?

Über jegliche Hilfe würde ich mich sehr freuen.

Viele Güße
Carsten

#include <SoftwareSerial.h>

SoftwareSerial RS232(2, 3);
 

char rs232_inChar;

void setup()
{
  Serial.begin(9600);
  while (!Serial) {
    ; 
  }
  RS232.begin(9600);
}

void loop()
{
  while (RS232.available() >= 2
  ) 
  {
    rs232_inChar = RS232.read();            
    {                 
      Serial.print(rs232_inChar, HEX);           
      
      Serial.print('\n');                   
                    
    }
  }
}
  • wenn du genau beschreiben würdest was du da auslesen willst (link, Datasheet,...) dann hat vieleicht jemand was fertiges, und wenn nicht was fertiges dann zumindest Lust an etwas dauerhaften mit dir daran zu arbeiten.
  • vor allem wie die Nachricht aufgebaut ist wird benötigt, insbesondere auch ob es ein Endzeichen gibt
  • zum Lernen wie man ein Gerät über Seriell liest, rate ich dir das Serial Input Basics Tutorial durchzumachen und genau so zu verfahren wie dort beschrieben: Serial Input Basics - updated
  • Wenn du weist wie man die Daten in ein char array einliest, kannst du dann auch gezielt auf einzelne Indeces zugreifen.
  • Der Vorgang ist ja grundsätzlich übersichtlich:
    -- Einlesen bis zu einem Endkriterium (Endflag, Zeichenanzahl, Stream- Ende... was auch immer)
    -- Auswerten
    -- nur alle 5 Minuten einen Wert ausgeben

Und womit endet der Datensatz?
Hast du ein Datalogging von der Schnittstelle?
Wie oft wird dieser Datensatz mit welcher Baudrate gesendet?

Das hier ist das Übertragungsprotokoll, ich benötige aus dem struct Block die Gas_ppm Werte.

Ein Endzeichen gibt es nicht.
Mir ist nur bekannt daß die Daten bei 9600 Baud innerhalb von 200ms ankommen.

  • um welches Device handelt es sich genau? Link, Bild, Beschreibung?
  • stell bitte mal ein valide Musternachricht hier ein. Und zwar so dass wir diese als Beispiel nutzen können, z.B. als

byte message[] ={0x56, 0xAB, 0x56, 0x02, ...

KOMPLETT!

Außer der Link auf die Serial Basics hat eh schon gereicht, dann können wir das Thema beenden.

Danke für den Link, ich hatte schon mehrere Posts dazu gelesen, aber anscheinend immer die falschen bzw. waren die nicht hilfreich für mich. Werde es damit einmal versuchen.

#include <SoftwareSerial.h>

SoftwareSerial RS232(2, 3);

byte rs232_inChar;

byte idx;


void setup()
{
  Serial.begin(9600);
  while (!Serial)
  {
    ;
  }
  RS232.begin(9600);
}

void loop()
{
  if (RS232.available() > 0)
  {
    rs232_inChar = RS232.read();
    {
// *INDENT-OFF*
      switch (idx)
      {
        case 0:
          [[fallthrough]]
        case 2: if (rs232_inChar == 0x56) { idx++; } else { idx = 0; } break;
        case 1: if (rs232_inChar == 0xAB) { idx++; } else { idx = 0; } break;
        case 3: if (rs232_inChar == 0x20) { idx++; } else { idx = 0; } break;
        case 4 ... 117:                   { idx++; }                   break;
        case 118 ... 120:
          Serial.print(F("IndexPosition: "));
          Serial.print(idx);
          Serial.print('\t');
          Serial.print(F("Inhalt: "));
          Serial.println(rs232_inChar, HEX);
          break;
        default:
          idx = 0;
          break;
      }
// *INDENT-ON*
    }
  }
}
1 Like

Mein Ansatz basiert auf Serial Input Basic, gepackt in eine Klasse für das Gerät.

/*
   Serial Eheim

   Serial Receive Example for a fix length telegram with 4 start flags

   Valid telegram
   56 AB 56 02 01 02 03 ...

   56 AB 56 02                start flag / start marker
               01 02 03 ...   112  + 47 bytes

   based on a discussion in: https://forum.arduino.cc/t/daten-aus-einer-serieller-schnittstelle-auslesen-auswerten/1073809/4

   by noiasca
   2023-01-06

*/

class Device {
  protected:
    static const uint8_t numChars = 163;  // receiver buffer size
    uint8_t receivedChars[numChars];      // an array to store the received data
    uint8_t ndx = 0;                      // length of received message
    boolean newData = false;              // flag to indicate when new data is complete
    Stream &stream;                       // a reference to the serial interface
    void (*cbOnNewData)();                // gets called after we received a full message
    // extracted payload data
    uint8_t valueA = 0;  // 118
    uint8_t valueB = 0;  // 119
    uint8_t valueC = 0;  // 120

    void delBuffer() {
      memcpy(receivedChars, "", sizeof(receivedChars));
      ndx = 0;
    }

    void parseData()  // parse data and if according to specification store to internal variable
    {
      valueA = receivedChars[118];
      valueB = receivedChars[119];
      valueC = receivedChars[120];
    }

    void parseLine()  // process a telegram
    {
      //Serial.print(F("I56: This just in ... ")); Serial.println(receivedChars);         // output all
      parseData();
      if (cbOnNewData) cbOnNewData();
    }

  public:
    Device(Stream &stream)
      : stream(stream) {
      delBuffer();
    }

    uint8_t getValueA()  // return the last valid result
    {
      return valueA;
    }

    uint8_t getValueB()  // return the last valid result
    {
      return valueB;
    }

    uint8_t getValueC()  // return the last valid result
    {
      return valueC;
    }

    void setOnNewData(void (*cbOnNewData)())  // set a callback function. Gets called when new data was received
    {
      (*this).cbOnNewData = cbOnNewData;
    }

    void update()  // run this member function in loop()
    { 
      // 4 start flags, fix length
      int rc = stream.read();
      if (rc >= 0) 
      {
        // check first 4 positions:
        if (ndx == 0 && rc != 0x56 ||
            ndx == 1 && rc != 0xAB ||
            ndx == 2 && rc != 0x56 ||
            ndx == 3 && rc != 0x02 )
          delBuffer();
        else  // accept value
        {
          receivedChars[ndx] = rc;
          ndx++;
          if (ndx >= numChars) {
            ndx = 0;
            parseLine();
          }
        }
      }
    }
};


// create the sensor object and hand over the Serial interface to use:
HardwareSerial &mySerial = Serial;  // use Hardware Serial (for example for testing with the PC)

//#include <SoftwareSerial.h>     // on an Uno you can use SoftSerial
//SoftwareSerial mySerial(2, 3);  // RX, TX

// On a Mega you can simply use a reference to an existing HW Serial:
//HardwareSerial &mySerial = Serial1;

Device device(mySerial);  //create an instance of the device and handover a Serial (Stream) Interface

void output()  // simple output of data
{
  Serial.println(device.getValueA());
  Serial.println(device.getValueB());
  Serial.println(device.getValueC());
  Serial.println();
}

void timerOutput()  // a timer to output data to serial
{
  static uint32_t previousMillis = 0;  // time management
  const uint16_t interval = 3000;      // interval to display data
  if (millis() - previousMillis >= interval) {
    previousMillis = millis();
    output();
  }
}

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  //device.setOnNewData(output);  // register a callback function which gets called when a new telegram was received
}

void loop() {
  device.update();  // you must call the .update() method in your loop()
  timerOutput();
}

Das solltest Du nicht delBuffer nennen :wink: Oder dann den Buffer wirklich löschen.

naming things...

Vielen Dank, das sieht sehr gut und schön aus!

Leider habe ich jetzt ein ganz anderes Problem. Und dies führt wahrscheinlich dazu das ich nicht die Werte empfange.
Ich habe mir die Daten auch nochmal alle an den seriellen Monitor weiterleiten lassen und dort empfange ich immer anstatt AB nur FFFFFFAB. Das gleiche passiert auch wenn ich nur AB mit Hterm sende.
AB und noch einige andere werden immer mit FFFFFF am Anfang empfangen.

Weiß hier jemand zufällig woran das liegen könnte?

Beim Empfang mit Hterm passiert das jedoch nicht.

Ich lese aus deinem Post keine nachvollziehbare Information heraus mit der man dir helfen könnte.
Baudrate richtig? Verkabelung korrekt? Module richtig angeschlossen? Außerdem hast du noch nicht verlinkt, um welches Gerät es sich hierbei handelt.

Also sorry @noiasca, was soll das?
In seinem letzten Post steht, das die Ausgabe nicht ASCII sondern UTF ist
Das Gerät interressiert keinen, ausser Dich. Wozu?
120 Bytes seriell lesen ist doch nu keine Raketenwissenschaft.

Damit der nächste es mit dem Gerätenamen findet.

Du versuchst jetzt aber nicht jedem aufzuzwingen zu benennen, mit was er die auf dem Arduino zu empfangenen Bytes sendet?

PS: Das Ding kommt aus dem Industriebereich und ist kundenspezifisch konfiguriert. Was denkst Du wieviele User es gibt, die genau diesen Geber einsetzen?

Es reicht ein einziger der danach sucht und der dann vieleicht froh ist, etwas zu finden. Vieleicht. Vieleicht auch nicht. Die Information um welches Gerät es sich handelt zu teilen kann also Vorteile bringen. Diese Information zu verheimlichen ... da sehe ich keinen Vorteil.

Es reicht auch ein einziger Eintrag um den Code für andere Anwendungen unattraktiv zu machen, so das diese den ausschliessen.
Siehe die Thematic der ganzen Solardinger udn eHZ, die alle SML sprechen, aber jeder meint ausgerechnet seiner nicht.

Worauf willst Du jetzt eigentlich hinaus? Dass es keinen Sinn macht, eine Anfrage hier im Forum so anzulegen, dass möglichst viele Informationen zum Themenkomplex preis gegeben werden?

Also so, dass mögl. viele von dem Thread profitieren könnten? Weil sich ja evtl. eh alles wieder ändert?

Das ich dieses Nachtreten um den Sendertypen nicht verstehe.
Es werden 120 bytes gelesen.
Es ist bekannt wie die Nachricht aufgebaut ist.
Es ist klar,was gebraucht wird.
Mir erschliesst sich nicht, warum da nun unbedingt drin stehen muss "Ich benutze Gerät 0815 vom Hersteller 4711"
Welchen Mehrwert hat die Information über den Sender? Ausser das diese Information dahin führt, das andere Nutzer die nur 112bytes von irgendeinem anderen Gerät lesen wollen dann von vornherein schon weiterscrollen, weil ist ja speziell für was anderes...

Jain.
Beides kann hilfreich für Suchende sein.

Grüße Uwe