HC-12 von NANO zu MEGApro - keine Verbindung

Hallo liebe Coomunity. Eigentlich will ich niemanden mit meinen kleinen Problemen behelligen - und bisher hat es immer irgendwann irgendwie geklappt. Aber diesmal will es einfach nicht.

Es geht um LoRa via HC-12 Module. An einem Esp32(RX) und Nano(TX) hat es sogar schon funktioniert. Wollte/musste aber wieder auf einen ArduinoBoard für den Empfänger wechseln. Es ist ein MegaPro - der ist schön klein und hat genug Pins und Power um u.a. das angeschlossende E-Paper display zu steuern.

Ausgangslage:
Ein Sensor und ein HC12 Modul sind am guten alten NANO angeschlossen. Der Code hat sich seit dem es mal funktioniert hat nicht geändert und die TX-led leuchtet, wenn sie soll. Scheint beim Sender alles ok zu sein. Auch Serial gibt den richtigen debug Text aus.

Am MegaPro ist ebenfalls ein HC12 Modul mit der selber Chip-Typen-Nummer und natürlich auch ein 1000mf Kondensator verbaut. Bei beiden Boards ist RX-10 und TX-11 - wie empfohlen. Beide HC12 Module haben eine Ansteck-Antenne mit Spirale.

Verkabelung ist wie überall zu sehen: 5v,Grd,RX10, TX11 - nix auf SetPin.
Das Epaper am MegaPro ist auf den Pins: 7,8,9, 51, 52, 53 - und funktioniert.

Code:
Vom TX soll eine kleine Ineger übertragen werden, die beim RX etwas aufgeggliedert und interpretiert wird. Dann gibts dort ne menge Arrays, die dann fein auf dem Display mit full und partialUpdate angezeigt werden. Nix besonderes. Hat so grundsätzlich auf dem ESP32 schonmal funktioniert.

Was ich auch versuche - ich finde den Fehler einfach nicht. Seit Monaten versuche ich alles, was mir einfällt. Das Internet hatte bisher auch keine Lösung. Ich bin Profi oder Programmierer in jäglicher Form. Darum ist mein Code wohl auch sehr simpel.
Vielleicht könnt ihr helfen...

#define ENABLE_GxEPD2_GFX 1

// #include <Arduino.h>
// #include <SPI.h>
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <SoftwareSerial.h>

// #include "BitmapDisplay.h"
// #include "TextDisplay.h"

#include "GxEPD2_display_selection.h"
// #include "GxEPD2_display_selection_added.h"
// #include "GxEPD2_display_selection_new_style.h"

SoftwareSerial HC12(10, 11);  //   RX:Pin 10 - TX:Pin 11,

// BitmapDisplay bitmaps(display);

// Variablen: ### könnten die INTs auch Bytes sein?
const int vibrationPin = 2; // DataPin für Vibrator
int emeID = 0;
int emeStatus = 0;
int LastData = 0;
bool newData = false;
byte emeIDslot[20];
int timerArray[20];
int pingTimer[20];
byte startTimer[20];
byte minutes = 0;
byte seconds = 0;
byte emeStateArray[20];
byte acitiveEMEs = 0;
unsigned long nowTime = 0;
// char incomingByte;
// String receivedString;
// layout Variablen
int timerPosLeft = 82;
// Zeilenhöhe fehlt noch: (18)


void setup()
{
  HC12.begin(9600);   // Serielle Kommunikation zum HC-12-Modul (9600 ist im Modul eingestellt)
  delay(1000);
  Serial.begin(57200);
  Serial.println("setup");
  delay(1000);
  display.init(115200, true, 2, false); // USE THIS for Waveshare boards with "clever" reset circuit, 2ms reset pulse
  display.setRotation(2);
  pinMode(vibrationPin, OUTPUT);
  pinMode(10, INPUT); pinMode(11, OUTPUT);

  delay(1000);

  // beispiel EMEs (TESTDATA for Display)
  emeIDslot[11]=11;
  emeIDslot[12]=12;
  emeIDslot[13]=13;timerArray[13]=25;startTimer[13] = 1;
  vibration01();
  countEMEs();
  //
  DisplayFullUpdate();

}

