main.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /* A derivative work of...
  2. * --
  3. * Minimal WSPR beacon using Si5351Arduino library sourced from
  4. * https://gist.github.com/NT7S/2b5555aa28622c1b3fcbc4d7c74ad926
  5. * --
  6. * Based on code from:
  7. * Copyright (C) 2015 - 2016 Jason Milldrum
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. * --
  13. * [You know the drill for the rest of the GPL3.. -cjb]
  14. * --
  15. * Chris Baird,, <vk2cjb@gmail.com>
  16. */
  17. #include <Wire.h>
  18. #include "RTClib.h"
  19. #include "TimeLib.h"
  20. #include "LiquidCrystal_PCF8574.h"
  21. #include "si5351.h"
  22. #include "JTEncode.h"
  23. #include "int.h"
  24. #define TONE_SPACING 146 // ~1.46 Hz
  25. #define WSPR_CTC 10672 // CTC value for WSPR
  26. #define SYMBOL_COUNT WSPR_SYMBOL_COUNT
  27. #define CORRECTION 0 // Change this for your ref osc
  28. #define TX_LED_PIN 13
  29. // These devices are all on the one I2C bus
  30. LiquidCrystal_PCF8574 lcd(0x27); // bog-standard I2C LCD module
  31. RTC_DS3231 rtc; // needs to be programmed with the correct time beforehand
  32. Si5351 si5351;
  33. // 160
  34. // 80
  35. // 60
  36. // 40 7040000 - 7039900
  37. // 30
  38. // 20
  39. // 17
  40. // 15
  41. // 12
  42. // 10 28124600 - 28125200
  43. // 6
  44. // 4
  45. // 2
  46. #define TIMEMODULUS 6 // (every N minutes)
  47. #define STARTMINUTE 0
  48. //const unsigned long freq = (7039900UL); // xxx
  49. const unsigned long freq = (7039100UL); // xxx
  50. const char call[7] = "VK2CJB"; // xxx
  51. const char loc[5] = "QF57"; // xxx
  52. const si5351_drive drive[] =
  53. { SI5351_DRIVE_8MA, SI5351_DRIVE_6MA, SI5351_DRIVE_4MA, SI5351_DRIVE_2MA };
  54. const uint8_t drivedbm[] = { 10, 8, 6, 3, 0 };
  55. int drivelevel = 0;
  56. JTEncode jtencode;
  57. uint8_t my_tx_buffer[SYMBOL_COUNT];
  58. /* ---------------------------------------------------------------------- */
  59. // Timer interrupt vector. This toggles the variable we use to gate
  60. // each column of output to ensure accurate timing. Called whenever
  61. // Timer1 hits the count set below in setup().
  62. volatile bool proceed = false;
  63. ISR(TIMER1_COMPA_vect)
  64. {
  65. proceed = true;
  66. }
  67. /* ---------------------------------------------------------------------- */
  68. // Loop through the string, transmitting one character at a time.
  69. void encode()
  70. {
  71. uint8_t i;
  72. jtencode.wspr_encode(call, loc, drivedbm[drivelevel], my_tx_buffer);
  73. si5351.drive_strength(SI5351_CLK0, drive[drivelevel]);
  74. // Reset the tone to 0 and turn on the output
  75. si5351.set_clock_pwr(SI5351_CLK0, 1);
  76. digitalWrite(TX_LED_PIN, HIGH);
  77. delay(1000); /* let the si warm up a bit */
  78. // Now do the rest of the message
  79. for(i = 0; i < SYMBOL_COUNT; i++)
  80. {
  81. uint64_t frequency = (freq * 100) + (my_tx_buffer[i] * TONE_SPACING);
  82. si5351.set_freq(frequency, SI5351_CLK0);
  83. proceed = false;
  84. lcd.setCursor(6,1);
  85. lcd.print(i);
  86. lcd.setCursor(10,1);
  87. lcd.print(my_tx_buffer[i]);
  88. while (!proceed)
  89. continue;
  90. }
  91. // Turn off the output
  92. si5351.set_clock_pwr(SI5351_CLK0, 0);
  93. digitalWrite(TX_LED_PIN, LOW);
  94. }
  95. /* ---------------------------------------------------------------------- */
  96. void lcd_warn (char* s)
  97. {
  98. lcd.home();
  99. lcd.clear();
  100. lcd.print(s);
  101. }
  102. void setup ()
  103. {
  104. int i;
  105. // Use the Arduino's on-board LED as a keying indicator.
  106. pinMode(TX_LED_PIN, OUTPUT);
  107. digitalWrite(TX_LED_PIN, LOW);
  108. // Initialize the Si5351
  109. // Change the 2nd parameter in init if using a ref osc other
  110. // than 25 MHz
  111. si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, CORRECTION);
  112. si5351.set_freq(freq * 100, SI5351_CLK0);
  113. si5351.drive_strength(SI5351_CLK0, drive[drivelevel]);
  114. si5351.set_clock_pwr(SI5351_CLK0, 0); // Disable the clock initially
  115. // Set up Timer1 for interrupts every symbol period.
  116. noInterrupts(); // Turn off interrupts.
  117. TCCR1A = 0; // Set entire TCCR1A register to 0; disconnects
  118. // interrupt output pins, sets normal waveform
  119. // mode. We're just using Timer1 as a counter.
  120. TCNT1 = 0; // Initialize counter value to 0.
  121. TCCR1B = (1 << CS12) | // Set CS12 and CS10 bit to set prescale
  122. (1 << CS10) | // to /1024
  123. (1 << WGM12); // turn on CTC
  124. // which gives, 64 us ticks
  125. TIMSK1 = (1 << OCIE1A); // Enable timer compare interrupt.
  126. OCR1A = WSPR_CTC; // Set up interrupt trigger count;
  127. interrupts(); // Re-enable interrupts.
  128. /* ---------------------------------------- */
  129. lcd.begin(16, 2); // initialize the lcd
  130. lcd.setBacklight(255);
  131. lcd.home(); lcd.clear();
  132. lcd.noBlink();
  133. lcd.noCursor();
  134. if (!rtc.begin())
  135. {
  136. lcd_warn("Can't find RTC");
  137. while (1);
  138. }
  139. #if 0
  140. if (!rtc.isrunning())
  141. {
  142. lcd_warn("RTC NOT running");
  143. while (1);
  144. }
  145. #endif
  146. const char *banners[] = { "Y.A.A.W.B", "Yet.A.A.W.B", "Y.Another.A.W.B",
  147. "Y.A.Arduino.W.B", "Y.A.A.WSPR.B ", "Y.A.A.W.Beacon " };
  148. for (i = 0; i < 6; i++)
  149. {
  150. lcd.setCursor(0,0);
  151. lcd.print(banners[i]);
  152. lcd.setCursor(0, 1);
  153. lcd.print(F("VK2CJB 2018"));
  154. delay(1000);
  155. }
  156. lcd.home();
  157. lcd.clear();
  158. }
  159. /* ---------------------------------------------------------------------- */
  160. void lcd_clock (int d)
  161. {
  162. if (d < 0)
  163. {
  164. lcd.print (F("--"));
  165. return;
  166. }
  167. if (d < 10)
  168. lcd.print ('0');
  169. lcd.print(d);
  170. if (d == 99) /* just to catch the countdown scrap */
  171. lcd.print(' ');
  172. }
  173. /* ---------------------------------------------------------------------- */
  174. void loop ()
  175. {
  176. int min, sec, m;
  177. DateTime now = rtc.now();
  178. min = now.minute();
  179. sec = now.second();
  180. lcd.setCursor(0,0);
  181. lcd_clock(now.hour());
  182. lcd.print(':');
  183. lcd_clock(min);
  184. lcd.print(':');
  185. lcd_clock(sec);
  186. m = min % TIMEMODULUS;
  187. m = STARTMINUTE - m;
  188. if (m < 0)
  189. m += TIMEMODULUS;
  190. m *= 60;
  191. m -= sec;
  192. lcd.setCursor(9,0);
  193. lcd_clock(m);
  194. lcd.setCursor(0,1);
  195. lcd.print(F("Next "));
  196. lcd.print(drivedbm[drivelevel]);
  197. lcd.print(F("dBm"));
  198. if (m == 0)
  199. {
  200. lcd.setCursor(0,0);
  201. lcd.print(F("TRANSMITTING"));
  202. lcd.setCursor(0,1);
  203. lcd.print(drivedbm[drivelevel]);
  204. lcd.print(F("dBm "));
  205. encode();
  206. lcd.clear();
  207. drivelevel++;
  208. if (drivedbm[drivelevel] == 0)
  209. drivelevel = 0;
  210. }
  211. delay(500);
  212. }
  213. /* ---------------------------------------------------------------------- */