Barcodescanner mit Datenbank fürs Rüsten

Hallo zusammen,

kann mir jemand einschätzen was ich für die Umsetzung des folgenden Projekt Hardware- und Aufwandsmässig benötigen werden.

Ziel ist es auf ein Gerät mit RS232 Schnittstelle einen String zu übertragen. Hierfür mit Material mit einem Barcode gekennzeichnet abgescant. Die eingescannte Zahl wird in einer Bibliothek gesucht und durch den benötigten String ersetzt und auf das Gerät übertragen.

Im Endeffekt geht es um einen Scanner mit Bibithek im Hintergrund die Material mit Gerät verheiratet und sicherstellt, dass das Material mit Alternativnamen richtig gerüstet wird.

Danke euch!

Gruß

max

musst du genauer spezifizieren.
Wie viele Attribute je Eintrag?
Wie groß ist jedes einzelne Attribut in byte?
Wie viele Einträge insgesamt (wie viele hast am Anfang, wie viele können es werden)?

Daraus errechnet sich der Speicherbedarf für deine "Datenbank". Wir brauchen eine exakte KB/MB/GBByte Angabe von dir wie viel da an Daten lokal vorhanden sein sollen.

Dann braucht es noch eine Info zur RS232: Mit welcher Geschwindigkeit arbeitet diese?

So aus dem Bauch raus würde ich mit einem NodeMCU (ESP8266) / Wemos D1 oder einem ESP32 anfangen.

Liegt nur an deinen Fähigkeiten. Oder anders ausgedrückt? Was bringst du an Vorwissen und insbesondere Microcontrollerprogrammierung mit?

Die Bib hat im Endeffekt sehr rudimentäres Aussehen.
Es wäre eine CSV-Datei die so Aussehen würde:

String ;EAN 8 Barcode
R0805_01;70002893
R0805_02;70002873
R0805_03;70002368
R0805_04;70002763
R0805_05;70002615
R0805_06;70002559
R0805_07;70002172
R0805_08;70002948
R0805_09;70002933
R0805_10;70002658
R1206_01;70002702
R1206_02;70002475
R1206_03;70002822
R1206_04;70002280
R1206_05;70002319
R1206_06;70002633
R1206_07;70002046
R1206_08;70002114
R1206_09;70002462
R1206_10;70002319
R1206_11;70002902
R1206_12;70002906
R1206_13;70002738
R1206_14;70002689
CDFP14_1;70002180
CDFP14_2;70002450
CDFP14_3;70002442
CDFP14_4;70002019
DO-213AA_01;70002822
DO-213AA_02;70002799
DO-213AA_03;70002135
DO-213AA_04;70002055

Wir reden von max. Daten im KB-Bereich.
Über die RS232 werde ich mich noch Informierne und melde mich.

Zu den Vorkentnissen: Ich bringe nur ein fortgeschrittenes Anfängerwissen mit. Mir fehlen leider noch objektbezogene Erfahrungen.

ok, das sind nur 618 Zeichen und davon 32 Strichpunkte.
Das passt im prinzip auch in den Flash vom Uno falls du dich damit besser auskennst.
Dann muss der Leser hat mit den niedrigen Geschwindigkeiten von SoftSerial Umgehen können - aber das wirst auch schaffen.

Oder vieleicht besser ein Arduino Mega - der hat 4 Serielle Schnittstellen. Weil wenn ich dich richtig lese hast du

deinen Controller und

  • den Barcode Scanner (ist der auch Seriell?)
  • dein "Gerät" wo es hin soll ... (Seriell)

Du möchtest zwischen einem PC, welcher die letztendliche Verarbeitung des Barcodes vornimmt, und dem Barcodescanner, einen "Übersetzer" bauen?

Warum schreibst Du nicht ein Programm für den PC, setzt den Eingabefokus drauf, wandelst den String um und sendest ihn weiter an die richtige Applikation?
I.d.R. fungieren die Barcodescanner doch als nichts anderes als eine Tastatureingabe.
Dann sparst Du Dir das ganze gebastel.

