Jetzt hat es endlich gefunkt! (433MHz Modul HC-12) - [Teil 3] - AZ-Delivery

Willkommen zum dritten Teil der Serie mit dem Funkmodul HC-12. Im ersten Teil hatten wir mit den Grundeinstellungen eine typische Fernschreibverbindung zwischen PC, Raspberry Pi oder Micro Controller hergestellt. Im zweiten Teil hatten wir die Datenfernübertragung (DFÜ) gesehen sowie Änderungen an den Einstellungen des Funkmoduls vorgenommen und Messwerte per Funk übertragen.

In diesem Teil möchte die die Grundlagen vermitteln, wie man über Funk etwas fernsteuern kann, damit die Maker auch zu ihrem Recht kommen.

Bei meinem Projekt möchte ich am Ende eine Stoppuhr haben, bei der das Start- und das Stoppsignal über Funk gesendet werden. Im Datenblatt des HC-12 steht, dass im Freien bei voller Leistung Funksignale bis 1000m sicher übertragen werden. Das reicht für den Sportplatz oder den Turnierplatz.

Diese Aufgabe kann man in zwei Teilaufgaben herunterbrechen: das Fernschalten mit dem Funksignal und die eigentliche Stoppuhr. Für beide Aspekte habe ich im Internet recherchiert und bin jeweils fündig geworden. Für die Auswahl der Hardware habe ich die Kollegen nach ihrer Empfehlung gefragt. Alle waren einhellig der Meinung, dass ein einfacher Micro Controller wie der Arduino Nano die beste Option ist, da dieser keine Hintergrundaufgaben wie WLAN-Suche usw. durchführt und damit die Messwerte nicht verfälscht werden.

Benötigte Hardware 

Anzahl Bauteil Notiz
2 - 3 Arduino Nano
2 - 3 Funkmodule HC-12
1 LCD1602 mit I2C-Adapter
2 Schalter (Lichtschranken)
2 - 3 LEDs und 330 Ohm Widerstände
2 - 3 Steckbretter und Jumper-Kabel
2 - 3 Battery Expansion Modul 18650 und Li-Ion-Akku 18650


Anmerkung:
Um Kabelsalat am Ziel zu vermeiden, möchte ich auch das Stoppsignal über Funk übertragen; deshalb ggf. 3 Micro Controller mit HC-12.

Für die bessere Nachvollziehbarkeit beginne ich mit zwei Programmen von Tom Heylen zur Funkübertragung eines Taster-Signals zum Schalten einer LED, die ich für meine Zwecke weiterentwickelt habe. Auf den beiden MCs laufen dafür unterschiedliche Sketche und auch die Schaltungen sind unterschiedlich, aber sehr einfach: Bei einem MC ein Taster (später für das Startsignal) an Pin 8 und am anderen MC eine LED mit Vorwiderstand an Pin 13 (ggf. auch nur LED_BUILTIN), die später anzeigen soll, dass die Stoppuhr gestartet wurde.

Bild 1: Schaltung Arduino Nano mit HC-12 und Button (Start oder Stopp)

Bild 2: Schaltung Arduino Nano mit HC-12 und LCD1602 mit I2C-Adapter

 

//HC-12 start button Send
//based on code from Tom Heylen
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2, 3); //RX, TX
int buttonPin = 4;
int led = 7;
void setup() {
    pinMode(buttonPin, INPUT_PULLUP);
    pinMode(led,OUTPUT);
    digitalWrite(led,LOW);
    Serial.begin(9600);
    softSerial.begin(9600);
    }
void loop() {
    int buttonState = digitalRead(buttonPin);
    if(buttonState == 0) { //if button is down
        Serial.println(1234); //send unique code to the Serial Monitor
        softSerial.println(1234); //send unique code to the HC-12
        digitalWrite(led,HIGH);
        delay(5000);
        digitalWrite(led,LOW);
        }
    delay(20); //delay little for better serial communication
    }


 

Für das Hochladen des Sketches und danach die Möglichkeit zur Nutzung des Serial Monitors wird die serielle Hardware-Schnittstelle benutzt. Den HC-12 Sender/Empfänger schließen wir deshalb an die Pins 2 und 3 an und definieren dafür eine serielle Software-Schnittstelle mit der Programmbibliothek SoftwareSerial.

Gemäß Datenblatt bedeutet höhere Übertragungsgeschwindigkeit stets kürzere Reichweite. Deshalb benutze ich wieder die Grundeinstellungen mit jeweils 9600 Baud.

Wie heute üblich schalte ich den Taster gegen Ground. Deshalb muss der pinmode für den buttonPin auf INPUT_PULLUP gesetzt werden. In der Endlos-Schleife wird auf das Drücken des Tasters gewartet, um dann einen willkürlich gewählten, vierstelligen Code zu senden, zur Überwachung mit Serial.println() an den Serial Monitor und an den HC-12 mit softSerial.println(). Nicht nur um Prellen des Tasters, sondern später ein unerwünschtes erneutes Senden des Startsignals zu verhindern, wird eine längere Pause mit delay eingefügt. Die 20 ms Pause am Ende sorgt für den korrekten Ablauf des Programms während des Wartens auf den Tastendruck.

Für die Lichtschranke im Ziel kann grundsätzlich das gleiche Programm mit einem unterschiedlichen Code verwendet werden. Mehr dazu später.


Der Programmcode für den Empfänger ist etwas komplexer:

 //HC-12 Toggle button Receive
//based on code from Tom Heylen
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2, 3); // RX, TX
int ledPin = 13;
unsigned long last = millis();     //set timer
void setup() {
    Serial.begin(9600);
    softSerial.begin(9600);
    pinMode(ledPin, OUTPUT);
    }
void loop() {
    boolean ledState = digitalRead(ledPin); //check if the LED is                                                                  //turned on or off. Returns 1 or 0
    if(softSerial.available() > 1){
        int input = softSerial.parseInt(); //read serial input and convert                                                              //to integer (-32,768 to 32,767)
        if(millis() - last > 250){   // if time now is 250 milliseconds                                                          // greater than last time
            if(input == 1234){ //if button code is 1234
                Serial.println(input);
                digitalWrite(ledPin, HIGH);
            }
            if(input == 9876){//if button code is 9876
                Serial.println(input);
                digitalWrite(ledPin, LOW);
            }
        }
    last = millis();           //reset timer
    softSerial.flush();     //clear the serial buffer for unwanted inputs
    }
delay(20);        //delay little for better serial communication
}

Wieder benutzen wir die Bibliothek SoftwareSerial für den HC-12. In der Endlosschleife  wird zu Beginn mit softSerial.parseINT() jegliches Signal aufgenommen, in eine Integerzahl umgewandelt und der Variablen input zugeordnet. Dann wird die Eingabe input überprüft. Sofern input mit dem Code für das Einschalten der LED (hier: 1234) übereinstimmt, wird die LED eingeschaltet. Sofern input mit dem Code für das Ausschalten der LED (hier: 9876) übereinstimmt, wird die LED ausgeschaltet.

Alle anderen empfangenen Signale werden mit softSerial.flush() „in’s Klo gespült“, also gelöscht. Nach 20 Millisekunden beginnt die Schleife von vorn und wartet auf ein Signal.

Wie gesagt: Diese beiden Sketche können für ferngesteuerte Schaltvorgänge benutzt werden. Wer so eine Lampe oder Kaffeemaschine einschalten möchte, braucht nur ein Relais anstelle der LED zu verwenden.

Wer bei einem Rennen mehr als einen Teilnehmer stoppen möchte, hat mit der Physik ein kleines Problem: Sofern nicht für jede Spur eine Lichtschranke verwendet werden kann (wegen Verletzungsgefahr fast ausgeschlossen), können mit einer Lichtschranke nur die Zeit des Ersten gemessen und ggf. das Zielfoto ausgelöst werden. Selbstverständlich könnte auch ein weit abgeschlagener Zweiter erneut mit der Lichtschranke gemessen, aber bei einem Kopf an Kopf-Rennen kann die Zeit des Zweiten nur aus dem Abstand auf dem Zielfoto ermittelt oder ein zweites Stopp-Signal muss manuell eingegeben werden.

Diese zweite Möglichkeit habe ich mit einem Taster am Micro Controller an der Ziellinie realisiert. Damit wird der Sketch für das Stoppsignal etwas umfangreicher als für das Startsignal.

//HC-12 stop button Send
//based on code from Tom Heylen
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2, 3); //RX, TX
int stopButton = 4;
int stopButton2 = 5;
int led = 7;
bool stopbuttonpressed = false;
bool stopbutton2pressed = false;
void setup() {
    pinMode(stopButton, INPUT_PULLUP);
    pinMode(stopButton2, INPUT_PULLUP);
    pinMode(led,OUTPUT);
    digitalWrite(led,LOW);
    Serial.begin(9600);
    softSerial.begin(9600);
    }
void loop() {
    int buttonState = digitalRead(stopButton);
    int buttonState2 = digitalRead(stopButton2);
    if(buttonState == 0 && !stopbuttonpressed){    //if button is down
        Serial.println(9876);            // send unique code to the receiver
                                                   // in this case 9876
        softSerial.println(9876);     // send unique code to the receiver
                                                  // in this case 9876
        digitalWrite(led,HIGH);
        stopbuttonpressed = true;
        }
    if(buttonState2 == 0 && stopbuttonpressed){ //if button2 is down
        Serial.println(2222);  // send unique code to the receiver
                                         // in this case 2222
        softSerial.println(2222);   // send unique code to the receiver
                                                // in this case 2222
        digitalWrite(led,LOW);
        stopbutton2pressed = true;
        }
    if(stopbutton2pressed == true){
        stopbuttonpressed = false;
        stopbutton2pressed = false;
        delay(5000);
        digitalWrite(led,LOW);
        }
delay(20);//delay little for better serial communication
}
 

Außer stopButton für die Lichtschranke benötigen wir stopButton2 für den manuellen Taster. Zudem müssen wir einige logische Abhängigkeiten berücksichtigen, denn der zweite Taster darf ja nicht vor dem ersten gedrückt werden. Wenn alles richtig gemacht wird, wird für den Ersten im Ziel der Code 9876 gesendet, für den Zweiten 2222.

Nun zum Schaltbild und Code für die Stoppuhr mit Zeitanzeige auf einem LCD1602 mit I2C-Adapter.

Bild 3: Schaltung Arduino Nano mit HC-12 und LCD1602 mit I2C-Adapter

Ggf. muss zunächst die Bibliothek LiquidCrystal_I2C mit dem Bibiotheksverwalter installiert werden.

 //HC-12 Stop Watch
//based on code from Tom Heylen
#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h> // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2);
SoftwareSerial softSerial(2, 3); // RX, TX
int ledPin = 13;
unsigned long last = millis(); //set timer
unsigned long start1, finished, finished2;
float elapsed, elapsed2;
bool startbuttonpressed = false;
bool stopbuttonpressed = false;
void setup() {
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("Ready! ");
    Serial.begin(9600);
    softSerial.begin(9600);
    pinMode(ledPin, OUTPUT);
    }
void displayResult()   {          //create a function called displayResult
    float m, s; //create float variables (numbers with dec point)
    elapsed = (finished - start1)/1000.0;   // elapsed time is finished
                                                                // minus start
    lcd.setCursor(0, 0);
    m = int(elapsed/60);
    s = elapsed - 60*m;

    Serial.print(int(m));
    Serial.print(" min ");
    Serial.print(s);
    Serial.println(" sec ");
    lcd.print(m,0);
    lcd.print(" min ");
    lcd.print(s,2);
    lcd.print(" sec ");
    }
void displayResult2()   {   //create a function called displayResult2
    float m2, s2; //create float variables (numbers with dec point)
    elapsed2 = (finished2 - start1)/1000.0; // elapsed time is finished
                                                                  // minus start
    lcd.setCursor(0, 1);
    m2 = int(elapsed2/60);
    s2 = elapsed2 - 60*m2;

    Serial.print(int(m2));
    Serial.print(" min ");
    Serial.print(s2);
    Serial.println(" sec ");
    lcd.setCursor(0, 1);
    lcd.print(m2,0);
    lcd.print(" min ");
    lcd.print(s2,2);
    lcd.print(" sec ");
    }
