rx5808-pro : Deluxe BOSCAM rx5808 32CH SPI Steuerung mit Arduino und TV out.

Status
Nicht offen für weitere Antworten.
#81
Ich hab da mal ein wenig weiter gemacht, hier der aktuelle Zwischenstand:

http://fpv-community.de/album.php?albumid=844&attachmentid=132111
RX5805 Pro Diversity Board V2 .jpg

Der 4052 dient als Switch, Video wird zwischen dem TV-OUT des Arduino, den beiden Empfängern und einem Pin des SPI-Ports (unten rechts) geschaltet, Audio nur zwischen den beiden Empfängern.
Die Leitungen für Video und alles, was an den 4052er kommt, habe ich auf der Oberseite der Platine geführt, um eine bessere Abschirmung zu erreichen, Audio läuft größtenteils auf der Unterseite, ebenso die ganzen Leitungen für Steuersignale und die Stromversorgung.
Am SPI-Port können dann ggf. noch weitere Empfänger auf einer Erweiterungs-Platine angeschlossen werden, z.B. für ein Multi-Diversity oder einen Tracker.
An dem LED-Port konnen dann entwerder die Brücken über die Lötjumper belassen werden und durch die Analog-Signale die RSSI-Werte weiterer Empfänger ausgelesen werden - oder man nutzt sie als LED- Anschlüsse, indem man die Leiterbahnen zwischen den Lotjumpern trennt und einen Vorwiderstand (0805 dürfte passen) für die LED´s einlötet.

Die bedrahteten Bauteile werde ich nach Möglichkeit noch verschwinden lassen, die stammen noch aus einem Versuch, die Platine einseitig zu routen, quasi als Drahtbrücken mit Funktion.

Die Positionen bzw. die Art der einzelnen Komponenten (Taster, LED-Port, SPI-Port, Power und Video-Port) sind noch verhandelbar...
:D

Das ist alles noch ein "work-in-progress", die RX5808 sind mittlerweile eingetroffen und auch schon auf SPI- Betrieb umgelötet - der Arduino läßt leider noch auf sich warten.
 
Zuletzt bearbeitet:
#82
Und der dritte Streich...

So, endlich sind alle wichtigen Kompomenten da, also insbesondere der Arduino und die RX5808.
Ab jetzt passen also auch die Positionen der entsprechenden Stiftleisten!
Das Layout habe ich noch einmal überarbeitet, bis auf den 7805 sind alle Bauteile in SMD-Bauweise,
die Widerstände sind alle als 1206er ausgeführt, das krieg man notfalls auch mit zittrigen Fingern noch von Hand gelötet. Für den 4052 muss man dann notfalls einen Vereinskollgegen fragen...
Die Lötjumper sind auch zugungsten von 1206er Widerständen (gebrückt) rausgeflogen. RX5805 Pro Diversity Board V3 .jpg

Jetzt muss sich nur noch ein mutiger Tester finden...
:D
Nein, im Ernst, das mach ich natürlich selber.
Kennt jemand einen preiskünstigen PCB-Service, der kleine Stückzahlen herstellen kann, ggf. mit Sondertarifen für Studenten?
 
Zuletzt bearbeitet:
#84
Schaut echt gut aus!
Vor allem fein das es jetzt doch nur ein Arduino geworden ist der alles macht.

Osh-Park kann ich auch nur empfehlen für die Platinen. Dauert zwar etwas, ist aber unschlagbar günstig.

Ich nehme auch gerne eine als Tester falls du eh mehr als eine bestellen musst, zahl sie auch.
Teile bis auf den 4052D sollte ich da haben.
 
Zuletzt bearbeitet:
#85
Erstmal danke für die Rückmeldungen!
Das Preis/Leistungsverhältnis bei OSH-Park ist ja wirklich super, 3 Platinen, wo man sonst für´s selbe Geld nur eine kriegt!

Da ich da eh bestellen werde, können die zwei ersten Mittester gerne die "Überschussware" haben, und weil es mich ja nichts extra kostet, können die auch gerne kostenlos über die Theke gehen, wenn dafür jemand anders den Software-Part übernimmt.
Da muss ja zumindest in Sachen Ansteuerung des 4052 noch was ergänzt werden. Das könnte ggf. sogar mit den Fehler-Meldungen (Blink-Codes an der LED am PIN13) des TVout kollidieren, also über einen "einfache "case-Struktur hinausgehen...
und da komme ich, fürchte ich , recht schnell an die Grenzen meiner Programmier-Fähigkeiten. Und, bevor das jemand missversteht: das soll keine "Du stehtst jetzt auf ewig in meiner Schuld, also programmier mir das gefälligst bis gestern "-Lösung werden.:p
Es geht einfach nur darum, dass ich die Platinen selbst nicht brauche (also bis auf eine), und lieber einen mir wohlgesonnen Foren-Kollegen habe, als einen reichen Platinenhersteller. ;-)
Im Kreis meiner Flieger-Kollegen wird sich da auch keine Abnehmer finden, die sind alle noch in den Anfängen der FPV-Fliegerei und verlassen sich da noch auf den Video-Empfänger in der Brille...

Bevor ich da jetzt aber in die Vollen gehe muss ich noch ein, zwei Fragen klären, notfalls indem ich einen E-Techniker nerve, aber vielleicht hat ja hier schon jemand die passende Idee oder das passende Wissen:

1. der 4052 hat drei Pins für die Spannungsversorgung GND, VEE und VCC.
GND und VCC sind klar (Masse und +5V), bei VEE habe ich bisher auch Masse angeklemmt, weil keine negative Spannung vorgesehen ist. Dazu spuckt eagle eine Warnung aus...
2. braucht man die Kondensatoren zwischen Video-Ausgang der Empfänger und Eingang des 4052er´s? Oder stören die vielleicht sogar an der Stelle? Folgende Hintergedanken: die Kondensatoren sind dazu da, den Gleichspannungsanteil des Video-Signals "wegzubügeln". Idealerweise sollte das Video-Signal hinter dem Kondensator also eine reine Wechselspannung mit der Mittelllage bei 0v sein - theoretisch. Mit der jetzigen Verschaltung am 4052er (VEE auf GND) kann der aber nur Signale zwischen 0 und +5V switchen, der "negative Signal-Anteil" könnte also verloren gehen - wieder: theoretisch. Und wenn ich mit meinem Bauchgefühl richtig liege, was wäre dann die einfachste Lösung: Kondensatoren weglassen (oder hinter dem 4052er einen einzelnen verbauen, der dann die restliche Schaltung vor Gleichströmen schützt bzw. entkoppelt)? Oder muss da ernsthaft noch ein Spannungsregulator für -5v her? Das Problem hatte ich bisher nicht, weil ich mich nur mit der DIP-Version des 4052er rumgeschlagen habe, bei der der VEE- Pin der SMD-Version anders belegt ist.
3. Jetzt ist noch Zeit/Gelegenheit, beispielsweise die Position der LED-Pins zu verändern (weiter auseinander ziehen, ggf. einen davon noch als zusätzlichen Taster herausführen), Lötösen für persönlich bevorzugte Steckverbinder etc. vorzusehen!

Für die ganz mutigen, die es so schon mal versuchen möchten, kann ich natürlich auch schon mal die entsprechenden eagle -Dateien zur Verfügung stellen, aber bevor ich keine Antworten auf die Fragen gefunden habe, die möglicherweise die Funktionialität einschränkern, werde ich selbst noch keine Platinen ordern und kann es auch guten Gewissens keinem empfehlen.

P.S.: mein gefährliches elktrotechnisches Halbwissen bitte ich zu entschuldigen, ich bin mir dessen bewußt, und frage lieber einmal zu oft, als hier etwas zu behaupten, von dem ich glaube, dass es richtig ist... :D
Die Idee, einen 4052er als Video-Switch zu benutzen, ist übrigens auch nicht meine eigene, die Lorbeeren dafür gehören dem Autor hier:
http://modellfluginfo.de/Modellflugzeug/Blog/fpv-diversity-empfaenger.php
Leider gibt es da keinen Schaltplan, sonst wären die ersten beiden Fragen schon erledigt...

Alternativ habe ich diese Schaltung hier gefunden:
http://www.stephanharms.de/RCVideoSwitch
Da wird genau der 4052er genutzt, um ein Video-Signal zu schalten. Da sind die Video-Eingänge (wie es auch im Datenblatt des Empfängers vorgesehen ist) mit Kondensatoren entkoppelt, zusätzlich sind aber noch 75Ohm- Widerstände verbaut (zu deren Existenz ein E-Techniker sicher auch mehr sagen kann, die 75 Ohm lassen jedenfalls irgendwas mit Impedanz oder Termiantoren bei mir klingeln...). Der fragliche VEE-Pins ist ebenfalls mit Masse verbunden. Zusätzlich ist auch noch eine sogenannte Schwarzwert-Wiederherstellung und ein OpAmp als Verstärker und Signalsplitter vorgesehen...
Da kommt die Frage auf: wollen wir das auch haben - also Splitter und Verstärker und/oder die Sache mit dem Schwarzwert?? Wäre ein IC und ein wenig Hühnerfutter mehr auf der Platine - aber dafür die Möglichkeit, parallel einen DVR zu betreiben, oder einen Zuschauer mit TFT oder Brille mitfliegen zu lassen...und (wenn ich das richtig verstehe) bessere Kontraste bzw. ein satteres Schwarz...
Kurz, alles Dinge, die mindestens auf der "nice to have"- Liste stehen, mich ein paar Minuten beim Zeichnen und alle Nutzer ca. 50 Cent an Bauteilen kosten dürfte.
 
Zuletzt bearbeitet:
#86
Also, Halbwissen habe ich leider auch zu viel ;_)

