DTMF Remote Betrieb für Funkgeräte

Ich habe mich entschieden mal wieder ein paar Beiträge hier zu platzieren. Es ist ja einige Zeit ins Land gegangen und auch meine Interessen haben sich erweitert.

Ich möchte ein wenig ausholen.

Auf den Spuren zurück zu den Anfängen der Computer Wissenschaften (um sie besser zu verstehen) bin ich nun wieder an einem anderen Ende der Technischen Informatik angekommen. Es ist diesmal nicht die Halbleiter Physik, sondern die Technik der draht-gebundenen und drahtlosen Nachrichtentechnik. Im Prinzip sind das die Randgebiete der Netzwerktechnik im Osi Modell auf der Bitübertragungsschicht bis hoch zur Vermittlungsschicht. Ich denke dass Computer an sich sehr interessant sind, noch besser wird es aber, wenn man sie mit einander verbindet. Und da ich auch Praxis und Theorie gerne Verbinde, kam ich um eine Amateurfunklizenz (eigentlich sind es mittlerweile schon zwei) zum Experimentieren mit drahtloser Datenübertragung nicht herum.

Im Hintergrund seht ihr die Stationsfunkgeräte für die Kurzwelle mit Morsetaste und die UKW Station links. An dieses BAOFENG UV5R, was eigentlich ein Handfunkgerät ist, ist nun einen Mikrocontroller (LGT328P) angeschlossen. Das ist eine leistungsstärkere Eigenentwicklung der Chinesen, der gut mit dem ATmega328p binär kompatibel ist. Es gibt wohl nur noch wenig Gründe einen ATmega zu verwenden, es sei denn man hat noch welche auf Lager.

Der Mikrocontroller befindet sich in der Box, wo auch der selbstbau Lautsprecher drin ist.
Neben dem Mikrocontroller ist auch ein DTMF Dekoder verbaut. DTMF Töne sind (sehr vereinfacht) diese „Piptöne“ die ein Telefon oder Handy erzeugen kann, wenn man die Nummern wählt. Das UKW Funkgerät leitet die Signale an den Dekoder weiter, der wiederum den Mikrocontroller steuert und der schlussendlich das Funkgerät steuert.

Weiter habe ich eine Sprach Synthesiser Lib verwendet, die mir eine Sprachausgabe über den Mikrocontroller erzeugt. So kann ich nun ohne Jana testen, ob ich noch im Empfangsbereich bin und kann ihr mit der roten LED signalisieren, dass sie mich doch bitte zurück rufen soll. Die DCS Codierung des BAOFENG verhindert, dass sich ein Witzbold so einfach mit einwählen kann. Ganz rechts seht ihr das Handfunkgerät, mit dem ich in der Gegend herumspaziere. Theoretisch könnte ich nun auch die Waschmaschine oder sonst was mit dem Funkgerät ein und ausschalten. Die Möglichkeiten sind unbegrenzt.

Ahja hier noch ein paar Implementierungsdetails:
Verbaut auf Lochraster, ein MC LGT328P (macht satte 32 MHz !), ein Bipolar-Transistor zur Steuerung der PTT Taster, ein N-Kanal MOS FET als Verpolungsschutz, der DTMF Decoder und ein paar Widerstände zum Einstellen der Arbeitspunkte…

… achja und eine Katze …

Den eigentlich Kabelsalat auf der Rückseite der Lochrasterplatine spar ich mir mal.

2 „Gefällt mir“

Hier mal noch der Quellcode … falls es Anmerkungen gibt (schlechter Stil, kritische Codesegmente,…) immer her damit und ja ich schreibe in diesem Fall C und C++.
Womöglich ist das mixen von C und C++ auch schon schlechter Stil, aber es lässt sich mit Arduino auch nur mit Aufwand umgehen und ich schreibe jetzt keine Main neu und auch keine Makefiles…

/*  Autor: lux (Michael Krause) HAM Call: DL5LUX
 *  
 *  This software realizes the remote control of an amateur radio station. 
 *  It is controlled by DTMF tones. Your radio itself does not need to be able 
 *  to decode DTMF tones. This is done by the external MT8870 chip.
 *  The software uses a cheap microcontroller (ATmega328) or equivalent like LGT8F328P, 
 *  with at least 16MHz clock. A synthesized speech output is used for communication.
 *  
 *  Please note: You should have an amateur radio license if you want to use this project. 
 *  
 *  
 *  Hardware-Notes
 *  
 *  LED Pins
 * -  blau (LED Pin 1)
 * -  grün (LED Pin 2)
 * -- GND  (LED Pin 3, common ground)
 * -  rot  (LED Pin 4)
----------------------------
    Parameter   WDT Reset Period
    WTO_64MS         64 ms   
    WTO_128MS       128 ms
    WTO_256MS       256 ms
    WTO_512MS       512 ms
    WTO_1S            1 s
    WTO_2S            2 s
    WTO_4S            4 s
    WTO_8S            8 s
    WTO_16S          16 s
    WTO_32S          32 s
*/