void loop() {
    if(softSerial.available() > 1){
        int input = softSerial.parseInt();   // read serial input and
                                          // convert to integer (-32,768 to 32,767)

        if(input == 1234 &&!startbuttonpressed) {    
            start1 = millis(); //start = current millis
            startbuttonpressed = true;
            Serial.println("Started ... ");
            lcd.setCursor(0, 0);
            lcd.print("Started ...");
            digitalWrite(ledPin, HIGH);
            delay(5000); // 5 seconds before stopbutton can be pressed
            }
        if(input == 9876 && startbuttonpressed && !stopbuttonpressed)
        { 
            finished = millis(); //finished = curent millis
            stopbuttonpressed = true;
            displayResult(); //call function displayResult
            digitalWrite(ledPin, LOW);
        }
        if(input == 2222 && startbuttonpressed && stopbuttonpressed)          {
            finished2 = millis(); //finished = curent millis
            stopbuttonpressed = true;
            displayResult2(); //call function displayResult
            digitalWrite(ledPin, LOW);
        }
    }
last = millis();         //reset timer
softSerial.flush();   / /clear the serial buffer for unwanted inputs
delay(20);              //delay little for better serial communication
}

Bild 3: Versuchsaufbau Arduino Nano mit Transceiver HC-12 und LCD1602

Bild 4: Versuchsaufbau Arduino Nano mit Transceiver HC-12 und Button

Für die Zeitberechnungen und Anzeigen der Minuten und Sekunden auf dem LCD1602 habe ich jeweils eine Funktion definiert, die in der Schleife aufgerufen wird, wenn die jeweiligen Bedingungen erfüllt sind. Die Zeitnahme wird gestartet, wenn der richtige Code 1234 gesendet wird und die Uhr nicht bereits gestartet war. Zugleich wird eine Verzögerung eingestellt, in der keine weiteren Signale empfangen werden. Die Zeit für den Ersten wird genommen, wenn der Code 9876 empfangen wird und die Uhr vorher gestartet und noch nicht gestoppt wurde. Und die Zeit für den Zweiten wird genommen, wenn der Code 2222 empfangen wird und zuvor sowohl das Startsignal als auch das Stoppsignal für den Ersten empfangen wurde.

Wenn alle Zeiten notiert sind, wird der Reset-Knopf an der Stoppuhr gedrückt und die nächste Zeitmessung kann beginnen. Die beiden Micro Controller für das Start- und Stoppsignal können, müssen aber nicht resettet werden.

Ein letztes Wort zur Genauigkeit. Ein Ingenieur von einer Firma, die professionelle Funk-Fernbedienungen herstellt, hat mir einmal gesagt, dass die Zeitverzögerung durch die Elektronik bis zu 0,2 Sekunden sein kann. Das kann ich mit meinen Möglichkeiten nicht nachmessen. Durch den zweiten Micro Controller mit Sender im Ziel wird dieser Fehler jedoch weitestgehend kompensiert.

Mit dem HC-12 Sender/Empfänger hat man als Amateurfunker die Möglichkeit, bei Sportveranstaltungen die Zeitnahme weitgehend kabellos zu realisieren. Von den theoretisch 100 einstellbaren Funkkanälen am HC-12 sind in Deutschland jedoch nur die ersten vier Kanäle (im Frequenzbereich 433,050 - 434,790 MHz) freigegeben. Und auch die Sendeleistung muss in Deutschland heruntergeregelt werden (Set Pin an GND, im Seriellen Monitor AT+P4 eingeben).

Chat, Datenfernübertragung und vor allem das Schalten und Fernsteuern im Nahbereich (mit voller Leistung bis zu 1000m, mit der in Deutschland zulässigen Leistung ca. 200m) werden mit dem HC-12 Funkmodul möglich; und zwar sowohl an Micro Controllern, Raspberry Pis als auch an PCs.

Viel Spaß dabei.

 

 Hier geht's zum Download

DisplaysFür arduinoProjekte für fortgeschrittene

5 comments

Maciej Krychowiak

Maciej Krychowiak

