Alkoholtestsensor Ze29a-c2h5oh mit Arduino verbinden und Daten lesen

Hallo, ich versuche momentan einen Alkoholsensor mit dem Arduino zu verbinden und die Daten (Alkoholgehalt) auszulesen. Als absoluter Neuling benutze ich Chat GPT um das Programm zu schreiben. Leider komme ich auch nach etlichen versuchen und Unterhaltungen nicht weiter, daher bin ich nun hier und hoffe auf ein Genie dass bescheid weis.

Komponenten:

  • Arduino UNO R3
  • Alkoholsensor Ze29a-c2h5oh von Winsen
  • Logic Level Converter 4-Kanal 5V / 3.3V (bastelgarage.ch Art Nr. 420116)

Leider kann ich als neuer Benutzer keine PDFs oder Bilder hochladen, daher ist hier kein Schema vorhanden. :frowning:

Den Folgenden Code hat Chat GPT mithilfe der Anleitung von Winsen erstellt. Auch nach genauerem vergleichen von Anleitung und Code werde ich leider nicht schlauer.

#include <SoftwareSerial.h>

// RX am Sensor auf Pin 10, TX am Sensor auf Pin 11
SoftwareSerial mySerial(10, 11);

void setup() {
  Serial.begin(9600); // Für die Kommunikation mit dem PC
  mySerial.begin(9600); // UART mit dem Sensor

  Serial.println("Initialisierung des Sensors...");
  delay(2000); // Wartezeit für Stabilisierung
}

void loop() {
  // Sende Befehl zum Lesen des Status
  byte command[] = {0xFF, 0x01, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A};
  mySerial.write(command, sizeof(command));
  delay(100);

  // Empfange Antwort
  if (mySerial.available()) {
    byte response[9];
    mySerial.readBytes(response, 9);

    Serial.println("Antwort des Sensors:");
    for (int i = 0; i < 9; i++) {
      Serial.print("0x");
      Serial.print(response[i], HEX);
      Serial.print(" ");
    }
    Serial.println();

    // Auswertung der Statusdaten
    if (response[2] == 0x31) {
      Serial.println("Sensor ist im Leerlauf.");
    } else if (response[2] == 0x32) {
      Serial.println("Sensor im Aufwärmmodus.");
    } else if (response[2] == 0x33) {
      Serial.println("Sensor wartet auf Blasen.");
    } else if (response[2] == 0x34) {
      Serial.println("Sensor im Blasmodus.");
    } else if (response[2] == 0x37) {
      Serial.println("Sensor zeigt Testergebnisse an.");
    } else {
      Serial.println("Unbekannter Status.");
    }
  }

  delay(1000);
}

Vielen Dank für die Zeit und Hilfe!

Hallo @huebbilein

herzliche willkommen im Arduino-Forum.

Sehr gut, dass du in deinem ersten Posting den Code als Code-Section gepostet hast.

Um Bilder und Datenblätter einfügen zu können musst du folgendes machen
Um auf Trustlevel 1 zu kommen muss man

mindestens 5 verschiedene Themen angeklickt haben
mindestens 30 Postings gelesen haben (= müssen auf dem Bildschirm angezeigt worden sein)
mindestens 10 Minuten lang im Forum unterwegs gewesen sein

Das ist null Problemo dass zu erfüllen.

chatGPT-Code wird hier gar nicht gerne gesehen. Aus folgendem Grund:

chatGPT wird immer wieder aufs neue Code produzieren der dann doch kleine Fehler enthält die schwer zu finden sind. Und bei jedem neuen Versuch chatGPT code üerarbeiten zu lassen wird es denn Code völlig umkrepmpeln.

Und völlig umgekrepmpelten Code immer wieder aufs neue analysieren das macht hier keiner.

Wenn du mal in die Threadliste schaust da gibt es threads mit mehreren hundert postings und mehr als 1000 views.