Zu den Kondensatoren in der Video-Leitung. Ich habe schon Sender und Empfänger mit und ohne aufgebaut. Große Unterschiede konnte ich nicht erkennen. Ich würde sie aber mal vorsehen. Weglassen ist einfacher als Nachflicken.

Die beiden LEDs für Ch1/Ch2 könnte man noch oben zu den Antennenbuchsen ziehen. So kann man da direkt die LEDs anlöten.

Video-Verstärker: Ja, ich denke das ist auf jeden Fall Sinnvoll. Bei mir käme das Ganze z.B: in die Bodenstation mit Monitor, Die hat dann noch nen Ausgang für die Brille und evtl nen Verbauten DVR. Hier wird es dann schon dünn mit dem einen Signal. Das Problem sieht man auch bei vielen Monitoren mit verbauten Empfängern, Steckt man noch die Brille an wird das Bild auf beiden Ausgängen matschig.

Bei der Software kann ich gerne helfen. Umbau des switchers auf den 4052 sollte kein großes Ding sein.

Gruß
Johannes
Wenn möglich würde ich den Verstärker auf jeden Fall vorsehen, alternativ dazu einen Signalabgriff vor dem Verstärker. Dann kann man bestücken oder eben auch nicht.
 

MarenB

Runter kommen sie immer!
#89
Ich hab dafür auch schon SMD-Becherelkos genommen, ging auch, ist aber nicht mehr klein, dann...

In der Audio-out-Leitung des RX empfiehlt sich noch ein 100nF in Reihe, ohne klingt das ganz kratzig :)
 
#91
...
Alternativ habe ich diese Schaltung hier gefunden:
http://www.stephanharms.de/RCVideoSwitch
Da wird genau der 4052er genutzt, um ein Video-Signal zu schalten. Da sind die Video-Eingänge (wie es auch im Datenblatt des Empfängers vorgesehen ist) mit Kondensatoren entkoppelt, zusätzlich sind aber noch 75Ohm- Widerstände verbaut (zu deren Existenz ein E-Techniker sicher auch mehr sagen kann, die 75 Ohm lassen jedenfalls irgendwas mit Impedanz oder Termiantoren bei mir klingeln...). Der fragliche VEE-Pins ist ebenfalls mit Masse verbunden. Zusätzlich ist auch noch eine sogenannte Schwarzwert-Wiederherstellung und ein OpAmp als Verstärker und Signalsplitter vorgesehen...
Da kommt die Frage auf: wollen wir das auch haben - also Splitter und Verstärker und/oder die Sache mit dem Schwarzwert?? Wäre ein IC und ein wenig Hühnerfutter mehr auf der Platine - aber dafür die Möglichkeit, parallel einen DVR zu betreiben, oder einen Zuschauer mit TFT oder Brille mitfliegen zu lassen...und (wenn ich das richtig verstehe) bessere Kontraste bzw. ein satteres Schwarz...
Kurz, alles Dinge, die mindestens auf der "nice to have"- Liste stehen, mich ein paar Minuten beim Zeichnen und alle Nutzer ca. 50 Cent an Bauteilen kosten dürfte.
Den 75Ohm Terminator kennst Du wahrscheinlich aus Deiner Hausantennen-/Kabelfernseh-anlage.

Im Videobereich ist es auch üblich mit 75Ohm Wellenwiderstand zu arbeiten. Du siehst das in der Schaltung das die OP-Amps mit 75Ohm in Reihe an den Ausgang gehen. OP-Amp Ri nahezu Null plus 75Ohm gibt eine Signalquelle mit eben diesem Innenwiderstand. Dann auf ein Koaxkabel mit ebenfalls 75Ohm Wellenwiderstand zu einem Eingang der ebenso mit 75Ohm an Masse geht - siehe Eingänge des Schalters. Witzig ist der Trick mit der Diodenklemmung, im professionellen Bereich wird das aufwendiger mit Sync Erkennung und Klemmung des Synchronbodens auf Null gemacht.

Mal eine ketzerische Idee: warum nicht den Arduino Video Ausgang hinter den 4052? Man könnte doch den Videoausgang des Arduino auf TriState schalten wenn er nicht benötigt wird. Oder wenn er aktiv sein soll den Inhibit des 4052 passend ansteuern?
Dann könnte man die beiden weiteren Eingänge des 4052 auf zwei Buchsen/Lötnägel führen um eventuell noch ein/zwei RX´e an einem Mast zu betreiben?
Ich habe aber noch nicht durch gezählt ob der Arduino genügend Pins dafür hat zugegeben.
Gruß Jörg
 
#92
Ok, fassen wir zusammen:
-die in Reihe geschalteten Kondensatoren, DC-Entkopplung oder was auch immer, machen an der Stelle Sinn, bleiben also drin!
Sollten die doch fehl am Platze sein, kann man die immernoch duch Drahtbrücken ersetzen und hätte dann das Äquivalent zu der Schaltung ohne kondensatoren - mit etwas Platzverschwendung...

- der Video-Verstärker ist, soweit ich das verstanden habe, zwingend erforderlich, weil der 4052er selber einen Eingangswiderstand hat, der so groß ist, dass man das nicht vernachlässigen sollte.

-LED´s auf der Antennenseite (oben/hinten/whatever): kein Problem, Pins A0 und A1 sind recht?

-Audio - ich hab nicht ganz verstanden, was da gewünscht war - oder es gab da ein Missverständnis der Schaltung. Ich habe entsprechende Kondensatoren vorgesehen (vielleicht sollte ich beim nächsten Mal die Scematics mit hochladen...), der Einfachheit halber haben die die selbe Baugröße/Bauform wie die Kondensatoren für Spannungsfilter und Video-DC-Entkoppung. Vielleicht sind die deswegen nicht aufgefallen... :)

-Umschaltung zwischen den Video-Eingängen: das würde ich das per toggle-Button machen, der Arduino muss dann ja eh den 4052er ansteuern, also A3 mit zusätzlichem Lötjumper für einen weiteren Taster? Oder, alternativ: wichtig sind ja eh nur die Eingänge vom Arduino und das jeweils beste Video-Signal. Das Signal vom Arduino wird aber nur gebraucht, solange man die Funktionen des Menü´s benutzt, will sagen, an den Knöpfchen spielt. Da könnte man doch auch für jeden Knofpdruck auf (up-menu-down) ein Timeout programmieren, der erst das Arduino-Signal einblendet und nach Ablauf dann wieder auf "best video" zurückschaltet, ggf. mit dem Save-Button als Exit- Funktion und ohne timeout, etwa im Scanner-Modus...

Beim Post von Jörg habe ich dann gemerkt, ins welch unbekannte Gewässer ich mir mich - völlig unqualifiziert - getraut habe.
So etwa von der Hälfte glaube ich verstanden zu haben, worum es geht... :D.
Ich kann einfach nur Schaltpläne lesen und hab aus nem Bastelprojekt ein paar Erfahrungen mit Eagle...andere Stricken, spielen Suodku, ich knobel halt mal ganz gerne Schaltpläne zusammen...und dachte, ich versuchs mal! Ein paar andere Projekte hatte ich ja auch schon studiert - nur bei den Feinheiten der Analog und HF-Technik, da ist halt die große Lücke...

Was die Möglichkeit betrifft, noch weitere Empfängermodule anzuschließen:
Wenn man das original-Design mit den Dip-Switches an den analogen Eingängen beibehält, wird das nicht mehr gehen, zumal ja noch eine Spannungsüberwachung für die Versorgung vorgesehen wurde... Es sind dann einfach nicht genug freie Analog-Pins zur Auswertung der RSSI-Signale da. Für den ersten Entwurf wollte ich erstmal so nah am Original sein, wie möglich. Ich hatte ja schon ein schlechtes Gewissen, den Pin 13 (LED-Fehlercodes für Video-Out des Arduino) anders zu belegen...
Dementsprechend kann ich auch die Frage, welche Möglichkeiten bestehen, das Video-Signal des Ardunio hinter dem 4052er einzuschleifen, nur mit einem Achselzucken und einem respektvollen Nicken für die Idee quittieren... :D

Das ändert aber nichts daran, dass ich schon mal angefangen habe, die "große" Version zu entwerfen, die dann eben 4 statt 2 Empfänger und als Krönung sogar noch zwei Servo-Ausgänge (Stichwort Tracking) hat - dann aber eben keine Dip-Switches mehr. Die zusätzlich zu schaltenden Video- Signale werden dann von einem 4051er geswitched, die Audio-Signale parallel dazu von einem zweiten 4051er.
Spätestens dafür sind dann aber umfangreichere Änderungen am Code notwendig...nicht nur auskommentieren des Bereiches für die DIP-Swiches.
Dafür mach ich dann aber nen eigenen Thread auf...will ja nicht das Projekt vom Marko kapern! :D

Änderungen sind in Arbeit, melde mich, wenn es Fortschritte gibt!
:D
 
Zuletzt bearbeitet:
#93
OK, ich fass es auch nochmal kurz zusammen ;_)

Ok, fassen wir zusammen:
-die in Reihe geschalteten Kondensatoren, bleiben also drin alternativ duch Drahtbrücken ersetzen.
- der Video-Verstärker ist, soweit ich das verstanden habe, zwingend erforderlich, weil der 4052er selber einen Eingangswiderstand hat, der so groß ist, dass man das nicht vernachlässigen sollte.
-LED´s auf der Antennenseite (oben/hinten/whatever): kein Problem, Pins A0 und A1 sind recht?
-Audio - ich hab nicht ganz verstanden, was da gewünscht war.
-Umschaltung zwischen den Video-Eingängen: Das Signal vom Arduino wird aber nur gebraucht, solange man die Funktionen des Menü´s benutzt, will sagen, an den Knöpfchen spielt. Da könnte man doch auch für jeden Knofpdruck auf (up-menu-down) ein Timeout programmieren, der erst das Arduino-Signal einblendet und nach Ablauf dann wieder auf "best video" zurückschaltet, ggf. mit dem Save-Button als Exit- Funktion und ohne timeout, etwa im Scanner-Modus...
Kondensatorenlösung: Gut so!
Videoverstärker: ja bitte!
LEDs: A0 A1 is prima!
Audio: Kann von mir aus ganz weg, brauche ich nicht.
Umschaltung: Hab ich in der Prototypen Firmware genau so vorgesehen, das bei Aktionen wie Kanalwechsel, Scannen etc... auf den TV-Out umgeschaltet wird. Danach eben nach Timeout zurück. Im Freq. Analyser Modus bleibts auf TV-Out. Wie gesagt, is ma so vorgesehen aber nicht getestet.