#include <talkie.h> // emulates a hardware Texas Instruments speech synthesis architecture
#include "voice.h"  // predefined words for syntetized speech output
#include "MT8870.h" // smal DTMF lib for MT8870 IC
#include <PMU.h>    // Für den Sleep-Modus

//#define DEBUG       //enables debug level, needs more flash memory and some RAM     
#ifdef DEBUG          //precompiler conditional compilation directives for debugging
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif

#if DEBUG_TEST == 1
  #include <WDT.h>    // Watchdog-Timer lib
#endif

Talkie voice;             // initialize speech synthesis architecture
MT8870 DTMF;              // initialize DTMF Chip
const int buttonPin = A0; // define application pushbutton Port
const int PTTpin = A1;    // define PTT button Port of the VHF/UHF radio, puts the radio on air (needs ham license)

const int blueLED = 11;   // define blue LED Port
const int greenLED = 10;  // define green LED Port
const int redLED = 9;     // define red LED Port

char inputString[5];      //define received DTMF string, sent from other radios to this one
int inputIndex = 0;       //Variable for string indexing for inputString
char ch = 0;              //DTMF receives sequentially, this variable holds the latest received character

enum AppState             // possible states of the state machine for defined program control
{
  WAIT_FOR_INPUT,
  PROCESS_INPUT
};

AppState currentState = WAIT_FOR_INPUT; // define start state

unsigned long currentMillis;    // Initialize these two variables
unsigned long previousMillis;   //to control time-based actions in a non-blocking way (simple cooperative multitasking)

void setup() 
{
  #if DEBUG_TEST == 1
    Serial.begin(115200);
  #endif

  DTMF.begin(2, 4, 5, 6, 7);        //External MT8870 DTMF decoder, triggered MC on rising edge of Pin 2

  pinMode(blueLED, OUTPUT);         // Blue LED (multicolor LED)
  pinMode(greenLED, OUTPUT);        // Green LED (multicolor LED)
  pinMode(redLED, OUTPUT);          // Red LED (multicolor LED)
  pinMode(buttonPin, INPUT);        // Application related pushbutton, capacitive, no debouncing necessary);
  pinMode(PTTpin, OUTPUT);          // PTT button of the VHF/UHF radio
  const byte interruptPin = 2;      // Interrupt pin, shows incoming DTMF signal
  attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, RISING);
  #if DEBUG_TEST == 1
    wdt_enable(WDTO_8S);              // Watchdog-Timer, important for cooperative multitasking
  #endif
}

void loop() 
{
  currentMillis = millis(); //Count milliseconds since start of the microcontroller. 

  if (digitalRead(buttonPin) == HIGH) 
  {
    setRedLED(false); // Switch off red LED
  }

  if (currentState == PROCESS_INPUT) {
    currentState = WAIT_FOR_INPUT;
    processReceivedTone(ch);
  }
  
  #if DEBUG_TEST == 1
    wdt_reset(); // Reset watchdog timer
  #endif
}

// Function for processing received DTMF sounds
void processReceivedTone(char tone) 
{
  if (tone == '#') // # is the terminating character of each DTMF string
  {
    inputString[inputIndex] = '\0'; // close valid DTMF string

    // program the microcontroller to do interesting things with their radio
    switch (atoi(inputString)) 
    {
      case 159:
        #if DEBUG_TEST == 1
          Serial.println("159");
        #endif
        setGreenLED(true);
        setPTT(true);
        nonBlockingDelay(1500);
        voice.say(spDELTA);
        voice.say(spLIMA);
        voice.say(spFIVE);
        voice.say(spLIMA);
        voice.say(spUNIFORM);
        voice.say(spXRAY);
        voice.say(spIS);
        voice.say(spQ);
        voice.say(spR);
        voice.say(spV);
        setPTT(false);
        setGreenLED(false);
        break;

      case 0:
        #if DEBUG_TEST == 1
          Serial.println("0");
        #endif
        setGreenLED(true);
        setPTT(true);
        nonBlockingDelay(1300);
        voice.say(spCHECK);
        setPTT(false);
        setGreenLED(false);
        setRedLED(true);
        break;

      case 9:
        #if DEBUG_TEST == 1
          Serial.println("9");
        #endif
        setGreenLED(true);
        setPTT(true);
        nonBlockingDelay(1500);
        voice.say(spTHIS1);
        voice.say(spIS);
        voice.say(spDELTA);
        voice.say(spLIMA);
        voice.say(spFIVE);
        voice.say(spLIMA);
        voice.say(spUNIFORM);
        voice.say(spXRAY);
        setPTT(false);
        setGreenLED(false);
        break;
        
    }

    inputIndex = 0; // after valid DTMF code the index is reset.
  } 
  else if (inputIndex < 4) 
  {
    inputString[inputIndex] = tone;
    inputIndex++;
  } else 
  {
    inputIndex = 0; // reset index if more than 4 characters were received
    inputString[0] = '\0';
  }
}

// intnerrupt function to receive new DTMF characters via radio waves
void handleInterrupt() 
{
  ch = DTMF.read();
  currentState = PROCESS_INPUT;
}

// function to switch red LED
void setRedLED(bool state) 
{
  digitalWrite(redLED, state ? HIGH : LOW);
}
// function to switch blue LED
void setBlueLED(bool state) 
{
  digitalWrite(blueLED, state ? HIGH : LOW);
}
// function to switch green LED
void setGreenLED(bool state) 
{
  digitalWrite(greenLED, state ? HIGH : LOW);
}

// activate PTT, warning this key puts the station on the air
void setPTT(bool state) 
{
  digitalWrite(PTTpin, state ? HIGH : LOW);
}

// very simple cooperative multitasking function to control time-based actions in a non-blocking way. more will happen here in the future
void nonBlockingDelay(unsigned long duration) 
{
  previousMillis = currentMillis;
  while (currentMillis - previousMillis < duration) 
  {
    currentMillis = millis();
  }
}
1 „Gefällt mir“

Sehr cooles Projekt, danke fürs teilen! Ich verwende in letzter Zeit für meine Projekte auch kaum noch AVR-Chips, schnelle ARM-Chips sind einfach zu günstig (z.B. Arduino Nano RP2040 oder Adafruit Feather RP2040). Wie ist denn die Reichweite der Handfunkgeräte?

Der Code sieht für mich gut aus, mir sind nur zwei Sachen aufgefallen:

  1. Du verwendest beim Einfügen von Zeichen in inputString eine hart-kodierte 4 statt sowas wie sizeof inputString - 1.
  2. Die Funktion nonBlockingDelay behauptet, sie wäre non-blocking, ist dann aber blocking? Wir haben immer ein cleveres Marko für einen simplen Scheduler verwendet.

Und die Box ist ja mal in bester Tiband-Tradition :grin::

Ja die Nano RP2040 kosten im Schnitt so 4 Euro. Für ein chinesischen LGT328P zahle ich im Schnitt nur 2 Euro. Eigentlich könnte ich auch immer den RP2040 nehmen, aber der LGT328P soll ein paar sehr ausgeklügelte Energiesparfunktionen haben. Prinzipiell scheint ein 1 Kerne weniger Leistung aufzunehmen, von daher versuche ich zwischen diesen zwei MCs abzuwägen. LGT328P hat übrigens nichts mit dem ATmega zu tun. Er hat unter der Haube andere Register, andere Peripherie (12BIT-ADC, eien DAC, …) aber er hat keinen ROM.

Zur Funke: Grob würde ich sagen, komme ich mit einer X30 Antenne auf 10 Meter höhe bei halbwegs freier Bahn mit dem Handfunkgerät mit 5 Watt, 40 km weit. Mit einer kleineren Antenne sind so 20 km drin, wenn man nicht gerade im Wald/Gebäude ist und der Empfänger einen exponierten Standort hat.

Der Code sieht für mich gut aus, mir sind nur zwei Sachen aufgefallen…

Ja, da hast du den Nagel auf den Kopf getroffen. Die hart-kodierte 4 ist natürlich Scheiße.

Die Funktion nonBlockingDelay behauptet, sie wäre non-blocking,…

Stimmt, das was ich schrieb stimmt nicht ganz. Vielmehr versucht sie die verschwendete Zeit noch irgendwie sinnvoll zu nutzen. Ein einfacher Scheduler ist besser. Ich schau mir den mal an. Ich wäre den Funktionsumfang noch aufbohren. Ein DCF77 Funkmodul für die Zeitansage ist auch schon unterwegs.

Ich versuche übrigens auch die Reste zu verwerten, so habe ich den Karton des Funkgerätes gleich mit verwurstet. So ein cooler Tiband-Aufkleber wie bei dem Laborprojekt macht schon was her.

Das ist ja schön, die alte git.informatik.uni-leipzig.de Instanz gibt es ja immer noch. Kann man da noch was pushen ? :wink:

Krasse, ich fand die offiziellen 12$ für die Adafruit-Variante schon günstig, aber bei 4€ muss man echt gar nicht mehr nachdenken. :open_mouth:

Ich konnte auch noch pushen, nachdem mein Account deaktiviert wurde (ziemlich bald nach der Exmatrikulation), habs allerdings schon länger nicht mehr probiert.

Darauf ich bspw. keinen Zugriff mehr. Aber das gute alte Tiband Gitea wird ja weiterhin von @klemens gehostet. :slight_smile: