Use arduinomorse instead of Jled #38
| @ -10,9 +10,6 @@ | |||||||
|  |  | ||||||
| [env:esp32doit-devkit-v1] | [env:esp32doit-devkit-v1] | ||||||
| platform = espressif32 | platform = espressif32 | ||||||
| ;build_flags = |  | ||||||
| ;	-std=c++11 |  | ||||||
| ;	-std=gnu++11 |  | ||||||
| board = esp32doit-devkit-v1 | board = esp32doit-devkit-v1 | ||||||
| framework = arduino | framework = arduino | ||||||
| upload_speed = 921600 | upload_speed = 921600 | ||||||
| @ -23,10 +20,7 @@ lib_deps = | |||||||
| 	me-no-dev/AsyncTCP@^1.1.1 | 	me-no-dev/AsyncTCP@^1.1.1 | ||||||
| 	me-no-dev/ESP Async WebServer@^1.2.3 | 	me-no-dev/ESP Async WebServer@^1.2.3 | ||||||
| 	contrem/arduino-timer@^3.0.1 | 	contrem/arduino-timer@^3.0.1 | ||||||
| 	;kj7rrv/Telegraph@^1.0.0 |  | ||||||
| 	jandelgado/JLed@^4.13.1 | 	jandelgado/JLed@^4.13.1 | ||||||
| 	;adafruit/RTClib@^2.1.1 | 	https://github.com/adafruit/RTClib.git | ||||||
| 	https://github.com/adafruit/RTClib.git ; >=2.1.2 |  | ||||||
| 	adafruit/Adafruit BusIO@^1.14.3 | 	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 <AsyncTCP.h> | ||||||
| #include <SPIFFS.h> | #include <SPIFFS.h> | ||||||
| #include <Preferences.h> | #include <Preferences.h> | ||||||
| #include <arduino-timer.h> | #include "morse.h" | ||||||
| #include <jled.h> // jled |  | ||||||
| #include "jled/morse.h" //jled |  | ||||||
| #include <Adafruit_BusIO_Register.h> // for DS3231 | #include <Adafruit_BusIO_Register.h> // for DS3231 | ||||||
| #include <RTClib.h> // for DS3231 | #include <RTClib.h> // for DS3231 | ||||||
| #include <string> | #include <string> | ||||||
| @ -28,6 +26,10 @@ | |||||||
|  |  | ||||||
| AsyncWebServer server(80); | 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 | RTC_DS3231 rtc; // set up RTC | ||||||
| const int alarmPin = 4; // pin to monitor for RTC alarms | const int alarmPin = 4; // pin to monitor for RTC alarms | ||||||
|  |  | ||||||
| @ -61,9 +63,12 @@ bool programRunning; | |||||||
| int yourInputStepLength; | int yourInputStepLength; | ||||||
| int yourInputCycleID; | int yourInputCycleID; | ||||||
| int yourInputNtransmitters; | int yourInputNtransmitters; | ||||||
| int step_length = 10000; // 10 secs | //int step_length = 10000; // 10 secs | ||||||
| int cycle_id = 1; // number of this transmitter in cycle | //int cycle_id = 1; // number of this transmitter in cycle | ||||||
| int n_transmitters = 2; //number of transmitters total | //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) | // HTML web page to handle 3 input fields (inputString, inputSend, inputFloat) | ||||||
| const char index_html[] PROGMEM = R"rawliteral( | const char index_html[] PROGMEM = R"rawliteral( | ||||||
| @ -166,21 +171,6 @@ const char index_html[] PROGMEM = R"rawliteral( | |||||||
|   </script> |   </script> | ||||||
| </body></html>)rawliteral"; | </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) { | void notFound(AsyncWebServerRequest *request) { | ||||||
|   request->send(404, "text/plain", "Not found"); |   request->send(404, "text/plain", "Not found"); | ||||||
| @ -255,24 +245,10 @@ String processor(const String& var){ | |||||||
|   return String(); |   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: | // Set up arduinomorse pin and default WPM | ||||||
|     explicit MorseEffect(const char* message, uint16_t speed = 200) | LEDMorseSender sender(blinker, 10.0f) ; // the 'f' makes sure this is a float | ||||||
|         : morse_(message), speed_(speed) {} | // TODO also for keyer once blinker works | ||||||
|  |  | ||||||
|     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_; } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // Speed is milliseconds per dit, which is 1000 * (60 / (50 * WPM)) | // Speed is milliseconds per dit, which is 1000 * (60 / (50 * WPM)) | ||||||
| // 60 is 20 wpm, 120 is 10 wpm, 90 is 15 wpm, etc. | // 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 wpm = 10; | ||||||
| float ms_per_dit = 1000 * (60 / (50 * wpm)); | float ms_per_dit = 1000 * (60 / (50 * wpm)); | ||||||
| int word_space_ms = ms_per_dit * 7; | 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) | // 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() { | void setup() { | ||||||
|   Serial.begin(115200); |   Serial.begin(115200); | ||||||
|  |  | ||||||
|    |   // Get arduinomorse ready to go | ||||||
|  |   sender.setup(); | ||||||
|  |  | ||||||
|   pinMode(alarmPin, INPUT_PULLUP); // Set alarm pin as pullup |   pinMode(alarmPin, INPUT_PULLUP); // Set alarm pin as pullup | ||||||
|  |  | ||||||
| @ -411,10 +297,10 @@ void setup() { | |||||||
|  |  | ||||||
|   // Initialize the output variables as outputs |   // Initialize the output variables as outputs | ||||||
|   pinMode(keyer, OUTPUT); |   pinMode(keyer, OUTPUT); | ||||||
|   pinMode(blinker, OUTPUT); |   //pinMode(blinker, OUTPUT); | ||||||
|   // Set outputs to LOW |   // Set outputs to LOW | ||||||
|   digitalWrite(keyer, LOW); |   digitalWrite(keyer, LOW); | ||||||
|   digitalWrite(blinker, LOW); |   //digitalWrite(blinker, LOW); | ||||||
|  |  | ||||||
|   // Initialize SPIFFS |   // Initialize SPIFFS | ||||||
|   SPIFFS.begin(true); |   SPIFFS.begin(true); | ||||||
| @ -441,23 +327,17 @@ void setup() { | |||||||
|   // On restart, keep doing what you were doing before |   // On restart, keep doing what you were doing before | ||||||
|   yourInputMsg_old = yourInputMsg; |   yourInputMsg_old = yourInputMsg; | ||||||
|   if(yourInputMsg == 0){ |   if(yourInputMsg == 0){ | ||||||
|     morseToSend = morseTEST; |     sender.setMessage(String("test test test de w1cdn  ")); | ||||||
|     morseToSend_blink = morseTEST_blink; |  | ||||||
|   } else if(yourInputMsg == 1){ |   } else if(yourInputMsg == 1){ | ||||||
|     morseToSend = morseMOE; |     sender.setMessage(String("moe ")); | ||||||
|     morseToSend_blink = morseMOE_blink; |  | ||||||
|   } else if(yourInputMsg == 2){ |   } else if(yourInputMsg == 2){ | ||||||
|     morseToSend = morseMOI; |     sender.setMessage(String("moi ")); | ||||||
|     morseToSend_blink = morseMOI_blink; |  | ||||||
|   } else if(yourInputMsg == 3){ |   } else if(yourInputMsg == 3){ | ||||||
|     morseToSend = morseMOS; |     sender.setMessage(String("mos ")); | ||||||
|     morseToSend_blink = morseMOS_blink; |  | ||||||
|   } else if(yourInputMsg == 4){ |   } else if(yourInputMsg == 4){ | ||||||
|     morseToSend = morseMOH; |     sender.setMessage(String("moh ")); | ||||||
|     morseToSend_blink = morseMOH_blink; |  | ||||||
|   } else if(yourInputMsg == 5){ |   } else if(yourInputMsg == 5){ | ||||||
|     morseToSend = morseMO5; |     sender.setMessage(String("mo5 ")); | ||||||
|     morseToSend_blink = morseMO5_blink; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   WiFi.mode(WIFI_STA); |   WiFi.mode(WIFI_STA); | ||||||
| @ -490,10 +370,11 @@ void setup() { | |||||||
|       writeFile(SPIFFS, "/inputSend.txt", inputMessage.c_str()); |       writeFile(SPIFFS, "/inputSend.txt", inputMessage.c_str()); | ||||||
|       yourInputSend = inputMessage.toInt(); |       yourInputSend = inputMessage.toInt(); | ||||||
|       // if not running a program, set the program running off |       // if not running a program, set the program running off | ||||||
|       if(yourInputSend != 2){ |       //if(yourInputSend != 2){ | ||||||
|  |         // Cease all programs on new input | ||||||
|         startProgram = false; |         startProgram = false; | ||||||
|         programRunning = false; |         programRunning = false; | ||||||
|       } |       //} | ||||||
|     } |     } | ||||||
|     // GET inputWPM value on <ESP_IP>/get?inputWPM=<inputMessage> |     // GET inputWPM value on <ESP_IP>/get?inputWPM=<inputMessage> | ||||||
|     if (request->hasParam(PARAM_WPM)) { |     if (request->hasParam(PARAM_WPM)) { | ||||||
| @ -508,6 +389,21 @@ void setup() { | |||||||
|       // save previous state |       // save previous state | ||||||
|       yourInputMsg_old = yourInputMsg; |       yourInputMsg_old = yourInputMsg; | ||||||
|       yourInputMsg = inputMessage.toInt(); |       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> |     // GET inputStepLength value on <ESP_IP>/get?inputStepLength=<inputMessage> | ||||||
|     if (request->hasParam(PARAM_STEPLENGTH)) { |     if (request->hasParam(PARAM_STEPLENGTH)) { | ||||||
| @ -583,13 +479,6 @@ void setup() { | |||||||
|       //} |       //} | ||||||
|       Serial.println(yourInputStartTimeUnix); |       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 |       // Use alarm built into RTC | ||||||
|       rtc.setAlarm1(DateTime(yourInputStartTimeUnix), DS3231_A1_Date); |       rtc.setAlarm1(DateTime(yourInputStartTimeUnix), DS3231_A1_Date); | ||||||
|       //rtc.setAlarm1(DateTime(2020, 6, 25, 15, 34, 0), DS3231_A2_Date); |       //rtc.setAlarm1(DateTime(2020, 6, 25, 15, 34, 0), DS3231_A2_Date); | ||||||
| @ -607,37 +496,26 @@ void setup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void loop() { | 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. |   // Only do this when the message has been updated. | ||||||
|   if(yourInputMsg != yourInputMsg_old){ |   // if(yourInputMsg != yourInputMsg_old){ | ||||||
|     //morseToSend.Stop(JLed::eStopMode::FULL_OFF).Update(); |   //     if(yourInputMsg == 0){ | ||||||
|       if(yourInputMsg == 0){ |   //       sender.setMessage(String("test test test de w1cdn ")); | ||||||
|         morseToSend = morseTEST; |   //     } else if(yourInputMsg == 1){ | ||||||
|         morseToSend_blink = morseTEST_blink; |   //       sender.setMessage(String("moe ")); | ||||||
|       } else if(yourInputMsg == 1){ |   //     } else if(yourInputMsg == 2){ | ||||||
|         morseToSend = morseMOE; |   //       sender.setMessage(String("moi ")); | ||||||
|         morseToSend_blink = morseMOE_blink; |   //     } else if(yourInputMsg == 3){ | ||||||
|       } else if(yourInputMsg == 2){ |   //       sender.setMessage(String("mos ")); | ||||||
|         morseToSend = morseMOI; |   //     } else if(yourInputMsg == 4){ | ||||||
|         morseToSend_blink = morseMOI_blink; |   //       sender.setMessage(String("moh ")); | ||||||
|       } else if(yourInputMsg == 3){ |   //     } else if(yourInputMsg == 5){ | ||||||
|         morseToSend = morseMOS; |   //       sender.setMessage(String("mo5 ")); | ||||||
|         morseToSend_blink = morseMOS_blink; |   //     } | ||||||
|       } else if(yourInputMsg == 4){ |   //   // Keeps the key/led from locking up | ||||||
|         morseToSend = morseMOH; |   //   yourInputMsg_old = yourInputMsg; | ||||||
|         morseToSend_blink = morseMOH_blink; |   // } | ||||||
|       } else if(yourInputMsg == 5){ |  | ||||||
|         morseToSend = morseMO5; |  | ||||||
|         morseToSend_blink = morseMO5_blink; |  | ||||||
|       } |  | ||||||
|     // 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 |   // 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 |   // 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 |   // Once alarm has started the program, set things up to run | ||||||
|   if(startProgram == true){ |   if(startProgram == true){ | ||||||
|     //auto morse_cycle = morseEffectMOS; |     //Serial.println("Start sending"); | ||||||
|     //int period = morse_cycle.Period() + word_space_ms; |     start_millis = millis() + ((yourInputCycleID - 1) * yourInputStepLength); | ||||||
|     //int repeats = step_length / period; |     stop_millis = start_millis + yourInputStepLength; | ||||||
|     //int remainder_wait = step_length - (period * repeats); |     if(yourInputCycleID == 1){ | ||||||
|     //int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait)); |       pause_until_millis = stop_millis + (yourInputStepLength * (yourInputNtransmitters - 1));  | ||||||
|  |     } else { | ||||||
|     // Nothing makes it out of this scope... |     // Subtract 2 rather than 1 here to account for start_millis duration at beginning of repeat. | ||||||
|     // blinker_continuous = JLed(blinker).UserFunc(&morse_cycle).Repeat(repeats).DelayAfter(word_space_ms); |       pause_until_millis = stop_millis + (yourInputStepLength * (yourInputNtransmitters - 2));  | ||||||
|     // blinker_continuous_wait = JLed(blinker).Off(total_wait); |     } | ||||||
|     // JLed morses_blink[] = { |     //sender.startSending(); | ||||||
|     //     blinker_continuous, |  | ||||||
|     //     blinker_continuous_wait |  | ||||||
|     // }; |  | ||||||
|     // auto morses_sequence_blink = JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink); |  | ||||||
|  |  | ||||||
|     morses_sequence_blink.Forever().Update(); |  | ||||||
|     programRunning = true; |     programRunning = true; | ||||||
|     startProgram = false; |     startProgram = false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // if you want to send continuous code, and it's not sending, then start it up |   // if you want to send continuous code, and it's not sending, then start it up | ||||||
|   if((yourInputSend == 1) & (morseToSend.IsRunning() == false)){ |   if((yourInputSend == 1)){ | ||||||
|     //jled |     if (!sender.continueSending()){ | ||||||
|     morseToSend.Reset().Update(); |           // Set the internal counters to the message's beginning. | ||||||
|     morseToSend_blink.Reset().Update(); |           // Here, this results in repeating the message indefinitely. | ||||||
|  |           sender.startSending(); | ||||||
|   // 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 you want to send cycle code and it's not sending, then start it up |   // 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)){ |   } else if((yourInputSend == 2) & (programRunning == true)){ | ||||||
|     morseToSend.Reset().Update(); |       if((millis() < start_millis)){ | ||||||
|     //morseToSend_blink.Reset().Update(); |         // Shut the pin off manually | ||||||
|     morses_sequence_blink.Reset(); |         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 |   // if the cycle program is not running | ||||||
|   } else if((yourInputSend == 2) & (programRunning == false)){ |   } else if((yourInputSend == 2) & (programRunning == false)){ | ||||||
|     morses_sequence_blink.Stop(); |     // do we need something here? | ||||||
|   // if you don't want to send code |   // if you don't want to send code | ||||||
|   } else { |   } else if(yourInputSend == 0){ | ||||||
|     // stop sending and make sure the pin is off |     //sender.setMessage(String("")) ; // Not sure this is the right way to stop things. | ||||||
|     morseToSend.Stop(JLed::eStopMode::FULL_OFF).Update(); |     // Shut the pin off manually | ||||||
|     morseToSend_blink.Stop(JLed::eStopMode::FULL_OFF).Update(); |     digitalWrite(blinker, LOW); | ||||||
|     morses_sequence_blink.Stop(); |  | ||||||
|   } |   } | ||||||
|   //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