// WSPR-Bake 28 + 50MHz, Sendezyklus synchronisiert mit GPS-RX // Grundlage: Simple JT65/JT9/JT4/FT8/WSPR/FSQ beacon for Arduino, with the Etherkit IN3HER 15.10.2022 // Si5351A Breakout Board, by Jason Milldrum NT7S. // Version ohne JTEncode mit fixen Buffer erstellt mit Etherkit MakeBuffer oder mit Symbolgenerator // https://swharden.com/blog/2021-03-27-wspr-code-generator/ // Achtung: Die Symbole mit Komma trennen! // Version mit Timer1 fuer Interrupts jeder Symbolperiode. // 15.12.2022 Frequenzoffset nach Minute geƤndert // 15.12.2022 nur noch Ausgang 1 wird benutzt 3 x BS170 Endstufe // 19.12.2022 Version 2 PPS-Interrupt eingebaut // funktioniert, TX on im Bereich von ca 40mS // 20.05.2023 Aenderungen effektive TX Frequenz PLLA 10m * 28; 6m PLLA * 16; // Jetzt funktioniert auch 6m jeder Minute! // WSPR TS = 1,465Hz; Del = 683mS 683*15,625=10672; Sc = 162; TX = 110,6Sec; Toene = 0-3; #include #include #include #include "Wire.h" #include #include String ver = "WSPR mit GPS Ver. 2"; static const int RXPin = 4, TXPin = 3; static const uint32_t GPSBaud = 9600; Si5351 si5351; TinyGPSPlus gps; SoftwareSerial ss(RXPin, TXPin); //------------------------------ #define Mode_10m_on 5 // GND = 10m ein #define Mode_6m_on 6 // GND = 6m ein #define LED_TX_10m 7 // Ein wenn 10m auf TX #define LED_TX_6m 8 // Ein wenn 6m auf TX #define PPS 2 // 1PPS Eingang zm synchronisieren #define BUTTON_DIG 10 // Manuelles starten des Senders ohne Daten zum Testen #define TX_on 12 // Zum schalten der Endstufe #define LED_PIN 13 // Ein wenn TX ein boolean sendet, sync; uint8_t Stunde, Minute, Sekunde; uint8_t ii; int ic_10m, ic_6m; long il; unsigned long Ref = 10000000UL; //10 MHz Ref. unsigned long long freq_6m = 50294500ULL; // +1,5kHz RX mit FT817 auf DIG 50.293,00 unsigned long long freq_10m = 28126100ULL; // +1,5kHz RX mit FT817 auf DIG 28.124,60 auf WSJT 28.126203MHz unsigned long long f_offset, F_Tx, ftx; int s; const byte interruptPin = 2; volatile byte pps_p = HIGH; volatile long mil; //------Mode Data------------------------------------------------------ #define ctc_b 15.625; //Basis der Delayberechnung unsigned long tone_spacing = 146UL; uint16_t tone_ctc = 683*ctc_b; //tone_delay = 678 683*15,625=10672 uint8_t symbol_count = 162; uint16_t tx_delay = 1000; // Zeit nach 1PPS bis zum Sendebeginn Sendebeginn 1Sek nach dem 1PPS Signal //------Minute TX on------------------------------------------------------ //char MinTx_10m[]={0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58}; char MinTx_10m[]={0,4,8,10,14,18,20,24,28,30,34,38,40,44,48,50,54,58}; // Minuten Sendung 10m //char MinTx_6m[]={0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58}; char MinTx_6m[]= {2,6,12,16,22,26,32,36,42,46,52,56}; // Minuten Sendung 6m //------Channel symbols------------------------------------------------------ // WSPR (in3her JN56 20dBm) char data_10m[]={1,1,0,0,2,2,2,2,3,2,2,2,3,3,3,0,2,0,3,2,0,1,0,1,1,1,3,2,0,0,0,2,2,2,3,0,2,3,2,3,0,0,2,0,2,0,1,2,3,1,2,0,3,1,2,1,2,2,2,3,3,2,1,0,2,2,2,1,1,0,3,0,3,2,1,0,1,2,2,3,2,0,1,2,3,3,0,0,2,1,1,0,1,0,3,0,0,0,3,2,2,2,0,0,3,0,2,1,0,0,3,3,1,0,1,1,2,0,3,3,0,1,2,2,0,3,1,1,0,0,2,2,2,1,2,3,0,2,3,1,0,0,0,0,2,0,0,1,1,2,1,0,1,1,2,2,2,3,3,0,2,2}; // WSPR (in3her JN56 20dBm) char data_6m[]={1,1,0,0,2,2,2,2,3,2,2,2,3,3,3,0,2,0,3,2,0,1,0,1,1,1,3,2,0,0,0,2,2,2,3,0,2,3,2,3,0,0,2,0,2,0,1,2,3,1,2,0,3,1,2,1,2,2,2,3,3,2,1,0,2,2,2,1,1,0,3,0,3,2,1,0,1,2,2,3,2,0,1,2,3,3,0,0,2,1,1,0,1,0,3,0,0,0,3,2,2,2,0,0,3,0,2,1,0,0,3,3,1,0,1,1,2,0,3,3,0,1,2,2,0,3,1,1,0,0,2,2,2,1,2,3,0,2,3,1,0,0,0,0,2,0,0,1,1,2,1,0,1,1,2,2,2,3,3,0,2,2}; //--------------------------------------------------------------------------- // Global variables used in ISRs volatile bool proceed = false; ISR(TIMER1_COMPA_vect) { proceed = true; } //--------------------------------------------------------------------------------- void pps() { pps_p = 1; mil = millis(); } //--------------------------------------------------------------------------------- // Loop through the string, transmitting one character at a time. void encode() { uint8_t i; unsigned long f, f1; String s; byte l13 = LOW; f = F_Tx / 1000000; f1 = F_Tx - f * 1000000; s = String(f) + "," + String(f1) + "MHz TX on ....."; Serial.print(s); F_Tx = F_Tx * 100; ftx = F_Tx - 219; // 219 = tonespacing * 1,465Sek if (f < 30) si5351.set_freq_manual(ftx, (ftx * 28), SI5351_CLK1); // Anfangsklick eliminieren else si5351.set_freq_manual(ftx, (ftx * 16), SI5351_CLK1); si5351.output_enable(SI5351_CLK1, 1); for(i = 0; i < symbol_count; i++) { if (f < 30) { f_offset = data_10m[i] * tone_spacing; ftx = F_Tx + f_offset - 219; // 219 = tonespacing * 1,465Sek si5351.set_freq_manual(ftx, (ftx * 28), SI5351_CLK1); } else { f_offset = data_10m[i] * tone_spacing; ftx = F_Tx + f_offset - 219; si5351.set_freq_manual(ftx, (ftx * 16), SI5351_CLK1); } proceed = false; while(!proceed); // GPSrx(); l13 = !l13; digitalWrite(LED_PIN, l13); } si5351.output_enable(SI5351_CLK1, 0); Serial.println(" off"); } //--------------------------------------------------------------------------------- void setup() { delay(100); Serial.begin(9600); Serial.println('\n'); Serial.println(ver); Serial.println("SI5351 Ref. = 10MHz"); Serial.println("Mode = WSPR"); boolean i2c_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, Ref, 0); if (i2c_found) Serial.println("SI5351 found!"); else Serial.println("SI5351 not found!"); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); pinMode(LED_TX_6m, OUTPUT); digitalWrite(LED_TX_6m, LOW); pinMode(LED_TX_10m, OUTPUT); digitalWrite(LED_TX_10m, LOW); pinMode(TX_on, OUTPUT); digitalWrite(TX_on, LOW); //TX enable pinMode(BUTTON_DIG, INPUT_PULLUP); //Triggereingang pinMode(Mode_6m_on, INPUT_PULLUP); // GND = ein pinMode(Mode_10m_on, INPUT_PULLUP); // GND = ein //------------------------------------------------- // Set CLK output //si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA); // 2MA=+1dBm; 4MA=+6dBm; 8MA=+12dBm; si5351.output_enable(SI5351_CLK0, 0); si5351.output_enable(SI5351_CLK1, 0); si5351.output_enable(SI5351_CLK2, 0); //------------------------------------------------- ic_10m = sizeof(MinTx_10m) / sizeof(char); ic_6m = sizeof(MinTx_6m) / sizeof(char); Serial.print("10m TX Minute: "); for(ii = 0; ii < ic_10m; ii++) { s = MinTx_10m[ii]; Serial.print(s); Serial.print(", "); } Serial.println(); Serial.print("6m TX Minute: "); for(ii = 0; ii < ic_6m; ii++) { s = MinTx_6m[ii]; Serial.print(s); Serial.print(", "); } Serial.println(); //------------------------------------------------- ss.begin(GPSBaud); //------------------------------------------------- // Set up Timer1 for interrupts every symbol period. noInterrupts(); // Turn off interrupts. TCCR1A = 0; // Set entire TCCR1A register to 0; disconnects // interrupt output pins, sets normal waveform // mode. We're just using Timer1 as a counter. TCNT1 = 0; // Initialize counter value to 0. TCCR1B = (1 << CS12) | // Set CS12 and CS10 bit to set prescale (1 << CS10) | // to /1024 (1 << WGM12); // turn on CTC // which gives, 64 us ticks TIMSK1 = (1 << OCIE1A); // Enable timer compare interrupt. OCR1A = tone_ctc; // Set up interrupt trigger count; interrupts(); // Re-enable interrupts. //------------------------------------------------- pinMode( interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), pps, FALLING); //FALLING fallende Flanke //------------------------------------------------- sendet = false; delay(1000); } //------------------------------------------------------------------------------------------ static void smartDelay(unsigned long ms) { unsigned long start = millis(); do { while (ss.available()) gps.encode(ss.read()); } while (millis() - start < ms); } //---------------------------------------------------------------------------------------------- void GPSrx(int t) { smartDelay(t); // bei 250 Fehler "59" 500 = ok if (gps.time.isValid() && ( gps.time.age() < t)) { Minute = gps.time.minute(); Sekunde = gps.time.second(); sync = true; } else sync = false; if (millis() > 5000 && gps.charsProcessed() < 10) { Serial.println("NO GPS detectet!"); delay(1000); // while(true); } } //---------------------------------------------------------------------------------------------- void f_Tx_offset() { F_Tx = F_Tx + Minute; //Sendefrequenz um den Wert der Minute verschieben if (Minute > 29) F_Tx = F_Tx - 60; } //---------------------------------------------------------------------------------------------- void Senden() { uint8_t ii; sendet = true; Serial.println("PPS"); if (digitalRead(Mode_10m_on) == LOW) { //Pin5 for(ii = 0; ii < ic_10m; ii++) { if (MinTx_10m[ii] == Minute) { F_Tx = freq_10m; f_Tx_offset(); digitalWrite(LED_TX_10m, HIGH); digitalWrite(TX_on, HIGH); si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_4MA); // 2MA=+1dBm; 4MA=+6dBm; 8MA=+12dBm; s_delay(tx_delay); encode(); s_delay(500); digitalWrite(TX_on, LOW); digitalWrite(LED_TX_10m, LOW); } } } if (digitalRead(Mode_6m_on) == LOW) { //Pin 6 for(ii = 0; ii < ic_6m; ii++) { if (MinTx_6m[ii] == Minute) { F_Tx = freq_6m; f_Tx_offset(); digitalWrite(LED_TX_6m, HIGH); digitalWrite(TX_on, HIGH); si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA); // 2MA=+1dBm; 4MA=+6dBm; 8MA=+12dBm; s_delay(tx_delay); encode(); s_delay(500); digitalWrite(TX_on, LOW); digitalWrite(LED_TX_6m, LOW); } } } sendet = false; } //---------------------------------------------------------------------------------------------- void s_delay(unsigned long ms) { unsigned long start = millis(); while (millis() - start < ms); } //---------------------------------------------------------------------------------------------- void TXmanuell() { if(digitalRead(BUTTON_DIG) == LOW) { // zum TX Testen s_delay(50); // delay to debounce if (digitalRead(BUTTON_DIG) == LOW) { sendet = true; if (digitalRead(Mode_10m_on) == LOW) { //Pin5 digitalWrite(LED_TX_10m, HIGH); digitalWrite(TX_on, HIGH); si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_4MA); // 2MA=+1dBm; 4MA=+6dBm; 8MA=+12dBm; si5351.output_enable(SI5351_CLK1, 1); ftx = freq_10m * 100; si5351.set_freq_manual(ftx, (ftx * 28), SI5351_CLK1); s_delay(15000); // 15 Sek si5351.output_enable(SI5351_CLK1, 0); digitalWrite(TX_on, LOW); digitalWrite(LED_TX_10m, LOW); } if (digitalRead(Mode_6m_on) == LOW) { //Pin 6 digitalWrite(LED_TX_6m, HIGH); digitalWrite(TX_on, HIGH); si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA); // 2MA=+1dBm; 4MA=+6dBm; 8MA=+12dBm; si5351.output_enable(SI5351_CLK1, 1); ftx = freq_6m * 100; si5351.set_freq_manual(ftx, (ftx * 16), SI5351_CLK1); s_delay(15000); si5351.output_enable(SI5351_CLK1, 0); digitalWrite(TX_on, LOW); digitalWrite(LED_TX_6m, LOW); } sendet = false; } } } //---------------------------------------------------------------------------------------------- void loop() { if (pps_p == 1) { if (Sekunde || 59) GPSrx(400); else { Sekunde++; Zeit(); } pps_test(); if (Sekunde == 0) { if ((!sendet) && (Minute %2 == 0) && (sync)) { //PPS gekommen aber Datenstrom ca 200mS spaeter Senden(); } } il = 0; } pps_p = 0; //--------------GPS PPS Led---------------------- if (!sendet) { il++; if (il < 200) digitalWrite(LED_PIN, 1); else digitalWrite(LED_PIN, 0); //--------------Manuell Senden-------------------------------- TXmanuell(); } } //----------------------Zeit anpassen-------------------------- void Zeit() { Sekunde++; if (Sekunde > 59) { Sekunde = 0; Minute++; if (Minute > 59) Minute = 0; } } //----------------------Testen--------------------------------- void pps_test() { // zum PPS Testen Serial.print("PPS "); Serial.print(Minute); Serial.print(":"); Serial.print(Sekunde); if (sync)Serial.println(" Sync"); else Serial.println(" -"); }