Set up scheduled program cycles #24
							
								
								
									
										1
									
								
								vulpes/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vulpes/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -3,4 +3,5 @@ | ||||
| .vscode/c_cpp_properties.json | ||||
| .vscode/launch.json | ||||
| .vscode/ipch | ||||
| .vscode/* | ||||
| */config.h | ||||
| @ -10,20 +10,17 @@ | ||||
|  | ||||
| [env:esp32doit-devkit-v1] | ||||
| platform = espressif32 | ||||
| ;build_flags = | ||||
| ;	-std=c++11 | ||||
| ;	-std=gnu++11 | ||||
| board = esp32doit-devkit-v1 | ||||
| framework = arduino | ||||
| upload_speed = 921600 | ||||
| monitor_speed = 115200 | ||||
| monitor_filters = esp32_exception_decoder | ||||
| build_type = debug | ||||
| lib_deps =  | ||||
| 	me-no-dev/AsyncTCP@^1.1.1 | ||||
| 	me-no-dev/ESP Async WebServer@^1.2.3 | ||||
| 	contrem/arduino-timer@^3.0.1 | ||||
| 	kj7rrv/Telegraph@^1.0.0 | ||||
| 	jandelgado/JLed@^4.13.0 | ||||
| 	;adafruit/RTClib@^2.1.1 | ||||
| 	https://github.com/adafruit/RTClib.git ; >=2.1.2 | ||||
| 	jandelgado/JLed@^4.13.1 | ||||
| 	https://github.com/adafruit/RTClib.git | ||||
| 	adafruit/Adafruit BusIO@^1.14.3 | ||||
| 	;jchristensen/DS3232RTC@^2.0.1 | ||||
| 	erropix/ESP32 AnalogWrite@^0.2 | ||||
|  | ||||
| @ -1,17 +0,0 @@ | ||||
| # JLed morse example | ||||
|  | ||||
| This examples demonstrates an efficient method to generate morse code on  | ||||
| an micro controller like the Arduino. | ||||
|  | ||||
| The morse example uses the morse alphabet encoded in a binary tree to  | ||||
| generate morse code using a JLed user defined brightness class. The text | ||||
| to be morsed is transformed into morse code and then transformed into a  | ||||
| sequence of `1` and `0` which are written out to a GPIO controlling a LED or | ||||
| a sound generator.  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Author  | ||||
|  | ||||
| Jan Delgado | ||||
|  | ||||
| @ -1,53 +0,0 @@ | ||||
| // Copyright (c) 2019 Jan Delgado <jdelgado[at]gmx.net> | ||||
| // https://github.com/jandelgado/jled | ||||
|  | ||||
| #ifndef EXAMPLES_MORSE_BITSET_H_ | ||||
| #define EXAMPLES_MORSE_BITSET_H_ | ||||
|  | ||||
| // a simple bit set with capacity of N bits, just enough for the morse demo | ||||
| class Bitset { | ||||
|  private: | ||||
|     size_t n_; | ||||
|     uint8_t* bits_; | ||||
|  | ||||
|  protected: | ||||
|     // returns num bytes needed to store n bits. | ||||
|     static constexpr size_t num_bytes(size_t n) { | ||||
|         return n > 0 ? ((n - 1) >> 3) + 1 : 0; | ||||
|     } | ||||
|  | ||||
|  public: | ||||
|     Bitset() : Bitset(0) {} | ||||
|  | ||||
|     Bitset(const Bitset& b) : Bitset() { *this = b; } | ||||
|  | ||||
|     explicit Bitset(size_t n) : n_(n), bits_{new uint8_t[num_bytes(n)]} { | ||||
|         memset(bits_, 0, num_bytes(n_)); | ||||
|     } | ||||
|  | ||||
|     Bitset& operator=(const Bitset& b) { | ||||
|         if (&b == this) return *this; | ||||
|         const auto size_new = num_bytes(b.n_); | ||||
|         if (num_bytes(n_) != size_new) { | ||||
|             delete[] bits_; | ||||
|             bits_ = new uint8_t[size_new]; | ||||
|             n_ = b.n_; | ||||
|         } | ||||
|         memcpy(bits_, b.bits_, size_new); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     virtual ~Bitset() { | ||||
|         delete[] bits_; | ||||
|         bits_ = nullptr; | ||||
|     } | ||||
|     void set(size_t i, bool val) { | ||||
|         if (val) | ||||
|             bits_[i >> 3] |= (1 << (i & 7)); | ||||
|         else | ||||
|             bits_[i >> 3] &= ~(1 << (i & 7)); | ||||
|     } | ||||
|     bool test(size_t i) const { return (bits_[i >> 3] & (1 << (i & 7))) != 0; } | ||||
|     size_t size() const { return n_; } | ||||
| }; | ||||
| #endif  // EXAMPLES_MORSE_BITSET_H_ | ||||
| @ -1,121 +0,0 @@ | ||||
| // Copyright (c) 2019 Jan Delgado <jdelgado[at]gmx.net> | ||||
| // https://github.com/jandelgado/jled | ||||
| #include <Arduino.h> | ||||
| #include <inttypes.h> | ||||
| #include <stddef.h> | ||||
| #include "bitset.h"  // NOLINT | ||||
|  | ||||
| #ifndef EXAMPLES_MORSE_MORSE_H_ | ||||
| #define EXAMPLES_MORSE_MORSE_H_ | ||||
|  | ||||
| // The Morse class converts a text sequence into a bit sequence representing | ||||
| // a morse code sequence. | ||||
| class Morse { | ||||
|     // pre-ordered tree of morse codes. Bit 1 = 'dah',  0 = 'dit'. | ||||
|     // Position in string corresponds to position in binary tree starting w/ 1 | ||||
|     // see https://www.pocketmagic.net/morse-encoder/ for info on encoding | ||||
|     static constexpr auto LATIN = | ||||
|         "*ETIANMSURWDKGOHVF*L*PJBXCYZQ**54*3***2*******16*******7***8*90"; | ||||
|  | ||||
|     static constexpr auto DURATION_DIT = 1; | ||||
|     static constexpr auto DURATION_DAH = 3 * DURATION_DIT; | ||||
|     static constexpr auto DURATION_PAUSE_CHAR = DURATION_DAH; | ||||
|     static constexpr auto DURATION_PAUSE_WORD = 7 * DURATION_DIT; | ||||
|  | ||||
|  protected: | ||||
|     char upper(char c) const { return c >= 'a' && c <= 'z' ? c - 32 : c; } | ||||
|     bool isspace(char c) const { return c == ' '; } | ||||
|  | ||||
|     // returns position of char in morse tree. Count starts with 1, i.e. | ||||
|     // E=2, T=3, etc. | ||||
|     size_t treepos(char c) const { | ||||
|         auto i = 1u; | ||||
|         while (LATIN[i++] != c) { | ||||
|         } | ||||
|         return i; | ||||
|     } | ||||
|  | ||||
|     // returns uint16_t with size of morse sequence (dit's and dah's) in MSB | ||||
|     // and the morse sequence in the LSB | ||||
|     uint16_t pos_to_morse_code(int code) const { | ||||
|         uint8_t res = 0; | ||||
|         uint8_t size = 0; | ||||
|         while (code > 1) { | ||||
|             size++; | ||||
|             res <<= 1; | ||||
|             res |= (code & 1); | ||||
|             code >>= 1; | ||||
|         } | ||||
|         return res | (size << 8); | ||||
|     } | ||||
|  | ||||
|     template <typename F> | ||||
|     uint16_t iterate_sequence(const char* p, F f) const { | ||||
|         // call f(count,val) num times, incrementing count each time | ||||
|         // and returning num afterwards. | ||||
|         auto set = [](int num, int count, bool val, F f) -> int { | ||||
|             for (auto i = 0; i < num; i++) f(count + i, val); | ||||
|             return num; | ||||
|         }; | ||||
|  | ||||
|         auto bitcount = 0; | ||||
|         while (*p) { | ||||
|             const auto c = upper(*p++); | ||||
|             if (isspace(c)) {  // space not part of alphabet, treat separately | ||||
|                 bitcount += set(DURATION_PAUSE_WORD, bitcount, false, f); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             const auto morse_code = pos_to_morse_code(treepos(upper(c))); | ||||
|             auto code = morse_code & 0xff;  // dits (0) and dahs (1) | ||||
|             auto size = morse_code >> 8;    // number of dits and dahs in code | ||||
|             while (size--) { | ||||
|                 bitcount += set((code & 1) ? DURATION_DAH : DURATION_DIT, | ||||
|                                 bitcount, true, f); | ||||
|  | ||||
|                 // pause between symbols := 1 dit | ||||
|                 if (size) { | ||||
|                     bitcount += set(DURATION_DIT, bitcount, false, f); | ||||
|                 } | ||||
|                 code >>= 1; | ||||
|             } | ||||
|  | ||||
|             if (*p && !isspace(*p)) { | ||||
|                 bitcount += set(DURATION_PAUSE_CHAR, bitcount, false, f); | ||||
|             } | ||||
|         } | ||||
|         return bitcount; | ||||
|     } | ||||
|  | ||||
|  public: | ||||
|     // returns ith bit of morse sequence | ||||
|     bool test(uint16_t i) const { return bits_->test(i); } | ||||
|  | ||||
|     // length of complete morse sequence in in bits | ||||
|     size_t size() const { return bits_->size(); } | ||||
|  | ||||
|     Morse() : bits_(new Bitset(0)) {} | ||||
|  | ||||
|     explicit Morse(const char* s) { | ||||
|         const auto length = iterate_sequence(s, [](int, bool) -> void {}); | ||||
|         auto bits = new Bitset(length); | ||||
|         iterate_sequence(s, [bits](int i, bool v) -> void { bits->set(i, v); }); | ||||
|         bits_ = bits; | ||||
|     } | ||||
|  | ||||
|     ~Morse() { delete bits_; } | ||||
|  | ||||
|     // make sure that the following, currently not needed, methods are not used | ||||
|     Morse(const Morse&m) {*this = m;} | ||||
|     Morse& operator=(const Morse&m) { | ||||
|         delete bits_; | ||||
|         bits_ = new Bitset(*m.bits_); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|  private: | ||||
|     // stores morse bit sequence | ||||
|     const Bitset* bits_ = nullptr; | ||||
| }; | ||||
|  | ||||
| #endif  // EXAMPLES_MORSE_MORSE_H_ | ||||
| @ -16,23 +16,22 @@ | ||||
| #include <AsyncTCP.h> | ||||
| #include <SPIFFS.h> | ||||
| #include <Preferences.h> | ||||
| #include <arduino-timer.h> | ||||
| // #include <Telegraph.h> | ||||
| //#include <morse.h> //arduino morse | ||||
| //#include <Morse.h> //etherkit morse | ||||
| #include <jled.h> // jled | ||||
| #include "jled/morse.h" //jled | ||||
| #include "morse.h" | ||||
| #include <Adafruit_BusIO_Register.h> // for DS3231 | ||||
| #include <RTClib.h> // for DS3231 | ||||
| //#include <DS3232RTC.h> //for DS3231 | ||||
| //#include <sstream> | ||||
| #include <string> | ||||
|  | ||||
| // download zip from https://github.com/me-no-dev/ESPAsyncWebServer and install. | ||||
| #include <ESPAsyncWebServer.h> | ||||
|  | ||||
| AsyncWebServer server(80); | ||||
|  | ||||
| // Assign output variables to GPIO pins | ||||
| const int keyer = 32; //LED_BUILTIN for on-board (dev);//26 for LED; //32 for transmitter keyer | ||||
| const int blinker = LED_BUILTIN; | ||||
|  | ||||
| RTC_DS3231 rtc; // set up RTC | ||||
| const int alarmPin = 4; // pin to monitor for RTC alarms | ||||
|  | ||||
| // Read from config.h | ||||
| const char* ssid = WIFI_SSID; | ||||
| @ -44,6 +43,11 @@ const char* PARAM_WPM = "inputWPM"; | ||||
| const char* PARAM_MSG = "inputMsg"; | ||||
| const char* PARAM_FLOAT = "inputFloat"; | ||||
| const char* PARAM_TIME = "inputTimeUnix"; | ||||
| const char* PARAM_START = "inputStartTimeUnix"; | ||||
| const char* PARAM_RUNNING = "programRunning"; | ||||
| const char* PARAM_STEPLENGTH = "inputStepLength"; | ||||
| const char* PARAM_CYCLEID = "inputCycleID"; | ||||
| const char* PARAM_NTRANS = "inputNtransmitters"; | ||||
|  | ||||
| // Global variables | ||||
| String yourInputString; | ||||
| @ -53,28 +57,80 @@ int yourInputMsg; | ||||
| int yourInputMsg_old; // to save previous state and check changes | ||||
| float yourInputFloat; | ||||
| uint32_t yourInputTime; //to keep time | ||||
| uint32_t yourInputStartTimeUnix; | ||||
| bool startProgram; | ||||
| bool programRunning; | ||||
| int yourInputStepLength; | ||||
| int yourInputCycleID; | ||||
| int yourInputNtransmitters; | ||||
| //int step_length = 10000; // 10 secs | ||||
| //int cycle_id = 1; // number of this transmitter in cycle | ||||
| //int n_transmitters = 2; //number of transmitters total | ||||
| long start_millis = 0; | ||||
| long stop_millis = 0; | ||||
| long pause_until_millis = 0; | ||||
|  | ||||
| // HTML web page to handle 3 input fields (inputString, inputSend, inputFloat) | ||||
| const char index_html[] PROGMEM = R"rawliteral( | ||||
| <!DOCTYPE HTML><html><head> | ||||
|   <link rel="icon" href="data:,"> | ||||
|   <title>ESP Input Form</title> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|   <script> | ||||
|     var putDate = function(form) { | ||||
|       form.inputTimeUnix.value = Math.floor(Date.now() / 1000); | ||||
|     }; | ||||
|   </script></head><body> | ||||
|   <form action="/get" target="hidden-form" onsubmit="putDate(this);"> | ||||
|     inputString (current value %inputString%): <input type="text" name="inputString" value=%inputString%><br> | ||||
|   <script type="text/javascript"> | ||||
|     // Utility from https://webreflection.medium.com/using-the-input-datetime-local-9503e7efdce | ||||
|     Date.prototype.toDatetimeLocal = function toDatetimeLocal() { | ||||
|     var | ||||
|       date = this, | ||||
|       ten = function (i) { | ||||
|         return (i < 10 ? '0' : '') + i; | ||||
|       }, | ||||
|       YYYY = date.getFullYear(), | ||||
|       MM = ten(date.getMonth() + 1), | ||||
|       DD = ten(date.getDate()), | ||||
|       HH = ten(date.getHours()), | ||||
|       II = ten(date.getMinutes()), | ||||
|       SS = ten(date.getSeconds()) | ||||
|     ; | ||||
|     return YYYY + '-' + MM + '-' + DD + 'T' + | ||||
|              HH + ':' + II + ':' + SS; | ||||
|     } | ||||
|  | ||||
|     Sending program (cycle doesn't work yet) (current value: <b>%inputSend%</b>): | ||||
|     // Submit timestamps as unix seconds when form is submitted | ||||
|     var putDate = function(form) { | ||||
|       form.inputTimeUnix.value = Math.floor(Date.now() / 1000);// - new Date().getTimezoneOffset()*60; | ||||
|       form.inputStartTimeUnix.value = ((Date.parse(js_start_time_unix_entry.value))/1000); | ||||
|       //document.getElementById("js_start_time_unix").value = ((Date.parse(js_start_time_unix_entry.value))/1000); | ||||
|     } | ||||
|     // Fill in page values | ||||
|     window.onload = function() { | ||||
|       s = %inputStartTimeUnix%; | ||||
|       current_start = new Date(s * 1000); | ||||
|       document.getElementById('current-start').innerHTML = current_start.toLocaleString(); | ||||
|       // Show the local time as a string | ||||
|       local_time_unix = new Date().toLocaleString();//toUTCString(); | ||||
|       document.getElementById('local-time-unix').innerHTML = local_time_unix.toString(); | ||||
|       // Fill in the start time field as local time | ||||
|       document.getElementById('js_start_time_unix_entry').value = current_start.toDatetimeLocal(); | ||||
|  | ||||
|       // Fill in the other form fields | ||||
|       document.getElementById("send-program").value = %inputSend%; | ||||
|       document.getElementById("message").value = %inputMsg%; | ||||
|     } | ||||
|  | ||||
|   </script></head><body> | ||||
|   <h1>Vulpes Radio Orienteering Controller</h1> | ||||
|   <p>Local time: <b><span id=local-time-unix></span></b></p> | ||||
|  | ||||
|   <form action="/get"  onsubmit="putDate(this);" accept-charset=utf-8> | ||||
|     <h2>General Settings</h2> | ||||
|     <p>Sending program: | ||||
|     <select name="inputSend" id="send-program"> | ||||
|       <option value="0">0 -Off</option> | ||||
|       <option value="0" >0 - Off</option> | ||||
|       <option value="1">1 - Continuous</option> | ||||
|       <option value="2">2 - Cycle</option> | ||||
|     </select><br> | ||||
|  | ||||
|     Message (current value <b>%inputMsg%</b>): | ||||
|     Message: | ||||
|     <select name="inputMsg" id="message"> | ||||
|       <option value="0">0 - TEST TEST TEST DE W1CDN</option> | ||||
|       <option value="1">1 - MOE</option> | ||||
| @ -82,49 +138,39 @@ const char index_html[] PROGMEM = R"rawliteral( | ||||
|       <option value="3">3 - MOS</option> | ||||
|       <option value="4">4 - MOH</option> | ||||
|       <option value="5">5 - MO5</option> | ||||
|     </select><br> | ||||
|     </select></p> | ||||
|  | ||||
|     <h2>Cycle Settings</h2> | ||||
|     <p>Only applies when <em>Sending Program</em> is set to "2 - Cycle". You cannot set a cycle start date more than a month in advance.</p> | ||||
|     <p>Cycle start time <input type="datetime-local" id="js_start_time_unix_entry" /><br> | ||||
|     Current value: <b><span id=current-start></span></b> | ||||
|      | ||||
|     WPM (current value %inputWPM%): <input type="number " name="inputWPM" value = %inputWPM%> (doesn't work yet)<br> | ||||
|     <!-- JS converts the entered start time to a unix timestamp, and copies that value | ||||
|     to this hidden field so the user doesn't have to see it. --> | ||||
|     <input type="hidden" name="inputStartTimeUnix" id="js_start_time_unix" /></p> | ||||
|     <p> | ||||
|       Step length: <input type="number" name="inputStepLength" min=1000 step=1000 value = %inputStepLength%> milliseconds <br> | ||||
|       Cycle ID: <input type="number" name="inputCycleID" min=1 value = %inputCycleID%><br> | ||||
|       Number of transmitters: <input type="number" name="inputNtransmitters" min=1 value = %inputNtransmitters%><br> | ||||
|     </p> | ||||
|      | ||||
|     <!-- This field is hidden so people don't change the submit time (it will be wrong). | ||||
|     The value is automatically filled in with JS. --> | ||||
|     <input type="hidden" name="inputTimeUnix" id="js_time_unix"> | ||||
|  | ||||
|     Current time (UTC): %inputTimeUnix% | ||||
|     <input type="hidden" name="inputTimeUnix" id="js_time_unix"><br> | ||||
|     <!-- Extra fields just in case I need them -->   | ||||
|     <input type="hidden" name="inputWPM" value = %inputWPM%> | ||||
|     <input type="hidden" name="inputString" value = %inputString%> | ||||
|     <input type="hidden" name="inputFloat" value = %inputFloat%> | ||||
|  | ||||
|     inputFloat (current value %inputFloat%): <input type="number " name="inputFloat" value = %inputFloat%><br> | ||||
|     <input type="submit" value="Submit""> | ||||
|   </form> | ||||
|   <iframe style="display:none" name="hidden-form"></iframe> | ||||
|   <iframe style="display:none" name="hidden-form" id="hidden-form"></iframe> | ||||
|   <script type="text/javascript"> | ||||
|      | ||||
|   </script> | ||||
| </body></html>)rawliteral"; | ||||
|  | ||||
| // Auxiliary variables to store the current output state | ||||
| //String output26State = "off"; | ||||
| //String output27State = "off"; | ||||
|  | ||||
| // Assign output variables to GPIO pins | ||||
| const int keyer = 32; //LED_BUILTIN for on-board (dev);//26 for LED; //32 for transmitter keyer | ||||
| const int blinker = LED_BUILTIN; | ||||
|  | ||||
| // Timers | ||||
| //auto timer = timer_create_default(); | ||||
| Timer<1> timer; | ||||
| auto time_until_start = timer_create_default(); | ||||
|  | ||||
| // Example from https://github.com/contrem/arduino-timer#examples | ||||
| bool toggle_led(void *) { | ||||
|   digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // toggle the LED | ||||
|   return true; // keep timer active? true | ||||
| } | ||||
|  | ||||
| // Toggle GPIO pin (LED or relay) | ||||
| // bool toggle_gpio_26(void *) { | ||||
| //   if(output26State == "off"){ | ||||
| //     output26State = "on"; | ||||
| //     digitalWrite(output26, HIGH); | ||||
| //   } else { | ||||
| //     output26State = "off"; | ||||
| //     digitalWrite(output26, LOW); | ||||
| //   } | ||||
| //   return true; // keep timer active? true | ||||
| // } | ||||
|  | ||||
| void notFound(AsyncWebServerRequest *request) { | ||||
|   request->send(404, "text/plain", "Not found"); | ||||
| @ -177,98 +223,32 @@ String processor(const String& var){ | ||||
|   else if(var == "inputMsg"){ | ||||
|     return readFile(SPIFFS, "/inputMsg.txt"); | ||||
|   } | ||||
|   else if(var == "inputStepLength"){ | ||||
|     return readFile(SPIFFS, "/inputStepLength.txt"); | ||||
|   } | ||||
|   else if(var == "inputCycleID"){ | ||||
|     return readFile(SPIFFS, "/inputCycleID.txt"); | ||||
|   } | ||||
|   else if(var == "inputNtransmitters"){ | ||||
|     return readFile(SPIFFS, "/inputNtransmitters.txt"); | ||||
|   } | ||||
|   else if(var == "inputFloat"){ | ||||
|     return readFile(SPIFFS, "/inputFloat.txt"); | ||||
|   } else if(var == "inputTimeUnix"){ | ||||
|     return rtc.now().timestamp(); | ||||
|   } | ||||
|   } else if(var == "inputStartTimeUnix"){ | ||||
|     // Webform breaks if this value is empty. | ||||
|     String temp = readFile(SPIFFS, "/inputStartTimeUnix.txt"); | ||||
|     if(temp == ""){ | ||||
|       temp = "0"; | ||||
|     } | ||||
|     return temp; | ||||
|   }  | ||||
|   return String(); | ||||
| } | ||||
|  | ||||
| // // vvvvv Modify some functions from KB1OIQ's controller. | ||||
| // // This section hasn't been tested on the hardware. | ||||
|  | ||||
| // //int dit_len = 60 ; //milliseconds; https://morsecode.world/international/timing.html | ||||
|  | ||||
| // //================================================================================ | ||||
| // // stop_26(): set GPIO 26 to LOW. Used for dot(), dash(). | ||||
| // //================================================================================ | ||||
| // bool stop_26(void *){ | ||||
| //   output26State = "off"; | ||||
| //   digitalWrite(output26, LOW); | ||||
| //   return false; // keep timer active? true | ||||
| // } | ||||
|  | ||||
| // //================================================================================ | ||||
| // // dit(): transmit a single dit | ||||
| // //================================================================================ | ||||
| // void dit(int dit_len = 1000) { | ||||
| //   output26State = "on"; | ||||
| //   digitalWrite(output26, HIGH); | ||||
| //   timer.in(dit_len, stop_26); | ||||
| // } | ||||
|  | ||||
| // //================================================================================ | ||||
| // // dah(): transmit a single dah | ||||
| // //================================================================================ | ||||
| // void dah(int dit_len = 1000) { | ||||
| //   output26State = "on"; | ||||
| //   digitalWrite(output26, HIGH); | ||||
| //   timer.in(dit_len * 3, stop_26); | ||||
| // } | ||||
|  | ||||
| // //================================================================================ | ||||
| // // char_space()): transmit a character space | ||||
| // //================================================================================ | ||||
| // // A function that does nothing except (hopefully) block the timer. | ||||
| // bool empty(void *) { | ||||
| //     return false; | ||||
| //   } | ||||
|  | ||||
| // void char_space(int dit_len = 1000) { | ||||
|    | ||||
| //   timer.in(dit_len, empty); | ||||
| // } | ||||
|  | ||||
| // void k(){ | ||||
| //   Serial.println("K"); | ||||
| //   dah(); | ||||
| //   char_space(); | ||||
| //   dit(); | ||||
| //   char_space(); | ||||
| //   dah(); | ||||
| // } | ||||
|  | ||||
| // // ^^^^ | ||||
|  | ||||
| // //telegraph | ||||
| // //Telegraph telegraph(LED_BUILTIN, 10, HIGH); | ||||
| // Telegraph telegraph26(output26, 10, HIGH); | ||||
|  | ||||
| //arduinomorse | ||||
| //LEDMorseSender sender(LED_BUILTIN); | ||||
|  | ||||
| //Etherkit Morse | ||||
| //Morse morse(LED_BUILTIN, 15); | ||||
|  | ||||
| //jled from https://github.com/jandelgado/jled/blob/master/examples/morse/morse_effect.h | ||||
| class MorseEffect : public jled::BrightnessEvaluator { | ||||
|     Morse morse_; | ||||
|     // duration of a single 'dit' in ms | ||||
|     const uint16_t speed_; | ||||
|  | ||||
|  public: | ||||
|     explicit MorseEffect(const char* message, uint16_t speed = 200) | ||||
|         : morse_(message), speed_(speed) {} | ||||
|  | ||||
|     uint8_t Eval(uint32_t t) const override { | ||||
|         const auto pos = t / speed_; | ||||
|         if (pos >= morse_.size()) return 0; | ||||
|         return morse_.test(pos) ? 255 : 0; | ||||
|     } | ||||
|  | ||||
|     uint16_t Period() const override { return (morse_.size() + 1) * speed_; } | ||||
| }; | ||||
| // Set up arduinomorse pin and default WPM | ||||
| LEDMorseSender sender(blinker, 10.0f) ; // the 'f' makes sure this is a float | ||||
| // TODO also for keyer once blinker works | ||||
|  | ||||
| // Speed is milliseconds per dit, which is 1000 * (60 / (50 * WPM)) | ||||
| // 60 is 20 wpm, 120 is 10 wpm, 90 is 15 wpm, etc. | ||||
| @ -276,55 +256,6 @@ class MorseEffect : public jled::BrightnessEvaluator { | ||||
| float wpm = 10; | ||||
| float ms_per_dit = 1000 * (60 / (50 * wpm)); | ||||
| int word_space_ms = ms_per_dit * 7; | ||||
| // Hardcoding messages and WPM  for now, will come back and make it more flexible. | ||||
| MorseEffect morseEffectTEST("TEST TEST TEST DE W1CDN", ms_per_dit); | ||||
| MorseEffect morseEffectMOE("MOE", ms_per_dit); | ||||
| MorseEffect morseEffectMOI("MOI", ms_per_dit); | ||||
| MorseEffect morseEffectMOS("MOS", ms_per_dit); | ||||
| MorseEffect morseEffectMOH("MOH", ms_per_dit); | ||||
| MorseEffect morseEffectMO5("MO5", ms_per_dit); | ||||
|  | ||||
| // CW for keyer | ||||
| auto morseTEST = | ||||
|     JLed(keyer).UserFunc(&morseEffectTEST).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOE = | ||||
|     JLed(keyer).UserFunc(&morseEffectMOE).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOI = | ||||
|     JLed(keyer).UserFunc(&morseEffectMOI).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOS = | ||||
|     JLed(keyer).UserFunc(&morseEffectMOS).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOH = | ||||
|     JLed(keyer).UserFunc(&morseEffectMOH).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMO5 = | ||||
|     JLed(keyer).UserFunc(&morseEffectMO5).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseToSend = morseTEST; // set this up to overwrite later | ||||
|  | ||||
| // CW for blinker | ||||
| auto morseTEST_blink = | ||||
|     JLed(blinker).UserFunc(&morseEffectTEST).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOE_blink = | ||||
|     JLed(blinker).UserFunc(&morseEffectMOE).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOI_blink = | ||||
|     JLed(blinker).UserFunc(&morseEffectMOI).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOS_blink = | ||||
|     JLed(blinker).UserFunc(&morseEffectMOS).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMOH_blink = | ||||
|     JLed(blinker).UserFunc(&morseEffectMOH).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseMO5_blink = | ||||
|     JLed(blinker).UserFunc(&morseEffectMO5).DelayAfter(word_space_ms).Forever(); | ||||
| auto morseToSend_blink = morseTEST_blink; // set this up to overwrite later | ||||
|  | ||||
|  | ||||
| // format and print a time_t value | ||||
| // void printTime(time_t t) | ||||
| // { | ||||
| //     char buf[25]; | ||||
| //     char m[4];    // temporary storage for month string (DateStrings.cpp uses shared buffer) | ||||
| //     strcpy(m, monthShortStr(month(t))); | ||||
| //     sprintf(buf, "%.2d:%.2d:%.2d %s %.2d %s %d", | ||||
| //         hour(t), minute(t), second(t), dayShortStr(weekday(t)), day(t), m, year(t)); | ||||
| //     Serial.println(buf); | ||||
| // } | ||||
|  | ||||
| //================================================================================ | ||||
| // setup(): stuff that only gets done once, after power up (KB1OIQ's description) | ||||
| @ -332,13 +263,10 @@ auto morseToSend_blink = morseTEST_blink; // set this up to overwrite later | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
|   // https://github.com/JChristensen/DS3232RTC/blob/master/examples/TimeRTC/TimeRTC.ino | ||||
|   // rtc.begin(); | ||||
|   // setSyncProvider(rtc.get);   // the function to get the time from the RTC | ||||
|   //   if(timeStatus() != timeSet) | ||||
|   //       Serial.println("Unable to sync with the RTC"); | ||||
|   //   else | ||||
|   //       Serial.println("RTC has set the system time"); | ||||
|   // Get arduinomorse ready to go | ||||
|   sender.setup(); | ||||
|  | ||||
|   pinMode(alarmPin, INPUT_PULLUP); // Set alarm pin as pullup | ||||
|  | ||||
|   if (! rtc.begin()) { | ||||
|     Serial.println("Couldn't find RTC"); | ||||
| @ -356,51 +284,34 @@ void setup() { | ||||
|     //rtc.adjust(DateTime(2023, 9, 2, 17, 32, 0)); | ||||
|   } | ||||
|  | ||||
|   // Timer example, blink main LED | ||||
|   pinMode(LED_BUILTIN, OUTPUT); // set LED pin to OUTPUT | ||||
|   // call the toggle_led function every 10000 millis (10 second) | ||||
|   //timer.every(10000, toggle_led); | ||||
|   // call the toggle_gpio_26 function  | ||||
|   //timer.every(1000, toggle_gpio_26); | ||||
|   // Report the RTC time | ||||
|   Serial.print("RTC time on startup: "); | ||||
|   Serial.println(rtc.now().unixtime()); | ||||
|   Serial.println(rtc.now().timestamp()); | ||||
|  | ||||
|   // Are there any RTC alarms set? | ||||
|   DateTime alarm_one = rtc.getAlarm1(); // Get the current time | ||||
|   char buff[] = "Alarm 1 set for at hh:mm:ss DDD, DD MMM YYYY"; | ||||
|   Serial.print(alarm_one.toString(buff)); | ||||
|   Serial.println(" (only HH:MM:SS day-of-month are accurate)"); | ||||
|  | ||||
|   // Initialize the output variables as outputs | ||||
|   pinMode(keyer, OUTPUT); | ||||
|   pinMode(blinker, OUTPUT); | ||||
|   //pinMode(blinker, OUTPUT); | ||||
|   // Set outputs to LOW | ||||
|   digitalWrite(keyer, LOW); | ||||
|   digitalWrite(blinker, LOW); | ||||
|   //digitalWrite(blinker, LOW); | ||||
|  | ||||
|   // Initialize SPIFFS | ||||
|   SPIFFS.begin(true); | ||||
|   //#ifdef ESP32 | ||||
|     if(!SPIFFS.begin(true)){ | ||||
|       Serial.println("An Error has occurred while mounting SPIFFS"); | ||||
|       return; | ||||
|     } | ||||
|   //#else | ||||
|     if(!SPIFFS.begin()){ | ||||
|       Serial.println("An Error has occurred while mounting SPIFFS"); | ||||
|       return; | ||||
|     } | ||||
|   //#endif | ||||
|  | ||||
|   // Make sure files exist, maybe with defaults here | ||||
|   // if(SPIFFS.exists("/inputString.txt") == 0){ | ||||
|   //   writeFile(SPIFFS, "/inputString.txt", "CQ"); | ||||
|   // } | ||||
|   // if(SPIFFS.exists("/inputSend.txt") == 0){ | ||||
|   //   writeFile(SPIFFS, "/inputSend.txt", "0"); | ||||
|   // } | ||||
|   // if(SPIFFS.exists("/inputWPM.txt") == 0){ | ||||
|   //   writeFile(SPIFFS, "/inputWPM.txt", "10"); | ||||
|   // } | ||||
|   // if(SPIFFS.exists("/inputMsg.txt") == 0){ | ||||
|   //   writeFile(SPIFFS, "/inputMsg.txt", "0"); | ||||
|   // } | ||||
|   // if(SPIFFS.exists("/inputFloat.txt") == 0){ | ||||
|   //   writeFile(SPIFFS, "/inputFloat.txt", "1.1"); | ||||
|   // } | ||||
|  | ||||
|  | ||||
|   // Read in existing data | ||||
|   yourInputString = readFile(SPIFFS, "/inputString.txt"); | ||||
| @ -408,27 +319,25 @@ void setup() { | ||||
|   yourInputWPM = readFile(SPIFFS, "/inputWPM.txt").toInt(); | ||||
|   yourInputMsg = readFile(SPIFFS, "/inputMsg.txt").toInt(); | ||||
|   yourInputFloat = readFile(SPIFFS, "/inputFloat.txt").toFloat(); | ||||
|   yourInputStartTimeUnix = readFile(SPIFFS, "/inputStartTimeUnix.txt").toInt(); | ||||
|   yourInputStepLength = readFile(SPIFFS, "/inputStepLength.txt").toInt(); | ||||
|   yourInputCycleID = readFile(SPIFFS, "/inputCycleID.txt").toInt(); | ||||
|   yourInputNtransmitters = readFile(SPIFFS, "/inputNtransmitters.txt").toInt(); | ||||
|  | ||||
|   // On restart, keep doing what you were doing before | ||||
|   yourInputMsg_old = yourInputMsg; | ||||
|   if(yourInputMsg == 0){ | ||||
|     morseToSend = morseTEST; | ||||
|     morseToSend_blink = morseTEST_blink; | ||||
|     sender.setMessage(String("test test test de w1cdn  ")); | ||||
|   } else if(yourInputMsg == 1){ | ||||
|     morseToSend = morseMOE; | ||||
|     morseToSend_blink = morseMOE_blink; | ||||
|     sender.setMessage(String("moe ")); | ||||
|   } else if(yourInputMsg == 2){ | ||||
|     morseToSend = morseMOI; | ||||
|     morseToSend_blink = morseMOI_blink; | ||||
|     sender.setMessage(String("moi ")); | ||||
|   } else if(yourInputMsg == 3){ | ||||
|     morseToSend = morseMOS; | ||||
|     morseToSend_blink = morseMOS_blink; | ||||
|     sender.setMessage(String("mos ")); | ||||
|   } else if(yourInputMsg == 4){ | ||||
|     morseToSend = morseMOH; | ||||
|     morseToSend_blink = morseMOH_blink; | ||||
|     sender.setMessage(String("moh ")); | ||||
|   } else if(yourInputMsg == 5){ | ||||
|     morseToSend = morseMO5; | ||||
|     morseToSend_blink = morseMO5_blink; | ||||
|     sender.setMessage(String("mo5 ")); | ||||
|   } | ||||
|  | ||||
|   WiFi.mode(WIFI_STA); | ||||
| @ -460,6 +369,12 @@ void setup() { | ||||
|       inputMessage = request->getParam(PARAM_SEND)->value(); | ||||
|       writeFile(SPIFFS, "/inputSend.txt", inputMessage.c_str()); | ||||
|       yourInputSend = inputMessage.toInt(); | ||||
|       // if not running a program, set the program running off | ||||
|       //if(yourInputSend != 2){ | ||||
|         // Cease all programs on new input | ||||
|         startProgram = false; | ||||
|         programRunning = false; | ||||
|       //} | ||||
|     } | ||||
|     // GET inputWPM value on <ESP_IP>/get?inputWPM=<inputMessage> | ||||
|     if (request->hasParam(PARAM_WPM)) { | ||||
| @ -474,18 +389,60 @@ void setup() { | ||||
|       // save previous state | ||||
|       yourInputMsg_old = yourInputMsg; | ||||
|       yourInputMsg = inputMessage.toInt(); | ||||
|  | ||||
|       // Check the message every time the form is submitted. | ||||
|       if(yourInputMsg == 0){ | ||||
|         sender.setMessage(String("test test test de w1cdn ")); | ||||
|       } else if(yourInputMsg == 1){ | ||||
|         sender.setMessage(String("moe ")); | ||||
|       } else if(yourInputMsg == 2){ | ||||
|         sender.setMessage(String("moi ")); | ||||
|       } else if(yourInputMsg == 3){ | ||||
|         sender.setMessage(String("mos ")); | ||||
|       } else if(yourInputMsg == 4){ | ||||
|         sender.setMessage(String("moh ")); | ||||
|       } else if(yourInputMsg == 5){ | ||||
|         sender.setMessage(String("mo5 ")); | ||||
|       } | ||||
|     } | ||||
|     // GET inputStepLength value on <ESP_IP>/get?inputStepLength=<inputMessage> | ||||
|     if (request->hasParam(PARAM_STEPLENGTH)) { | ||||
|       inputMessage = request->getParam(PARAM_STEPLENGTH)->value(); | ||||
|       writeFile(SPIFFS, "/inputStepLength.txt", inputMessage.c_str()); | ||||
|       yourInputStepLength = inputMessage.toInt(); | ||||
|     } | ||||
|     // GET inputCycleID value on <ESP_IP>/get?inputCycleID=<inputMessage> | ||||
|     if (request->hasParam(PARAM_CYCLEID)) { | ||||
|       inputMessage = request->getParam(PARAM_CYCLEID)->value(); | ||||
|       writeFile(SPIFFS, "/inputCycleID.txt", inputMessage.c_str()); | ||||
|       yourInputCycleID = inputMessage.toInt(); | ||||
|     } | ||||
|     // GET inputNtransmitters value on <ESP_IP>/get?inputNtransmitters=<inputMessage> | ||||
|     if (request->hasParam(PARAM_NTRANS)) { | ||||
|       inputMessage = request->getParam(PARAM_NTRANS)->value(); | ||||
|       writeFile(SPIFFS, "/inputNtransmitters.txt", inputMessage.c_str()); | ||||
|       yourInputNtransmitters = inputMessage.toInt(); | ||||
|     } | ||||
|     // GET inputTimeUnix value on <ESP_IP>/get?inputTimeUnix=<inputMessage> | ||||
|     if (request->hasParam(PARAM_TIME)) { | ||||
|       inputMessage = request->getParam(PARAM_TIME)->value(); | ||||
|       Serial.println(inputMessage); | ||||
|       //https://stackoverflow.com/a/22733127/2152245 | ||||
|       yourInputTime = atol(inputMessage.c_str()); | ||||
|       Serial.print("yourInputTime: "); | ||||
|       Serial.println(yourInputTime); | ||||
|       // update the RTC time | ||||
|       rtc.adjust(DateTime(yourInputTime)); | ||||
| ; | ||||
|       DateTime now = rtc.now(); | ||||
|       // Might work to fix random errors? If date is far in the future, | ||||
|       // try to update again. | ||||
|       // replace if with while if you want it to try a bunch... | ||||
|       if(now.year() > 2040){ | ||||
|         Serial.print("Year is "); | ||||
|         Serial.println(now.year()); | ||||
|         Serial.println("RTC can't set time. Trying again."); | ||||
|         rtc.adjust(DateTime(yourInputTime)); | ||||
|       } | ||||
|  | ||||
|       Serial.print("UTC time from browser: "); | ||||
|       Serial.print(now.year(), DEC); | ||||
|       Serial.print('/'); | ||||
| @ -501,6 +458,9 @@ void setup() { | ||||
|       Serial.print(':'); | ||||
|       Serial.print(now.second(), DEC); | ||||
|       Serial.println(); | ||||
|  | ||||
|       Serial.print("rtc.now().unixtime(): "); | ||||
|       Serial.println(rtc.now().unixtime()); | ||||
|     } | ||||
|     // GET inputFloat value on <ESP_IP>/get?inputFloat=<inputMessage> | ||||
|     if (request->hasParam(PARAM_FLOAT)) { | ||||
| @ -508,127 +468,118 @@ void setup() { | ||||
|       writeFile(SPIFFS, "/inputFloat.txt", inputMessage.c_str()); | ||||
|       yourInputFloat = inputMessage.toFloat(); | ||||
|     } | ||||
|     // else { | ||||
|     //   inputMessage = "No message sent"; | ||||
|     // } | ||||
|     request->send(200, "text/plain", inputMessage); | ||||
|  | ||||
|     // GET inputStartTimeUnix value on <ESP_IP>/get?inputStartTimeUnix=<inputMessage> | ||||
|     if (request->hasParam(PARAM_START)) { | ||||
|       inputMessage = request->getParam(PARAM_START)->value(); | ||||
|       Serial.println(inputMessage); | ||||
|       // if a start time isn't entered, don't overwrite the old one | ||||
|       //if(!(inputMessage != NULL && inputMessage[0] == '\0')){ | ||||
|         writeFile(SPIFFS, "/inputStartTimeUnix.txt", inputMessage.c_str()); | ||||
|         yourInputStartTimeUnix = atol(inputMessage.c_str()); | ||||
|       //} | ||||
|       Serial.println(yourInputStartTimeUnix); | ||||
|        | ||||
|       // Use alarm built into RTC | ||||
|       rtc.setAlarm1(DateTime(yourInputStartTimeUnix), DS3231_A1_Date); | ||||
|       //rtc.setAlarm1(DateTime(2020, 6, 25, 15, 34, 0), DS3231_A2_Date); | ||||
|       DateTime alarm_one = rtc.getAlarm1(); // Get the current alarm time | ||||
|       char buff[] = "Alarm 1 set for at hh:mm:ss DDD, DD MMM YYYY"; | ||||
|       Serial.print(alarm_one.toString(buff)); | ||||
|       Serial.println(" (only HH:MM:SS day-of-month are accurate)"); | ||||
|     } | ||||
|     // https://techtutorialsx.com/2018/01/14/esp32-arduino-http-server-external-and-internal-redirects/ | ||||
|     request->redirect("/"); | ||||
|   }); | ||||
|   server.onNotFound(notFound); | ||||
|   server.begin(); | ||||
|  | ||||
|   //telegraph | ||||
|   //telegraph.send("CQ CQ CQ"); | ||||
|   //telegraph26.send("CQ CQ CQ DE W1CDN K"); | ||||
|  | ||||
|   // arduinomorse | ||||
|   // sender.setup(); | ||||
| 	// sender.setMessage(String("73 de kb3jcy ")); | ||||
| 	// sender.startSending(); | ||||
|  | ||||
|    | ||||
|  | ||||
| } | ||||
|  | ||||
| void loop() { | ||||
|     // Timers | ||||
|   time_until_start.tick(); | ||||
|   timer.tick(); | ||||
|  | ||||
|   // DateTime now = rtc.now(); | ||||
|   // Serial.print(now.year(), DEC); | ||||
|   // Serial.print('/'); | ||||
|   // Serial.print(now.month(), DEC); | ||||
|   // Serial.print('/'); | ||||
|   // Serial.print(now.day(), DEC); | ||||
|   // Serial.print(" ("); | ||||
|   // Serial.print(now.dayOfTheWeek()); | ||||
|   // Serial.print(") "); | ||||
|   // Serial.print(now.hour(), DEC); | ||||
|   // Serial.print(':'); | ||||
|   // Serial.print(now.minute(), DEC); | ||||
|   // Serial.print(':'); | ||||
|   // Serial.print(now.second(), DEC); | ||||
|   // Serial.println(); | ||||
|  | ||||
|  | ||||
|   //arduinomorse | ||||
|   //sender.continueSending(); | ||||
|  | ||||
|   // See which message we are sending | ||||
|   // Only do this when the message has been updated. | ||||
|   if(yourInputMsg != yourInputMsg_old){ | ||||
|     //morseToSend.Stop(JLed::eStopMode::FULL_OFF).Update(); | ||||
|     if(yourInputMsg == 0){ | ||||
|     morseToSend = morseTEST; | ||||
|     morseToSend_blink = morseTEST_blink; | ||||
|   } else if(yourInputMsg == 1){ | ||||
|     morseToSend = morseMOE; | ||||
|     morseToSend_blink = morseMOE_blink; | ||||
|   } else if(yourInputMsg == 2){ | ||||
|     morseToSend = morseMOI; | ||||
|     morseToSend_blink = morseMOI_blink; | ||||
|   } else if(yourInputMsg == 3){ | ||||
|     morseToSend = morseMOS; | ||||
|     morseToSend_blink = morseMOS_blink; | ||||
|   } else if(yourInputMsg == 4){ | ||||
|     morseToSend = morseMOH; | ||||
|     morseToSend_blink = morseMOH_blink; | ||||
|   } else if(yourInputMsg == 5){ | ||||
|     morseToSend = morseMO5; | ||||
|     morseToSend_blink = morseMO5_blink; | ||||
|   } | ||||
|     // Keeps the key from locking up | ||||
|     yourInputMsg_old = yourInputMsg; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   // if you want to send continuous code, and it's not sending, then start it up | ||||
|   if((yourInputSend == 1) & (morseToSend.IsRunning() == false)){ | ||||
|     //jled | ||||
|     morseToSend.Reset().Update(); | ||||
|     morseToSend_blink.Reset().Update(); | ||||
|     //morse.send("CQ CQ CQ DE W1CDN K"); //etherkit morse | ||||
|     //telegraph26.send("CQ CQ CQ DE W1CDN K"); //telegraph | ||||
|  | ||||
|   // if you want to send continuous code, and it is sending, keep sending | ||||
|   } else if((yourInputSend == 1) & (morseToSend.IsRunning() == true)){ | ||||
|     morseToSend.Update(); | ||||
|     morseToSend_blink.Update(); | ||||
|    | ||||
|   // if you want to send cycle code and it is sending, keep sending | ||||
|   } else if((yourInputSend == 2) & (morseToSend.IsRunning() == true)){ | ||||
|     morseToSend.Update(); | ||||
|     morseToSend_blink.Update(); | ||||
|  | ||||
|   // if you want to send cycle code and it's not sending, then start it up | ||||
|   } else if((yourInputSend == 2) & (morseToSend.IsRunning() == true)){ | ||||
|     morseToSend.Reset().Update(); | ||||
|     morseToSend_blink.Reset().Update(); | ||||
|        | ||||
|   // if you don't want to send code | ||||
|   } else { | ||||
|     // stop sending and make sure the pin is off | ||||
|     morseToSend.Stop(JLed::eStopMode::FULL_OFF).Update(); | ||||
|     morseToSend_blink.Stop(JLed::eStopMode::FULL_OFF).Update(); | ||||
|   } | ||||
|   //morseToSend.Update(); | ||||
|  | ||||
|    | ||||
|    | ||||
|   // Blink LED according to seconds entered | ||||
|   // if (yourInputInt > 0) { | ||||
|   //     Serial.println("GPIO 26 on"); | ||||
|   //     output26State = "on"; | ||||
|   //     digitalWrite(output26, HIGH); | ||||
|   //     delay(yourInputInt * 1000); | ||||
|   //     Serial.println(yourInputInt); | ||||
|   //     Serial.println("GPIO 26 off"); | ||||
|   //     output26State = "off"; | ||||
|   //     digitalWrite(output26, LOW); | ||||
|   //     delay(yourInputInt * 1000); | ||||
|   // } else { | ||||
|   //     output26State = "off"; | ||||
|   // if(yourInputMsg != yourInputMsg_old){ | ||||
|   //     if(yourInputMsg == 0){ | ||||
|   //       sender.setMessage(String("test test test de w1cdn ")); | ||||
|   //     } else if(yourInputMsg == 1){ | ||||
|   //       sender.setMessage(String("moe ")); | ||||
|   //     } else if(yourInputMsg == 2){ | ||||
|   //       sender.setMessage(String("moi ")); | ||||
|   //     } else if(yourInputMsg == 3){ | ||||
|   //       sender.setMessage(String("mos ")); | ||||
|   //     } else if(yourInputMsg == 4){ | ||||
|   //       sender.setMessage(String("moh ")); | ||||
|   //     } else if(yourInputMsg == 5){ | ||||
|   //       sender.setMessage(String("mo5 ")); | ||||
|   //     } | ||||
|   //   // Keeps the key/led from locking up | ||||
|   //   yourInputMsg_old = yourInputMsg; | ||||
|   // } | ||||
|  | ||||
|   // This statement from https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-RTClib-Adafruit-Alarm-Poll-alarmFired/DS3231-RTClib-Adafruit-Alarm-Poll-alarmFired.ino | ||||
|   // Check if alarm by polling SQW alarm pin | ||||
|   if((yourInputSend == 2) & (digitalRead(alarmPin) == LOW)) { | ||||
|     // Print current time and date | ||||
|     DateTime now = rtc.now(); // Get the current time | ||||
|     char buff[] = "Alarm triggered at hh:mm:ss DDD, DD MMM YYYY"; | ||||
|     Serial.println(now.toString(buff)); | ||||
|     startProgram = true; | ||||
|  | ||||
|     // Disable and clear alarm | ||||
|     rtc.clearAlarm(1);  | ||||
|     rtc.clearAlarm(2); // clear the other one just in case  | ||||
|   } | ||||
|  | ||||
|   // Once alarm has started the program, set things up to run | ||||
|   if(startProgram == true){ | ||||
|     //Serial.println("Start sending"); | ||||
|     start_millis = millis() + ((yourInputCycleID - 1) * yourInputStepLength); | ||||
|     stop_millis = start_millis + yourInputStepLength; | ||||
|     if(yourInputCycleID == 1){ | ||||
|       pause_until_millis = stop_millis + (yourInputStepLength * (yourInputNtransmitters - 1));  | ||||
|     } else { | ||||
|     // Subtract 2 rather than 1 here to account for start_millis duration at beginning of repeat. | ||||
|       pause_until_millis = stop_millis + (yourInputStepLength * (yourInputNtransmitters - 2));  | ||||
|     } | ||||
|     //sender.startSending(); | ||||
|     programRunning = true; | ||||
|     startProgram = false; | ||||
|   } | ||||
|  | ||||
|   // if you want to send continuous code, and it's not sending, then start it up | ||||
|   if((yourInputSend == 1)){ | ||||
|     if (!sender.continueSending()){ | ||||
|           // Set the internal counters to the message's beginning. | ||||
|           // Here, this results in repeating the message indefinitely. | ||||
|           sender.startSending(); | ||||
|           } | ||||
|   // if you want to send cycle code and it's not sending, then start it up | ||||
|   } else if((yourInputSend == 2) & (programRunning == true)){ | ||||
|       if((millis() < start_millis)){ | ||||
|         // Shut the pin off manually | ||||
|         digitalWrite(blinker, LOW); | ||||
|       } else if((millis() >= start_millis) & (millis() <= stop_millis)){ | ||||
|         if (!sender.continueSending()){ | ||||
|           // Set the internal counters to the message's beginning. | ||||
|           // Here, this results in repeating the message indefinitely. | ||||
|           sender.startSending(); | ||||
|           } | ||||
|       } else if((millis() >= stop_millis) & (millis() <= pause_until_millis)){ | ||||
|         // do nothing in this case --  in between cycles | ||||
|         // Shut the pin off manually | ||||
|         digitalWrite(blinker, LOW); | ||||
|       } else if((millis() >= pause_until_millis)){ | ||||
|         startProgram = true; | ||||
|       } | ||||
|   // if the cycle program is not running | ||||
|   } else if((yourInputSend == 2) & (programRunning == false)){ | ||||
|     // do we need something here? | ||||
|   // if you don't want to send code | ||||
|   } else if(yourInputSend == 0){ | ||||
|     //sender.setMessage(String("")) ; // Not sure this is the right way to stop things. | ||||
|     // Shut the pin off manually | ||||
|     digitalWrite(blinker, LOW); | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										232
									
								
								vulpes/src/morse.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										232
									
								
								vulpes/src/morse.cpp
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,232 @@ | ||||
| // Morse Code sending library | ||||
|  | ||||
| #include <morse.h> | ||||
|  | ||||
| // MorseSender | ||||
| int MorseSender::copyTimings( | ||||
| 	morseTiming_t *rawOut, | ||||
| 	morseBitmask_t definition) | ||||
| { | ||||
| 	int t = 0; | ||||
| 	boolean foundSentinel = false; | ||||
| 	for(morseBitmask_t mask = MORSE_BITMASK_HIGH_BIT; | ||||
| 		mask > 0; mask = mask >> 1) | ||||
| 	{ | ||||
| 		boolean isDah = (mask & definition) > 0; | ||||
| 		if(!foundSentinel) | ||||
| 		{ | ||||
| 			if (isDah) { foundSentinel = true; } | ||||
| 			continue; | ||||
| 		} | ||||
| 		rawOut[2*t] = isDah ? DAH : DIT; | ||||
| 		rawOut[2*t + 1] = DIT; | ||||
| 		t++; | ||||
| 	} | ||||
| 	return t; | ||||
| } | ||||
| unsigned int MorseSender::fillTimings(char c) | ||||
| { | ||||
| 	int t = 0; | ||||
| 	unsigned int start = 0; | ||||
| 	if (c >= 'a' && c <= 'z') | ||||
| 	{ | ||||
| 		t = copyTimings(timingBuffer, MORSE_LETTERS[c-'a']); | ||||
| 	} | ||||
| 	else if (c >= '0' && c <= '9') | ||||
| 	{ | ||||
| 		int n = c - '0'; | ||||
| 		boolean ditsFirst = (n <= 5); | ||||
| 		if (!ditsFirst) | ||||
| 		{ | ||||
| 			n -= 5; | ||||
| 		} | ||||
| 		while(t < 5) | ||||
| 		{ | ||||
| 			timingBuffer[2*t] = ((t < n) == ditsFirst) ? DIT : DAH; | ||||
| 			timingBuffer[2*t + 1] = DIT; | ||||
| 			t++; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		int s = 0; | ||||
| 		while(MORSE_PUNCT_ETC[s].c != END) | ||||
| 		{ | ||||
| 			if(MORSE_PUNCT_ETC[s].c == c) | ||||
| 			{ | ||||
| 				t = copyTimings(timingBuffer, | ||||
| 					MORSE_PUNCT_ETC[s].timing); | ||||
| 				break; | ||||
| 			} | ||||
| 			s++; | ||||
| 		} | ||||
| 		if (MORSE_PUNCT_ETC[s].c == END) | ||||
| 		{ | ||||
| 			start = t = 1; // start on a space | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	timingBuffer[2*t - 1] = DAH; | ||||
| 	timingBuffer[2*t] = END; | ||||
|  | ||||
| 	/* | ||||
| 	Serial.print("Refilled timing buffer for '"); | ||||
| 	Serial.print(c); | ||||
| 	Serial.print("': "); | ||||
| 	int i = start; | ||||
| 	while(timingBuffer[i] != END) | ||||
| 	{ | ||||
| 		Serial.print((int)timingBuffer[i]); | ||||
| 		Serial.print(", "); | ||||
| 		i++; | ||||
| 	} | ||||
| 	Serial.println("END"); | ||||
| 	*/ | ||||
|  | ||||
| 	return start; | ||||
| } | ||||
|  | ||||
| // see note in header about pure-virtual-ness | ||||
| void MorseSender::setOn() {}; | ||||
| void MorseSender::setOff() {}; | ||||
|  | ||||
| // noop defaults | ||||
| void MorseSender::setReady() {}; | ||||
| void MorseSender::setComplete() {}; | ||||
|  | ||||
| MorseSender::MorseSender(unsigned int outputPin, float wpm) : | ||||
| 	pin(outputPin) | ||||
| { | ||||
| 	setWPM(wpm); | ||||
| } | ||||
|  | ||||
| void MorseSender::setup() { pinMode(pin, OUTPUT); } | ||||
|  | ||||
| void MorseSender::setWPM(float wpm) | ||||
| { | ||||
| 	setSpeed((morseTiming_t)(1000.0*60.0/(max(1.0f, wpm)*DITS_PER_WORD))); | ||||
| } | ||||
|  | ||||
| void MorseSender::setSpeed(morseTiming_t duration) | ||||
| { | ||||
| 	DIT = max(duration, (morseTiming_t) 1); | ||||
| 	DAH = 3*DIT; | ||||
| } | ||||
|  | ||||
| void MorseSender::setMessage(const String newMessage) | ||||
| { | ||||
| 	message = newMessage; | ||||
|  | ||||
| 	// Force startSending() before continueSending(). | ||||
| 	messageIndex = message.length(); | ||||
|  | ||||
| 	// If a different message was in progress, make sure it stops cleanly. | ||||
| 	if (timingIndex % 2 == 0) { | ||||
| 		setOff(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MorseSender::sendBlocking() | ||||
| { | ||||
| 	//Serial.println("Sending blocking: "); | ||||
| 	//Serial.println(message); | ||||
| 	startSending(); | ||||
| 	while(continueSending()); | ||||
| } | ||||
|  | ||||
| void MorseSender::startSending() | ||||
| { | ||||
| 	messageIndex = 0; | ||||
| 	if (message.length() == 0) { return; } | ||||
| 	timingIndex = fillTimings(message[0]); | ||||
| 	setReady(); | ||||
| 	if (timingIndex % 2 == 0) { | ||||
| 		setOn(); | ||||
| 		//Serial.print("Starting with on, duration="); | ||||
| 	} else { | ||||
| 		//Serial.print("Starting with off, duration="); | ||||
| 	} | ||||
| 	lastChangedMillis = millis(); | ||||
| 	//Serial.println((int)timingBuffer[timingIndex]); | ||||
| } | ||||
|  | ||||
| boolean MorseSender::continueSending() | ||||
| { | ||||
| 	if(messageIndex >= message.length()) { return false; } | ||||
|  | ||||
| 	unsigned long elapsedMillis = millis() - lastChangedMillis; | ||||
| 	if (elapsedMillis < timingBuffer[timingIndex]) { return true; } | ||||
|  | ||||
| 	timingIndex++; | ||||
| 	if (timingBuffer[timingIndex] == END) | ||||
| 	{ | ||||
| 		messageIndex++; | ||||
| 		if(messageIndex >= message.length()) { | ||||
| 			setOff(); | ||||
| 			setComplete(); | ||||
| 			return false; | ||||
| 		} | ||||
| 		timingIndex = fillTimings(message[messageIndex]); | ||||
| 	} | ||||
|  | ||||
| 	lastChangedMillis += elapsedMillis; | ||||
| 	//Serial.print("Next is       "); | ||||
| 	if (timingIndex % 2 == 0) { | ||||
| 		//Serial.print("(on) "); | ||||
| 		setOn(); | ||||
| 	} else { | ||||
| 		//Serial.print("(off) "); | ||||
| 		setOff(); | ||||
| 	} | ||||
| 	//Serial.println((int)timingBuffer[timingIndex]); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void *MorseSender::operator new(size_t size) { return malloc(size); } | ||||
| void MorseSender::operator delete(void* ptr) { if (ptr) free(ptr); } | ||||
|  | ||||
|  | ||||
| // SpeakerMorseSender | ||||
|  | ||||
| // void SpeakerMorseSender::setOn() { tone(pin, frequency); } | ||||
| // void SpeakerMorseSender::setOff() { | ||||
| // 	if (carrFrequency == CARRIER_FREQUENCY_NONE) { | ||||
| // 		noTone(pin); | ||||
| // 	} else { | ||||
| // 		tone(pin, carrFrequency); | ||||
| // 	} | ||||
| // } | ||||
| // void SpeakerMorseSender::setReady() { setOff(); } | ||||
| // void SpeakerMorseSender::setComplete() { noTone(pin); } | ||||
| // SpeakerMorseSender::SpeakerMorseSender( | ||||
| // 	int outputPin, | ||||
| // 	unsigned int toneFrequency, | ||||
| // 	unsigned int carrierFrequency, | ||||
| // 	float wpm) | ||||
| // 	: MorseSender(outputPin, wpm), | ||||
| // 	frequency(toneFrequency), | ||||
| // 	carrFrequency(carrierFrequency) {}; | ||||
|  | ||||
|  | ||||
| // LEDMorseSender | ||||
|  | ||||
| void LEDMorseSender::setOn() { digitalWrite(pin, activeLow ? LOW : HIGH); } | ||||
| void LEDMorseSender::setOff() { digitalWrite(pin, activeLow ? HIGH : LOW); } | ||||
| LEDMorseSender::LEDMorseSender(int outputPin, bool activeLow, float wpm) | ||||
| 	: MorseSender(outputPin, wpm), activeLow(activeLow) {}; | ||||
| LEDMorseSender::LEDMorseSender(int outputPin, float wpm) | ||||
| 	: MorseSender(outputPin, wpm), activeLow(false) {}; | ||||
|  | ||||
| // PWMMorseSender | ||||
|  | ||||
| void PWMMorseSender::setOn() { analogWrite(pin, brightness); } | ||||
| void PWMMorseSender::setOff() { analogWrite(pin, 0); } | ||||
| void PWMMorseSender::setBrightness(byte bright) { | ||||
| 	brightness = bright; | ||||
| } | ||||
| PWMMorseSender::PWMMorseSender( | ||||
| 		int outputPin, | ||||
| 		float wpm, | ||||
| 		byte bright) | ||||
| 	: MorseSender(outputPin, wpm), brightness(bright) {}; | ||||
							
								
								
									
										276
									
								
								vulpes/src/morse.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										276
									
								
								vulpes/src/morse.h
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,276 @@ | ||||
| #pragma once | ||||
| /** | ||||
|  * Generate and send Morse Code on an LED or a speaker. Allow sending | ||||
|  * in a non-blocking manner (by calling a 'continue sending' method | ||||
|  * every so often to turn an LED on/off, or to call tone/noTone appropriately). | ||||
|  * | ||||
|  * All input should be lowercase. Prosigns (SK, KN, etc) have special | ||||
|  * character values #defined. | ||||
|  * | ||||
|  * See also: | ||||
|  * Morse decoder (using binary tree): | ||||
|  * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289074596/15 | ||||
|  * Generator (on playground): | ||||
|  * http://www.arduino.cc/playground/Code/Morse | ||||
|  */ | ||||
|  | ||||
| // for malloc and free, for the new/delete operators | ||||
| #include <stdlib.h> | ||||
| #include <analogWrite.h> | ||||
|  | ||||
| // Arduino language types | ||||
| #if defined(ARDUINO) && ARDUINO >= 100 | ||||
| #include "Arduino.h" | ||||
| #else | ||||
| #include "WProgram.h" | ||||
| #endif | ||||
|  | ||||
| #define WPM_DEFAULT	12.0 | ||||
| // PARIS WPM measurement: 50; CODEX WPM measurement: 60 (Wikipedia:Morse_code) | ||||
| #define DITS_PER_WORD	50 | ||||
| // Pass to SpeakerMorseSender as carrierFrequency to suppress the carrier. | ||||
| #define CARRIER_FREQUENCY_NONE	0 | ||||
|  | ||||
| // Bitmasks are 1 for dah and 0 for dit, in left-to-right order; | ||||
| //	the sequence proper begins after the first 1 (a sentinel). | ||||
| //	Credit for this scheme to Mark VandeWettering K6HX ( brainwagon.org ). | ||||
| typedef unsigned int             morseTiming_t; | ||||
| typedef unsigned char	morseBitmask_t; // see also MAX_TIMINGS | ||||
| #define MORSE_BITMASK_HIGH_BIT	B10000000 | ||||
|  | ||||
| // sentinel | ||||
| #define END             0 | ||||
|  | ||||
| // the most timing numbers any unit will need; ex: k = on,off,on,off,on,end = 5 | ||||
| #define MAX_TIMINGS     15 | ||||
|  | ||||
| // Punctuation and Prosigns | ||||
| #define PROSIGN_SK	'S' | ||||
| #define PROSIGN_KN	'K' | ||||
| #define PROSIGN_BT	'B' | ||||
| typedef struct { | ||||
| 	char c; | ||||
| 	morseBitmask_t timing; | ||||
| } specialTiming; | ||||
| const specialTiming MORSE_PUNCT_ETC[] = { | ||||
| 	{'.',		B1010101}, | ||||
| 	{'?',		B1001100}, | ||||
| 	{'/',		B110010}, | ||||
| 	{PROSIGN_SK,	B1000101}, | ||||
| 	{PROSIGN_KN,	B110110}, | ||||
| 	{PROSIGN_BT,	B110001}, | ||||
| 	{END,		B1}, | ||||
| }; | ||||
|  | ||||
| // Morse Code (explicit declaration of letter timings) | ||||
| const morseBitmask_t MORSE_LETTERS[26] = { | ||||
| 	/* a */ B101, | ||||
| 	/* b */ B11000, | ||||
| 	/* c */ B11010, | ||||
| 	/* d */ B1100, | ||||
| 	/* e */ B10, | ||||
| 	/* f */ B10010, | ||||
| 	/* g */ B1110, | ||||
| 	/* h */ B10000, | ||||
| 	/* i */ B100, | ||||
| 	/* j */ B10111, | ||||
| 	/* k */ B1101, | ||||
| 	/* l */ B10100, | ||||
| 	/* m */ B111, | ||||
| 	/* n */ B110, | ||||
| 	/* o */ B1111, | ||||
| 	/* p */ B10110, | ||||
| 	/* q */ B11101, | ||||
| 	/* r */ B1010, | ||||
| 	/* s */ B1000, | ||||
| 	/* t */ B11, | ||||
| 	/* u */ B1001, | ||||
| 	/* v */ B10001, | ||||
| 	/* w */ B1011, | ||||
| 	/* x */ B11001, | ||||
| 	/* y */ B11011, | ||||
| 	/* z */ B11100, | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Define the logic of converting characters to on/off timing, | ||||
|  * and encapsulate the state of one sending-in-progress Morse message. | ||||
|  * | ||||
|  * Subclasses define setOn and setOff for (for example) LED and speaker output. | ||||
|  */ | ||||
| class MorseSender { | ||||
| protected: | ||||
| 	const unsigned int pin; | ||||
| 	// The setOn and setOff methods would be pure virtual, | ||||
| 	// but that has compiler issues. | ||||
| 	// See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1167672075 . | ||||
|  | ||||
| 	/** | ||||
| 	 * Called to set put the output in 'on' state, during a dit or dah. | ||||
| 	 */ | ||||
| 	virtual void setOn(); | ||||
| 	virtual void setOff(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Called before sending a message. Used for example to enable a | ||||
| 	 * carrier. (Noop in the base class.) | ||||
| 	 */ | ||||
| 	virtual void setReady(); | ||||
| 	virtual void setComplete(); | ||||
|  | ||||
| private: | ||||
| 	morseTiming_t DIT, DAH; | ||||
| 	String message; | ||||
|  | ||||
| 	// on,off,...,wait,0 list, millis | ||||
| 	morseTiming_t timingBuffer[MAX_TIMINGS+1]; | ||||
|  | ||||
| 	// index of the character currently being sent | ||||
| 	unsigned int messageIndex; | ||||
| 	// timing unit currently being sent | ||||
| 	unsigned int timingIndex; | ||||
|  | ||||
| 	// when this timing unit was started | ||||
| 	unsigned long lastChangedMillis; | ||||
|  | ||||
| 	/** | ||||
| 	 * Copy definition timings (on only) to raw timings (on/off). | ||||
| 	 * @return the number of 'on' timings copied | ||||
| 	 */ | ||||
| 	int copyTimings(morseTiming_t *rawOut, | ||||
| 		morseBitmask_t definition); | ||||
|  | ||||
| 	/** | ||||
| 	 * Fill a buffer with on,off,..,END timings (millis) | ||||
| 	 * @return the index at which to start within the new timing sequence | ||||
| 	 */ | ||||
| 	unsigned int fillTimings(char c); | ||||
|  | ||||
| public: | ||||
| 	/** | ||||
| 	 * Create a sender which will output to the given pin. | ||||
| 	 */ | ||||
| 	MorseSender(unsigned int outputPin, float wpm=WPM_DEFAULT); | ||||
|  | ||||
| 	/** | ||||
| 	 * To be called during the Arduino setup(); set the pin as OUTPUT. | ||||
| 	 */ | ||||
| 	void setup(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Set the words per minute (based on PARIS timing). | ||||
| 	 */ | ||||
| 	void setWPM(float wpm); | ||||
|  | ||||
| 	/** | ||||
| 	 * Set the duration, in milliseconds, of a DIT. | ||||
| 	 */ | ||||
| 	void setSpeed(morseTiming_t duration); | ||||
|  | ||||
| 	/** | ||||
| 	 * Set the message to be sent. | ||||
| 	 * This halts any sending in progress. | ||||
| 	 */ | ||||
| 	void setMessage(const String newMessage); | ||||
|  | ||||
| 	/** | ||||
| 	 * Send the entirety of the current message before returning. See the "simple" | ||||
| 	 * example, which uses sendBlocking to send one message. | ||||
| 	 */ | ||||
| 	void sendBlocking(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Prepare to send and begin sending the current message. After calling this, | ||||
| 	 * call continueSending repeatedly until it returns false to finish sending | ||||
| 	 * the message. See the "speeds" example, which calls startSending and | ||||
| 	 * continueSending on two different senders. | ||||
| 	 */ | ||||
| 	void startSending(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Switch outputs on and off (and refill the internal timing buffer) | ||||
| 	 * as necessary to continue with the sending of the current message. | ||||
| 	 * This should be called every few milliseconds (at a significantly | ||||
| 	 * smaller interval than a DIT) to produce a legible fist. | ||||
| 	 * | ||||
| 	 * @see startSending, which must be called first | ||||
| 	 * @return false if sending is complete, otherwise true (keep sending) | ||||
| 	 */ | ||||
| 	boolean continueSending(); | ||||
|  | ||||
| 	void *operator new(size_t size); | ||||
| 	void operator delete(void* ptr); | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Adapt Morse sending to use the Arduino language tone() and noTone() | ||||
|  * functions, for use with a speaker. | ||||
|  * | ||||
|  * If a carrierFrequency is given, instead of calling noTone, call tone | ||||
|  * with a low frequency. This is useful ex. for maintaining radio links. | ||||
|  */ | ||||
| class SpeakerMorseSender: public MorseSender { | ||||
| 	private: | ||||
| 		unsigned int frequency; | ||||
| 		unsigned int carrFrequency; | ||||
| 	protected: | ||||
| 		virtual void setOn(); | ||||
| 		virtual void setOff(); | ||||
| 		virtual void setReady(); | ||||
| 		virtual void setComplete(); | ||||
| 	public: | ||||
| 		// concert A = 440 | ||||
| 		// middle C = 261.626; higher octaves = 523.251, 1046.502 | ||||
| 		SpeakerMorseSender( | ||||
| 			int outputPin, | ||||
| 			unsigned int toneFrequency=1046, | ||||
| 			unsigned int carrierFrequency=CARRIER_FREQUENCY_NONE, | ||||
| 			float wpm=WPM_DEFAULT); | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Sends Morse on a digital output pin. | ||||
|  */ | ||||
| class LEDMorseSender: public MorseSender { | ||||
| 	private: | ||||
| 		bool activeLow; | ||||
| 	protected: | ||||
| 		virtual void setOn(); | ||||
| 		virtual void setOff(); | ||||
| 	public: | ||||
| 		/** | ||||
| 		 * Creates a LED Morse code sender with the given GPIO pin.  The optional | ||||
| 		 * boolean activeLow indicates LED is ON with digital LOW value. | ||||
| 		 * @param outputPin GPIO pin number | ||||
| 		 * @param activeLow set to true to indicate the LED ON with digital LOW value.  default: false | ||||
| 		 * @param wpm words per minute, default: WPM_DEFAULT | ||||
| 		 */ | ||||
| 		LEDMorseSender(int outputPin, bool activeLow = false, float wpm=WPM_DEFAULT); | ||||
|  | ||||
| 		/** | ||||
| 		 * Creates a LED Morse code sender with the given GPIO pin.  This constructor is for backward compability. | ||||
| 		 * @param outputPin GPIO pin number | ||||
| 		 * @param wpm words per minute | ||||
| 		 */ | ||||
| 		LEDMorseSender(int outputPin, float wpm); | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Sends Morse on an analog output pin (using PWM). The brightness value is | ||||
|  * between 0 and 255 and is passed directly to analogWrite. | ||||
|  */ | ||||
| class PWMMorseSender: public MorseSender { | ||||
| 	private: | ||||
| 		byte brightness; | ||||
| 	protected: | ||||
| 		virtual void setOn(); | ||||
| 		virtual void setOff(); | ||||
| 	public: | ||||
| 		PWMMorseSender(int outputPin, float wpm=WPM_DEFAULT, byte brightness=255); | ||||
| 		void setBrightness(byte brightness); | ||||
| }; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user