Set up scheduled program cycles #24

Merged
W1CDN merged 41 commits from program-cycle into main 2023-09-15 16:51:45 -05:00
4 changed files with 3 additions and 391 deletions
Showing only changes of commit f5aa43ef76 - Show all commits

View File

@ -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.
![morse example](../../doc/morse.jpg)
## Author
Jan Delgado

View File

@ -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_

View File

@ -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_

View File

@ -16,10 +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 "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>
@ -253,126 +250,12 @@ String processor(const String& var){
LEDMorseSender sender(blinker, 10.0f); // the 'f' makes sure this is a float LEDMorseSender sender(blinker, 10.0f); // the 'f' makes sure this is a float
// TODO also for keyer once blinker works // TODO also for keyer once blinker works
// Timers
Timer<1> timer;
//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_; }
};
// 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.
// https://morsecode.world/international/timing.html // https://morsecode.world/international/timing.html
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)
@ -444,31 +327,18 @@ 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;
// morseToSend_blink = morseTEST_blink;
sender.setMessage(String("test test test de w1cdn ")); sender.setMessage(String("test test test de w1cdn "));
} else if(yourInputMsg == 1){ } else if(yourInputMsg == 1){
// morseToSend = morseMOE;
// morseToSend_blink = morseMOE_blink;
sender.setMessage(String("moe ")); sender.setMessage(String("moe "));
} else if(yourInputMsg == 2){ } else if(yourInputMsg == 2){
// morseToSend = morseMOI;
// morseToSend_blink = morseMOI_blink;
sender.setMessage(String("moi ")); sender.setMessage(String("moi "));
} else if(yourInputMsg == 3){ } else if(yourInputMsg == 3){
// morseToSend = morseMOS;
// morseToSend_blink = morseMOS_blink;
sender.setMessage(String("mos ")); sender.setMessage(String("mos "));
} else if(yourInputMsg == 4){ } else if(yourInputMsg == 4){
// morseToSend = morseMOH;
// morseToSend_blink = morseMOH_blink;
sender.setMessage(String("moh ")); sender.setMessage(String("moh "));
} else if(yourInputMsg == 5){ } else if(yourInputMsg == 5){
// morseToSend = morseMO5;
// morseToSend_blink = morseMO5_blink;
sender.setMessage(String("mo5 ")); sender.setMessage(String("mo5 "));
} }
//sender.startSending();
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password); WiFi.begin(ssid, password);
@ -610,38 +480,22 @@ 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){
// morseToSend = morseTEST;
//morseToSend_blink = morseTEST_blink;
sender.setMessage(String("test test test de w1cdn ")); sender.setMessage(String("test test test de w1cdn "));
} else if(yourInputMsg == 1){ } else if(yourInputMsg == 1){
// morseToSend = morseMOE;
sender.setMessage(String("moe ")); sender.setMessage(String("moe "));
// morseToSend_blink = morseMOE_blink;
} else if(yourInputMsg == 2){ } else if(yourInputMsg == 2){
// morseToSend = morseMOI;
sender.setMessage(String("moi ")); sender.setMessage(String("moi "));
// morseToSend_blink = morseMOI_blink;
} else if(yourInputMsg == 3){ } else if(yourInputMsg == 3){
// morseToSend = morseMOS;
sender.setMessage(String("mos ")); sender.setMessage(String("mos "));
// morseToSend_blink = morseMOS_blink;
} else if(yourInputMsg == 4){ } else if(yourInputMsg == 4){
// morseToSend = morseMOH;
sender.setMessage(String("moh ")); sender.setMessage(String("moh "));
// morseToSend_blink = morseMOH_blink;
} else if(yourInputMsg == 5){ } else if(yourInputMsg == 5){
// morseToSend = morseMO5;
sender.setMessage(String("mo5 ")); sender.setMessage(String("mo5 "));
// morseToSend_blink = morseMO5_blink;
} }
// Keeps the key/led from locking up // Keeps the key/led from locking up
yourInputMsg_old = yourInputMsg; yourInputMsg_old = yourInputMsg;
@ -663,67 +517,25 @@ 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;
//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");
//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)));
start_millis = millis(); start_millis = millis();
stop_millis = start_millis + step_length; stop_millis = start_millis + step_length;
pause_millis = stop_millis + step_length; pause_millis = stop_millis + step_length;
sender.startSending(); //arduinomorse sender.startSending(); //arduinomorse
programRunning = true; programRunning = true;
startProgram = false; startProgram = false;
// Serial.println(yourInputSend);
// Serial.println(programRunning);
} }
// 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)){;// & (morseToSend.IsRunning() == false)){
//jled
// morseToSend.Reset().Update();
// morseToSend_blink.Reset().Update();
if (!sender.continueSending()){ if (!sender.continueSending()){
// Set the internal counters to the message's beginning. // Set the internal counters to the message's beginning.
// Here, this results in repeating the message indefinitely. // Here, this results in repeating the message indefinitely.
sender.startSending(); 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();
// //Serial.println("Continue cycle");
// sender.continueSending();
// 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)){//& (morses_sequence_blink.Update() == false)
//morseToSend.Reset().Update();
//morseToSend_blink.Reset().Update();
//morses_sequence_blink.Reset();
//Serial.println("Start up cycle");
if((millis() > start_millis) & (millis() < stop_millis)){ if((millis() > start_millis) & (millis() < stop_millis)){
if (!sender.continueSending()){ if (!sender.continueSending()){
// Set the internal counters to the message's beginning. // Set the internal counters to the message's beginning.
@ -737,19 +549,10 @@ void loop() {
} }
// 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)){
//Serial.println("Program is over, stop sending") // do we need something here?
//morses_sequence_blink.Stop();
//sender.setMessage(String(""));
// if you don't want to send code // if you don't want to send code
} else if(yourInputSend == 0){ } else if(yourInputSend == 0){
//Serial.println("Stop sending");
// 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();
sender.setMessage(String("")); // Not sure this is the right way to stop things. sender.setMessage(String("")); // Not sure this is the right way to stop things.
} }
//morseToSend.Update();
//sender.continueSending();
} }