Code:
  #define SHOWTIME 2

......
        if(state == STATE_MANUAL) // MANUAL MODE
        {
            // handling of keys
            if( digitalRead(buttonSeek) == LOW)        // channel UP
            {
#ifdef DIVERSITY
                // switch Monitor to TV-Out for SHOWTIME
                switchRX(true, SHOWTIME);
#endif
.......
#ifdef DIVERSITY
            else {
                // switch Monitor to RX as we found a channel
                switchRX(false, -1);
            }            
#endif
.......

void switchRX(boolean tvout, int keepTime) {
  
    led1on(rssi >= rssi2);  
  
    uint16_t now = millis();
    // we should keep the current state
    if (now < nextSwitchCheck) {
      return;
    }
    nextSwitchCheck = now + (keepTime * 1000);
    
    if (tvout) {
       switcher.write(90);
    } else if (rssi >= rssi2) {
       switcher.write(0);
    } else {
       switcher.write(180);      
    }
}

void led1on(boolean led1on) {
   digitalWrite(led1, led1on);
   digitalWrite(led2, !led1on); 
}
switcher ist momentan ein Servo-Ausgang für den Turnigy Video Umschalter, aber den auf den 4052er ist ja nicht der Rede wert.

Bin gepannt!
 
#94
So, hab noch mal ein wenig gebastelt...

-Schwarzabgleich und Video-Verstärker/splitter sind drin, zusätzlicher Anschluss ebenfalls
-Audio habe ich sicherheitshalber auch noch mal durch einen OP AMP gejagt, der als Spannungsfolger geschaltet ist (nicht jeder will Audio haben - aber manche brauchen es für Telemetrie, deshalb lieber gleich "richtig", man muss es ja nicht bestücken)
-A0 und A1 antennenseitig (oben/hinten/whatever) mit LED´s verbunden, natürlich mit Vorwiderstand
-A3 als zusätzlichen Taster herausgeführt (wofür war den nochmal? hat sich das ggf erledigt, weil da jetzt der 4052 drin ist?)
-Messung der Eingangsspannung erfolgt über A7

Hier der Schaltplan
RX5805 Pro Diversity Scematic V4 .jpg

und hier auch der Layout

RX5805 Pro Diversity Board V4 .jpg

So sollte es funktionieren, passende Software vorrausgesetzt...
:)
Ich flitz morgen mal in den Conrad und hol mir ein wenig Material, um das ganze mal auf Streifenraster aufzubauen - ohne Audio! :D

Wenn das dann funktioniert, kommt die richtige Platine!
Wenn da jemand schon mal loslegen möchte, die eagle-Dateien kann ich gerne verschicken.
 
Zuletzt bearbeitet:
#95
Jetzt mal ganz kurz: wo finde ich denn einge gemoddete Version der Software? Also zumindest so weit gemoddet, dass die Umschaltung per Turnigy-Video-Switcher funktioniert? Dann kann ich zumindest mal versuchen, da die entsprechenden Änderungen selbst einzubauen - dafür könnte es gerade so reichen...
Und an welchem Pin hängt der PWM- Eingang des Turnigy Video-Switches?
 
#96
Also, momentan gibts glaub ich nur meinen Prototypen der Software.
Alles ungetestet, vor allem die Sache mit dem umschalten.

Hier mal der Sketch, aufbauend auf der letzten stable vom rx5808pro.
Alle Änderungen als ifdef DIVERSITY eingebaut.
Code:
/*
 * SPI driver based on fs_skyrf_58g-main.c Written by Simon Chambers
 * TVOUT by Myles Metzel 
 * Scanner by Johan Hermen
 * Inital 2 Button version by Peter (pete1990)
 * Refactored and GUI reworked by Marko Hoepken
 * Universal version my Marko Hoepken
 
 CHANGED BY DER_FRICKLER!!!!

The MIT License (MIT)

Copyright (c) 2015 Marko Hoepken

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <TVout.h>
#include <fontALL.h>
#include <avr/pgmspace.h>
#include <EEPROM.h>
#include <Servo.h>

#define DIVERSITY

#define spiDataPin 10
#define slaveSelectPin 11
#define spiClockPin 12
#define rssiPin   A6

// this two are minimum required 
#define buttonSeek 2
#define buttonMode 3
// optional comfort buttons
#define buttonDown 4
#define buttonSave 5
// Buzzer
#define buzzer 6

#ifdef DIVERSITY
  #define led1 A0
  #define led2 A1

  #define rssiPin2  A5
  #define EEPROM_ADR_RSSI2_MIN_L 6
  #define EEPROM_ADR_RSSI2_MIN_H 7
  #define EEPROM_ADR_RSSI2_MAX_L 8
  #define EEPROM_ADR_RSSI2_MAX_H 9
   
  #define VOLTAGE
  #define voltagePin A4
  #define VOLTAGE_MAX       15000
  #define VOLTAGE_CELL_MIN  3300
  
  #define DIV_Y_OFFSET 9
  #define SHOWTIME 2
  
  #define switcherPin  A3 
  Servo switcher;
  
#else
  #define DIP
  // pins for DIP switch
  #define dip_ch0 A0
  #define dip_ch1 A1
  #define dip_ch2 A2
  #define dip_band0 A3
  #define dip_band1 A4
  #define dip_enable A5
#endif


// key debounce delay in ms
// NOTE: good values are in the range of 100-200ms
// shorter values will make it more reactive, but may lead to double trigger
#define KEY_DEBOUNCE 200

// Set you TV format (PAL = Europe = 50Hz, NTSC = INT = 60Hz)
//#define TV_FORMAT NTSC
#define TV_FORMAT PAL

#define led 13
// RSSI default raw range
#define RSSI_MIN_VAL 90
#define RSSI_MAX_VAL 300
// 75% threshold, when channel is printed in spectrum
#define RSSI_SEEK_FOUND 75 
// 80% under max value for RSSI 
#define RSSI_SEEK_TRESHOLD 80
// scan loops for setup run
#define RSSI_SETUP_RUN 10

#define STATE_SEEK_FOUND 0
#define STATE_SEEK 1
#define STATE_SCAN 2
#define STATE_MANUAL 3
#define STATE_SWITCH 4
#define STATE_SAVE 5
#define STATE_RSSI_SETUP 6

#define START_STATE STATE_SEEK
#define MAX_STATE STATE_MANUAL

#define CHANNEL_BAND_SIZE 8
#define CHANNEL_MIN_INDEX 0
#define CHANNEL_MAX_INDEX 31

#define CHANNEL_MAX 31
#define CHANNEL_MIN 0

#define TV_COLS 128
#define TV_ROWS 96
#define TV_Y_MAX TV_ROWS-1
#define TV_X_MAX TV_COLS-1
#define TV_SCANNER_OFFSET 14
#define SCANNER_BAR_SIZE 48
#define SCANNER_LIST_X_POS 4
#define SCANNER_LIST_Y_POS 16
#define SCANNER_MARKER_SIZE 2

#define EEPROM_ADR_STATE 0
#define EEPROM_ADR_TUNE 1
#define EEPROM_ADR_RSSI_MIN_L 2
#define EEPROM_ADR_RSSI_MIN_H 3
#define EEPROM_ADR_RSSI_MAX_L 4
#define EEPROM_ADR_RSSI_MAX_H 5
//#define DEBUG

// Channels to sent to the SPI registers
const uint16_t channelTable[] PROGMEM = {
  // Channel 1 - 8
  0x2A05,    0x299B,    0x2991,    0x2987,    0x291D,    0x2913,    0x2909,    0x289F,    // Band A
  0x2903,    0x290C,    0x2916,    0x291F,    0x2989,    0x2992,    0x299C,    0x2A05,    // Band B
  0x2895,    0x288B,    0x2881,    0x2817,    0x2A0F,    0x2A19,    0x2A83,    0x2A8D,    // Band E
  0x2906,    0x2910,    0x291A,    0x2984,    0x298E,    0x2998,    0x2A02,    0x2A0C  // Band F / Airwave
};

// Channels with their Mhz Values
const uint16_t channelFreqTable[] PROGMEM = {
  // Channel 1 - 8
  5865, 5845, 5825, 5805, 5785, 5765, 5745, 5725, // Band A
  5733, 5752, 5771, 5790, 5809, 5828, 5847, 5866, // Band B
  5705, 5685, 5665, 5645, 5885, 5905, 5925, 5945, // Band E
  5740, 5760, 5780, 5800, 5820, 5840, 5860, 5880  // Band F / Airwave
};

// do coding as simple hex value to save memory.
const uint8_t channelNames[] PROGMEM = {
  0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
  0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
  0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8,
  0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8
};

// All Channels of the above List ordered by Mhz
const uint8_t channelList[] PROGMEM = {
  19, 18, 17, 16, 7, 8, 24, 6, 9, 25, 5, 10, 26, 4, 11, 27, 3, 12, 28, 2, 13, 29, 1, 14, 30, 0, 15, 31, 20, 21, 22, 23
};

uint8_t channel = 0;
uint8_t channelIndex = 0;
uint8_t rssi = 0;
uint8_t rssi_scaled = 0;
uint8_t hight = 0;
uint8_t state = START_STATE;
uint8_t state_last_used=START_STATE;
uint8_t last_state= START_STATE+1; // force screen draw
uint8_t writePos = 0;
uint8_t switch_count = 0;
uint8_t man_channel = 0;
uint8_t last_channel_index = 0;
uint8_t force_seek=0;
unsigned long time_of_tune = 0;        // will store last time when tuner was changed
uint8_t last_maker_pos=0;
uint8_t last_active_channel=0;
uint8_t first_channel_marker=1;
uint8_t update_frequency_view=0;
uint8_t seek_found=0;
uint8_t last_dip_channel=255;
uint8_t last_dip_band=255;
uint8_t scan_start=0;
uint8_t first_tune=1;
uint8_t force_menu_redraw=0;
uint16_t rssi_min=0;
uint16_t rssi_max=0;
uint16_t rssi_setup_min=0;
uint16_t rssi_setup_max=0;
uint16_t rssi_seek_found=0;
uint16_t rssi_setup_run=0;

#ifdef DIVERSITY
  uint8_t rssi2 = 0;
  uint8_t rssi2_scaled = 0;
  
  uint16_t rssi2_min=0;
  uint16_t rssi2_max=0;
  uint16_t rssi2_setup_min=0;
  uint16_t rssi2_setup_max=0;
  
  unsigned long time_of_voltcheck = 0;      // will store last time voltage was checked
  unsigned long nextSwitchCheck = 0;        // will store next time when video out will be switched
#endif


TVout TV;

// SETUP ----------------------------------------------------------------------------
void setup() 
{    
    // IO INIT
    // initialize digital pin 13 LED as an output.
    pinMode(led, OUTPUT); // status pin for TV mode errors
    // buzzer
    pinMode(buzzer, OUTPUT); // Feedback buzzer (active buzzer, not passive piezo)   
    digitalWrite(buzzer, HIGH);
    // minimum control pins
    pinMode(buttonSeek, INPUT);
    digitalWrite(buttonSeek, INPUT_PULLUP);
    pinMode(buttonMode, INPUT);
    digitalWrite(buttonMode, INPUT_PULLUP);
    // optional control
    pinMode(buttonDown, INPUT);
    digitalWrite(buttonDown, INPUT_PULLUP);
    pinMode(buttonSave, INPUT);
    digitalWrite(buttonSave, INPUT_PULLUP);


#ifdef DIVERSITY
    pinMode(led1, OUTPUT); // rx1 led
    pinMode(led2, OUTPUT); // rx2 led
    
    switcher.attach(switcherPin);
    switcher.write(90);
#else
    // dip switches
    pinMode(dip_ch0, INPUT);
    digitalWrite(dip_ch0, INPUT_PULLUP);      
    pinMode(dip_ch1, INPUT);
    digitalWrite(dip_ch1, INPUT_PULLUP);   
    pinMode(dip_ch2, INPUT);
    digitalWrite(dip_ch2, INPUT_PULLUP);   
    pinMode(dip_band0, INPUT);
    digitalWrite(dip_band0, INPUT_PULLUP);   
    pinMode(dip_band1, INPUT);
    digitalWrite(dip_band1, INPUT_PULLUP);   
    pinMode(dip_enable, INPUT);
    digitalWrite(dip_enable, INPUT_PULLUP);   
#endif    

#ifdef DEBUG
    Serial.begin(115200);
    Serial.println(F("START:")); 
#endif
    // SPI pins for RX control
    pinMode (slaveSelectPin, OUTPUT);
    pinMode (spiDataPin, OUTPUT);
    pinMode (spiClockPin, OUTPUT);
    // tune to first channel

    
    // init TV system
    char retVal = TV.begin(TV_FORMAT, TV_COLS, TV_ROWS);
    // 0 if no error.
    // 1 if x is not divisable by 8.
    // 2 if y is to large (NTSC only cannot fill PAL vertical resolution by 8bit limit)
    // 4 if there is not enough memory for the frame buffer.
    if (retVal > 0) {
        // on Error flicker LED
        while (true) { // stay in ERROR for ever
            digitalWrite(13, !digitalRead(13));
            delay(100);
        }
    }
    TV.select_font(font4x6);
    // Setup Done - LED ON
    digitalWrite(13, HIGH);
    
    // use values only of EEprom is not 255 = unsaved
    uint8_t eeprom_check = EEPROM.read(EEPROM_ADR_STATE);
    if(eeprom_check == 255) // unused
    {
        EEPROM.write(EEPROM_ADR_STATE,START_STATE);
        EEPROM.write(EEPROM_ADR_TUNE,CHANNEL_MIN_INDEX);
        // save 16 bit
        EEPROM.write(EEPROM_ADR_RSSI_MIN_L,lowByte(RSSI_MIN_VAL));        
        EEPROM.write(EEPROM_ADR_RSSI_MIN_H,highByte(RSSI_MIN_VAL));    
        // save 16 bit
        EEPROM.write(EEPROM_ADR_RSSI_MAX_L,lowByte(RSSI_MAX_VAL));
        EEPROM.write(EEPROM_ADR_RSSI_MAX_H,highByte(RSSI_MAX_VAL));
        
#ifdef DIVERSITY
        // save 16 bit
        EEPROM.write(EEPROM_ADR_RSSI2_MIN_L,lowByte(RSSI_MIN_VAL));        
        EEPROM.write(EEPROM_ADR_RSSI2_MIN_H,highByte(RSSI_MIN_VAL));    
        // save 16 bit
        EEPROM.write(EEPROM_ADR_RSSI2_MAX_L,lowByte(RSSI_MAX_VAL));
        EEPROM.write(EEPROM_ADR_RSSI2_MAX_H,highByte(RSSI_MAX_VAL));
#endif
        
    }
    // debug reset EEPROM
    //EEPROM.write(EEPROM_ADR_STATE,255);    
        
    // read last setting from eeprom
    state=EEPROM.read(EEPROM_ADR_STATE);
    channelIndex=EEPROM.read(EEPROM_ADR_TUNE);
    rssi_min=((EEPROM.read(EEPROM_ADR_RSSI_MIN_H)<<8) | (EEPROM.read(EEPROM_ADR_RSSI_MIN_L)));
    rssi_max=((EEPROM.read(EEPROM_ADR_RSSI_MAX_H)<<8) | (EEPROM.read(EEPROM_ADR_RSSI_MAX_L)));
    
#ifdef DIVERSITY
    rssi2_min=((EEPROM.read(EEPROM_ADR_RSSI2_MIN_H)<<8) | (EEPROM.read(EEPROM_ADR_RSSI2_MIN_L)));
    rssi2_max=((EEPROM.read(EEPROM_ADR_RSSI2_MAX_H)<<8) | (EEPROM.read(EEPROM_ADR_RSSI2_MAX_L)));
#endif    

    force_menu_redraw=1;
}

// LOOP ----------------------------------------------------------------------------
void loop() 
{      
    /*******************/
    /*   Mode Select   */
    /*******************/
    state_last_used=state; // save save settings
    if (digitalRead(buttonMode) == LOW) // key pressed ?
    {        
      
#ifdef DIVERSITY
        // switch Monitor to TV-Out as we are in the menu
        switchRX(true, -1);
#endif

        beep(50); // beep & debounce
        delay(KEY_DEBOUNCE/2); // debounce 
        beep(50); // beep & debounce
        delay(KEY_DEBOUNCE/2); // debounce
        // on entry wait for release
        while(digitalRead(buttonMode) == LOW)
        {
                // wait for MODE release
        }        
        #define MAX_MENU 4
        #define MENU_Y_SIZE 15
        
        uint8_t menu_id=0;
        // Show Mode Screen            
        if(state==STATE_SEEK_FOUND)
        {
            state=STATE_SEEK;
        }
        uint8_t in_menu=1;
        uint8_t in_menu_time_out=10; // 10x 200ms = 2 seconds
        /*
        Enter Mode menu
        Show current mode
        Change mode by MODE key
        Any Mode will refresh screen
        If not MODE changes in 2 seconds, it uses last selected mode
        */
        do
        {
            TV.clear_screen();
            // simple menu
            TV.select_font(font8x8);
            TV.draw_rect(0,0,127,95,  WHITE);
            TV.draw_line(0,14,127,14,WHITE);
            TV.printPGM(10, 3,  PSTR("MODE SELECTION"));
            TV.printPGM(10, 5+1*MENU_Y_SIZE, PSTR("Auto Search"));
            TV.printPGM(10, 5+2*MENU_Y_SIZE, PSTR("Band Scanner"));
            TV.printPGM(10, 5+3*MENU_Y_SIZE, PSTR("Manual Mode"));                                
            TV.printPGM(10, 5+4*MENU_Y_SIZE, PSTR("Switch Mode"));                                
            TV.printPGM(10, 5+5*MENU_Y_SIZE, PSTR("Save Setup"));                                
            // selection by inverted box
            switch (menu_id) 
            {    
                case 0: // auto search
                    TV.draw_rect(8,3+1*MENU_Y_SIZE,100,12,  WHITE, INVERT);
                    state=STATE_SEEK;
                    force_seek=1;
                    seek_found=0;
                break;
                case 1: // Band Scanner
                    TV.draw_rect(8,3+2*MENU_Y_SIZE,100,12,  WHITE, INVERT);   
                    state=STATE_SCAN;
                    scan_start=1;
                break;
                case 2: // manual mode 
                    TV.draw_rect(8,3+3*MENU_Y_SIZE,100,12,  WHITE, INVERT); 
                    state=STATE_MANUAL;
                break;
                case 3: // DIP mode 
                    TV.draw_rect(8,3+4*MENU_Y_SIZE,100,12,  WHITE, INVERT); 
                    state=STATE_SWITCH;
                    last_dip_channel=255; // force update
                break;
                case 4: // Save settings 
                    TV.draw_rect(8,3+5*MENU_Y_SIZE,100,12,  WHITE, INVERT); 
                    state=STATE_SAVE;
                break;
            } // end switch    
            

            
            while(digitalRead(buttonMode) == LOW)
            {
                // wait for MODE release
                in_menu_time_out=10;
            }                
            while(--in_menu_time_out && (digitalRead(buttonMode) == HIGH)) // wait for next mode or time out
            {
                delay(200); // timeout delay
            }    
            if(in_menu_time_out==0) 
            {
                in_menu=0; // EXIT
                beep(KEY_DEBOUNCE/2); // beep & debounce
                delay(50); // debounce 
                beep(KEY_DEBOUNCE/2); // beep & debounce
                delay(50); // debounce 
            }
            else // no timeout, must be keypressed
            {
                in_menu_time_out=10;
                beep(50); // beep & debounce
                delay(KEY_DEBOUNCE); // debounce 
                /*********************/
                /*   Menu handler   */
                /*********************/
                if (menu_id < MAX_MENU)
                {
                    menu_id++; // next state
                } 
                else 
                {
                    menu_id = 0; 
                }                  
            }
        } while(in_menu);
        last_state=255; // force redraw of current screen
        switch_count = 0;       
        // clean line?
        TV.print(TV_COLS/2, (TV_ROWS/2), "             ");
        
#ifdef DIVERSITY
        // switch Monitor to RX as we left the Menu
        switchRX(false, -1);
#endif

    } 
    else // key pressed
    { // reset debounce      
        switch_count = 0;    
    }
    
#ifdef DIP
    /***********************/
    /*   Static SWITCH MODE   */
    /***********************/
    // set to SWITCH mode if SWITCH_ENABLE is low (no interactive mode)
    if(digitalRead(dip_enable) == LOW)
    {
        state=STATE_SWITCH;
    }
#endif    
    
    /***********************/
    /*     Save buttom     */
    /***********************/
    // hardware save buttom support (if no display is used)
    if(digitalRead(buttonSave) == LOW)
    {
        state=STATE_SAVE;        
    }        
    /***************************************/
    /*   Draw screen if mode has changed   */
    /***************************************/
    if(force_menu_redraw || state != last_state)
    {
        force_menu_redraw=0;
        /************************/
        /*   Main screen draw   */
        /************************/            
        // changed state, clear an draw new screen       
        TV.clear_screen();  
        // simple menu
        #define TV_Y_GRID 14
        #define TV_Y_OFFSET 3        
        switch (state) 
        {    
            case STATE_SCAN: // Band Scanner
            case STATE_RSSI_SETUP: // RSSI setup
                TV.select_font(font8x8);
                TV.draw_rect(0,0,TV_X_MAX,1*TV_Y_GRID,  WHITE); // upper frame
                if(state==STATE_SCAN)
                {
                    TV.printPGM(10, TV_Y_OFFSET,  PSTR(" BAND SCANNER"));                 
                }
                else
                {

   #ifdef DIVERSITY
                    // switch Monitor to TV-Out as we are in the menu
                    switchRX(true, -1);
                    
                    TV.printPGM(4, TV_Y_OFFSET,  PSTR("DIV. RSSI SETUP"));
                    TV.select_font(font4x6);                     
                    TV.print(2, SCANNER_LIST_Y_POS, "A RSSI Min:     RSSI Max:   ");   
                    TV.print(2, SCANNER_LIST_Y_POS+DIV_Y_OFFSET,"B RSSI Min:     RSSI Max:   ");  
                    TV.draw_rect(0,1*(TV_Y_GRID)+DIV_Y_OFFSET,TV_X_MAX,9,  WHITE); 
                    rssi2_min=0;
                    rssi2_max=400; // set to max range
                    rssi2_setup_min=400;
                    rssi2_setup_max=0;   
   #else 
                    TV.printPGM(10, TV_Y_OFFSET,  PSTR("  RSSI SETUP "));
                    TV.select_font(font4x6);                     
                    TV.print(10, SCANNER_LIST_Y_POS, "RSSI Min:     RSSI Max:   ");
   #endif
                    
                    // prepare new setup
                    rssi_min=0;
                    rssi_max=400; // set to max range
                    rssi_setup_min=400;
                    rssi_setup_max=0;   
                    rssi_setup_run=RSSI_SETUP_RUN;
                }   
                TV.draw_rect(0,1*TV_Y_GRID,TV_X_MAX,9,  WHITE); // list frame
                TV.draw_rect(0,TV_ROWS - TV_SCANNER_OFFSET,TV_X_MAX,13,  WHITE); // lower frame                
                TV.select_font(font4x6);
                TV.print(2, (TV_ROWS - TV_SCANNER_OFFSET + 2), "5645");
                TV.print(57, (TV_ROWS - TV_SCANNER_OFFSET + 2), "5800");
                TV.print(111, (TV_ROWS - TV_SCANNER_OFFSET + 2), "5945");
                // trigger new scan from begin
                channel=CHANNEL_MIN;
                writePos=SCANNER_LIST_X_POS; // reset channel list
                channelIndex = pgm_read_byte_near(channelList + channel);  
                scan_start=1;
            break;
            case STATE_MANUAL: // manual mode 
            case STATE_SEEK: // seek mode
            case STATE_SWITCH: // SWITCH mode            
                TV.select_font(font8x8);
                TV.draw_rect(0,0,TV_X_MAX,TV_Y_MAX,  WHITE); // outer frame
                if (state == STATE_MANUAL)
                {
                    TV.printPGM(10, TV_Y_OFFSET,  PSTR(" MANUAL MODE"));                
                }
                else if(state == STATE_SEEK)
                {
                    TV.printPGM(10, TV_Y_OFFSET,  PSTR("AUTO MODE SEEK"));                
                }
                else if(state == STATE_SWITCH)
                {
                    TV.printPGM(10, TV_Y_OFFSET,  PSTR("  SWITCH MODE "));                
                }                
                TV.draw_line(0,1*TV_Y_GRID,TV_X_MAX,1*TV_Y_GRID,WHITE);
                TV.printPGM(5,TV_Y_OFFSET+1*TV_Y_GRID,  PSTR("BAND: "));                
                TV.draw_line(0,2*TV_Y_GRID,TV_X_MAX,2*TV_Y_GRID,WHITE);    
                TV.printPGM(5 ,TV_Y_OFFSET-1+2*TV_Y_GRID,  PSTR("1 2 3 4 5 6 7 8"));            
                TV.draw_line(0,3*TV_Y_GRID,TV_X_MAX,3*TV_Y_GRID,WHITE);    
                TV.printPGM(5,TV_Y_OFFSET+3*TV_Y_GRID,  PSTR("FREQ:     GHz"));    
                TV.draw_line(0,4*TV_Y_GRID,TV_X_MAX,4*TV_Y_GRID,WHITE);    
                TV.select_font(font4x6);                
                TV.printPGM(5,TV_Y_OFFSET+4*TV_Y_GRID,  PSTR("RSSI:"));    
                TV.draw_line(0,5*TV_Y_GRID-4,TV_X_MAX,5*TV_Y_GRID-4,WHITE);        
                // frame for tune graph
                TV.draw_rect(0,TV_ROWS - TV_SCANNER_OFFSET,TV_X_MAX,13,  WHITE); // lower frame                
                TV.print(2, (TV_ROWS - TV_SCANNER_OFFSET + 2), "5645");
                TV.print(57, (TV_ROWS - TV_SCANNER_OFFSET + 2), "5800");
                TV.print(111, (TV_ROWS - TV_SCANNER_OFFSET + 2), "5945");
                TV.select_font(font8x8);
                first_channel_marker=1;
                update_frequency_view=1;
                force_seek=1;
            break;
            case STATE_SAVE:
                EEPROM.write(EEPROM_ADR_STATE,state_last_used);
                EEPROM.write(EEPROM_ADR_TUNE,channelIndex);             
                TV.select_font(font8x8);
                TV.draw_rect(0,0,127,95,  WHITE);
                TV.draw_line(0,14,127,14,WHITE);
                TV.printPGM(10, 3,  PSTR("SAVE SETTINGS"));                
                TV.printPGM(10, 5+1*MENU_Y_SIZE, PSTR("Mode:"));
                switch (state_last_used) 
                {    
                    case STATE_SCAN: // Band Scanner
                        TV.printPGM(50,5+1*MENU_Y_SIZE,  PSTR("Scanner")); 
                    break;
                    case STATE_MANUAL: // manual mode 
                        TV.printPGM(50,5+1*MENU_Y_SIZE,  PSTR("Manual"));                    
                    break;
                    case STATE_SEEK: // seek mode
                        TV.printPGM(50,5+1*MENU_Y_SIZE,  PSTR("Search"));                     
                    break;
                    case STATE_SWITCH: // SWITCH mode    
                        TV.printPGM(50,5+1*MENU_Y_SIZE,  PSTR("Switch")); 
                    break;                
                }
                TV.printPGM(10, 5+2*MENU_Y_SIZE, PSTR("Band:")); 
                // print band
                if(channelIndex > 23)
                {
                    TV.printPGM(50,5+2*MENU_Y_SIZE,  PSTR("F/Airwave"));            
                }
                else if (channelIndex > 15)
                {
                    TV.printPGM(50,5+2*MENU_Y_SIZE,  PSTR("E        "));            
                }
                else if (channelIndex > 7)
                {
                    TV.printPGM(50,5+2*MENU_Y_SIZE,  PSTR("B        "));            
                }
                else
                {
                    TV.printPGM(50,5+2*MENU_Y_SIZE,  PSTR("A        "));            
                }                
                TV.printPGM(10, 5+3*MENU_Y_SIZE, PSTR("Chan:"));   
                uint8_t active_channel = channelIndex%CHANNEL_BAND_SIZE+1; // get channel inside band
                TV.print(50,5+3*MENU_Y_SIZE,active_channel,DEC);
                TV.printPGM(10, 5+4*MENU_Y_SIZE, PSTR("FREQ:     GHz"));      
                TV.print(50,5+4*MENU_Y_SIZE, pgm_read_word_near(channelFreqTable + channelIndex));                 
                TV.printPGM(10, 5+5*MENU_Y_SIZE, PSTR("--- SAVED ---"));
                uint8_t loop=0;
                for (loop=0;loop<5;loop++)
                {
                    beep(100); // beep 
                    delay(100);                     
                }                
                delay(1000);
                TV.select_font(font4x6);                 
                TV.printPGM(10, 14+5*MENU_Y_SIZE,     PSTR("Hold MODE to enter RSSI setup"));
                delay(1000);
                delay(1000);                
                if (digitalRead(buttonMode) == LOW) // to RSSI setup
                {
                    TV.printPGM(10, 14+5*MENU_Y_SIZE, PSTR("ENTERING RSSI SETUP ......   " ));                 
                    uint8_t loop=0;
                    for (loop=0;loop<10;loop++)
                    {
                        #define RSSI_SETUP_BEEP 25
                        beep(RSSI_SETUP_BEEP); // beep & debounce
                        delay(RSSI_SETUP_BEEP); // debounce                      
                    }
                    state=STATE_RSSI_SETUP;
                    while(digitalRead(buttonMode) == LOW)
                    {
                        // wait for release
                    }
                    delay(KEY_DEBOUNCE);  // debounce
                } 
                else
                {
                    TV.printPGM(10, 14+5*MENU_Y_SIZE, PSTR("                             "));                
                    delay(1000);                    
                    state=state_last_used; // return to saved function
                }
                    force_menu_redraw=1; // we change the state twice, must force redraw of menu
                   
            // selection by inverted box            
            break;
        } // end switch
        last_state=state;
    }
    /*************************************/
    /*   Processing depending of state   */
    /*************************************/

    /*****************************************/
    /*   Processing MANUAL MODE / SEEK MODE  */
    /*****************************************/
    if(state == STATE_MANUAL || state == STATE_SEEK || state == STATE_SWITCH)
    {
        if(state == STATE_MANUAL) // MANUAL MODE
        {
            // handling of keys
            if( digitalRead(buttonSeek) == LOW)        // channel UP
            {
#ifdef DIVERSITY
                // switch Monitor to TV-Out for SHOWTIME
                switchRX(true, SHOWTIME);
#endif
                beep(50); // beep & debounce
                delay(KEY_DEBOUNCE); // debounce            
                channelIndex++;
                if (channelIndex > CHANNEL_MAX_INDEX) 
                {  
                    channelIndex = CHANNEL_MIN_INDEX;
                }
                update_frequency_view=1;        
            }
            else if( digitalRead(buttonDown) == LOW) // channel DOWN
            {
#ifdef DIVERSITY
                // switch Monitor to TV-Out for SHOWTIME
                switchRX(true, SHOWTIME);
#endif
                beep(50); // beep & debounce
                delay(KEY_DEBOUNCE); // debounce 
                channelIndex--;
                if (channelIndex > CHANNEL_MAX_INDEX) // negative overflow
                {  
                    channelIndex = CHANNEL_MAX_INDEX;
                }    
                update_frequency_view=1;        
            }
#ifdef DIVERSITY
            else {
                // switch Monitor to RX as we found a channel
                switchRX(false, -1);
            }            
#endif  
        }
        
#ifdef DIP
        if(state == STATE_SWITCH) // SWITCH MODE
        {
            // read band DIP switch (invert since switch pulls to gnd)
            uint8_t dip_band= (((digitalRead(dip_band1)<<1) | digitalRead(dip_band0)) ^0x3);
            // read channel DIP switch (invert since switch pulls to gnd)
            uint8_t dip_channel = (((digitalRead(dip_ch2)<<2) | ((digitalRead(dip_ch1))<<1) | (digitalRead(dip_ch0))) ^0x7);
            if((dip_band != last_dip_band) || (dip_channel != last_dip_channel)) // check for changes to avoid unrequred tuning
            {
                last_dip_band=dip_band;
                last_dip_channel=dip_channel;
                // caclulate index of channel in 4x8 array
                channelIndex=CHANNEL_BAND_SIZE*last_dip_band + last_dip_channel;
                update_frequency_view=1;
            }   
                    
        }  
#endif  
        
        // display refresh handler
        if(update_frequency_view) // only updated on changes
        {
            // show current used channel of bank
            if(channelIndex > 23)
            {
                TV.printPGM(50,TV_Y_OFFSET+1*TV_Y_GRID,  PSTR("F/Airwave"));            
            }
            else if (channelIndex > 15)
            {
                TV.printPGM(50,TV_Y_OFFSET+1*TV_Y_GRID,  PSTR("E        "));            
            }
            else if (channelIndex > 7)
            {
                TV.printPGM(50,TV_Y_OFFSET+1*TV_Y_GRID,  PSTR("B        "));            
            }
            else
            {
                TV.printPGM(50,TV_Y_OFFSET+1*TV_Y_GRID,  PSTR("A        "));            
            }
            // show channel inside band
            uint8_t active_channel = channelIndex%CHANNEL_BAND_SIZE; // get channel inside band
            if(!first_channel_marker)
            {
                // clear last marker
                TV.draw_rect(last_active_channel*16+2 ,TV_Y_OFFSET-2+2*TV_Y_GRID,12,12,  BLACK, INVERT); // mark current channel
            }
            first_channel_marker=0;
            // set new marker
            TV.draw_rect(active_channel*16+2 ,TV_Y_OFFSET-2+2*TV_Y_GRID,12,12,  WHITE, INVERT); // mark current channel            
            last_active_channel=active_channel;                        
            // show frequence
            TV.print(50,TV_Y_OFFSET+3*TV_Y_GRID, pgm_read_word_near(channelFreqTable + channelIndex));            
        }                
        // show signal strength            
        #define RSSI_BAR_SIZE 100
        rssi_scaled=map(rssi, 1, 100, 1, RSSI_BAR_SIZE);        
        // clear last bar
        TV.draw_rect(25, TV_Y_OFFSET+4*TV_Y_GRID, RSSI_BAR_SIZE,4 , BLACK, BLACK);
        //  draw new bar
        TV.draw_rect(25, TV_Y_OFFSET+4*TV_Y_GRID, rssi_scaled, 4 , WHITE, WHITE);        
        // print bar for spectrum
        channel=channel_from_index(channelIndex); // get 0...31 index depending of current channel            
        wait_rssi_ready();
        #define SCANNER_BAR_MINI_SIZE 14
        rssi = readRSSI();
        rssi_scaled=map(rssi, 1, 100, 1, SCANNER_BAR_MINI_SIZE);
        
#ifdef DIVERSITY
        rssi2 = readRSSI2();
        rssi2_scaled=map(rssi2, 1, 100, 5, SCANNER_BAR_SIZE);
#endif
        
        hight = (TV_ROWS - TV_SCANNER_OFFSET - rssi_scaled);
        // clear last bar
        TV.draw_rect((channel * 4), (TV_ROWS - TV_SCANNER_OFFSET - SCANNER_BAR_MINI_SIZE), 3, SCANNER_BAR_MINI_SIZE , BLACK, BLACK);
        //  draw new bar
        TV.draw_rect((channel * 4), hight, 3, rssi_scaled , WHITE, WHITE);
        // set marker in spectrum to show current scanned channel
        if(channel < CHANNEL_MAX_INDEX)
        {
            // clear last square
            TV.draw_rect((last_maker_pos * 4)+2, (TV_ROWS - TV_SCANNER_OFFSET + 8),SCANNER_MARKER_SIZE,SCANNER_MARKER_SIZE,  BLACK, BLACK);                
            // draw next
            TV.draw_rect((channel * 4)+2, (TV_ROWS - TV_SCANNER_OFFSET + 8),SCANNER_MARKER_SIZE,SCANNER_MARKER_SIZE,  WHITE, WHITE);
            last_maker_pos=channel;
        }
        else
        {
          //  No action on last position to keep frame intact
        }        
        // handling for seek mode after screen and RSSI has been fully processed
        if(state == STATE_SEEK) //
        { // SEEK MODE
            if(!seek_found) // search if not found
            {
              
#ifdef DIVERSITY
                // switch Monitor to TV-Out as we are in seek
                switchRX(true, -1);
#endif
              
                if ((!force_seek) && (rssi > RSSI_SEEK_TRESHOLD)) // check for found channel
                {
                    seek_found=1; 
                    // beep twice as notice of lock
                    beep(100);
                    delay(100);
                    beep(100);
                } 
                else 
                { // seeking itself
                    force_seek=0;
                    // next channel
                    if (channel < CHANNEL_MAX) 
                    {
                        channel++;
                    } else {
                        channel=CHANNEL_MIN;
                    }    
                    channelIndex = pgm_read_byte_near(channelList + channel);        
                }        
            }
            else
            { // seek was successful            
                TV.printPGM(10, TV_Y_OFFSET,  PSTR("AUTO MODE LOCK"));
                if (digitalRead(buttonSeek) == LOW) // restart seek if key pressed
                {
                    beep(50); // beep & debounce
                    delay(KEY_DEBOUNCE); // debounce                 
                    force_seek=1;
                    seek_found=0; 
                    TV.printPGM(10, TV_Y_OFFSET,  PSTR("AUTO MODE SEEK"));        
                } else {
#ifdef DIVERSITY
                // switch Monitor to RX as we found a channel
                switchRX(false, -1);
#endif        
                }      
            }
        }        
        TV.delay_frame(1); // clean redraw
    }
    /****************************/
    /*   Processing SCAN MODE   */
    /****************************/
    else if (state == STATE_SCAN || state == STATE_RSSI_SETUP) 
    {
#ifdef DIVERSITY
        // switch Monitor to TV-Out
        switchRX(true, -1);
#endif
      
        // force tune on new scan start to get right RSSI value
        if(scan_start)
        {
            scan_start=0;
            setChannelModule(channelIndex);    
            last_channel_index=channelIndex;
            // keep time of tune to make sure that RSSI is stable when required
            time_of_tune=millis();
        }
        // channel marker
        if(channel < CHANNEL_MAX_INDEX)
        {
            // clear last square
            TV.draw_rect((last_maker_pos * 4)+2, (TV_ROWS - TV_SCANNER_OFFSET + 8),SCANNER_MARKER_SIZE,SCANNER_MARKER_SIZE,  BLACK, BLACK);                
            // draw next
            TV.draw_rect((channel * 4)+2, (TV_ROWS - TV_SCANNER_OFFSET + 8),SCANNER_MARKER_SIZE,SCANNER_MARKER_SIZE,  WHITE, WHITE);
            last_maker_pos=channel;
        }
        else
        {
          //  No action on last position to keep frame intact
        }
        // print bar for spectrum
        wait_rssi_ready();
        // value must be ready
        rssi = readRSSI();
        rssi_scaled=map(rssi, 1, 100, 5, SCANNER_BAR_SIZE);
                       
#ifdef DIVERSITY
        rssi2 = readRSSI2();
        rssi2_scaled=map(rssi2, 1, 100, 5, SCANNER_BAR_SIZE);
#endif
        
        hight = (TV_ROWS - TV_SCANNER_OFFSET - rssi_scaled);
        
        // clear last bar
        TV.draw_rect((channel * 4), (TV_ROWS - TV_SCANNER_OFFSET - SCANNER_BAR_SIZE), 3, SCANNER_BAR_SIZE , BLACK, BLACK);
        //  draw new bar
        TV.draw_rect((channel * 4), hight, 3, rssi_scaled , WHITE, WHITE);
        // print channelname
        if(state == STATE_SCAN)        
        {
            if (rssi > RSSI_SEEK_TRESHOLD) 
            {
                TV.draw_rect(writePos, SCANNER_LIST_Y_POS, 20, 6,  BLACK, BLACK);
                TV.print(writePos, SCANNER_LIST_Y_POS, pgm_read_byte_near(channelNames + channelIndex), HEX);
                TV.print(writePos+10, SCANNER_LIST_Y_POS, pgm_read_word_near(channelFreqTable + channelIndex));
                writePos += 30;
                // mark bar
                TV.print((channel * 4) - 3, hight - 5, pgm_read_byte_near(channelNames + channelIndex), HEX);            
            }
        }       
        // next channel
        if (channel < CHANNEL_MAX) 
        {
            channel++;
        } else {
            channel=CHANNEL_MIN;
            writePos=SCANNER_LIST_X_POS; // reset channel list
            if(state == STATE_RSSI_SETUP)        
            {
                if(!rssi_setup_run--)    
                {
                    // setup done
                    rssi_min=rssi_setup_min;
                    rssi_max=rssi_setup_max;
                    // save 16 bit
                    EEPROM.write(EEPROM_ADR_RSSI_MIN_L,(rssi_min & 0xff));        
                    EEPROM.write(EEPROM_ADR_RSSI_MIN_H,(rssi_min >> 8));    
                    // save 16 bit
                    EEPROM.write(EEPROM_ADR_RSSI_MAX_L,(rssi_max & 0xff));
                    EEPROM.write(EEPROM_ADR_RSSI_MAX_H,(rssi_max >> 8));                    
                    
#ifdef DIVERSITY
                    rssi2_min=rssi2_setup_min;
                    rssi2_max=rssi2_setup_max;
                    // save 16 bit
                    EEPROM.write(EEPROM_ADR_RSSI2_MIN_L,(rssi2_min & 0xff));        
                    EEPROM.write(EEPROM_ADR_RSSI2_MIN_H,(rssi2_min >> 8));    
                    // save 16 bit
                    EEPROM.write(EEPROM_ADR_RSSI2_MAX_L,(rssi2_max & 0xff));
                    EEPROM.write(EEPROM_ADR_RSSI2_MAX_H,(rssi2_max >> 8)); 
#endif                    
                    
                    state=EEPROM.read(EEPROM_ADR_STATE);
                    beep(1000);
                }
            }            
        }    
        // new scan possible by press scan
        if (digitalRead(buttonSeek) == LOW) // force new full new scan
        {
            beep(50); // beep & debounce
            delay(KEY_DEBOUNCE); // debounce         
            last_state=255; // force redraw by fake state change ;-)
            channel=CHANNEL_MIN;
            writePos=SCANNER_LIST_X_POS; // reset channel list
            scan_start=1;
        }            
        // update index after channel change
        channelIndex = pgm_read_byte_near(channelList + channel);            
    }

    /*****************************/
    /*   General house keeping   */
    /*****************************/    
    if(last_channel_index != channelIndex)         // tune channel on demand
    {
        setChannelModule(channelIndex);    
        last_channel_index=channelIndex;
        // keep time of tune to make sure that RSSI is stable when required
        time_of_tune=millis();
        // give 3 beeps when tuned to give feedback of correct start
        if(first_tune)
        {
            first_tune=0;
            #define UP_BEEP 100
            beep(UP_BEEP);
            delay(UP_BEEP);
            beep(UP_BEEP);
            delay(UP_BEEP);
            beep(UP_BEEP);
        }
    }
    //rssi = readRSSI();    

#ifdef VOLTAGE
    checkVoltage();
#endif

}

/*###########################################################################*/
/*******************/
/*   SUB ROUTINES  */
/*******************/    

void beep(uint16_t time)
{
    digitalWrite(buzzer, LOW);
    delay(time);
    digitalWrite(buzzer, HIGH);
}

uint8_t channel_from_index(uint8_t channelIndex)
{
    uint8_t loop=0;
    uint8_t channel=0;
    for (loop=0;loop<=CHANNEL_MAX;loop++)
    {
        if(pgm_read_byte_near(channelList + loop) == channelIndex)
        {
            channel=loop;
            break;
        }
    }
    return (channel);
}    

void wait_rssi_ready()
{
    // CHECK FOR MINIMUM DELAY
    // check if RSSI is stable after tune by checking the time
    uint16_t tune_time = millis()-time_of_tune;
    // module need >20ms to tune.
    // 30 ms will to a 32 channel scan in 1 second.
    #define MIN_TUNE_TIME 30
    if(tune_time < MIN_TUNE_TIME)
    {
        // wait until tune time is full filled
        delay(MIN_TUNE_TIME-tune_time);
    }
}
        

uint16_t readRSSI() 
{
    uint16_t rssi = 0;
    for (uint8_t i = 0; i < 10; i++) 
    {
        rssi += analogRead(rssiPin);
    }
    rssi=rssi/10; // average
    // special case for RSSI setup
    if(state==STATE_RSSI_SETUP)
    { // RSSI setup
        if(rssi < rssi_setup_min)
        {
            rssi_setup_min=rssi;
            TV.print(50, SCANNER_LIST_Y_POS, "   ");
            TV.print(50, SCANNER_LIST_Y_POS, rssi_setup_min , DEC);            
        }
        if(rssi > rssi_setup_max)
        {
            rssi_setup_max=rssi;
        TV.print(110, SCANNER_LIST_Y_POS, "   ");
        TV.print(110, SCANNER_LIST_Y_POS, rssi_setup_max , DEC);                    
        }    
        // dump current values
    }   
    //TV.print(50, SCANNER_LIST_Y_POS-10, rssi_min , DEC);  
    //TV.print(110, SCANNER_LIST_Y_POS-10, rssi_max , DEC); 
    // scale AD RSSI Valaues to 1-100%     
    //#define RSSI_DEBUG 

    // Filter glitches
    #ifdef RSSI_DEBUG
        TV.print(1,20, "RAW:             ");
        TV.print(30,20, rssi, DEC);    
    #endif
    rssi = constrain(rssi, rssi_min, rssi_max);    //original 90---250
    rssi=rssi-rssi_min; // set zero point (value 0...160)
    rssi = map(rssi, 0, rssi_max-rssi_min , 1, 100);   // scale from 1..100%
    #ifdef RSSI_DEBUG
        TV.print(1,40, "SCALED:           ");    
        TV.print(50,40, rssi, DEC);    
    #endif
    
    return (rssi);
}


#ifdef DIVERSITY
uint16_t readRSSI2() 
{
    uint16_t rssi = 0;
    for (uint8_t i = 0; i < 10; i++) 
    {
        rssi += analogRead(rssiPin2);
    }
    rssi=rssi/10; // average
    // special case for RSSI setup
    if(state==STATE_RSSI_SETUP)
    { // RSSI setup
        if(rssi < rssi2_setup_min)
        {
            rssi2_setup_min=rssi;
            TV.print(50, SCANNER_LIST_Y_POS+DIV_Y_OFFSET, "   ");
            TV.print(50, SCANNER_LIST_Y_POS+DIV_Y_OFFSET, rssi2_setup_min , DEC);            
        }
        if(rssi > rssi2_setup_max)
        {
            rssi2_setup_max=rssi;
            TV.print(110, SCANNER_LIST_Y_POS+DIV_Y_OFFSET, "   ");
            TV.print(110, SCANNER_LIST_Y_POS+DIV_Y_OFFSET, rssi2_setup_max , DEC);                    
        }    
        // dump current values
    }   
    //TV.print(50, SCANNER_LIST_Y_POS-10, rssi_min , DEC);  
    //TV.print(110, SCANNER_LIST_Y_POS-10, rssi_max , DEC); 
    // scale AD RSSI Valaues to 1-100%     
    //#define RSSI_DEBUG 

    // Filter glitches
    #ifdef RSSI2_DEBUG
        TV.print(1,20, "RAW:             ");
        TV.print(30,20, rssi, DEC);    
    #endif
    rssi = constrain(rssi, rssi2_min, rssi2_max);    //original 90---250
    rssi=rssi-rssi2_min; // set zero point (value 0...160)
    rssi = map(rssi, 0, rssi2_max-rssi2_min , 1, 100);   // scale from 1..100%
    
    #ifdef RSSI2_DEBUG
        TV.print(1,40, "SCALED:           ");    
        TV.print(50,40, rssi, DEC);    
    #endif
    
    return (rssi);
}

