123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /* A derivative work of...
- * --
- * Minimal WSPR beacon using Si5351Arduino library sourced from
- * https://gist.github.com/NT7S/2b5555aa28622c1b3fcbc4d7c74ad926
- * --
- * Based on code from:
- * Copyright (C) 2015 - 2016 Jason Milldrum
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * --
- * [You know the drill for the rest of the GPL3.. -cjb]
- * --
- * Chris Baird,, <vk2cjb@gmail.com>
- */
- #include <Wire.h>
- #include "RTClib.h"
- #include "TimeLib.h"
- #include "LiquidCrystal_PCF8574.h"
- #include "si5351.h"
- #include "JTEncode.h"
- #include "int.h"
- #define TONE_SPACING 146 // ~1.46 Hz
- #define WSPR_CTC 10672 // CTC value for WSPR
- #define SYMBOL_COUNT WSPR_SYMBOL_COUNT
- #define CORRECTION 0 // Change this for your ref osc
- #define TX_LED_PIN 13
- // These devices are all on the one I2C bus
- LiquidCrystal_PCF8574 lcd(0x27); // bog-standard I2C LCD module
- RTC_DS3231 rtc; // needs to be programmed with the correct time beforehand
- Si5351 si5351;
- // 160
- // 80
- // 60
- // 40 7040000 - 7039900
- // 30
- // 20
- // 17
- // 15
- // 12
- // 10 28124600 - 28125200
- // 6
- // 4
- // 2
- #define TIMEMODULUS 6 // (every N minutes)
- #define STARTMINUTE 0
- //const unsigned long freq = (7039900UL); // xxx
- const unsigned long freq = (7039100UL); // xxx
- const char call[7] = "VK2CJB"; // xxx
- const char loc[5] = "QF57"; // xxx
- const si5351_drive drive[] =
- { SI5351_DRIVE_8MA, SI5351_DRIVE_6MA, SI5351_DRIVE_4MA, SI5351_DRIVE_2MA };
- const uint8_t drivedbm[] = { 10, 8, 6, 3, 0 };
- int drivelevel = 0;
- JTEncode jtencode;
- uint8_t my_tx_buffer[SYMBOL_COUNT];
- /* ---------------------------------------------------------------------- */
- // Timer interrupt vector. This toggles the variable we use to gate
- // each column of output to ensure accurate timing. Called whenever
- // Timer1 hits the count set below in setup().
- volatile bool proceed = false;
- ISR(TIMER1_COMPA_vect)
- {
- proceed = true;
- }
- /* ---------------------------------------------------------------------- */
- // Loop through the string, transmitting one character at a time.
- void encode()
- {
- uint8_t i;
- jtencode.wspr_encode(call, loc, drivedbm[drivelevel], my_tx_buffer);
- si5351.drive_strength(SI5351_CLK0, drive[drivelevel]);
- // Reset the tone to 0 and turn on the output
- si5351.set_clock_pwr(SI5351_CLK0, 1);
- digitalWrite(TX_LED_PIN, HIGH);
- delay(1000); /* let the si warm up a bit */
- // Now do the rest of the message
- for(i = 0; i < SYMBOL_COUNT; i++)
- {
- uint64_t frequency = (freq * 100) + (my_tx_buffer[i] * TONE_SPACING);
- si5351.set_freq(frequency, SI5351_CLK0);
- proceed = false;
- lcd.setCursor(6,1);
- lcd.print(i);
- lcd.setCursor(10,1);
- lcd.print(my_tx_buffer[i]);
- while (!proceed)
- continue;
- }
- // Turn off the output
- si5351.set_clock_pwr(SI5351_CLK0, 0);
- digitalWrite(TX_LED_PIN, LOW);
- }
- /* ---------------------------------------------------------------------- */
- void lcd_warn (char* s)
- {
- lcd.home();
- lcd.clear();
- lcd.print(s);
- }
- void setup ()
- {
- int i;
- // Use the Arduino's on-board LED as a keying indicator.
- pinMode(TX_LED_PIN, OUTPUT);
- digitalWrite(TX_LED_PIN, LOW);
- // Initialize the Si5351
- // Change the 2nd parameter in init if using a ref osc other
- // than 25 MHz
- si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, CORRECTION);
- si5351.set_freq(freq * 100, SI5351_CLK0);
- si5351.drive_strength(SI5351_CLK0, drive[drivelevel]);
- si5351.set_clock_pwr(SI5351_CLK0, 0); // Disable the clock initially
- // 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 = WSPR_CTC; // Set up interrupt trigger count;
- interrupts(); // Re-enable interrupts.
- /* ---------------------------------------- */
- lcd.begin(16, 2); // initialize the lcd
- lcd.setBacklight(255);
- lcd.home(); lcd.clear();
- lcd.noBlink();
- lcd.noCursor();
- if (!rtc.begin())
- {
- lcd_warn("Can't find RTC");
- while (1);
- }
- #if 0
- if (!rtc.isrunning())
- {
- lcd_warn("RTC NOT running");
- while (1);
- }
- #endif
- const char *banners[] = { "Y.A.A.W.B", "Yet.A.A.W.B", "Y.Another.A.W.B",
- "Y.A.Arduino.W.B", "Y.A.A.WSPR.B ", "Y.A.A.W.Beacon " };
- for (i = 0; i < 6; i++)
- {
- lcd.setCursor(0,0);
- lcd.print(banners[i]);
- lcd.setCursor(0, 1);
- lcd.print(F("VK2CJB 2018"));
- delay(1000);
- }
- lcd.home();
- lcd.clear();
- }
- /* ---------------------------------------------------------------------- */
- void lcd_clock (int d)
- {
- if (d < 0)
- {
- lcd.print (F("--"));
- return;
- }
- if (d < 10)
- lcd.print ('0');
- lcd.print(d);
- if (d == 99) /* just to catch the countdown scrap */
- lcd.print(' ');
- }
- /* ---------------------------------------------------------------------- */
- void loop ()
- {
- int min, sec, m;
- DateTime now = rtc.now();
- min = now.minute();
- sec = now.second();
- lcd.setCursor(0,0);
- lcd_clock(now.hour());
- lcd.print(':');
- lcd_clock(min);
- lcd.print(':');
- lcd_clock(sec);
- m = min % TIMEMODULUS;
- m = STARTMINUTE - m;
- if (m < 0)
- m += TIMEMODULUS;
- m *= 60;
- m -= sec;
- lcd.setCursor(9,0);
- lcd_clock(m);
- lcd.setCursor(0,1);
- lcd.print(F("Next "));
- lcd.print(drivedbm[drivelevel]);
- lcd.print(F("dBm"));
- if (m == 0)
- {
- lcd.setCursor(0,0);
- lcd.print(F("TRANSMITTING"));
- lcd.setCursor(0,1);
- lcd.print(drivedbm[drivelevel]);
- lcd.print(F("dBm "));
- encode();
- lcd.clear();
- drivelevel++;
- if (drivedbm[drivelevel] == 0)
- drivelevel = 0;
- }
- delay(500);
- }
- /* ---------------------------------------------------------------------- */
|