Drahtlose Fernsteuerung mit Feedback

Nabend allerseits

Ich bin am Bauen einer Fernsteuerung für eine Gartenbeleuchtung und eine Kamera. Per Handsender (TX) kann der Videolink ein- und ausgeschaltet werden. Ebenso die Beleuchtung (Relais 3), hier aber mit einer toggle-Funktion.
Zusätzlich möchte ich ein Feedback von der Box im Garten, die mir sagt, ob der Videolink/Lichter ein, oder aus ist. Dafür sendet das Funkmodul 1x pro Minute den Zustand. Später soll noch die Leerlaufspannung der Akkus übermittelt werden (setze ich später um)

Die Relais-Funktionen funktionieren soweit einwandfrei. Nicht aber das Feedback. Irgendwas scheint faul zu sein. Das Display zeigt aktuell immer “OFF” für Videolink und Lights.

Code für die Box im Garten:

#include <SoftwareSerial.h>

int Relais1 = 12;
int Relais2 = 13;
int Relais3 =  8;
int LightsState = 11;
int VideoLinkState = 9;

const byte numChars = 32;
char messageReceived[numChars];
char keyWord1[] = "ActivateTimer1";
char keyWord2[] = "StopTimer1";
char keyWord3[] = "ToggleRelais3";
char command1[] = "VideoLinkON";
char command2[] = "VideoLinkOFF";
char command3[] = "LightsON";
char command4[] = "LightsOFF";
unsigned long last = millis(); //set timer
const unsigned long INTERVAL = 1000L*60*1; // Intervall 1 Min.
unsigned long lastRun = 0 - INTERVAL; // 

int StateOfLights;
int StateOfVideoLink;

SoftwareSerial HC12(2, 3);



void setup() {
  HC12.begin(9600);
  Serial.begin(9600); // set up Serial library at 9600 bps - this is the speed the serial interface will work all
  pinMode (Relais1, OUTPUT);
  pinMode (Relais2, OUTPUT);
  pinMode (Relais3, OUTPUT);
  pinMode (LightsState, INPUT);
  pinMode (VideoLinkState, INPUT);
}

void loop() {

  boolean Relais3state = digitalRead(Relais3); //check if Relais3 ist on or off. Returns 1 or 0
  static byte ndx = 0;
  char charReceived;
  char endMarker = '\n';

  while (HC12.available() > 0) {
    charReceived = HC12.read();
    if (charReceived != endMarker) {
      messageReceived[ndx] = charReceived;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      messageReceived[ndx] = '\0';
      ndx = 0;
      delay(100);
      
      while (HC12.available() > 0) HC12.read();
      if(strcmp(messageReceived, keyWord1) == 0) {
      digitalWrite(Relais1, HIGH);
      delay(100);
      digitalWrite(Relais1, LOW);
      }
      else if(strcmp(messageReceived, keyWord2) == 0) {
      digitalWrite(Relais2, HIGH);
      delay(100);
      digitalWrite(Relais2, LOW);
      }  
      else if(millis() - last > 250){
        if(Relais3state == 0 && (strcmp(messageReceived, keyWord3) == 0)){
        digitalWrite(Relais3, HIGH);
      }
      else if(Relais3state == 1 && (strcmp(messageReceived, keyWord3) == 0)){
          digitalWrite(Relais3, LOW); 
        }
      }
      
      last = millis();
    }
  }

  
if (millis() - lastRun >= INTERVAL) {

      StateOfLights = digitalRead(LightsState);
      StateOfVideoLink = digitalRead(VideoLinkState);

      if(StateOfLights == HIGH) {
      HC12.print(command1);
      }
      else if(StateOfLights == LOW) {
      HC12.print(command2);
      }

      else if(StateOfVideoLink == HIGH) {
      HC12.print(command3);
      }
      else if(StateOfVideoLink == LOW) {
      HC12.print(command4);

      lastRun += INTERVAL;
      
   }
  }
}

Code für den Handsender:

#include <SoftwareSerial.h>
#include <U8g2lib.h>
#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>


U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /*
data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 4);

SoftwareSerial HC12(2, 3); // RX | TX
int button1 = 6;
int button2 = 7;
int button3 = 8;
int buttonState1 = LOW;
int buttonState2 = LOW;
int buttonState3 = LOW;
char command1[] = "ActivateTimer1\n";
char command2[] = "StopTimer1\n";
char command3[] = "ToggleRelais3\n";
char keyWord4[] = "VideoLinkON\n";
char keyWord5[] = "VideoLinkOFF\n";
char keyWord6[] = "LightsON\n";
char keyWord7[] = "LightsOFF\n";
const byte numChars = 32;
char messageReceived[numChars];
char VideoLinkStatus[] = "OFF";
char LightsStatus[] = "OFF";


void draw(){
  u8g2.setFont(u8g_font_profont12);
  u8g2.drawStr(0, 12, "VIDEO LINK:");
  u8g2.drawStr(0, 36, "GARDEN LIGHTS:");
  u8g2.drawStr(0, 60, "BATTERY VOLTAGE:");
  u8g2.setCursor(90,12);
  u8g2.println(VideoLinkStatus);
  u8g2.setCursor(90,36);
  u8g2.println(LightsStatus);
  delay(10);
}

void setup() {
  pinMode(button1, INPUT);
  pinMode (button2, INPUT);
  pinMode (button3, INPUT);
  Serial.begin(9600);
  HC12.begin(9600);
  u8g2.begin();


}

void loop() {

  buttonState1 = digitalRead(button1);
  buttonState2 = digitalRead(button2);
  buttonState3 = digitalRead(button3);
  static byte ndx = 0;
  char charReceived;
  char endMarker = '\n';


  if (buttonState1 == HIGH) {
    HC12.print(command1);
  }
  else if (buttonState2 == HIGH) {
    HC12.print(command2);
  }

  else if (buttonState3 == HIGH) {
    HC12.print(command3);
  }


  while (HC12.available() > 0) {
    charReceived = HC12.read();
    if (charReceived != endMarker) {
      messageReceived[ndx] = charReceived;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      messageReceived[ndx] = '\0';
      ndx = 0;
      delay(100);
      
      while (HC12.available() > 0) HC12.read();
      if(strcmp(messageReceived, keyWord4) == 0) {
      strncpy( VideoLinkStatus, "ON", sizeof(VideoLinkStatus) );
      VideoLinkStatus[sizeof(VideoLinkStatus)-1] = 0;
      delay(100);
      
      }
      else if(strcmp(messageReceived, keyWord5) == 0) {
      strncpy( VideoLinkStatus, "OFF", sizeof(VideoLinkStatus) );
      VideoLinkStatus[sizeof(VideoLinkStatus)-1] = 0;
      delay(100);
      }

      else if(strcmp(messageReceived, keyWord6) == 0) {
      strncpy( LightsStatus, "ON", sizeof(LightsStatus) );
      LightsStatus[sizeof(LightsStatus)-1] = 0;
      delay(100);
      }

      else if(strcmp(messageReceived, keyWord7) == 0) {
      strncpy( LightsStatus, "OFF", sizeof(LightsStatus) );
      LightsStatus[sizeof(LightsStatus)-1] = 0;
      delay(100);
      }
    }
  }
    u8g2.firstPage();  
  do 
    {
     draw();      
    }
  while( u8g2.nextPage() );
delay(50);
  
  
  }

(deleted)

Danke für den Hinweis Peter!

Es muss aber noch etwas anderes faul sein, da beim Handsender alles auf "OFF" bleibt, obwohl die Inputs auf der anderen Seite auf High sind.

(deleted)

Tach Peter

Ja der Loop wird schön im 1min-Intervall ausgeführt (serial.print in der Funktion). Versuche jetzt noch zu schauen, ob das Modul sendet (Spektrum Analyzer). Aber bin ziemlich sicher, dass es sendet

Also: Das Modul sendet. Hab die Packete auf dem Spektrum gesehen.

(deleted)

Habe ich getan und zwar so:

while (HC12.available() > 0) {
    charReceived = HC12.read();
    Serial.println("SerialCheck1"); //
    Serial.println(charReceived); //
    if (charReceived != endMarker) {
      messageReceived[ndx] = charReceived;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      messageReceived[ndx] = '\0';
      ndx = 0;
      //delay(100);
      
      while (HC12.available() > 0) HC12.read();
      Serial.println("SerialCheck2");
      Serial.println(charReceived); //                 Read incoming
      if(strcmp(messageReceived, keyWord4) == 0) {
      strncpy( VideoLinkStatus, "ON", sizeof(VideoLinkStatus) );
      VideoLinkStatus[sizeof(VideoLinkStatus)-1] = 0;
      delay(100);

Doch er kam offensichtlich nie in die erste while-schleife, denn mir wurde nichts geprintet (Serial-Monitor-Verbindung gecheckt)

Ich habe gestern einen Fehler gemacht. Weil ich den Handsender per USB gespeist habe, kam im Serial Monitor nichts vom Funkmodul rein.

Mit diesem Code:

while (HC12.available() > 0) {
    charReceived = HC12.read();
    Serial.println("SerialCheck1"); //
    //Serial.println(charReceived); //
    if (charReceived != endMarker) {
      messageReceived[ndx] = charReceived;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
      Serial.println(messageReceived);
    }
    else {
      messageReceived[ndx] = '\0';
      ndx = 0;
      
      //delay(100);
      
      while (HC12.available() > 0) HC12.read();
      Serial.println("SerialCheck2");
      Serial.println(messageReceived); //                 Read incoming
      if(strcmp(messageReceived, keyWord4) == 0) {
      strncpy( VideoLinkStatus, "ON", sizeof(VideoLinkStatus) );
      VideoLinkStatus[sizeof(VideoLinkStatus)-1] = 0;
      delay(100);

kommt nun der von der anderen Seite gesendete String Char für Char raus:

SerialCheck1
L
SerialCheck1
Li
SerialCheck1
Lig
SerialCheck1
Ligh
SerialCheck1
Light
SerialCheck1
Lights
SerialCheck1
LightsO
SerialCheck1
LightsOF
SerialCheck1
LightsOFF

Das heisst aber immer noch, dass er nicht in die zweite while-Schleife reingeht. Was jemand warum?

(deleted)

Das komische ist ja, dass die selbe Schlaufen-Struktur für das Einlesen der Chars und die weitere Verarbeitung beim Code für die "Garten-Box" funktioniert.

Kann es sein, dass es mit diesem Abschnitt zu tun hat, den es ja im anderen Code nicht gibt?

    u8g2.firstPage();  
  do 
    {
     draw();      
    }
  while( u8g2.nextPage() );

Warum er nicht in den else Zweig kommt?
(Oder was meinst du mit der "zweiten while-Schleife"?)

Weil if (charReceived != endMarker)   nie false wird?

Wieso nicht? Bei jedem command habe ich ja am Ende einen endMarker ( \n)

Hi

Davon sehe zumindest ich Nichts - warum gibst Du Dir nicht SÄMTLICHE Zeichen aus, Die Du empfängst?
(Zeichen <33 als Zeichen UND HEX, drunter nur HEX)

Ist Dein endMarker überhaupt das richtige Zeichen?
Wäre ja blöd, auf ein /n zu warten und ein 0x00 kommt …

MfG

Hallo postmaser-ino

Ja Du bzw. ihr habt Recht. Ich habe bei den keyWords beim RX-Scetch die \n am Ende vergessen. Ziemlich dumm von mir.

Wie würdest du denn "sämtliche Zeichen" ausgeben? Sorry ich bin noch zu wenig versiert im Programmieren.

Danke allen für die Inputs!

Bin noch am debuggen. Es muss wohl was mit der strncpy nicht stimmen. Denn messageReceived stimmt mit KeyWord6 (char keyWord6 = "LightsON\n":wink: überein:

      while (HC12.available() > 0) HC12.read();
      Serial.println("SerialCheck2");
      Serial.println(messageReceived); //                 Read incoming
      if(strcmp(messageReceived, keyWord6) == 0) {
      strncpy( VideoLinkStatus, "ON", sizeof(VideoLinkStatus) );
      VideoLinkStatus[sizeof(VideoLinkStatus)-1] = 0;
      delay(100);

Der Serial Monitor zeigt mir das:

Serial Begin
L
Li
Lig
Ligh
Light
Lights
LightsO
LightsON
SerialCheck2
LightsON

Beim Display ändert sich "OFF" allerdings nicht zu "ON".
Weiss wer wieso?

Danke!

Hi

Ungefähr so - für Zeilenende (also Umbruch in neue Zeile) musst Du selber sorgen.

void printchar(byte zeichen){
   if (zeichen>=32 && zeichen<128){
      Serial.write(zeichen);  //druckbares Zeichen
   }else{
      Serial.print('.');          //der Rest - unter 32 Steuerzeichen, über 127 'Alles Mögliche'
   }
   Serial.print('(');
   if (zeichen<0x10){   //ist der Wert kleiner 16 (0x10 in HEX, die erste 2-stellige HEX-Zahl)
      Serial.print('0');    //führende Null ausgeben
   }
   Serial.print(zeichen,HEX);
   Serial.print(')');
}

Das Ganze gibt’s auch in schön - Suchwort ‘Hex-dump’ - auf Wikipedia

MfG

Edit
Eine Klammer Zu war zu viel - kompiliert (in einen beliebigen Sketch reinkopiert, bringt also keine Warnungen)

Moin

Der "Hex-Dump" von messageReceived zeigt mir folgendes:

!(21)L(4C)i(69)g(67)h(68)t(74)s(73)O(4F)F(46)F(46)

Der endMarker (habe ihn wie empfohlen geändert) kommt also am Anfang?

Problem solved!
Ich habe einen Fehler mit dem endMarker gemacht :fearful:

Die letzte Aufgabe bei meinem Projekts ist nun noch, eine float variable zu senden.

Bei der Box im Garten wird die Spannung des Akkus gemessen und so übermittelt:

      HC12.print("Voltage");
      HC12.print(VoltagePrint); // send Voltage over RF
      HC12.print("!");

Nun muss ich auf der anderen Seite (TX-Code) diesen String nach der Einleseroutine verarbeiten. Ich denke ich sollte die ersten drei 7 Zeichen des Arrays, also "Voltage" als Bedingung vergleichen (evt. strncmp). Dann diese sieben subtrahieren und dann restlichen Teil, also die float variable printen.

Ich weiss aber nicht, wie ich das anstellen soll.

Hier nochmals die Einleseroutine:

while (HC12.available() > 0) {
    charReceived = HC12.read();
    if (charReceived != endMarker1 && charReceived != endMarker2) {
      messageReceived[ndx] = charReceived;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      messageReceived[ndx] = '\0';
      ndx = 0;
      
      delay(100);

Danke allen!

laurin123:
Problem solved!
Ich habe einen Fehler mit dem endMarker gemacht :fearful:

Dann wäre es wünschenswert, wenn Du die Lösung hier präsentierst.

Gruß Tommy