Arduino Uno, 434Mhz Empfänger und Dekodierung von Manchester kodierten Daten.

Ich versuche mittels Arduino und einem 434Mhz Empfänger die Daten eines Funkthermometers zu dekodieren.

Was ich bereits weiss:
Das Thermometer sendet alle 12 Sekunden Manchester kodierte Daten unter Verwendung von On-Off keying.
Jede Übetragung startet mit 8 kurzen Pulsen von etwa 250uS breite und mit einem Abstand von 5ms.
Danach folgen 12 byte Manchester kodierte Daten mit einer vierfachen Wiederholung der gesamten Übetragung.

Ich habe mir nun diesen Sketch mehr oder weniger zusammengebaut unter zur Hilfenahme anderen Funkempfänger Projekte.

Wäre toll wenn sich das mal jemand anschauen könnte, da ich bzgl. der Timer Sache und dem Interrupt nicht Themenfest bin.

#define SET_INPUT_CAPTURE_RISING_EDGE() (TCCR1B |= _BV(ICES1))
#define SET_INPUT_CAPTURE_FALLING_EDGE() (TCCR1B &= ~_BV(ICES1))

#define STATE_START_PULSES 0
#define STATE_FIRST_BIT    1
#define STATE_DATA      2

typedef unsigned int       uint; //16bit
typedef signed int         sint; //16bit

uint current_time;
uint duration;
uint previous_duratiom;
uint start_pulse_counter=0;
uint detection_state=0;

ISR(TIMER1_OVF_vect)
{
}

ISR(TIMER1_CAPT_vect){
  current_time = ICR1;
  duration = (current_time - previous_duratiom);
  previous_duratiom = current_time;
   
if (detection_state == STATE_START_PULSES)
    {
    if ((duration >= 1255) && (duration <= 1315)) {
      start_pulse_counter++;
      Serial.print(start_pulse_counter);
      Serial.print(" ");
      Serial.println(duration);
      if (start_pulse_counter == 8) {
        detection_state = STATE_FIRST_BIT;
        start_pulse_counter = 0;
      }
    } else {
      start_pulse_counter = 0;
      } 
    }
  else if (detection_state == STATE_FIRST_BIT)
  {
    detection_state = STATE_DATA;
  }
  else if (detection_state == STATE_DATA)
  {
    //Serial.println(duration);
  }
}

void setup() {
  Serial.begin(115200);
  DDRB = 0x2F;             // B00101111
  DDRB  &= ~(1<<DDB0);     // PBO(ICP1) input = PIN8
  PORTB &= ~(1<<PORTB0);   // ensure pullup resistor is also disabled
  DDRD  |=  B11000000;     // (1<<PORTD6);   //DDRD  |=  (1<<PORTD7); (example of B prefix)
  TCCR1A = B00000000;      //Normal mode of operation, TOP = 0xFFFF, TOV1 Flag Set on MAX
  TCCR1B = ( _BV(ICNC1) | _BV(CS11) | _BV(CS10) );
  SET_INPUT_CAPTURE_RISING_EDGE();
  TIMSK1 = ( _BV(ICIE1) | _BV(TOIE1) );
  Serial.println("Ready to receive data");
}

void loop() {}

Als Anhang beigefügt die mit Audacity mitgeschnittenen Daten.

Irgendwas mit dem Timer scheint noch nicht ganz richtig zu sein, da ich hier unter duration nicht auf die ermittelten 250uS breite mit 5ms Abstand komme.

Schimpft bitte mit mir, wenn irgendwelche Infos fehlen oder ich etwas vergessen habe.

Danke

rx434_arduino.wav (99 KB)

Im Interrupt-Handler darfst Du keine Ausgaben auf die serielle Schnittstelle machen. Dies wäre zum einen viel zu langsam, aber noch viel wichtiger ist, dass es zu einem Deadlock führen kann, also einem Einfrieren des Arduinos (weil die Interrupts gesperrt sind und nur ein Interrupt den seriellen Puffer leeren kann).

Versuche, soviel wie möglich aus dem Interrupt herauszunehmen und im loop() abzuhandeln, der Interrupt-Handler sollte so kurz wie möglich sein.

Alle Variablen, die Du im normalen Code und im Interrupt-Handler verwenden willst, musst Du "volatile" deklarieren.

Habe ich das richtig verstanden, an die Dekodierung der Daten hast Du Dich noch nicht gemacht, weil die Start-Pulse noch nicht richtig erkannt werden?

daran hab ich mich auch schon gesetzt aber ich war/bin mir mit der Erkennung der Start-Pulse nicht sicher.
Bzw. mit dem konfigurierten Timer. Ob der so richtig wäre...

Die Start-Pulse werden auch erkannt, aber ich glaube ich muss in der nachfolgenden Abhandlung von Rising Edge
auf Falling Edge hin und her wechseln um die Pulsbreite richtig zu erkennen. Hier weiss ich aber nicht ob der Arduino
das zulässt und wie er sich dann verhällt. Da fehlt mir einfach die Erfahrung mit dem Teil. Den Sketch hab ich daher
nicht geposted kann ich aber gerne per PN machen.

Ich mache das mit

attachInterrupt (RXDATA, isr, CHANGE )

In der ISR dann

#define RX_INT (PIND>>3)&B1    
void isr ()
{
const bool RXPIN = RX_INT;     
 if(RXPIN)...

Ich habe meine Erkennung dahingehened geändert, dass nicht auf bestimmte Pulszeiten gewartet wird, sondern die Flanken direkt in eine Bitfolge umgewandelt werden, und in der Loop wird dann alles ausgewertet.
Dazu habe ich mir eine Bitbuffer Klasse erstellt.

So hab ich das auch mal versucht, jedoch mit Pulsbreitenerkennung und bin auf keinen grünen Zweig gekommen.

@mde110
hast du evtl. Lust den Sketch mit uns zu teilen?

Die Start-Pulse werden auch erkannt, aber ich glaube ich muss in der nachfolgenden Abhandlung von Rising Edge auf Falling Edge hin und her wechseln um die Pulsbreite richtig zu erkennen.

Du könntest das jeweils im Interrupt-Handler umprogrammieren (also von steigender auf fallende Flanke und umgekehrt. Damit kriegst Du dann immer die richtigen Timings. Es wäre vielleicht auch nicht verkehrt, wenn Du den Counter von Timer1 dann jeweils zurücksetzen würdest, dann kriegst du gleich die Laufzeiten raus.

Allerdings kannst Du mit Deinem Setup nur Zeiten mit einer maximalen Auflösung von 16µs erfassen. Genügt das? Den Interrupt-Handler solltest Du dann aber ziemlich ausmisten und nur das allernötigste dort drin tun, sonst könntest Du den einen oder anderen Puls verpassen (abhängig, wie schnell die reinkommen).

Übrigens: in Deinem WAV-File finde ich nur 4 Pulse zum Starten. Ist das nicht schön aufgenommen oder war das ein Verschreiber Deinerseits?

pylon:
Du könntest das jeweils im Interrupt-Handler umprogrammieren (also von steigender auf fallende Flanke und umgekehrt.

Ganz schön optimistisch!

Die angemessene Lösung hat natürlich mde110 vorgestellt, nämlich dass ein CHANGE Interrupt definiert wird und beim Eintritt in die Interrupt-Behandlungsroutine wird als erstes der Pinstatus ausgelesen. Als Arduino-Programmierer darf man das aber natürlich auch gerne ohne Makro und ohne direkte Portzugriffe nur mit digitalRead machen. Und je nachdem ob beim Start der Interruptsrouthine ein HIGH oder LOW vorliegt kann man mit einer Funktion sowohl HIGH- als auch LOW-Pulslängen ermitteln (und irgendwo zwischenspeichern, z.B. in einem FIFO-Puffer, der dann in der loop ausgewertet werden kann).

hey,

Danke für den ganzen Input. Nun bin ich in der Zwickmühle, dass ich nicht genau weiss wie ich weiter machen soll.
Alles nochmal auf Anfang oder eben auf die andere Methode umsteigen...

@pylon
Das ist die gesamte Übertragung also inkl. der Widerholungen. Du musst stark reinzoomen und dann erkennst du die 8 Startpulse inkl. Daten.

nothinghere:
@mde110
hast du evtl. Lust den Sketch mit uns zu teilen?

Ach, fertigen Code wollen wir gleich haben...
Habe mein Projekt mal stark minimiert. Quasi ein Minimalbeispiel.

Ganz schön optimistisch!

Das wechseln der Flanke ist der Austausch eines Bits im Register. Wieso genau ist es quasi "über-optimistisch", dass das stattfinden kann? Selbst wenn der Counter des Timers 1 noch rückgesetzt wird, ist das alleweil schneller als ein einziger Durchlauf des Timers (der Prescaler ist auf 64 eingestellt).
Also bitte nicht nur Behauptungen aufstellen, sondern begründen!

Das ist die gesamte Übertragung also inkl. der Widerholungen. Du musst stark reinzoomen und dann erkennst du die 8 Startpulse inkl. Daten.

Ich habe schon reingezoomt, aber am Anfang sind immer noch nur 4 Pulse. Zwischen den Blöcken kann ich 8 Pulse ausmachen, am Anfang aber nicht.

Ich habe schon reingezoomt, aber am Anfang sind immer noch nur 4 Pulse. Zwischen den Blöcken kann ich 8 Pulse ausmachen, am Anfang aber nicht.

Ach das meinst du. Ich seh es... Sorry da hab ich einfach geschlampt beim exportieren der .wav Datei. Im Original sind dort natürlich auch 8 Pulse.

Hast Du meinen Vorschlag eingebaut? Falls ja, poste den resultierenden Code.

Ach, nicht der Rede wert nothinghere.

Hi,

ich war die Tage etwas im Stress und konnte erstmal nicht weiter machen.

Erstmal dickes Danke an mde110 für deinen Sketch. Schaue ich mir am WE mal an.
Zuerst versuche ich es nochmal meinen fertig zu stellen und die Anregungen hier aus dem Thread einzubauen.

Als Anhang mein aktueller Stand. Aber da fehlt wie gesagt noch etwas Nachbesserung übers WE wenn ich Zeit habe.
Das ist alles eher Quick & Dirty.

rx_sketch.ino (4.84 KB)

Funktioniert Dein Sketch? new_data und save_data sind noch nicht volatile deklariert. Dann setzt Du TCNT1 zurück, berechnest die Länge der Pulse aber immer noch mittels Differenz von einem gespeicherten Wert. So auf die Schnelle würde ich also sagen, das kann nicht funktionieren.

Das Ändern des Interrupt-Triggers von steigender zu fallender Flanke (bzw. umgekehrt) sollte bei jedem Durchlauf geschehen, sonst wird die Biterkennung verunmöglicht (vorausgesetzt, bei mir findet momentan kein massiver Denkfehler statt).