Richtig sauber wäre natürlich das Programm mit der Übersetzungstabelle zu erweitern, welches momentan den Barcode verarbeitet... Aber ich nehme an das geht nicht/ist zu teuer.

Annahme, der Barcode Reader ist Serial,
Annahme, das Gerät ist eine Maschine die keinen vom Nutzer programmierbaren PC hat
Dann könnte man mit einem Microcontroller den EAN auf einen beliebigen Begriff "ändern" lassen.

die paar Daten gehen auch am Uno im RAM.
In der Praxis würde man dann eher einen Mega nehmen.
nur ein PoC

/*
  Empfängt Daten an der Schnittstelle "Reader"
  Wandelt diese gemäß translate um
  Sendet die Daten an die Schnittstelle "Target"

  Kombination von Beispiel 2 aus Serial Input Basics - updated 
  https://forum.arduino.cc/t/serial-input-basics-updated/382007

  Warum: https://forum.arduino.cc/t/barcodescanner-mit-datenbank-furs-rusten/900857
  
  PoC by noiasca
*/
struct Data
{
  const char * ean;
  const char * out;
};

Data translate[] {
  {"70002893", "R0805_01"},
  {"70002873", "R0805_02"},
  {"70002368", "R0805_03"},
  {"70002763", "R0805_04"},
  {"70002615", "R0805_05"},
  {"70002559", "R0805_06"},
  {"70002172", "R0805_07"},
  {"70002948", "R0805_08"},
  {"70002933", "R0805_09"},
  {"70002658", "R0805_10"},
  {"70002702", "R1206_01"},
  {"70002475", "R1206_02"},
  {"70002822", "R1206_03"},
  {"70002280", "R1206_04"},
  {"70002319", "R1206_05"},
  {"70002633", "R1206_06"},
  {"70002046", "R1206_07"},
  {"70002114", "R1206_08"},
  {"70002462", "R1206_09"},
  {"70002319", "R1206_10"},
  {"70002902", "R1206_11"},
  {"70002906", "R1206_12"},
  {"70002738", "R1206_13"},
  {"70002689", "R1206_14"},
  {"70002180", "CDFP14_1"},
  {"70002450", "CDFP14_2"},
  {"70002442", "CDFP14_3"},
  {"70002019", "CDFP14_4"},
  {"70002822", "DO-213AA_01"},
  {"70002799", "DO-213AA_02"},
  {"70002135", "DO-213AA_03"},
  {"70002055", "DO-213AA_04"}
};


// just for a lokal debugging, "real" Serial
HardwareSerial &Debug = Serial;
// the barcode reader:
HardwareSerial &Reader = Serial;             // am Mega z.B. Serial1
// the target which should receive the data
HardwareSerial &Target = Serial;             // am Mega z.B. Serial2

const byte numChars = 16;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;

void setup() {
  Reader.begin(115200);
  Target.begin(115200);
  Debug.begin(115200);
  Debug.println(F("D Serial translator"));

  // init the display...
}

