Problem bei mehreren Zeichen über MAX487

Hallo zusammen,

ich bin gerade mal wieder im Krieg mit meinen kleinen Nachbauten:

Nach wie vor ist die Hauptfront die Kommunikation.
Ich verwende ja dafür die MAX487 (Datenblatt).

Mein Aufbau schaut wiefolgt aus:

Arduino 1:
Arduino-Nachbau mit MAX487 und Tastaturmatrix.
Aktueller Sketch:

#include <Keypad.h>

const byte ROWS = 6; // Four rows
const byte COLS = 3; // Three columns
// Define the Keymap
char keys[ROWS][COLS] = {
  {'A','R','B'},
  {'C','X','D'},
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'M','*','C'}
};
// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 5, 4, 3, 9, 10, 11 };
// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[COLS] = { 8, 7, 6 }; 

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup()
{
  pinMode(2, OUTPUT);
  Serial.begin(115200);
}

void loop()
{
  char key = kpd.getKey();
 
  if (key)
  {
    /*digitalWrite(2, HIGH);
    Serial.write(key);
    digitalWrite(2,LOW);*/
    digitalWrite(2,HIGH);
    Serial.println("Hallo");
    digitalWrite(2,LOW);
  }
}

Arduino 2:
Arduino-Nachbau mit MAX487 und Display.
Aktueller Sketch:

#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;
byte data;

void setup()
{
  Serial.begin(115200);
  
  lcd.begin(20,4);
  
  for (int i = 0; i< 2; i++)
  {
    lcd.backlight();
    delay(500);
    lcd.noBacklight();
    delay(500);
  }
  lcd.backlight();
  
  lcd.setCursor(0,0);
  lcd.print("Archery Results");
  lcd.setCursor(0,1);
  lcd.print("Phillip Seelig");
  delay(3000);
  lcd.clear();
}

void loop()
{
  if (Serial.available() > 0)
  {
    lcd.clear();
    lcd.setCursor(0,0);
    Serial.readBytes(befehl, 250);
    lcd.print(befehl);
    /*lcd.setCursor(0,1);
    Serial.readBytes(befehl, 50);
    lcd.print(befehl);*/
  }
}

Das Problem ist jetzt folgendes:
Drück ich am Arduino 1 eine Taste, so zeigt der Serialmonitor (im Arduino IDE über den USB-Konverter) "Hallo" an. Schau ich allerdings auf das Display von Arduino 2, so wird nur "H" angezeigt.
Steck ich RX/TX vom USB-Konverter am Arduino 2 an, drück bei Arduino 1 auf eine Taste und beobachte den Serialmonitor am PC, so rührt sich dort gar nichts, am Display von Arduino 2 erscheint wieder "H".
Geb ich im Serialmonitor (immer noch an Arduino 2) "Hallo" ein, so erscheint das auch am Display von Arduino 2.

Also scheint es doch folgende Probleme zu geben:
Die MAX187 senden immer nur 1 Byte. Warum?
Hab ich den USB-Konverter am Empfänger (nach dem MAX187, also parallel zum ATMega), so kann ich über den Serialmonitor keine Daten lesen. Warum?

Ich steh mal wieder - oder besser gesagt immer noch? - am Rande der Ratlosigkeit, was die Kommunikation angeht.. und hoff auf eure Hilfe.

LG und guten Start ins Wochenende

Fipsi

Serial Monitor geht, aber dein Sketch nicht? Dann probier mal das Auslesen besser zu machen:
http://forum.arduino.cc/index.php?topic=264546.msg1869774#msg1869774

SERIAL_BUFFER_SIZE kannst dabei kleiner machen

Wenn ich jetzt schreib:

varibale = processSerial();

dann hab ich in varibale den empfangenen Text, oder?
Und SERIAL_BUFFER_SIZE in Bit oder Byte?

LG

Fipsi

Nein. Wie soll das gehen wenn die Funktion void als Rückgabewert hat? :~ :~

Sobald checkSerial() true zurückgibt steht der String komplett in serialBuffer. Deshalb das hier:

void processSerial()
{
     Serial.println(serialBuffer);
}

Du kannst es auch so machen:

void loop()
{
   if(readSerial())
   {
       Serial.println(serialBuffer);
   }
}

Aber sobald die Auswertung komplexer ist und man z.B. noch String Parsing macht ist es besser man hat das in einer eigenen Funktion

Und SERIAL_BUFFER_SIZE in Bit oder Byte?

Byte natürlich. Es ist ein char Array!

Ich hasse die Dateitypen in C hust.

Ich glaub, meine Auswertung kann man unter "komplexer" einordnen hüstelchen.

Danke, ich werds dann nachher mal ausprobieren.

LG

Fipsi

Und aus nachher wurde zwei tage später, denn der PC wollte nicht mehr.. grummel.

Jedenfalls, gerade meinen Sketch geändert:

#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;
byte data;

const int SERIAL_BUFFER_SIZE = 81;
char serialBuffer[SERIAL_BUFFER_SIZE];

void setup()
{
  Serial.begin(115200);
  
  lcd.begin(20,4);
  
  for (int i = 0; i< 2; i++)
  {
    lcd.backlight();
    delay(500);
    lcd.noBacklight();
    delay(500);
  }
  lcd.backlight();
  
  lcd.setCursor(0,0);
  lcd.print("Archery Results");
  lcd.setCursor(0,1);
  lcd.print("Phillip Seelig");
  delay(3000);
  lcd.clear();
}

void loop()
{
  if(readSerial())
       processSerial();
}

bool readSerial()
{
  static unsigned int index;

  if(Serial.available() > 0)
  {	
    char c = Serial.read();

    if(c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if(c == '\n')
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

void processSerial()
{
     lcd.print(serialBuffer);
}

Jetzt spuckt der Display gar nichts mehr aus .___.

LG

Fipsi

Die Routine erwartet, dass der empfangene Text mit einem Linefeed abgeschlossen ist (wegen dem c == '\n'). Wenn du also auf der anderen Seite println() machst, sollte es theoretisch passen.

Probier es erst mal mit Serial Monitor. Den Monitor dabei so einstellen, dass er ein LF am Ende schickt und dann solltest du das Empfangen was du eintippst. Da kannst du auch das was du empfängst auf Serial zurückschicken.

Was mir aus der bisherigen Beschreibung nicht ganz klar ist:
ich hoffe das hast da nicht irgendwie den den Atmega parallel über USB am PC und über den MAX487 mit dem anderen Arduino verbunden? Das geht nicht. Wenn du zwei Serial Verbindungen gleichzeitig brauchst musst du eine mit SoftwareSerial (besser AltSoftSerial) realisieren!

Ah ja, mit dem Serial Monitor funktionierts einwandfrei :slight_smile:

Aber wenn ichs über den anderen Arduino machen will, dann nicht. Sketch:

#include <Keypad.h>

const byte ROWS = 6; // Four rows
const byte COLS = 3; // Three columns
// Define the Keymap
char keys[ROWS][COLS] = {
  {'A','R','B'},
  {'C','X','D'},
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'M','*','C'}
};
// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = { 5, 4, 3, 9, 10, 11 };
// Connect keypad COL0, COL1 and COL2 to these Arduino pins.
byte colPins[COLS] = { 8, 7, 6 }; 

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup()
{
  pinMode(2, OUTPUT);
  Serial.begin(115200);
}

void loop()
{
  char key = kpd.getKey();
 
  if (key)
  {
    /*digitalWrite(2, HIGH);
    Serial.write(key);
    digitalWrite(2,LOW);*/
    digitalWrite(2,HIGH);
    Serial.println("Hallo");
    digitalWrite(2,LOW);
  }
}

Und doch.. zum probieren hatte ich gerade den USB-Konverter zum PC und den MAX487 parallel geschalten. Wenn ich über den Serial Monitor was geschickt hab, wurde es problemlos am LCD angezeigt. (ebenso auch, wenn ich über den USB-Konverter was empfangen und gleichzeitig über den MAX geschickt hab.) Aber wenn ich über den MAX was schick, dann zeigt der LCD (mit dem neuem Code) nichts an, egal, ob der USB-Konverter angesteckt ist oder nicht.

LG

Fipsi

So, nachdem meine Festplatte verreckt ist und ich jetzt endlich ne neue hab, kanns ja jetzt weiter gehn..:

Ich will jetzt gerade mal den Empfänger vorbereiten.. festgestellt hab ich mir das folgendermaßen:

Es werden immer die "gleichen Datentypen" geschickt.. also damit mein ich jetzt, es ist immer gleich aufgebaut:

  • drei Ziffern zum ansprechen über die ID
  • 1 Ziffer für die Aufgabe
  • und dann für die jeweilige Aufgabe noch weitere Ziffern

Aber die Blöcke sind immer gleich lang. Ich hab mir jetzt gedacht, ich könnte doch zuerst die ersten drei Ziffern auffangen, entsprechen die der ID des Boards, liest er weiter, wenn nicht, hört er nicht weiter zu. Als nächstes fängt er die Aufgabenziffer ab. Von dieser hängt dann ab, was er mit den nächsten Blöcken macht.
Ich arbeite ja jetzt zum empfangen mit diesem Code:

void loop()
{
  if(readSerial())
       processSerial();
}

bool readSerial()
{
  static unsigned int index;

  if(Serial.available() > 0)
  {	
    char c = Serial.read();

    if(c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if(c == '\n')
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

void processSerial()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(serialBuffer);
}

Könnte ich das jetzt folgendermaßen abändern?

void loop()
{
  if(readSerial())
       if (serialBuffer[0].serialBuffer[1].serialBuffer[2] == scheiben_ID)
       {
           
       }
}

Ich hab jetzt nur keine Ahnung, wie ich in C aus drei Ziffern eine Zahl machen kann.. da bräuchte ich noch nen Anstoß^^.
Aber würde das so ungefähr funktionieren? Wenn ja, dann hätte ich dend reh endlich raus^^.

LG
Fipsi

Könnte ich das jetzt folgendermaßen abändern?

Code:

void loop()
{
if(readSerial())
if (serialBuffer[0].serialBuffer[1].serialBuffer[2] == scheiben_ID)
{

}
}
Aber würde das so ungefähr funktionieren? Wenn ja, dann hätte ich dend reh endlich raus^^.

Leider nein... Deine Erfindung wie du den . verwenden willst, ist leider kein C, und damit dem Compiler völlig fremd.

Ich hab jetzt nur keine Ahnung, wie ich in C aus drei Ziffern eine Zahl machen kann.. da bräuchte ich noch nen Anstoß^^.

Da gibt es z.B. atoi(char*)
oder in deinem Fall geht auch dieses:

unsigned int mini_atoi(char * buf)
{
    unsigned int result = 0;
    while (*buf != '\0') 
     { 
        result = result * 10 + *buf - '0';
        buf++ ; // nächstes Zeichen
     }
     return result;
}

Wenn du das verstehst, und also auch dessen Grenzen erkennst, hättest du den Dreh endlich raus :wink:

Dafuq.. hab ganz vergessen, den akt. Code zu posten:

[...]
// Serial Buffer
const int SERIAL_BUFFER_SIZE = 45;
char serialBuffer[SERIAL_BUFFER_SIZE];
[...]
// Serialempfänger
bool readSerial()
{
  static unsigned int index;

  if(Serial.available() > 0)
  {	
    char c = Serial.read();

    if(c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if(c == '\n')
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}
[...]
  if(readSerial())
  {
    int id_sended = (serialBuffer[0] * 100) + (serialBuffer[1] * 10) + serialBuffer[2];
    
    if ((id_sended == scheibe_ID) || (id_sended == 999))
    {
       do_status = serialBuffer[3];
       sended_data = 0;
    }
  }

So sollte das doch gehn? Zumindest der Compiler jammert nicht^^.

LG

Fipsi

Was gibt er aus? Bzw wo ist das Problem?

Edit:

char c = Serial.read();

Du liest hier char ein. Solange nicht anders angegeben ist ein char signed.

Mach einfach

char c = Serial.read() - '0';

=(
Nein. Das geht nicht. Nur weil der Compiler nicht meckert ist das noch lange nicht richtig.

Schau dir mal die ASCII Tabelle an:

Welchen Wert hat '0'? 48! Nicht 0. Um also vom ASCII Zeichen auf die Zahl zu kommen musst du - '0' machen. Wie oben:
"result = result * 10 + *buf - '0';"

Wenn dir solche Sachen nicht klar sind, verwende lieber fertige Konvertierungsfunktionen wie atoi():
http://www.cplusplus.com/reference/cstdlib/atoi/

Dein Code erwartet außerdem eine ganz bestimmte Anzahl von Ziffern und du fragst nicht ab ob die wirklich da sind.

sschultewolter:
Mach einfach

char c = Serial.read() - '0';

Wieso? Er will vielleicht auch irgendwann mal was anderes als Zahlen lesen. Der Einlese-Funktion sollte vollkommen wurschst sein ob das Zahlen oder Text sind. Die liest einfach alles und schreibt es wie es ist in den Puffer.

Die Konvertierung von ASCII nach Zahlen macht man wenn alles fertig eingelesen wurde.

EDIT:
Außerdem zerstörst du damit die gesamte Funktion, da danach auf if(c == '\n') abgefragt wird! Wenn überhaupt müsste man die '0' erst beim Abspeichern in das Array abziehen. Dann hat man aber immer noch das Problem, dass man dann keinen Text mehr empfangen kann.

Jetzt bitte noch mal zusammengefasst, was falsch ist.. ih beiden verwirrt mich.

Ich will nur Zahlen senden. Text würde nichts bringen und Anweisungen codier ich in Zahlen.
Und ich hab auch keine faste Anzahl an gesendeten Ziffern. Das ist je nach Befehl (4 Stelle) unterschiedlich.

LG

Fipsi

Hab mich vom unteren Teil seines Sketches ablenken lassen :* Dort will er wieder mit Zahlen und nicht Chars arbeiten.

Alternativ kannst du auch die Abfragen innerhalb der Interrupt Routine versuchen anzuwenden. Er setzt einfach eine Variable, sobald ein neuer String komplett empfangen wurde. Muss aber in der loop entsprechend zurückgesetzt werden. Direkt nutzten wirst du das nicht können.

// lass ich doch mal weg bevor noch mehr Verwirrung herscht ;)

Siehe #Reply12

Ignoriere was sschultewolter gesagt hat. Er macht das an der falschen Stelle.

Am besten du verwendest wie gesagt atoi(). Da musst du nicht über sowas nachdenken und bist flexibel was die Länge der Zahlen betrifft. Wenn du mehrere Zahlen auf einmal senden willst, dann trenne sie mit Kommas und verwende strtok() + atoi().

z.B.

const int MAX_NUMBERS = 3;
int numbers[MAX_NUMBERS];

char str[] = "123,456,789";

int index = 0;
char* ptr = strtok(str, ",");
while(ptr != NULL && index < MAX_NUMBERS)
{
  numbers[index++] = atoi(ptr);
  ptr = strtok(NULL, ",");
}

Danach stehen im Array jeweils die drei Zahlen 123 und 456 und 789

Das glaub ich jetzt nicht...
Jetzt hab ich das bekommen, was ich 3 Threads lang wollte, als ich nen anderen Weg gefunden hab schrei.

Mein "Protokol" ist ja folgendermaßen aufgebaut:

Empfänger-ID, Befehls-ID, evtl. Zusatzziffern

Zu erst wollte ich es ja über "XXX;X[;XXXXXXXXXXXXXXXXXXXXX]" senden und dann in die einzelnen Teile splittern und in nem Array haben.. das ist doch das, was der letzte Code von Serenifly jetzt ist, oder?
Weil mir das aber bisher keiner sagen konnte, und ichs auch nirgends gefunden hab, wollte ichs einfach "XXXX[XXXXXXXXX]" schicken und dann über den von mir vorher geschickten Code einfach die benötigten Ziffern rausangeln..

Was davon ist jetzt die elegantere, bessere und vorallem Speicherplatzsparendere Methode?

LG

Fipsi

strtok() + atoi() ist ziemlich sparsam was Flash betrifft. Wo hier insgesamt RAM draufgeht ist dass man erst mal alles in den seriellen Puffer der IDE liest, dann in einen eigenen Puffer kopiert und dann in ein Array konvertiert. Dadurch ist man aber auch völlig flexibel bezüglich der Beschaffenheit der Daten und was man damit macht.

In diesem speziellen Fall (nur positive Zahlen) könnte man aber auch das machen:

const int MAX_NUMBERS = 3;
int numbers[MAX_NUMBERS];

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  if (readSerial())
    processSerial();
}

bool readSerial()
{
  static byte index;
	
  if(Serial.available())
  {
	char c = Serial.read();

	if(c >= '0' && c <= '9' && index < MAX_NUMBERS)
	{
		numbers[index] = numbers[index] * 10 + c - '0';
	}
	else if(c == ',')
	{
		index++;
	}
	else if(c == '\n')
	{
		index = 0;
		return true;
	}
  }
	
  return false;
}

void processSerial()
{
  for(int i = 0; i < MAX_NUMBERS; i++)
	Serial.println(numbers[i]);
  Serial.println();

  memset(numbers, 0, sizeof(numbers));
}

Also direkt Zeichen für Zeichen auslesen und on-the-fly konvertieren.

index kann man auch außerhalb der Funktion verwalten. Dann weiß man wie viele Zahlen empfangen wurden.

Es geht nur um positive Zahlen (und Null'en), so schlecht, dass man Minus schießt bin nicht mal ich :'D

Ich kann jetzt aber nicht ganz raus lesen, was der von dir als letztes gepostete Code macht.. macht der wieder n gegliederten Array oder schmeißt der jede einzelne Ziffer in nen Array?

LG

Fipsi

Edit: Ich nehms zurück: der steckts in nen gegliederten Array.
Aber was macht der memset?