Use arduinomorse instead of Jled #38
| @ -10,9 +10,6 @@ | ||||
|  | ||||
| [env:esp32doit-devkit-v1] | ||||
| platform = espressif32 | ||||
| ;build_flags = | ||||
| ;	-std=c++11 | ||||
| ;	-std=gnu++11 | ||||
| board = esp32doit-devkit-v1 | ||||
| framework = arduino | ||||
| upload_speed = 921600 | ||||
| @ -23,10 +20,7 @@ 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.1 | ||||
| 	;adafruit/RTClib@^2.1.1 | ||||
| 	https://github.com/adafruit/RTClib.git ; >=2.1.2 | ||||
| 	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,9 +16,7 @@ | ||||
| #include <AsyncTCP.h> | ||||
| #include <SPIFFS.h> | ||||
| #include <Preferences.h> | ||||
| #include <arduino-timer.h> | ||||
| #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 <string> | ||||
| @ -28,6 +26,10 @@ | ||||
|  | ||||
| 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 | ||||
|  | ||||
| @ -61,9 +63,12 @@ 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 | ||||
| //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( | ||||
| @ -166,21 +171,6 @@ const char index_html[] PROGMEM = R"rawliteral( | ||||
|   </script> | ||||
| </body></html>)rawliteral"; | ||||
|  | ||||
| // 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; | ||||
|  | ||||
| // Example from https://github.com/contrem/arduino-timer#examples | ||||
| bool toggle_led(void *) { | ||||
|   //Serial.print("Timer time: "); | ||||
|   //Serial.println(rtc.now().timestamp()); | ||||
|   digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // toggle the LED | ||||
|   return true; // keep timer active? true | ||||
| } | ||||
|  | ||||
| void notFound(AsyncWebServerRequest *request) { | ||||
|   request->send(404, "text/plain", "Not found"); | ||||
| @ -255,24 +245,10 @@ String processor(const String& var){ | ||||
|   return String(); | ||||
| } | ||||
|  | ||||
| //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. | ||||
| @ -280,97 +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. | ||||
| // Extra space at the end to get around https://github.com/jandelgado/jled/issues/122 on cycle mode | ||||
| 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 | ||||
|  | ||||
| // Cycle stuff | ||||
| auto morse_cycle = morseEffectMOS; | ||||
| int period = morse_cycle.Period() + word_space_ms; | ||||
| int repeats = step_length / period; | ||||
| int remainder_wait = step_length - (period * repeats); | ||||
| int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait)); | ||||
| auto blinker_continuous = JLed(blinker).UserFunc(&morse_cycle).Repeat(repeats).DelayAfter(word_space_ms); | ||||
| auto blinker_continuous_wait = JLed(blinker).Off(total_wait); | ||||
| JLed morses_blink[] = { | ||||
|     blinker_continuous, | ||||
|     blinker_continuous_wait | ||||
| }; | ||||
| auto morses_sequence_blink = JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| JLedSequence* sequence = NULL; | ||||
| JLedSequence* make_sequence(JLedSequence* seq, const char* message, int wpm, int step_length, int n_transmitters){ | ||||
|   int ms_per_dit = 60;//1000 * (60 / (50 * wpm)); | ||||
|   int word_space_ms = ms_per_dit * 7; | ||||
|   MorseEffect morse_effect(message, ms_per_dit); | ||||
|   int period = morse_effect.Period(); | ||||
|   int repeats = 2;//step_length / period; | ||||
|   int remainder_wait = step_length - (period * repeats); | ||||
|   int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait)); | ||||
|   Serial.print("total_wait: "); Serial.println(total_wait); | ||||
|   JLed morses_blink[] = { | ||||
|     JLed(blinker).UserFunc(&morse_effect).Repeat(repeats).DelayAfter(word_space_ms), | ||||
|     JLed(blinker).Off(total_wait) | ||||
|   }; | ||||
|   if (seq){ | ||||
|     delete seq; | ||||
|     //seq = new JLedSequence(JLedSequence::eMode::SEQUENCE, leds); | ||||
|     seq = new JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink); | ||||
|   } | ||||
|   return seq; | ||||
| } | ||||
| // Initial definition of the sequence | ||||
| JLedSequence* morses_sequence_blink_test = make_sequence(sequence, "MOE", 10, 10000, 2); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| //================================================================================ | ||||
| // start_program(): a function to start the planned program at the planned time | ||||
| //================================================================================ | ||||
| // bool start_program(){ | ||||
| //   Serial.println("The scheduled program has started."); | ||||
| //   startProgram = true; | ||||
| //   return false; | ||||
| // } | ||||
|  | ||||
| //================================================================================ | ||||
| // setup(): stuff that only gets done once, after power up (KB1OIQ's description) | ||||
| @ -378,7 +263,8 @@ JLedSequence* morses_sequence_blink_test = make_sequence(sequence, "MOE", 10, 10 | ||||
| void setup() { | ||||
|   Serial.begin(115200); | ||||
|  | ||||
|    | ||||
|   // Get arduinomorse ready to go | ||||
|   sender.setup(); | ||||
|  | ||||
|   pinMode(alarmPin, INPUT_PULLUP); // Set alarm pin as pullup | ||||
|  | ||||
| @ -411,10 +297,10 @@ void setup() { | ||||
|  | ||||
|   // 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); | ||||
| @ -441,23 +327,17 @@ void setup() { | ||||
|   // 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); | ||||
| @ -490,10 +370,11 @@ void setup() { | ||||
|       writeFile(SPIFFS, "/inputSend.txt", inputMessage.c_str()); | ||||
|       yourInputSend = inputMessage.toInt(); | ||||
|       // if not running a program, set the program running off | ||||
|       if(yourInputSend != 2){ | ||||
|       //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)) { | ||||
| @ -508,6 +389,21 @@ 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)) { | ||||
| @ -582,13 +478,6 @@ void setup() { | ||||
|         yourInputStartTimeUnix = atol(inputMessage.c_str()); | ||||
|       //} | ||||
|       Serial.println(yourInputStartTimeUnix); | ||||
|  | ||||
|       // We can't use arduino-timer for starting a program because  | ||||
|       // it relies on millis(), which reset on power cycle. | ||||
|       // timer.at(millis() + 10000, toggle_led); | ||||
|       // Serial.println(millis()); | ||||
|       // auto active_tasks = timer.size(); | ||||
|       // Serial.println(active_tasks); | ||||
|        | ||||
|       // Use alarm built into RTC | ||||
|       rtc.setAlarm1(DateTime(yourInputStartTimeUnix), DS3231_A1_Date); | ||||
| @ -607,37 +496,26 @@ void setup() { | ||||
| } | ||||
|  | ||||
| void loop() { | ||||
|     // Timers | ||||
|   timer.tick(); | ||||
|  | ||||
|   morses_sequence_blink_test->Forever().Update(); | ||||
|  | ||||
| // See which message we are sending | ||||
|   // 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/led from locking up | ||||
|     yourInputMsg_old = yourInputMsg; | ||||
|   } | ||||
|   // 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 | ||||
| @ -655,58 +533,53 @@ void loop() { | ||||
|  | ||||
|   // Once alarm has started the program, set things up to run | ||||
|   if(startProgram == true){ | ||||
|     //auto morse_cycle = morseEffectMOS; | ||||
|     //int period = morse_cycle.Period() + word_space_ms; | ||||
|     //int repeats = step_length / period; | ||||
|     //int remainder_wait = step_length - (period * repeats); | ||||
|     //int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait)); | ||||
|  | ||||
|     // Nothing makes it out of this scope... | ||||
|     // blinker_continuous = JLed(blinker).UserFunc(&morse_cycle).Repeat(repeats).DelayAfter(word_space_ms); | ||||
|     // blinker_continuous_wait = JLed(blinker).Off(total_wait); | ||||
|     // JLed morses_blink[] = { | ||||
|     //     blinker_continuous, | ||||
|     //     blinker_continuous_wait | ||||
|     // }; | ||||
|     // auto morses_sequence_blink = JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink); | ||||
|  | ||||
|     morses_sequence_blink.Forever().Update(); | ||||
|     //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) & (morseToSend.IsRunning() == false)){ | ||||
|     //jled | ||||
|     morseToSend.Reset().Update(); | ||||
|     morseToSend_blink.Reset().Update(); | ||||
|  | ||||
|   // 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) & (programRunning == true) &(morses_sequence_blink.Update() == true)){ | ||||
|     morseToSend.Update(); | ||||
|     //morseToSend_blink.Update(); | ||||
|     morses_sequence_blink.Update(); | ||||
|  | ||||
|   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) & (morses_sequence_blink.Update() == false)){ | ||||
|     morseToSend.Reset().Update(); | ||||
|     //morseToSend_blink.Reset().Update(); | ||||
|     morses_sequence_blink.Reset(); | ||||
|   } 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)){ | ||||
|     morses_sequence_blink.Stop(); | ||||
|     // do we need something here? | ||||
|   // 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(); | ||||
|     morses_sequence_blink.Stop(); | ||||
|   } 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); | ||||
|   } | ||||
|   //morseToSend.Update(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										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