void switchRX(boolean tvout, int keepTime) {
  
    led1on(rssi >= rssi2);  
  
    uint16_t now = millis();
    // we should keep the current state
    if (now < nextSwitchCheck) {
      return;
    }
    nextSwitchCheck = now + (keepTime * 1000);
    
    if (tvout) {
       switcher.write(90);
    } else if (rssi >= rssi2) {
       switcher.write(0);
    } else {
       switcher.write(180);      
    }
}

void led1on(boolean led1on) {
   digitalWrite(led1, led1on);
   digitalWrite(led2, !led1on); 
}

#endif

#ifdef VOLTAGE
uint16_t readVoltage() 
{
    uint16_t voltage = 0;
    for (uint8_t i = 0; i < 10; i++) 
    {
        voltage += analogRead(voltagePin);
    }
    voltage=voltage/10; // average
    voltage = map(voltage, 0, 1024 , 0, VOLTAGE_MAX);
    voltage = constrain(voltage, 0, VOLTAGE_MAX);
    return (voltage);
}

void checkVoltage() {
  
  // check only every 5 sec
  uint16_t check_time = millis()-time_of_voltcheck;
  if(check_time < 5000) {
      return;
  }
  time_of_voltcheck = millis();
  
  uint16_t voltage = readVoltage();
  
  if (voltage > 9000) {
    voltage = voltage/3;
  } else if (voltage > 6000) {
    voltage = voltage/2;    
  }
  
  if (voltage < VOLTAGE_CELL_MIN) {
    beep(100);
  }
  
}
#endif


