Serial-Daten aufspalten

Hallo zusammen,

wie in meinem letzten Thread schon gesagt, werde ich mich jetzt auf die Entwicklung von Hard- und Software konzentrieren.
Da die Hardware ja schon halbwegs abgeschlossen ist (zumindest für’s erste) und ich jetzt auf die Platinen warte, hab ich mich schon mal an den Code gesetzt um die einzelnen “Bestandteile” zu lernen.

Beim Serial bin ich da schon auf ein Problem gestoßen:

Ich hab ja vor, einige Daten vom Steuergerät (Master) auf die Terminals (Slave) zu lasen. Ich such gerade nach dem richtigen Weg, diese Daten zu schicken.
Mein aktueller Versuchsaufbau:
Arduino Uno (hatte ganz vergessen, dass ich den mal gekauft hab hust), LCD-Display, ISP-Programmer und USB-Konverter.
Über den ISP-Programmier hab ich die Stromversorgung und programmier den Uno. An diesem ist auch der LCD angeschlossen. Den USB-Konverter benutze ich, um vom PC (Arduino IDE) über den Serial Monitor Daten an den Uno zu schicken, der die erhaltenen Daten auf dem Display anzeigen soll.

Um die geplante Struktur des Datenversands mal kurz zu erläutern:
Es geht nur um Zahlen. Diese sollen in unterschiedlich großen Blöcken verschickt werden, ja nach Info. Diese Blöcke haben zwischen 1 und 3 Ziffern (mit einer Ausnahme: ein Block soll “–XXX” sein (XXX für jeweils eine Ziffer)).
Ich dachte, die Blöcke über Trennzeichen anzeigen zu können (z. B.: --XXX;XX;XXX;XX;XXX;XX;XX;XXX;XX;XX;XXX;XX;XX;XXX[…]), da jeder Block entweder ein Befehl oder Daten sind. Es muss aber nicht dieses Muster sein. Ebenso gut könnte es auch: --XXX;XX sein - je nach Befehl.

Ich bin bisher noch nicht dahinter gekommen, wie ich die geschickten Daten aufsplitten kann.

Für den Versuch hatte ich folgenden Sketchansatz:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

char befehl[50];
char stopp_zeichen[] = {";"};
int new_data = 0;

void setup()
{
  Serial.begin(9600);
  
    lcd.begin(16,2);
  for (int i = 0; i< 2; i++)
  {
    lcd.backlight();
    delay(500);
    lcd.noBacklight();
    delay(500);
  }
  lcd.backlight();
  
  lcd.setCursor(0,0);
  lcd.print("Test");
  delay(2500);
  lcd.clear();
  lcd.setCursor(0,0);
}

void loop()
{
  if (Serial.available() > 0)
  {
    
    Serial.readBytesUntil(stopp_zeichen[0], befehl, 50);
    new_data = 1;
  }
  if (new_data == 1)
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(befehl);
    new_data = 0;
  }
}

Hab ich jetzt in Serial Monitor "Hallo;Bye " eingegeben, so wurde zuerst “Hallo” angezeigt und eine Sekunde “Bye”. Meinem Verständnis durch diese Seite sollte er aber doch nur “Hallo” einlesen und den Rest verschmeißen, oder in nen anderen Teil von der Variable befehl speichern?

Ich hoffe, ihr könnt mir hier ein wenig weiter helfen.

Vielen Dank schon mal.

LG

Fipsi

Hab ich jetzt in Serial Monitor "Hallo;Bye " eingegeben, so wurde zuerst "Hallo" angezeigt und eine Sekunde "Bye".

Ist ja auch richtig so, Hallo ist ein Befehlt und Bye ist ein Befehl. Alle Befehle die du eingibst, werden angezeigt. Die Befehle die nicht sein sollen, werden ja auch nicht eingegeben, logisch :D

Ja ne… so halbwegs… das ist ja das Problem^^.
Ich brauch schon alles, was geschickt wird, aber wie bekomm ich so Zugriff drauf, dass ich damit arbeiten kann?
Als schnell zusammengewürfeltes Beispiel (muss jetzt von der Syntax her nicht korrekt sein hust) :

if (befehl[0] == "--".scheib_num)
{
	if (befehl[1] == "01")
	{
		if (befehl[2] == "01")
		{
		
		}
		else if (befehl[2] == "02")
		{
		
		}
	}
	else if (befehl[1] == "02")
	{
		if (befehl[2] == "01")
		{
		
		}
		else if (befehl[2] == "02)
		{
			
		}
	}
}
else if (befehl[0] == "--999")
{
	if (befehl[1] == "01")
	{
		if (befehl[2] == "01")
		{
		
		}
		else if (befehl[2] == "02")
		{
		
		}
	}
	else if (befehl[1] == "02")
	{
		if (befehl[2] == "01")
		{
		
		}
		else if (befehl[2] == "02)
		{
			
		}
	}
}

Ich weiß, dass es so nicht ganz realisierbar ist, ich hab jetzt nur versucht deutlich zu machen, was ich mein (und wie ich bei PHP ran gehen würde).
Ich hoffe, so ist es ejtzt verständlich.

LG

Fipsi

Würde das ganze mit case machen:

const byte ENABLE_PIN = 2;
const byte ledPin13 = 13; // the pin that the green LED is attached to
const byte ledPin12 = 12; // the pin that the red LED is attached to


void setup() {
  Serial.begin(14400);
  //rs485.begin (9600);
  pinMode (ENABLE_PIN, OUTPUT);


}

void loop() {
  

   // see if there's incoming serial data:
   if (Serial.available() > 2) {
   char command =Serial.read();
   byte Slave =Serial.read();
   byte Slavevalue =Serial.read();
   byte returnvalue = 0;
   
    //Kommando auswerten
    switch(command) {
 case 'S':
      Serial.print("S Kommando empfangen, setze Slave ");
      Serial.print(Slave);
      Serial.print(" auf ");
      Serial.println(Slavevalue);
      pinMode(Slave,OUTPUT);
      digitalWrite(Slave,Slavevalue);

      //gesetzten wert auch zurückliefern
      returnvalue=Slavevalue;
      break;
      
 case 'R':
      returnvalue = digitalRead(Slave);
      Serial.print("R Kommando empfangen, lese Pin ");
      Serial.print(Slave);
      Serial.print(". Wert = ");
      Serial.println(returnvalue);
      break;
      
 default:
      Serial.println("Fehler, unbekanntes Kommando");    
      break;
    }//switch(command)
   }// Serial.available
   
 }// loop

Am besten du verwendest ASCII-codierte Zahlen als Kommandos und keine Wörter. Dadurch wird das einfacher.

Kommandos kann man schön mit enums darstellen:

enum commands { COMMAND_1, COMMAND_2, COMMAND_3 };

Den Namen werden dann automatisch Zahlen von 0 an zugewiesen

Dann kannst du einfach sowas machen:

int currentByte = Serial.read() - '0';

if(currentByte == COMMAND_1)
{
}

Das reicht für 10 Kommandos. Wenn es mehr sind muss man erst mal eine größere Zahl einlesen. Aber das musst du danach sowieso machen.

Die Zahlen kann man bequem Ziffer für Ziffer aufsummieren. Das habe ich hier für eine einzelne Zahl gezeigt: http://forum.arduino.cc/index.php?topic=257248.msg1819200#msg1819200

Das kann man so anpassen, dass er eine Zahl bis zu einem Trennzeichen aufsummiert. Wenn das Trennzeichen kommt speichert man die Zahl ab und liest eine weitere ein. Die Zahlen kann man dann entweder als Kommando interpretieren oder die Daten in einem Array speichern.

Dann brauchst du nur nur ein paar Zustands-Variablen die dir Anzeigen wo man gerade ist: * verarbeite Kommando * verarbeite Daten * Index-Variable für die Zahlen um sie in einem Array abzuspeichern

Auch das geht schön mit enums

Ob die fertig bist weißt du entweder über das Kommando. Oder du schickt am Schluss ein Ende-Zeichen.

Das hier geht auch in die Richtung http://www.gammon.com.au/forum/?id=11425&reply=1#reply1

Wörter verwende ich eh nicht. Nur Ziffern, die Trennzeichen und die "--" als Anzeige für eine neue Befehlskette. Und wie codier ich die Zahlen dann richtig in ASCII?

Das mit den Kommandos muss dann wie ausschauen beim versenden?

Serial.print("COMMAND_1");

oder wie?

Die längste Befehlskette, die ich bisher geplant hab, schaut in den Blöcken aufgetrennt, folgendermaßen aus: - "--XXX": "--" Als Kennzeichnung des Beginns einer neuen Befehlskette, "XXX" für drei Ziffern zum ansprechen der Slaves. - "XX": Zwei Ziffern, die eine Befehlskategorie angeben - ("1") Scheibennummer eingeben - ("2") Scheibennummern einsammeln und auf Vollständigkeit überprüfen - ("3") Überprüfen, ob Terminal erreichbar ist (alle 30 Sekunden) - ("4") Terminals sperren und Daten einsammeln (große Anzahl von Daten der Slaves) - ("5") Daten an Terminals ausgeben und Temrinals freigeben - "1"/"2" ob 3 oder 6 Pfeile eingegeben werden - "1"/"0" Schütze A vorhanden oder nicht - "XXX" Ringzahlen Schütze A - "XXX" X'er Schütze A - "XXX" 10'er Schütze A - "1"/"0" Schütze B vorhanden oder nicht - "XXX" Ringzahlen Schütze B - "XXX" X'er Schütze B - "XXX" 10'er Schütze B - "1"/"0" Schütze C vorhanden oder nicht - "XXX" Ringzahlen Schütze C - "XXX" X'er Schütze C - "XXX" 10'er Schütze C - "1"/"0" Schütze D vorhanden oder nicht - "XXX" Ringzahlen Schütze D - "XXX" X'er Schütze D - "XXX" 10'er Schütze D (diese Daten soll immer nur das entsprechende Terminal speichern; beim einsammeln werden diese Daten an's Steuergerät geschickt)

Ich hatte noch mehr Befehle, aber die wollen mir gerade um's verrecken nicht einfallen. Die längste Befehlskette jedenfalls ist 49 Zeichen lang plus 20 Trennzeichen.

Wie ich da die Kommandos reingebacken bekomm, hab ich ehrlich gesagt noch nicht richtig verstanden.

Vielen Dank und LG

Fipsi

Nimm doch einfach die Modbus Library:

enum 
{     
  // just add or remove registers and your good to go...
  // The first register starts at address 0
  ADC_VAL,     
  PWM_VAL,        
  HOLDING_REGS_SIZE // leave this one
  // total number of registers for function 3 and 16 share the same register array
  // i.e. the same address space
};
void loop()
{

  modbus_update();
  
  holdingRegs[ADC_VAL] = analogRead(A0); // update data to be read by the master to adjust the PWM
  
  analogWrite(LED, holdingRegs[PWM_VAL]>>2); // constrain adc value from the arduino master to 255

  
}

Fipsi: Das mit den Kommandos muss dann wie ausschauen beim versenden?

Serial.print("COMMAND_1");

oder wie?

Das sind Integer!

http://de.wikipedia.org/wiki/Aufz%C3%A4hlungstyp http://openbook.galileocomputing.de/c_von_a_bis_z/015_c_strukturen_010.htm#mj259bc5aff9087f7c3548408b56b74318

Die Besonderheit bei enums in C ist das sie nicht typisiert sind und daher frei austauschbar mit Integern sind. Hinter jeder enum Konstante steckt lediglich eine Zahl.

Du kannst das enum auch weglassen und statt dessen einfach auf die blanken Zahlen abfragen. Oft es halt praktisch wenn man ansprechende Namen hat, damit sofort sieht was da gemacht wird.

skorpi08: Nimm doch einfach die Modbus Library:

enum 
{     
  // just add or remove registers and your good to go...
  // The first register starts at address 0
  ADC_VAL,     
  PWM_VAL,        
  HOLDING_REGS_SIZE // leave this one
  // total number of registers for function 3 and 16 share the same register array
  // i.e. the same address space
};
void loop()
{

  modbus_update();     holdingRegs[ADC_VAL] = analogRead(A0); // update data to be read by the master to adjust the PWM     analogWrite(LED, holdingRegs[PWM_VAL]>>2); // constrain adc value from the arduino master to 255

  }

Gibt's dazu noch mehr als nur die Startseite von dem Link? Damit und mit dem Beispiel allein blick ich da nicht ganz durch.

Serenifly:

Fipsi: Das mit den Kommandos muss dann wie ausschauen beim versenden?

Serial.print("COMMAND_1");

oder wie?

Das sind Integer!

http://de.wikipedia.org/wiki/Aufz%C3%A4hlungstyp http://openbook.galileocomputing.de/c_von_a_bis_z/015_c_strukturen_010.htm#mj259bc5aff9087f7c3548408b56b74318

Die Besonderheit bei enums in C ist das sie nicht typisiert sind und daher frei austauschbar mit Integern sind. Hinter jeder enum Konstante steckt lediglich eine Zahl.

Du kannst das enum auch weglassen und statt dessen einfach auf die blanken Zahlen abfragen. Oft es halt praktisch wenn man ansprechende Namen hat, damit sofort sieht was da gemacht wird.

Okay, ich glaub, jetzt steig ich dahinter: Beim Empfänger (also Slave) soll ich nur die Kommandonamen verwenden, die sozusagen von den Serialdaten interpretiert werden. Aber das löst doch dann immer noch nicht mein Problem mit der Aufsplittung?

LG

Fipsi

Gibt es ja, allerdings nicht in deiner Sprache. Hatte ich dir schon gepostet in dem Platinen Thread( glaub der wars). http://www.machsupport.com/forum/index.php?topic=23489.0

Ist eigentlich ganz simple.

enum 
{     
  ADC_VAL,     
  PWM_VAL,        
  HOLDING_REGS_SIZE // leave this one
};

Hier gibts ADC_VAL, PWM_VAL die sind halt als Platzhalter. Wie Serenifly erklärt hatte, wie es bei enum zugeht.

modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);

/* parameters(HardwareSerial* SerialPort, long baudrate, unsigned char byteFormat, unsigned char ID, unsigned char transmit enable pin, unsigned int holding registers size, unsigned int* holding register array) */

Ist somit auch erklärt was hier was ist. Baudrate wäre 9600, Slave ID wäre hier die 1 und der EnablePin wäre 2.

  holdingRegs[ADC_VAL] = analogRead(A0); // update data to be read by the master to adjust the PWM
  
  analogWrite(LED, holdingRegs[PWM_VAL]>>2); // constrain adc value from the arduino master to 255

Das erste was übertragen wird ist ADC_VAL. Es hat den Wert von analogRead(A0). Das zweite wäre PWM_VAL, ist dann halt der Wert wie hell die LED leuchten soll.

Wichtig ist dass die Rheienfolge beim Master und Slave dieselbe ist, also in diesem Fall ADC_VAL als erstes übertragen und dann PWM_VAL.

Ach ja, ich erinner mich. Ich weiß nicht mehr genau, fällt mir auch beim machfachen überfliegen nicht mehr ein, warum das nicht das richtige war.. aber ich glaub, da mussten doch die Slaves alle fest eingeschrieben werden? Das kann ich bei mir ja nicht machen, weil ich eine variable Anzahl hab.

Was ich mir erhofft hatte, war was, wie ich's aus PHP kenn: In einer Variaable krieg ich alle Daten aneinanderhängend geliefert:

$variable = "daten1;daten2;daten3;daten4;daten5";

Durch die explode-Funktion hab ich dann einen Array bekommen, über den ich auf die einzelnen Daten zugreifen konnte:

$array = explode(";", $variable);
echo $array[0]; // daten1
echo $array[1]; // daten2
echo $array[2] // daten3
echo $array[3] // daten4
echo $array[4] // daten5

Gibt es sowas in der Art in C bzw. eben bei Arduino nicht? Ich hab jedenfalls nichts gefunden :/

LG

Fipsi

enum 
{     
daten1,daten2,daten3,daten4,daten5
};
Serial.write(daten1);
Serial.write(daten2);
Serial.write(daten3);
Serial.write(daten4);
Serial.write(daten5);

Das verschicken ist nich das Problem.. sondern das empfangen und dann aufsplitten...

LG

Fipsi

genau so.

Hä? Wie genau so? Es is Sonntag Abend nach nem langem Wochenende… geht’s bitte auch für die, deren Kopf unter der Woche geraucht und jetzt ein wenig Entspannung sucht?^^ Hast du evtl. ein kurzes CodeBeispiel? Also vom senden und empfangen?

LG

Fipsi

Ja, siehe Modbus Master und Slave Beispiele.

Ich weiß grad nicht, ob ich lachen oder heuln soll.. ich checks einfach nicht... am Rande der Verzweifelung steh

LG

Fipsi

Kenn ich, wart einfach noch 1 Jahr, dann klappts :D

Ich hatte eigentlich vor, bis Ende Januar die ersten Prototypen fertig zu haben ^^. Ich glaub, für heut hab ich einfach schon genug gemacht.. solangs mir niemand noch mehr vereinfacht, komm ich da heut nicht mehr drauf :/ ^^.

LG

Fipsi

Hast hoffentlich nicht gesagt, Januar 2015. :zipper_mouth_face: Ich dachte, so wie ich es aus den anderen Thread zu CAD und PCB Layout herausgelesen hab, dass die Software soweit schon steht und die lediglich das Gerät kleiner wollten.

/IRONIE Wenn das so weiter geht, bekommt skorpi uvm. % von den Verleihgebühren /IRONIE_OFF