void loop() {

   while (HC12.available()) { ///Empfangen von neuen Daten
    String receivedString = HC12.readString();
    Serial.println(receivedString);
    if (receivedString.length() == 3) {
      LastData = receivedString.toInt();
      Serial.print("Empfangene Zahl: "); Serial.println(LastData);
      Serial.print("Anzahl der EMEs: "); Serial.println(acitiveEMEs);
      newData = true;
    }
    delay(1000);
  }
  // while (HC12.available()) { ///Empfangen von neuen Daten
  //   incomingByte = HC12.read();
  //   receivedString += char(incomingByte);
  //   }
  // delay(100);
  // if (receivedString.length() == 3) {
  //     LastData = receivedString.toInt(); // String wird zu einer integer Zahl umgewandelt -NICE
  //     Serial.print("Empfangene Zahl: "); Serial.println(LastData);
  //     Serial.print("Anzahl der EMEs: "); Serial.println(acitiveEMEs);
  //     newData = true;}
  

  if(newData){  /// Datenpaket Empfangen !!!
    emeStatus = LastData/100; emeID = LastData - (emeStatus*100); // Aufschlüsselung der Funk Daten
    emeStateArray[emeID] = emeStatus;

    // DisplayFullUpdate(); // TEST full update

    if(emeIDslot[emeID] == 0){  // neues EME noch nicht erkannt --> full display Update
      emeIDslot[emeID] = emeID;
      countEMEs();
      DisplayFullUpdate();
    }    
    nowTime = millis()/1000;

    if(emeStateArray[emeID]==2){  //bewegung erkannt
      vibration01(); 
      timerArray[emeID] = nowTime; // timer wir auf jetzte - also NULL gesetzt
      startTimer[emeID] = 1;      // timer wird aktiviert (bool)
      }

    if(emeStateArray[emeID]==1){  //status ok (Lebenszeichen)
      pingTimer[emeID]=millis()/1000;
      }

    emeIDspeichern();
    DisplayFullUpdate(); // in "new data"-if-Loop
  }

  // Abfrage ob irgendwelche Timer laufen - nur dann Partial Update
  int AllTimers = 0;
  for(int i=0;i<20;i++){
    AllTimers = AllTimers + startTimer[i];
  }
  if (AllTimers > 0) {
      DisplayPartUpdate();
  }

  newData = false;
  emeStateArray[emeID] = 0;   // eme Status wird zurückgesetzt für nächste Übertragung
  emeID = 0;
  //deadEME();
  // delay(250);
}

void DisplayFullUpdate(){
  //Serial.println("FullDisplayUpdate");
  display.firstPage();
  display.fillScreen(GxEPD_WHITE);
  display.setFont(&FreeSans9pt7b);
  display.setTextColor(GxEPD_BLACK);
  // ab hier: dynamische chronologische EME Tabellierung ohne Timer Position
   for(int x=0;x<acitiveEMEs;x){
     for(int i=0;i<20;i++){ //durchlauf aller ID slots - Welche EMEs sind online (works)
       if(emeIDslot[i]!=0){ // nicht null heißt online und wird angezeigt
         display.setCursor(1, 20+x*18);display.print("e");
         if(i<10){display.print("0");display.print(i);}
         else{display.print(i);}
         Serial.println(emeIDslot[i]);
         x++;
       }
     }
   }
  //DEBUG on Display
  display.setCursor(3,200); display.print(acitiveEMEs);

  while(display.nextPage());
  DisplayPartUpdate(); // immer am Edne von full update
}

void DisplayPartUpdate(){  // hier nur Timer updates !!! Alles andere zum static Full upadte
  display.setFont(&FreeSans9pt7b);
  display.setTextColor(GxEPD_BLACK);
  display.setPartialWindow(timerPosLeft, 0, 128-timerPosLeft, acitiveEMEs*20); // 1 Updatewindow für alle erkannten EMEs
  display.firstPage();
  display.fillScreen(GxEPD_WHITE); // Reienfolge hier wichtig!
  for(int x=0;x<acitiveEMEs;x){  // ### Bis jetzt nur Timer -> Mehr infos müssen ins UpdateWindow
    for(int i=0;i<20;i++){ 
      if(emeIDslot[i]!=0){  
        display.setCursor(timerPosLeft, 20+x*18);
        if(startTimer[i]==0)
        {display.print("-------");} // übersichtline lines wenn keine bewegung erkannt wurde
        else{ // Umrechnung von Sekunden in MM:SS
        minutes = (millis()/1000 - timerArray[i]) / 60;
        seconds = (millis()/1000 - timerArray[i]) % 60;
        if(minutes<10){display.print("0");}
        display.print(minutes);
        display.print(":");
        if(seconds<10){display.print("0");}
        display.print(seconds);
        } 
        x++;
      }
    }
  }
  while(display.nextPage());
  //delay(100);//display.clearDisplay();display.display();
}

void countEMEs(){
  acitiveEMEs = 0;
  for(int i=0;i<20;i++){
    if(emeIDslot[i] != 0){
      acitiveEMEs++;
    }
  }
}

void deadEME(){ // EMEs müssen ein Lebenszeichen senden - sonst werden sie als Tot merkiert (ToDo)
  for(int i=0;i<20;i++){
    if(millis()/1000 - pingTimer[i] > 720){  /// wenn 720sek nicht vom eme gekommen ist
      emeIDslot[i] = 0; /// wird er aus der aktiven liste entfernt
    }
  }
}

void emeIDspeichern(){ /// hier wird die empfange emeID in das ID-Array geschrieben
  for(int i=0;i<20;i++){
    if(emeID==i){
      emeIDslot[i]=emeID;
    }
  }
}

void vibration01(){  /// 30 sec /// neue Bewegung erkannt ab 30sek nach der letzten 
  if(millis()/1000 - timerArray[emeID] > 30 || timerArray[emeID] == 0){
  digitalWrite(vibrationPin, HIGH);delay(300);digitalWrite(vibrationPin, LOW);   /// dreifach Vibration wenn das letzte Siganal 30sek her ist
  delay(100);
  digitalWrite(vibrationPin, HIGH);delay(300);digitalWrite(vibrationPin, LOW);
  delay(100);
  digitalWrite(vibrationPin, HIGH);delay(300);digitalWrite(vibrationPin, LOW); 
  }
  else{
  digitalWrite(vibrationPin, HIGH);delay(300);digitalWrite(vibrationPin, LOW);  // einfache Vibration wenn das Signal nicht neu ist.
  }  

}

TX - sender Code:

#include <SoftwareSerial.h>

const int pirPin = A2;         // Pin für den PIR-Bewegungsmelder
const int hc12TxPin = 11;     // Pin für HC-12 Lora TX
const int hc12RxPin = 10;     // Pin für HC-12 Lora RX
const int batteryPin = A0;    // Pin für den Batteriespannungssensor
const int ledPin = 13;        // Onboard-LED Pin

SoftwareSerial HC12(10, 11); // RX, TX

int motionCounter = 0;        // Zähler für erkannte Bewegungen
unsigned long lastMotionTime; // Zeitpunkt der letzten Bewegung
int pingTimer = 0;

const int unitID = 2;       // Einheits-ID

int status = 1;               // Status der Einheit (1 für Betriebsbereitschaft, 2 für erkannte Bewegung, 3 für eingeschränkte Betriebsbereitschaft)

void setup() {
  pinMode(pirPin, INPUT);
  pinMode(ledPin, OUTPUT);
  HC12.begin(9600);
  Serial.begin(57600);

  // Batteriespannung überprüfen
  float batteryVoltage = readBatteryVoltage();
  if (batteryVoltage < 7.0) {
    status = 3;
    sendLoRaMessage();  // Schwache Batterie: Nachricht mit "3" für eingeschränkte Betriebsbereitschaft
  } else {
    status = 1;
    sendLoRaMessage();  // Alles OK: Nachricht mit "1" für Betriebsbereitschaft
    blinkLED(3);             // LED blinkt 3-mal lang auf
  }
  //delay(10000); // 10 Sekunden Wartezeit
}

void loop() {
  if (checkMotion()) {
    // Bewegung erkannt, wechsle in den genauer-hingucken-Modus
    if (waitForMoreMotion()) {
      // Mehrere Bewegungen innerhalb des Zeitfensters erkannt, sende die Nachricht
      status = 2;
      sendLoRaMessage();
    }
  }

  // Batteriespannung alle 10 Minuten überprüfen
  if (millis() % (10 * 60 * 1000) == 0) {
    float batteryVoltage = readBatteryVoltage();
    if (batteryVoltage < 7.0) {
      status = 3;
      sendLoRaMessage();  // Schwache Batterie: Nachricht mit "3" für eingeschränkte Betriebsbereitschaft
    }
    else {
      status = 1;
    sendLoRaMessage();
    }
  }

  delay(1000);  // 2 Sekunden Wartezeit für die Bewegungsmelder-Abfrage
  // Serial.println(analogRead(pirPin));
}

bool checkMotion() {
  // Serial.println(analogRead(A2));
  if (analogRead(pirPin) >= 500) {
    // Bewegung erkannt
    lastMotionTime = millis();
    return true;
  }
  return false;
}

bool waitForMoreMotion() {
  motionCounter = 1;
  unsigned long startTime = millis();
  while (millis() - startTime < 10000) {
    if (checkMotion()) {
      motionCounter++;
      if (motionCounter >= 2 || (millis() - lastMotionTime >= 3000)) {
        // Drei Bewegungen erkannt oder positive Bewegungserkennung für 3 Sekunden konstant
        return true;
      }
    }
    delay(500);
  }
  return false;
}

void sendLoRaMessage() {
  if(unitID<10){  // wenn ID kleiner als 10 -> '0' hinzukopieren
    String message = String(status) + String(0) + String(unitID);
    Serial.println(message);  // Ausgabe auf der Konsole zur Überprüfung
    delay(1000);
    HC12.print(message); // Sende den Inhalt der Nachricht als String
  } else {  
    String message = String(status) + String(unitID);
    Serial.println(message);  // Ausgabe auf der Konsole zur Überprüfung
    delay(1000);
    HC12.print(message); // Sende den Inhalt der Nachricht als String
  }      
}

float readBatteryVoltage() {
  int sensorValue = analogRead(batteryPin);
  float voltage = sensorValue * (5.0 / 1023.0) * 2.0;  // Faktor 2.0 für Spannungsteiler, falls erforderlich
  return voltage;
}

void blinkLED(int count) {
  for (int i = 0; i < count; i++) {
    digitalWrite(ledPin, HIGH);
    delay(300);
    digitalWrite(ledPin, LOW);
    delay(300);
  }
}

Hast du es denn mit den HC-12 Modulen mal mit einem einfachen Testcode versucht, der nur eine einfache Kommunikation aufbaut ?
Und hast du die Module vorher konfiguriert, also so eingestellt, damit sie einheitliche Einstellungen haben ?
Hier findest du ein Beispiel für eine einfache Kommunikation der Module.

Ja, und Nein,
Ich habe den SoftwareSerial Exemple Code auf beiden versucht ohne jeglichen Erfolg.
Ich habe ein paar duzend HC-12 Module direkt aus China bestellt, die werden alle die gleiche Konfiguration haben. Wie gesagt - zur Sicherheit benutze ich nur Module mit den gleichen Chips. Aber vorher hat es auch mit unterschiedlichen Chips funktioniert.

Was genau heißt ja und nein ?
Und was hat vorher funktioniert ?

Ja zu Frage eins - Nein zu Frage zwei.
Die Funkverbindung zwischen verschiedenen HC-12 Modulen hat funktioniert. Daten wurden ordentlich übertragen. Aber das war zwischen ESP32 und NANO. Und die Hardware des ESP32 war nicht passend also wollte ich als Empfänger einen Mega haben. Einen kleinen - also den MegaPro.

Ok, dann solltest du auch mit dem Mega zuerst eine einfache Kommunikation aufbauen. Darauf dann die Erweiterung ergänzen.
Ich sehe in deinem Sketch kleine "Ungereimtheiten" die evtl. Probleme bereiten können.
Du gibst anfangs den Variablen Nummern

Verwendest die Variablen aber nicht mehr.

Mein dringender Tipp:
Baue eine einfache Kommunikation mit den Modulen auf und verwende mein verlinktes Beispiel dazu.

Richtig, die beiden Variablen werden nicht verwendet. Sind wohl übrig geblieben nach einem Test.
Wenn schon der SoftwareSerial-Beispiel-Code nicht funktioniert - wo soll ich anfangen?
Wenn ich das richtig gesehen habe, hat dein verlinktes Beispiel den gleichen Ansatz. Ich teste ihn trottzdem...

Die Hardware habe ich schon zig Mal getauscht, gewechselt und überprüft. Da scheint alles ok zu sein.

Daher vermute ich einen Fehler in deinem Code, den ich aktuell nicht prüfen kann, da ich mit einem Tablet mobil bin.
Somit ist der bessere, sichere Weg ist, einen einfachen Test zu starten. Wenn das funktioniert, was ich vermute, kannst du darauf aufbauen.
Zumindest ist das so meine eigene Erfahrung. Auch mit dem HC-12, den ich mehrfach einsetze.
Und bitte verwende für den Sketch die genannten Variablen und keine Magic Number.
Da wird die Fehlersuche oder eine Änderung einfacher.

Hallo megagerber,

mach es dir einfach:

Nehme einen HC12 und einen "normalen" Arduino und baue dir damit einen Monitorempfänger auf, der nur auf der eingestellen Empfangsfrequenz lauscht.

Somit kannst du einfach überprüfen wer, wann, was und wie sendet.

Viel Erfolg bei der drahtlosen Kunst.

wo zu das bei Mega? Mega hat 4 = VIER Serial Hardware Ports :wink:
Serial 0, 1 für USB
Serial1 18, 19
Serial2 16, 17
Serial3 14, 15
nimm doch Serial1 TX=18, RX=19

 Serial1 HC12(19, 18); // So in der Richtung

@fony
Der Tipp ist sehr gut, sollte aber evtl. erst angewndet werden, wenn die Kommunikation läuft. Nicht das noch ein zusätzliches Problem eingebaut wird.

Ok Danke. Ich werde mal einen Monitorempänger machen.
Ja ich habe schon gelesen, dass der Mega die Hardware-Serial-Pins hat. Nur wollte ich diese Baustelle erstmal nicht auf machen, weil ich nicht sicher bin wie schnell ich das mit dem HC-12 Code verbinden kann. Ich wollte es einfach belassen und so wie es schon mal funktionierte - bisher. Aber ich werde auch das mal austesten.

Gut gut, dann gehe ich mal all diese Tests durch und melde mich wieder, wenn ich ergebnisse habe.

Danke!

Mir ist da noch aufgefallen, du hast die Pins für den HC-12 auch als Output/Input definiert.
Das braucht nicht, das macht schon die Library bzw. später die Hardware.

Die Drähte RX/TX richtig stecken, die Baudrate programmieren und ab geht die Post.

Pinmode setzen, war auch ein "Tip" den jemand online erwähnt hatte.... naja

Vielleicht geht das mit Serial1 schnell - muss das aber erstmal verstehen - oder auch nur verstehen, dass es nichts neues zu verstehen gibt :wink:

Wird getestet!

Brauchst du def. nicht.

Das geht fix.

Serial1 ist nix anderes als Serial, nur für Port 1.

@megagerber
Was hast du am Empfänger (Mega) für ein Display dran und welche Pins verwendest du da ?
Das sieht da in deinem Sketch iw. Merkwürdig aus. SPI oder seriell ?

Deutet auf EPaper Display die sind SPI

Dann wundert mich dies hier:

und eine fehlende SPI.h.