Danke für die Erklärung. Das mit dem sniffing ist nicht so einfach. Ich habe das früher nach einem Beispiel ausprobiert, aber ohne Erfolg (kein Signal erkannt). Einige weitere Erklärungen im Internet sind zum Teil nicht so leicht zu verstehen und nachzumachen. Deshalb wäre ein Blogbeitrag darüber sehr hilfreich. Alles in allem gibt es recht viele Geräte die über 433 MHz kommunizieren, und die von denen genutzten Codes zu entziffern wäre sehr hilfreich. Viele Grüße.

Bernd Albrecht

Bernd Albrecht

@ Rene und Maciej: Jetzt habe ich Ihre Fragen verstanden. Sie wollen fremde 433MHz- Sender mit dem HC-12 verbinden und Daten auslesen.
Wie in der Einleitung zum ersten Teil beschrieben hatte ich wenig Erfolg mit den seit langem bekannten Sende- und Empfangsmodulen (XY-FST, XY-MK-5V) und Funksteckdosen bzw. anderen Sendern. Jedoch gibt es hierfür Programme wie RFSniffer für Arduino und Raspberry Pi.
Der HC-12 ist jedoch nicht nur ein kombinierter Sender und Empfänger (Transceiver), sondern auf der Platine befindet sich auch die MCU STM8S. In diesem kompletten Micro Controller kann man zwar einige Parameter wie Kanal (Frequenz) und Sende-Modus verändern, jedoch ist alles so programmiert, dass nur HC-12 untereinander funken und die Daten über die UART-Schnittstelle an einen Computer oder Micro Controller übertragen werden. An den Pins des HC-12 liegt also kein demoduliertes RF-Signal an, sondern Daten für die COM-Schnittstelle.

Rene

Rene

Hallo, ich habe ein ähnliches Problem wie Maciej: Ich möchte mit dem HC-12 vorhandene Außenthermometer auslesen. Ich habe mehrere einfache bis teure Wetterstationen, die teilweise (obwohl verschiedene Hersteller) dieselben Außenthermometer einbinden. Jetzt möchte ich die Daten der Außenthermometer mit meinen HC-12 Modulen auslesen und zentral per ESP32 im WLAN ablegen (alle Daten auf FritzBox laden). Leider habe ich es auch nicht geschafft, irgendein Signal aus der Luft abzugreifen.

migan

migan

Hallo,
perfektes Timing, Funkmodul und Battery Expansion Shield ausverkauft
Gruß migan

Maciej

Maciej

Hallo,

vielen Dank für den interessanten Beitrag. Ich möchte mal auf den Vorschlag zurückkommen, über Funk eine Lampe oder Kaffeemaschine, also ein Gerät mit 230 Volt, anzusteuern. Aus Sicherheitsgründen finde ich es besser, dafür eine gekaufte Funksteckdose zu benutzen, und mit dem Mikrokontroller + HC-12 die Fernbedienung zu ersetzen. Damit muss man gar nicht mit Verdrahtung von 230 V in Berührung kommen. Ich habe es auch mal probiert (mit dieser: https://www.amazon.de/Funksteckdosen-Fernbedienung-LED-Statusanzeige-Spritzwassergesch%C3%BCtzt-Kindersicherungsschutz/dp/B07BQSX4GM). Zunächst habe ich versucht, mit dem HC-12 Modul die Signale der Fernbedienung (z.B. Lampe ein) anzusehen, um dieselben Signale mit dem HC-12 dann abzusenden, mit der Hoffnung dass die Funksteckdose darauf reagiert. Leider war das komplett erfolglos. Der HC-12 empfängt keine Signale, zumindest er überträgt sie an meine Console nicht weiter. Ich habe die Sendefrequenz abgeglichen (habe bei HC-12 Kanal 1 gewählt um sich der Frequenz der Funksteckdose anzupassen), ich wüsste leider nicht, was man dann noch einstellen könnte, was die mögliche Ursache dafür war, dass ich nichts detektieren konnte. Ich habe mit der Baudrate gespielt, da sie wohl die in-air-Baudrate beeinflusst, aber auch da ohne Erfolg. Hätten Sie da einen Ratschlag für mich? Bzw. ein Blog-Eintrag mit dieser Idee wäre sehr schön (auch wenn mit einer anderen Funksteckdose – die sind nicht so teuer so dass ich eine andere gerne ausprobieren würde).
Viele Grüße

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery