Funksignal decodieren für Masterarbeit!! (Fishfinder zweckentfremden)

Hallo,

ich bin auf der Suche nach etwas Hilfe für mein Arduino Projekt. Erst einmal es geht um meine Masterarbeit in der ich nun auf einen Arduino zurückgreifen muss. Leider bin ich was das Thema angeht ein ziemliches Greenhorn. :smiley:

In der Arbeit geht es darum mit low cost Sensoren eine Sensorplattform zu erstellen mit der Tiefenmessungen durchgeführt und gespeichert werden können. Dabei soll das ganze mehr oder weniger von alleine laufen.

Mein Problem ist nun das ich als Tiefensensor einen Fishfinder quasi zweckentfremden soll. Dabei handelt es sich um den Lucky Wireless Fishfinder. (https://www.amazon.de/Lucky-Wireless-schwarz-matrix-Fisch-Finder/dp/B00MARDPQM/ref=sr_1_1?ie=UTF8&qid=1487507851&sr=8-1&keywords=Lucky+Fish+Finder)

Dieser misst die Tiefe sowie Temperatur und sendet diese Daten via Funk (433 mHz) an die Displayeinheit. Meine Aufgabe ist nun das Funksignal abzufangen zu decodieren und die Tiefe dann als NMEA string weiter an einen Logger zu senden.

Als Funkempfänger nutze ich einen High Selectivity Super Het Reciever. (http://www.tme.eu/de/Document/bffb17d7dfed9290c79afa53e3604064/650201138G.pdf)

An diesem kommt das Signal auch schon an. Um nun das Signal abzufangen habe ich dieses mit einem Analyseprogramm genauer untersucht --> Bilder im Anhang(„Salae Logic 1.2.11“ ).

Neue Datenpakete kommen alle 140 ms.

Es scheint, dass ein Datenpaket aus fünf Komponenten besteht:

  1. Die Gesamt tiefe als 16-Bit-Zahl
  2. Einer immer gleichen Pause von 27.8 ms
  3. Einem immer gleichen SYNC-Puls von 6.6 ms
  4. Einem Zeitraum von 60 ms, der ggf. eines oder mehrere Echos (möglicherweise Reflexionen) als steigende Flanken enthält. Die Zeit vom Ende des SYNC-Pulses bis zur steigenden Flanke des Echos entspricht der Signallaufzeit im Wasser bzw. ist proportional zur Tiefe des reflektierenden Objektes (a.k.a. „Fisch“). Er kann bis 40 m Tiefe messen was den 60 ms Laufzeit in etwa entspricht. (Schallgeschwindigkeit in Wasser 1400 - 1500 m/s)
  5. Der gemessenen Temperatur als 16-Bit-Zahl.

Nun möchte ich gerne nur die Zeit vom Ende des SYNC-Pulses bis zur ersten steigenden Flanke des Echos messen und diese dann in eine Tiefe umrechnen.
Ich habe schon viel über Timer und Interrupts gelesen aber weiß wohl noch nicht genau wie ich damit umgehen muss/sollte um diesen Zeitwert zu bekommen. Für das Projekt nutze ich den Arduino Mega 2560. Ich denke auch, dass ich bis zu dem Punkt angelangt bin an dem ich den SYNC-Impuls messen kann, aber wie genau ich dann an den Zeitwert des LOWs komme ist mir unklar.

Ich hoffe einer von euch kann mir helfen :slight_smile: Würde mich sehr über Tipps und Hilfe freuen!!!

Liebe Grüße :stuck_out_tongue:

neu.ino (855 Bytes)

Bist Du sicher, dass der SYNC Impuls tatsächlich das ist, was Du dafür hältst?

Aufgrund meiner Kenntnisse von 433 MHz Wettersensoren halte ich es für Unwahrscheinlich, dass in einem Funkprotokoll Daten schon VOR dem SYNC ausgesendet werden.

433MHz Wettersensoren senden im allgemeinen so:

1.) Trainingsbits (eine immer gleich lange Folge von HIGH und LOW mit gleichem Zeitabstand
2.) Ein überlanges SYNC als LOW von sehr langer Dauer (3ms bis 30ms).
3.) eine feste Anzahl Datenbits in Bitlängencodierung oder Manchestercodierung