Wenn du dich selber reinhängst bekommst du hier viel Unterstützung. Nur chatGPT musste dann komplett weglassen.

Hast du denn mal mit der Sensorbezeichnung und dem Stichwort "Arduino" gegoogelt?
Die Chancen stehen gut, dass google etwas findet.

Was ist die Frage?
Und bedenke:
Was müsste für die Beantwortung der dann gestellten Frage noch mitgeliefert werden, um eine sinnvolle Antwort zu geben?

Unbegründete Verallgemeinerung.

Hi @my_xy_projekt ,

es gibt ein ausführliches Manual zum Sensor

https://manuals.plus/de/winsen/ze29a-c2h5oh-alcohol-sensor-module-manual

Wenn man es denn da runterladen möchte :wink:

Dort nachlesbar:

und

Für die einzelnen Kommandos jeweils Erläuterungen zu den Parametern für Abfrage und die zu erwartende Antwort ...

@huebbilein ,
die Umsetzung sollte kein Hexenwerk sein, ist aber zeitlich ein wenig aufwendig. Ohne die Anleitung ist eine Unterstützung kaum sinnvoll. Kannst Du sie hier als PDF bereitstellen? Sonst wird es schwierig ...

ec2021

Hast du den Code mal auf den microcontroller gespielt?
Was wird dann im seriellen Monitor angezeigt?

Wenn du diese Fragen nicht beantworten kannst weil du nicht weist wie man es macht dann fangen die Fragen eben genau an dieser Stelle an.

@huebbilein ,

das Wesentliche, was in Deinem Post fehlt, ist eine Antwort auf die Frage

  • Wo steckst Du denn fest??

Hab ich schon hier gehabt.
Da meine Glaskugel zur Zeit in Wartung steckt, hab ich nur die Sequenzen verglichen und die passen :slight_smile:
Daher die Frage nach der Frage.... :grin:

Ist zwar nicht erforderlich, aber weil's Spaß macht:

  • Nachbildung des Sketches auf Wokwi https://wokwi.com/projects/420536876646490113

  • Allerdings mit einem MEGA, da der über mehrere UART-Serials verfügt ...

  • Serial2 sendet das Kommando

  • Serial1 empfangt es (und dort wird dann der Fake-Status eingebaut

const byte status = 0x31;  // Werte von 0x31 bis 0x37 sind definiert
// ...
    Serial1.readBytes(response, 9);
    // Hier zum Testen 
    response[2] = status;
// ...

Funktioniert natürlich ...

Aus Gründen der Vorsicht würde man bei der Rückantwort wohl grundsätzlich auf 0xFF im ersten Byte prüfen, um ggf. aufsynchronisieren zu können.

Ich vermute aber derzeit eher ein Verkabelungsproblem ...
Viel Spaß!
ec2021

Sketch
/*
  Forum: https://forum.arduino.cc/t/alkoholtestsensor-ze29a-c2h5oh-mit-arduino-verbinden-und-daten-lesen/1344484
  Wokwi: https://wokwi.com/projects/420536876646490113

*/

const byte status = 0x31;  // Werte von 0x31 bis 0x37 sind definiert

void setup() {
  Serial.begin(115200); // Für die Kommunikation mit dem PC
  Serial1.begin(9600);
  Serial2.begin(9600);
  Serial.println("Initialisierung des Sensors...");
  delay(2000); // Wartezeit für Stabilisierung
}

void loop() {
  // Sende Befehl zum Lesen des Status
  byte command[] = {0xFF, 0x01, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A};
  Serial2.write(command, sizeof(command));
  delay(100);

  // Empfange Antwort
  if (Serial1.available()) {
    byte response[9];
    Serial1.readBytes(response, 9);
    // Hier zum Testen 
    response[2] = status;

    Serial.println("Antwort des Sensors:");
    for (int i = 0; i < 9; i++) {
      Serial.print("0x");
      Serial.print(response[i], HEX);
      Serial.print(" ");
    }
    Serial.println();

    // Auswertung der Statusdaten
    if (response[2] == 0x31) {
      Serial.println("Sensor ist im Leerlauf.");
    } else if (response[2] == 0x32) {
      Serial.println("Sensor im Aufwärmmodus.");
    } else if (response[2] == 0x33) {
      Serial.println("Sensor wartet auf Blasen.");
    } else if (response[2] == 0x34) {
      Serial.println("Sensor im Blasmodus.");
    } else if (response[2] == 0x37) {
      Serial.println("Sensor zeigt Testergebnisse an.");
    } else {
      Serial.println("Unbekannter Status.");
    }
  }

  delay(1000);
}


Ich mag die Manual-Sammel-Seiten nicht so sehr; wer weiß, was die alles so versenden...

Ein Datenblatt gibt es auch direkt bei Winsen Sensor hier (PDF).
Allerdings ist es m.E. unvollständig - die Erklärung der Parameter / Datenbytes z.B. für das Kommando 0x87 (switch module working status) fehlt.

Wenn die Initialisierung nur aus Warten besteht und in der loop() nur Status (0x85) ausgelesen werden soll, ist der Sketch m.E. nicht mal falsch - wenn auch nicht schön.

Nein.
Du kannst aus dem idle (0x31) nach warm-up mit 0x32
Du kannst nach dem ReadResult 0x37 nach 0x32 oder 0x31
Nach jedem 0x87 Kommando bekommst Du 0x01 oder 0x02 als Antwort.

Bei mIr Seite 7.

RX/TX vertauscht oder Vin auf 3,3V....

"Meins" hat nur fünf :frowning:

1 Like

Hi @my_xy_projekt ,

ich habe das Ganze bei Wokwi mal ergänzt (nicht vollständig!).

https://wokwi.com/projects/420594289441266689

/*
  Forum: https://forum.arduino.cc/t/alkoholtestsensor-ze29a-c2h5oh-mit-arduino-verbinden-und-daten-lesen/1344484
  Wokwi: https://wokwi.com/projects/420594289441266689
  // Wokwi: https://wokwi.com/projects/420536876646490113


*/

const int frameLen {9};
const byte statusCmd[frameLen]          {0xFF, 0x01, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A};
const byte readResultsCmd[frameLen]     {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
const byte setIdleStateCmd[frameLen]    {0xFF, 0x01, 0x87, 0x31, 0x00, 0x00, 0x00, 0x00, 0x47};
const byte setWarmUpStateCmd[frameLen]  {0xFF, 0x01, 0x87, 0x32, 0x00, 0x00, 0x00, 0x00, 0x48};
const unsigned long cmds[] {statusCmd, readResultsCmd, setIdleStateCmd, setWarmUpStateCmd};
const int noOfCmds = sizeof(cmds) / sizeof(cmds[0]);

unsigned long lastRequestTime = 0;
int count = 0;
byte testerStatus = 0x31;

byte command[frameLen];
byte response[frameLen];

// Nur für Alkoholtester-Simulation
// ******************************
byte antwort[frameLen];
byte request[frameLen];
// ******************************

void setup() {
  Serial.begin(115200); // Für die Kommunikation mit dem PC
  Serial1.begin(9600);
  Serial2.begin(9600);
  Serial.println("Initialisierung des Sensors...");
  delay(2000); // Wartezeit für Stabilisierung
}

void loop() {
  sendCommand();
  receiveResponse();
  // Die folgende Zeile muss bei Anschluss eines echten Alkoholtesters
  // auskommentiert werden ...
  simulateAlkoTester();
}

void sendCommand() {
  if (millis() - lastRequestTime > 1000) {
    lastRequestTime = millis();
    // Sende Befehl zum Lesen des Status
    byte* cmd = cmds[count];
    Serial1.write(cmd, frameLen);
    count++;
    if (count >= noOfCmds) {
      count = 0;
    }
  }
}

void receiveResponse() {
  // Empfange Antwort
  if (Serial1.available()) {
    Serial1.readBytes(response, 9);
    Serial.print("Antwort des Sensors:\t");
    for (int i = 0; i < 9; i++) {
      Serial.print("0x");
      Serial.print(response[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
    handleResponse();
  }
}


void handleResponse() {
  // Auswertung der Daten
  switch (response[1]) {
    case 0x85:
      decodeStatus();
      break;
    case 0x86:
      decodeTestresult();
      break;
    case 0x87:
      decodeStateCmds();
      break;
  }
}

void decodeStateCmds() {
  if (response[2] == 1) {
    Serial.println("Sensor-Status geändert");
  } else {
    Serial.println("Sensor-Status konnte nicht geändert werden");
  }
}

void decodeStatus() {
  switch (response[2]) {
    case 0x31:
      Serial.println("Sensor ist im Leerlauf.");
      break;
    case  0x32:
      Serial.println("Sensor im Aufwärmmodus.");
      break;
    case  0x33:
      Serial.println("Sensor wartet auf Blasen.");
      break;
    case  0x34:
      Serial.println("Sensor im Blasmodus.");
      break;
    case  0x35:
      Serial.println("Blasmodus abgebrochen");
      break;
    case  0x36:
      Serial.println("Berechnungsstatus");
      break;
    case  0x37:
      Serial.println("Sensor zeigt Testergebnisse an.");
      break;
  }
}


void decodeTestresult() {
  int AlkoholWert = int(response[2]) * 256 + response[3];
  Serial.print("Alkoholwert = ");
  Serial.print(AlkoholWert);
  Serial.println(" mg/100ml");
}



// *********************************************************************
// *************  Ab hier AlkoTester Simulation ************************
// *********************************************************************

void simulateAlkoTester() {
  if (Serial2.available()) {
    Serial2.readBytes(request, 9);
    handleRequest();
  }
}

void handleRequest() {
  if (request[0] != 0xFF) return;
  for (int i = 0; i < frameLen; i++) {
    antwort[i] = 0x00;
  }
  antwort[0] = 0xFF;
  antwort[1] = request[2];
  switch (request[2]) {
    case 0x85:
      // Status Rückmeldung
      antwort[2] = testerStatus;
      break;
    case 0x86:
      // Read Testresult
      antwort[2] = 0x00;
      antwort[3] = 0x5A;
      antwort[7] = 0x02;
      break;
    case 0x87:
      // Switch Module Working Status
      switchStates();
      break;
  }
  byte checkSum = 0;
  for (int i = 1; i < frameLen - 1; i++) {
    checkSum += antwort[i];
  }
  antwort[frameLen - 1] = -checkSum;
  Serial2.write(antwort, frameLen);
}

void switchStates() {
  switch (request[3]) {
    case 0x31:
      antwort[2] = 0x01;
      testerStatus = 0x31;
      break;
    case 0x32:
      antwort[2] = 0x01;
      testerStatus = 0x32;
      break;
    default:
      // Dieser Status kann nicht gesetzt werden
      antwort[2] = 0x02;
      break;
  }
}

  • Serial2 simuliert Rückmeldung des Alkoholtesters gemäß der Doku.
  • Serial1 sendet Kommandos, empfängt und decodiert Rückmeldungen.

Die Zeile

3. Check value algorithm: (negative (data 1 + data 2 + ... + data 7)) + 1.

passt m.E. nicht zu den Beispieldaten. Die Checksumme in den Beispielen ergibt sich wie folgt:

  byte checkSum = 0;
  for (int i = 1; i < frameLen - 1; i++) {
    checkSum += antwort[i];
  }
  antwort[frameLen - 1] = -checkSum;

Den Sketch könnte man jetzt a) für alle Kommandos vervollständigen und b) die AlkoTester-Simulation in eine Klasse in einer include-Datei umwandeln, um sie komplett zu kapseln.

:wink:

Gruß
ec2021

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.