// Private function: from http://arduino.cc/playground/Code/AvailableMemory
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

void setChannelModule(uint8_t channel)
{
  uint8_t i;
  uint16_t channelData;

  //channelData = pgm_read_word(&channelTable[channel]);
  //channelData = channelTable[channel];
  channelData = pgm_read_word_near(channelTable + channel);

  // bit bash out 25 bits of data
  // Order: A0-3, !R/W, D0-D19
  // A0=0, A1=0, A2=0, A3=1, RW=0, D0-19=0
  SERIAL_ENABLE_HIGH();
  delayMicroseconds(1);  
  //delay(2);
  SERIAL_ENABLE_LOW();

  SERIAL_SENDBIT0();
  SERIAL_SENDBIT0();
  SERIAL_SENDBIT0();
  SERIAL_SENDBIT1();

  SERIAL_SENDBIT0();

  // remaining zeros
  for (i = 20; i > 0; i--)
    SERIAL_SENDBIT0();

  // Clock the data in
  SERIAL_ENABLE_HIGH();
  //delay(2);
  delayMicroseconds(1);  
  SERIAL_ENABLE_LOW();

  // Second is the channel data from the lookup table
  // 20 bytes of register data are sent, but the MSB 4 bits are zeros
  // register address = 0x1, write, data0-15=channelData data15-19=0x0
  SERIAL_ENABLE_HIGH();
  SERIAL_ENABLE_LOW();

  // Register 0x1
  SERIAL_SENDBIT1();
  SERIAL_SENDBIT0();
  SERIAL_SENDBIT0();
  SERIAL_SENDBIT0();

  // Write to register
  SERIAL_SENDBIT1();

  // D0-D15
  //   note: loop runs backwards as more efficent on AVR
  for (i = 16; i > 0; i--)
  {
    // Is bit high or low?
    if (channelData & 0x1)
    {
      SERIAL_SENDBIT1();
    }
    else
    {
      SERIAL_SENDBIT0();
    }

    // Shift bits along to check the next one
    channelData >>= 1;
  }

  // Remaining D16-D19
  for (i = 4; i > 0; i--)
    SERIAL_SENDBIT0();

  // Finished clocking data in
  SERIAL_ENABLE_HIGH();
  delayMicroseconds(1);
  //delay(2);

  digitalWrite(slaveSelectPin, LOW);
  digitalWrite(spiClockPin, LOW);
  digitalWrite(spiDataPin, LOW);
}


void SERIAL_SENDBIT1()
{
  digitalWrite(spiClockPin, LOW);
  delayMicroseconds(1);

  digitalWrite(spiDataPin, HIGH);
  delayMicroseconds(1);
  digitalWrite(spiClockPin, HIGH);
  delayMicroseconds(1);

  digitalWrite(spiClockPin, LOW);
  delayMicroseconds(1);
}

void SERIAL_SENDBIT0()
{
  digitalWrite(spiClockPin, LOW);
  delayMicroseconds(1);

  digitalWrite(spiDataPin, LOW);
  delayMicroseconds(1);
  digitalWrite(spiClockPin, HIGH);
  delayMicroseconds(1);

  digitalWrite(spiClockPin, LOW);
  delayMicroseconds(1);
}

void SERIAL_ENABLE_LOW()
{
  delayMicroseconds(1);
  digitalWrite(slaveSelectPin, LOW);
  delayMicroseconds(1);
}

void SERIAL_ENABLE_HIGH()
{
  delayMicroseconds(1);
  digitalWrite(slaveSelectPin, HIGH);
  delayMicroseconds(1);
}
 
#97
Vielen lieben Dank für den Code, werde ihn testen, sobald es soweit ist.

Also genauer: kompiliert und aufgespielt habe ich ihn schon, das gab bis auf den Treiber für die China-Version des Arduino Nano schon mal keine Probleme,also hab ich zumindest das mit den libraries richtig gemacht.
Fehler vom Compiler gab es natürlich auch keine. :D

Bei einem ersten Blick in den Code ist mir aufgefallen, dass A3 als Anschluss für den PWM-Eingang des Turnigy Video-Switches genutzt wird - dazu habe ich eine Verständnisfrage:
Ich dachte bisher immer, dass die Nutzung der Servo-librarie sich auf die digitalen Pins, genauer auf die digitalen Pins des Arduino mit PWM-Unterstützung beschränkt? Hab ich das falsch verstanden?
Die switcher- Subroutine habe ich so verstanden, dass bei 0 und 180 jeweils einer der Empfänger die Video-Quelle ist und bei 90 der Arduino als Quelle des Video-Signals genutzt wird, hab ich das richtig gelesen?
 
Zuletzt bearbeitet:
#98
Also, wie gesagt, nur gecoded, nicht getestet ;_)
Meines Wissens geht die Servo Lib an allen digitalen Pins, und alle A0-A5 können sowohl als Analog als auch als normale Digital Pins genutzt werden. nur A6-A7 sind reine Analog und die D eben reine Digitale.
Genau, die Servo Lib gibt die Gradzahlen fürs Servo aus, 0 und 180 sind Endausschlag links/rechts, 90 ist Mitte, das schaltet beim Switcher eben auf Eingang 1, 2, oder 3. Hier müsstest du eben die Eingänge deines Switcher ICs entsprechend schalten.
 
#99
So, nach längerer Rumbraterei und Fehlersuche habe ich die Platine gestern erstmals in Betrieb genommen, vorerst mit der Minimalbestückung (Widerstände, Kondensatoren, Arduino, 7805). Das hat soweit super geklappt, der TV-OUT des Arduino hat tadellos funktioniert!
Dann habe ich die Empfängermodule verlötet (hatte dabei erst noch irgendeinen Bug zu beheben), hab die Platine wieder mit Strom versorgt, TV-OUT funktioniert, beide Empfänger werden auf die richtige Frequenz eingestellt! Das war eine meiner größten Sorgen: dass ich bei den Empfängermodulen etwa falsch gemacht habe, bei dem Versuch, sie auf SPI umzubauen...aber offensichtlich hat auch das soweit geklappt. :D

Jetzt kommt der Punkt, wo es darum geht, die Switches zu verbauen, erstmal den Turnigy-Video-Switch, um den im Praxisbetrieb zu testen und um die funktionsweise nicht nur in Software zu lesen, sondern auch mal in der Praxis zu sehen...
...um dann den nächsten Schritt in Richtung 4052 zu gehen.
Dazu gleich schon mal eine Frage:
Ich würde jetzt hingehen, und sämtliche Zeilen, die etwas mit dem bisherigen DIVERSITY zu tun haben, kopieren, und unter 4052DIVERSITY erneut einfügen, nur bei der SWITCHER Subroutine müssten hat die entsprechenden Änderungen rein...
Oder gibt es elegantere Ansätze, etwa ein zusätzliches #ifdef 4052, dass dann nur statt dem Turnigy den 4052er Switch aktiviert?
 
So, hab mir der Code mal genauer angesehen.
Huiuiui, da hab ich mir ja was vorgenommen....

Zum warm werden hab ich mir erstmal angesehen, was denn die Unterschiede zum Originalversion sind - und, wo ich schon dabei war - auch gleichmal zur Raceband-Version.
Ist ja eigentlich noch halbwegs übersichtlich, auch wenn ich nur die Hälfte verstehe...
Dank an die fließigen Kommentarschreiber, sonst wäre es noch deutlich weniger. :D!
Ein erster Versuch, Diversity und Raceband zu kombinieren ist jedenfalls schon mal kläglich gescheitert - ein Hoch auf meine Programmier-Fertigkeiten!
:D
 
Zuletzt bearbeitet:
Status
Nicht offen für weitere Antworten.
FPV1

Banggood

Oben Unten