Hast Du genügend Daten empfangen und ausgewertet, um sicher zu sein, dass Dein Protokoll tatsächlich einen SYNC-Puls von 6.6 ms HIGH hat?

So beim ersten Draufblicken würde ich sagen, dass auch das der SYNC sein kann, was Du bezeichnest als:
Einer immer gleichen Pause von 27.8 ms

Also in dem Fall wäre ein LOW Impuls von 27.8ms das SYNC, das Du aus dem Datenstrom herausfischen müßtest.

Es ist aber gehupft wie gesprungen, ob Du als SYNC einen HIGH-Impuls von 6.6ms oder einen LOW-Impuls von 27.8ms hast, das macht software technisch keinen Unterschied.

Was hast Du als Empfängerhardware und als Arduino?

Ist es ein 433MHz 5V Empfänger? Und ein 5V Arduino-Board, z.B. UNO?

Wenn es ein UNO-kompatibles Board ist: Könntest Du einen der digitalen Pins Pin-2 oder Pin-3 für den Empfänger verwenden?

Ich würde den Timer am Ende des SYNC Impulses starten, und ihn dann bei jeder steigenden Flanke ablesen. Wenn Du das Signal auf den Input Capture Pin ICP legst, wird der Zählerstand bei der Flanke in das Input Capture Register ICR übertragen, und kann später ausgelesen werden.

Hallo,

danke erst mal für die Antworten!

Zu jurs:

Ich habe was das Signal angeht erst mal nur Vermutungen aufgestellt. Aber ich habe in anderen Foren auch schon gelesen das der Fischfinder wohl so arbeitet.

Als Empfängerhardware habe ich einen High Selectivity Super Het Reciever der auf 433 MHz arbeitet und einer maximalen Spannung von 3,6 V. Als Board verwende ich das Arduino Mega 2560.

Zu DrDiettrich:
Ja so in etwa habe ich mir das auch vorgestellt und auch schon versucht. Mir fehlt aber wohl bisher noch das Verständnis über Timer, obwohl ich schon viel gelesen haben. :confused: - Es hört sich einfach sehr leicht an einen Timer zu starten und zu stoppen, leider ist es das schreiben des Codes noch nicht. :smiley:

Aber ich werde es weiter versuchen :wink:

Liebe Grüße :slight_smile:

Haras1111:
Aber ich werde es weiter versuchen :wink:

Was für ein Muster möchtest Du in den Daten erkennen?

Zuerst einen "immer gleichen SYNC-Puls von 6.6 ms als HIGH", und nach der fallenden Flanke des Sync-Impulses dann die Zeitdauer bis zur nächsten steigenden Flanke?
Und diese Zeitdauer bis zur nächsten steigenden Flanke nach dem Sync-Impuls soll dann irgendwo ausgegeben werden?
Bekommst Du dafür keine Interrupt-Behandlungsroutine für den Datenpin selbst gebacken?

Sind alle übrigen HIGH Impulse in den empfangenen Daten kürzer als 6.6ms? Oder könnte eine Verwechslungsgefahr mit anderen HIGH-Signalen bestehen? Ansonsten würde ich zuerst mal versuchen, eine Funktion zu schreiben, die alle HIGH Signale mit einer Dauer zwischen 6000 und 7000µs (6.000ms und 7.000ms) aus den Empfangsdaten abzufischen, am besten mit einer Interrupt-Behandlungsroutine, die auf CHANGE getriggert wird.

Hast Du den Datenpin des Empfängers an einem interruptfähigen Pin des Controllers angeschlossen. also Pin-2 oder Pin-3?

Ich weiß leider nicht genau was du mit Muster meinst? :confused:

Ja genau ich möchte die Messung der Zeit von dem LOW haben, dass ab der fallenden Flanke vom SYNC Impuls gemessen werden soll.

Das mit der Interrupt Behandlungsroutine ist für mich noch etwas schwer. Zumindest, da ich ja wahrscheinlich zwei benutzen muss oder?
Ich habe jetzt einen Code geschrieben, der hoffentlich genau das tut was du sagtest. Einer der auf alle Ereignisse reagiert mit CHANGE. Mit einer if- Angabe fische ich dann alle Messungen ab die zwischen 6500µs und 7000µs liegen (leider bin ich mir auch nicht ganz sicher, dass die Angabe mit in die Change Anweisung sollte) :D.

volatile int change = 0;


void setup() {
   Serial.begin(4800);
  attachInterrupt(0, changing, CHANGE);

}

void loop() {
  // put your main code here, to run repeatedly:

}

void changing(){
   attachInterrupt(0, changing, CHANGE); 
   change = micros();

   if(change > 6500 and change < 7000)
   {      
      Serial.println(change);
   }
}

Meintest du in etwa sowas? Ich weiß nur leider nicht wie ich dann weiter komme. Wenn ich dann mit einem Interrupt weiter arbeiten würde, würde ich doch unendlich viele Werte bekommen oder? Ich will ja nur den einen Wert vom LOW....

Und ja als Datenpin benutze ich den Pin-2.

Haras1111:
Ich weiß leider nicht genau was du mit Muster meinst? :confused:

Stelle Dir vor, jemand zeigt Dir Farbtafeln und sagt: "Bei dreimal Rot nacheinender laufe los!", dann ist dreimal Rot das zu erkennende Muster. Bei Dir sind es Einsen und Nullen.

Bei der Zeitmessung möchtest Du die Zeitdifferenz zwischen fallender und steigender Signalflanke messen. Also mußt Du fallende und steigende Flanke unterscheiden. Bei fallender Flanke merkst Du Dir die Zeit, bei steigender berechnest Du die Differenz.

Hallo zusammen,

ist das Thema noch von Interesse? Ich arbeite an genau dem selben Thema und habe das Auslesen bereits gelöst als Arduino-Code. Ich kann gerne den Code zur Verfügung stellen und suche selbst Antworten auf folgende Fragen:

  • gibt es das 16bit-Signal vor dem Sync-Pulse wirklich? Ich sehe es bei mir nicht. Mein Signal ist aber auch deutlich verrauschter.
  • Wie interpretiere ich die 16 bit Temperatur? D.h. wie rechne ich den Wert in °C um?

Würde mich freuen, wenn der Thread noch lebt :slight_smile:

Viele Grüße!

OK, es scheint nach einer PM noch Interesse zu geben. Ich poste jetzt einfach mal meinen Code. Ich habe durchaus noch einige Ideen zur Interpretation der 16 bits usw., aber gerade wenig Zeit. Ich schreibe später noch mehr dazu. Noch ein Hinweis: nicht wundern, die Serial-Output-Routine ist komplizierter als nötig :). Liegt daran, dass der Code Teil eines größeren Projekts ist.

Also:

#define SONAR_NUM_ECHOS 5 // the (SONAR_NUM_ECHOS - 1) widest sonar returns within the sonar return window will be logged

volatile unsigned long pulseStartInt = 0, pulseEndInt = 0; // most recent RF signal pulse start/end timestamp, updated by interrupt routines

unsigned long pulseStart = 0, pulseEnd = 0, pulseWidth = 0; // current pulse timestamps (start,end,width)
unsigned long dataTransStart = 0; // start timestamp of current data transmission window (=end of current sync pulse)

const unsigned long sonarWindowEnd = 58000; // pulses within 58 ms of dataTransStart are sonar returns;
const unsigned long temperatureWindowEnd = 75000; // pulses after sonarWindowEnd but before temperatureWindowEnd are temperature bits;
const unsigned long syncPulseWidthMin = 6600, syncPulseWidthMax = 6800; // Min/Max sync pulse width

unsigned long sonarEchoStart[SONAR_NUM_ECHOS]; // array of sonar return start timestamps
unsigned long sonarEchoWidth[SONAR_NUM_ECHOS]; // array of sonar return widths

unsigned int temperatureRaw = 0;
byte currentTempBit = 0;


void setup()
{
  Serial.begin(9600);
  Serial.println(F("X;t;SPE;SS1;SW1;SS2;SW2;SS3;SW4;TMR")); // print header lines:

  pinMode(3, INPUT);
  attachInterrupt(1, isr1, CHANGE); // interrupt 1 on pin 3 executed on change HIGH <-> LOW
}


void loop()
{
  while(pulseEndInt == 0); // wait for a pulse

  noInterrupts();
  pulseStart = pulseStartInt;
  pulseEnd = pulseEndInt;
  pulseStartInt = 0;
  pulseEndInt = 0;
  interrupts();
  pulseWidth = pulseEnd - pulseStart;

  if(pulseEnd < dataTransStart + sonarWindowEnd) // pulse is inside the window for sonar returns
  {
    sonarEchoStart[0] = pulseStart - dataTransStart;
    sonarEchoWidth[0] = pulseWidth;

    for(byte k=0; k < SONAR_NUM_ECHOS - 1; k++) // sorting algorithm, sorts by sonarEchoWidth
    {
      if(sonarEchoWidth[k] > sonarEchoWidth[k+1])
      {
        unsigned long temp = sonarEchoWidth[k+1];
        sonarEchoWidth[k+1] = sonarEchoWidth[k];
        sonarEchoWidth[k]   = temp;
        temp = sonarEchoStart[k+1];
        sonarEchoStart[k+1] = sonarEchoStart[k];
        sonarEchoStart[k]   = temp;
      }
      else
        break;
    }

  }
  else if(pulseEnd < dataTransStart + temperatureWindowEnd) // pulse is inside the window for temperature bits
  {
    if(pulseWidth > 450) // it's a HIGH bit
      temperatureRaw |= (1 << currentTempBit);
    currentTempBit++; // TODO: currently no idea whether the 16 temperature bits are LSB 0 or MSB 0
    
    if(currentTempBit == 16) // data transmission complete, write results and start looking for the next sync pulse
    {
      writeLogLineSerial('X');
      dataTransStart = 0;
    }
  }
  else if((pulseWidth > syncPulseWidthMin) && (pulseWidth < syncPulseWidthMax)) // synchronisation pulse found
  {
    dataTransStart = pulseEnd; // set new data window start timestamp
    currentTempBit = 0; // clear values
    temperatureRaw = 0;
    for(byte k=0; k < SONAR_NUM_ECHOS; k++) // clear sonar echo buffer
    {
      sonarEchoStart[k] = 0;
      sonarEchoWidth[k] = 0;
    }  
  }
}


void isr1()
// Interrupt routine called on HI/LO change of Pin 3
{
  if(digitalRead(3) == HIGH) // change LO->HI? then record time.
    pulseStartInt = micros();
  else if(pulseStartInt) // change HI->LO and we have a start point?
    pulseEndInt = micros();
}


void writeLogLineSerial(char line)
// log parameters specified by line
{
  Serial.print(line); Serial.print(";");
  Serial.print(millis()); Serial.print(";"); // time of log entry in 1/1000s
  
  switch(line)
  {
    case 'X':
      Serial.print(dataTransStart); Serial.print(";");
      for(byte k=1; k < SONAR_NUM_ECHOS; k++)
      {
        Serial.print(sonarEchoStart[k]); Serial.print(";");
        Serial.print(sonarEchoWidth[k]); Serial.print(";");
      }
      Serial.print(temperatureRaw, BIN);
      break;
  }
  Serial.println();
}

Dieser Code fängt zuerst den SyncPulse, dann alle Sonar Returns, sortiert sie der Breite nach und speichert die größten 4 (=SONAR_NUM_ECHOS - 1) und dann die 16 bits der Temperatur. Wie gesagt, mit den mysteriösen 16 bits vor dem SyncPulse kann ich nichts anfangen.

Viel Spaß damit :slight_smile:

1 Like

Hallo zusammen,

auch wenn es schon eine Zeit her ist, habt Ihr es geschafft, das Protokoll von dem Lucky Sonar zu dekodieren?

Danke
VG Eddie

1 Like