void loop() {
  recvWithEndMarker();
  showNewData();

}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';  // Annahme wir bekommen einen Zeilende
  char rc;

  while (Reader.available() > 0 && newData == false) {
    rc = Reader.read();
    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

//
void showNewData() {
  if (newData == true) {
    newData = false;
    Debug.print("D Received: "); Debug.println(receivedChars);

    int result = getIndex(receivedChars);
    if (result>=0)
    {
      Debug.print("D translate: "); Debug.println(translate[result].out);
      Target.print(translate[result].out); Target.print("\n");             // achtgeben, was das target als Endezeichen braucht
      updateDisplay(receivedChars, translate[result].out);
    }
    else
    {
      Debug.print("D not found"); Debug.println();
      updateDisplay(receivedChars, "not found");  
    }
  }
}

// suche die Position der needle im Array
int getIndex(const char * needle)
 {
  int result = -1; // not found
  for (size_t i = 0; i < sizeof(translate)/sizeof(translate[0]); i++)
  {
    if (strcmp(needle, translate[i].ean) == 0) 
    {
      Debug.print(F("D found on index ")); Debug.println(i);
      return i;
    }
  }
  return result;
}

void updateDisplay(const char * ean, const char * out)
{
  // could be an external display
  // lcd.clear()
  // lcd.setCursor(0,0);
  // lcd.print("EAN: ");
  // lcd.print("Out: ");
  // lcd.setCursor(0,1);
  // lcd.print(ean);
  // lcd.print(out);

  // if code is unused
  (void)ean;
  (void)out;
}

Gelesene und gefundene EANs werden übersetzt an das Target gesandt
(erkennbar an den Zeilen ohne Debug-D)
Wird der EAN nicht gefunden, dann wird nichts gesandt:

Aufpassen was man schickt. Aktuell verlangt es ein NewLine am Ende.

(und ja, Array ins Progmem, EAN als Zahl ... ja kann man alles machen...)

Add on:
ich habe nun zunächst den EAN numerisch gemacht und dann doch noch probiert, das Array im Flash zu belassen.

Da das Struct Array auch wieder ein Char Array enthält, wars nicht ganz einfach für mich.

Auf

http://www.gammon.com.au/progmem

wird die Verwendung des PROGMEM gut beschrieben, aber genau das Beispiel mit dem Struct gabs nicht zum Copy paste. Daher: selbermachen.

/*
  Empfängt Daten an der Schnittstelle "Reader"
  Wandelt diese gemäß translate um
  Sendet die Daten an die Schnittstelle "Target"

  Kombination von Beispiel 2 aus Serial Input Basics - updated
  https://forum.arduino.cc/t/serial-input-basics-updated/382007

  Warum: https://forum.arduino.cc/t/barcodescanner-mit-datenbank-furs-rusten/900857

  PoC by noiasca

  von 2748/942 (PoC)
  auf 2648/736 bei Verwendung einer numerischer Variablen für den EAN
  auf 2686/232 mit PROGMEM

*/

const byte textSize = 11 + 1; // max size + 1 for Null 

struct Data
{
  uint32_t ean;
  char out[textSize];
};

const Data translate[] PROGMEM = {
  {0,        "not found"},
  {70002893, "R0805_01"},
  {70002873, "R0805_02"},
  {70002368, "R0805_03"},
  {70002763, "R0805_04"},
  {70002615, "R0805_05"},
  {70002559, "R0805_06"},
  {70002172, "R0805_07"},
  {70002948, "R0805_08"},
  {70002933, "R0805_09"},
  {70002658, "R0805_10"},
  {70002702, "R1206_01"},
  {70002475, "R1206_02"},
  {70002822, "R1206_03"},
  {70002280, "R1206_04"},
  {70002319, "R1206_05"},
  {70002633, "R1206_06"},
  {70002046, "R1206_07"},
  {70002114, "R1206_08"},
  {70002462, "R1206_09"},
  {70002319, "R1206_10"},
  {70002902, "R1206_11"},
  {70002906, "R1206_12"},
  {70002738, "R1206_13"},
  {70002689, "R1206_14"},
  {70002180, "CDFP14_1"},
  {70002450, "CDFP14_2"},
  {70002442, "CDFP14_3"},
  {70002019, "CDFP14_4"},
  {70002822, "DO-213AA_01"},
  {70002799, "DO-213AA_02"},
  {70002135, "DO-213AA_03"},
  {70002055, "DO-213AA_04"},
};

// just for a lokal debugging, "real" Serial
HardwareSerial &Debug = Serial;
// the barcode reader:
HardwareSerial &Reader = Serial;             // am Mega z.B. Serial1
// the target which should receive the data
HardwareSerial &Target = Serial;             // am Mega z.B. Serial2

const byte numChars = 16;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;

void setup() {
  Reader.begin(115200);
  Target.begin(115200);
  Debug.begin(115200);
  Debug.println(F("D Serial translator"));

  // init the display...
}

void loop() {
  recvWithEndMarker();
  showNewData();

}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';  // Annahme wir bekommen einen Zeilende
  char rc;

  while (Reader.available() > 0 && newData == false) {
    rc = Reader.read();
    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

//
void showNewData() {
  if (newData == true) {
    newData = false;
    Debug.print("D Received: "); Debug.println(receivedChars);
    uint32_t ean = atol(receivedChars);
    int index = getIndex(ean);
    Data resultData;                                                 // create a temporary buffer with one element
    memcpy_P (&resultData, &translate[index], sizeof(resultData));   // read http://www.gammon.com.au/progmem - ALL!!!
    Debug.print("D translate: "); Debug.println(resultData.out);
    updateDisplay(receivedChars, resultData.out);

    if (index > 0)                                                   // nur bei einem validen Ergebnis auch ans Target senden
    {
      Target.print(resultData.out); Target.print("\n");              // achtgeben, was das target als Endezeichen braucht
    }
  }
}

// suche die Position der needle im Array
size_t getIndex(uint32_t needle)
{
  //Debug.print(F("D needle ")); Debug.println(needle);
  size_t result = 0; // not found
  for (size_t i = 0; i < sizeof(translate) / sizeof(translate[0]); i++)
  {
    uint32_t entry = (uint32_t)pgm_read_dword_near(&translate[i].ean);
    //Debug.print(F("D entry ")); Debug.println(entry);
    if (needle == entry)
    {
      Debug.print(F("D found on index ")); Debug.println(i);
      return i;
    }
  }
  return result;
}

void updateDisplay(const char * ean, const char * out)
{
  // could be an external display
  // lcd.clear()
  // lcd.setCursor(0,0);
  // lcd.print("EAN: ");
  // lcd.print("Out: ");
  // lcd.setCursor(0,1);
  // lcd.print(ean);
  // lcd.print(out);

  // if code is unused
  (void)ean;
  (void)out;
}

Schon interessant wie "einfach" man 500 Byte SRAM sparen kann :wink:

P.S.: Die Fehlermeldung ist jetzt direkt in der Tabelle als Eintrag 0. Gefällt mir im Nachhinein nicht mehr so sehr. Aber ich lass das mal, ist ja nur zum spielen.

Ja, die letzte Variante finde ich schon gelungen....

Ein paar kleine Verbesserungen könnte man da aber auch noch unter bringen...

z.B. führt

  Reader.begin(9600);
  Target.begin(115200);
  Debug.begin(115200);

Zu unerwünschten Seiteneffekten.

Stream ist allgemeiner und würde auch Dateien, Funke usw. als Ziel erlauben.
Zudem kann man auf das memcpy_P verzichten.

Aber das sind alles nur Details.
Keins davon ist Überlebenswichtig.

/*
  Empfängt Daten an der Schnittstelle "Reader"
  Wandelt diese gemäß translate um
  Sendet die Daten an die Schnittstelle "Target"

  Kombination von Beispiel 2 aus Serial Input Basics - updated
  https://forum.arduino.cc/t/serial-input-basics-updated/382007

  Warum: https://forum.arduino.cc/t/barcodescanner-mit-datenbank-furs-rusten/900857

  PoC by noiasca

  von 2748/942 (PoC)
  auf 2648/736 bei Verwendung einer numerischer Variablen für den EAN
  auf 2686/232 mit PROGMEM

*/

using FlashStr = const __FlashStringHelper *;

const byte textSize = 11 + 1; // max size + 1 for Null 

struct Data
{
  uint32_t ean;
  char out[textSize];
};

const Data translate[] PROGMEM = {
  {0,        "not found"},
  {70002893, "R0805_01"},
  {70002873, "R0805_02"},
  {70002368, "R0805_03"},
  {70002763, "R0805_04"},
  {70002615, "R0805_05"},
  {70002559, "R0805_06"},
  {70002172, "R0805_07"},
  {70002948, "R0805_08"},
  {70002933, "R0805_09"},
  {70002658, "R0805_10"},
  {70002702, "R1206_01"},
  {70002475, "R1206_02"},
  {70002822, "R1206_03"},
  {70002280, "R1206_04"},
  {70002319, "R1206_05"},
  {70002633, "R1206_06"},
  {70002046, "R1206_07"},
  {70002114, "R1206_08"},
  {70002462, "R1206_09"},
  {70002319, "R1206_10"},
  {70002902, "R1206_11"},
  {70002906, "R1206_12"},
  {70002738, "R1206_13"},
  {70002689, "R1206_14"},
  {70002180, "CDFP14_1"},
  {70002450, "CDFP14_2"},
  {70002442, "CDFP14_3"},
  {70002019, "CDFP14_4"},
  {70002822, "DO-213AA_01"},
  {70002799, "DO-213AA_02"},
  {70002135, "DO-213AA_03"},
  {70002055, "DO-213AA_04"},
};

// just for a lokal debugging, "real" Serial
Stream &Debug = Serial;
// the barcode reader:
Stream &Reader = Serial;             // am Mega z.B. Serial1
// the target which should receive the data
Stream &Target = Serial;             // am Mega z.B. Serial2

const byte numChars = 16;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;

void setup() {
  Serial.begin(9600);
 // Target.begin(115200);
//  Debug.begin(115200);
  Debug.println(F("D Serial translator"));

  // init the display...
}

void loop() {
  recvWithEndMarker();
  showNewData();

}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';  // Annahme wir bekommen einen Zeilende
  char rc;

  while (Reader.available() > 0 && newData == false) {
    rc = Reader.read();
    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

//
void showNewData() {
  if (newData == true) {
    newData = false;
    Debug.print("D Received: "); Debug.println(receivedChars);
    uint32_t ean = atol(receivedChars);
    int index = getIndex(ean);
//    Data resultData;                                                 // create a temporary buffer with one element
    //memcpy_P (&resultData, &translate[index], sizeof(resultData));   // read http://www.gammon.com.au/progmem - ALL!!!
//    Debug.print("D translate: "); Debug.println(resultData.out);
    Debug.print("D translate: "); Debug.println(FlashStr(translate[index].out));
  //  updateDisplay(receivedChars, resultData.out);
    updateDisplay(receivedChars,FlashStr(translate[index].out));

    if (index > 0)                                                   // nur bei einem validen Ergebnis auch ans Target senden
    {
  //    Target.print(resultData.out); Target.print("\n");              // achtgeben, was das target als Endezeichen braucht
      Target.print(FlashStr(translate[index].out)); Target.print("\n");              // achtgeben, was das target als Endezeichen braucht
    }
  }
}

// suche die Position der needle im Array
size_t getIndex(uint32_t needle)
{
  //Debug.print(F("D needle ")); Debug.println(needle);
  size_t result = 0; // not found
  for (size_t i = 0; i < sizeof(translate) / sizeof(translate[0]); i++)
  {
    uint32_t entry = (uint32_t)pgm_read_dword_near(&translate[i].ean);
    //Debug.print(F("D entry ")); Debug.println(entry);
    if (needle == entry)
    {
      Debug.print(F("D found on index ")); Debug.println(i);
      return i;
    }
  }
  return result;
}


//void updateDisplay(const char * ean, const char * out)
void updateDisplay(const char * ean, FlashStr out)
{
  // could be an external display
  // lcd.clear()
  // lcd.setCursor(0,0);
  // lcd.print("EAN: ");
  // lcd.print("Out: ");
  // lcd.setCursor(0,1);
  // lcd.print(ean);
  // lcd.print(out);

  // if code is unused
  (void)ean;
  (void)out;
}
1 Like

die unterschiedlichen Baudraten machen IMHO ja nur bei unterschiedlichen Interfaces einen Sinn, und das sollte halt im Sketch vorbereitet sein.

ich glaube du hast in Zeile 130 noch einen bug.

Ich weiß....
Habe auch echt nix dagegen, wenn das flexibel vorbereitet ist.
Nur 3 mal die Baudrate von Serial setzen, das tut mir dann schon etwas weh, beim zuschauen.

OK, ich muss zugeben, dass mein Blick da auch etwas (zu?) "scharf" ist, war es doch auch mal meine Aufgabe/Job solche (versteckten?) Abhängigkeiten aufzuspüren und eindeutig aufzudröseln.

Die Umstellung auf Stream würde auch Software Serial und alle anderen
Streams erlauben.