vulpes/vulpes/src/main.cpp

509 lines
16 KiB
C++
Raw Normal View History

2023-08-22 12:23:47 -05:00
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-input-data-html-form/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
// include wifi password
#include "config.h"
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <SPIFFS.h>
#include <Preferences.h>
2023-08-26 20:55:28 -05:00
#include <arduino-timer.h>
#include <Telegraph.h>
//#include <morse.h> //arduino morse
//#include <Morse.h> //etherkit morse
#include <jled.h> // jled
2023-08-30 22:10:29 -05:00
#include "jled/morse.h" //jled
//#include "morse_effect.h" // jled
2023-08-22 12:23:47 -05:00
// download zip from https://github.com/me-no-dev/ESPAsyncWebServer and install.
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
// Read from config.h
const char* ssid = WIFI_SSID;
const char* password = WIFI_PASSWORD;
const char* PARAM_STRING = "inputString";
const char* PARAM_SEND = "inputSend";
2023-08-31 21:35:49 -05:00
const char* PARAM_WPM = "inputWPM";
const char* PARAM_MSG = "inputMsg";
2023-08-22 12:23:47 -05:00
const char* PARAM_FLOAT = "inputFloat";
// Global variables
String yourInputString;
int yourInputSend;
2023-08-31 21:35:49 -05:00
int yourInputWPM;
2023-09-01 10:01:49 -05:00
int yourInputMsg;
int yourInputMsg_old; // to save previous state and check changes
float yourInputFloat;
// HTML web page to handle 3 input fields (inputString, inputSend, inputFloat)
2023-08-22 12:23:47 -05:00
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html><head>
<title>ESP Input Form</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function submitMessage() {
<!--alert("Saved value to ESP SPIFFS");-->
setTimeout(function(){ document.location.reload(false); }, 500);
}
</script></head><body>
<form action="/get" target="hidden-form">
inputString (current value %inputString%): <input type="text" name="inputString" value=%inputString%><br>
2023-08-31 11:52:11 -05:00
2023-08-31 23:09:21 -05:00
Sending program (cycle doesn't work yet) (current %inputSend%):
<select name="inputSend" id="send-program">
<option value="0">Off</option>
2023-08-31 11:52:11 -05:00
<option value="1">Continuous</option>
<option value="2">Cycle</option>
2023-08-31 11:52:11 -05:00
</select><br>
2023-08-31 21:35:49 -05:00
2023-08-31 23:09:21 -05:00
Message (current %inputMsg%):
2023-08-31 21:35:49 -05:00
<select name="inputMsg" id="message">
<option value="0">CQ CQ CQ DE W1CDN</option>
<option value="1">MOE</option>
<option value="2">MOI</option>
<option value="3">MOS</option>
<option value="4">MOH</option>
<option value="5">MO5</option>
</select><br>
2023-08-31 11:52:11 -05:00
2023-08-31 23:09:21 -05:00
<!-- WPM (current value %inputWPM%): <input type="number " name="inputWPM" value = %inputWPM%> (doesn't work yet)<br> -->
2023-08-31 21:35:49 -05:00
inputFloat (current value %inputFloat%): <input type="number " name="inputFloat" value = %inputFloat%><br>
2023-08-22 12:23:47 -05:00
<input type="submit" value="Submit" onclick="submitMessage()">
</form>
<iframe style="display:none" name="hidden-form"></iframe>
</body></html>)rawliteral";
2023-08-22 14:13:47 -05:00
// Auxiliary variables to store the current output state
String output26State = "off";
String output27State = "off";
// Assign output variables to GPIO pins
const int output26 = 26;
const int output27 = 27;
2023-08-26 20:55:28 -05:00
// Timers
2023-08-27 22:35:16 -05:00
//auto timer = timer_create_default();
Timer<1> timer;
2023-08-26 20:55:28 -05:00
auto time_until_start = timer_create_default();
// Example from https://github.com/contrem/arduino-timer#examples
bool toggle_led(void *) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // toggle the LED
return true; // keep timer active? true
}
2023-08-26 21:08:35 -05:00
// Toggle GPIO pin (LED or relay)
bool toggle_gpio_26(void *) {
if(output26State == "off"){
output26State = "on";
digitalWrite(output26, HIGH);
} else {
output26State = "off";
digitalWrite(output26, LOW);
}
return true; // keep timer active? true
}
2023-08-22 12:23:47 -05:00
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
String readFile(fs::FS &fs, const char * path){
2023-08-22 14:13:47 -05:00
//Serial.printf("Reading file: %s\r\n", path);
2023-08-22 12:23:47 -05:00
File file = fs.open(path, "r");
if(!file || file.isDirectory()){
Serial.println("- empty file or failed to open file");
return String();
}
2023-08-22 14:13:47 -05:00
//Serial.println("- read from file:");
2023-08-22 12:23:47 -05:00
String fileContent;
while(file.available()){
fileContent+=String((char)file.read());
}
file.close();
2023-08-22 14:13:47 -05:00
//Serial.println(fileContent);
2023-08-22 12:23:47 -05:00
return fileContent;
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, "w");
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- write failed");
}
file.close();
}
2023-08-31 11:52:11 -05:00
// Replaces placeholder in web UI with stored values
2023-08-22 12:23:47 -05:00
String processor(const String& var){
//Serial.println(var);
if(var == "inputString"){
return readFile(SPIFFS, "/inputString.txt");
}
else if(var == "inputSend"){
return readFile(SPIFFS, "/inputSend.txt");
2023-08-22 12:23:47 -05:00
}
2023-08-31 21:35:49 -05:00
else if(var == "inputWPM"){
return readFile(SPIFFS, "/inputWPM.txt");
}
else if(var == "inputMsg"){
return readFile(SPIFFS, "/inputMsg.txt");
}
2023-08-22 12:23:47 -05:00
else if(var == "inputFloat"){
return readFile(SPIFFS, "/inputFloat.txt");
}
return String();
}
2023-08-27 22:35:16 -05:00
// vvvvv Modify some functions from KB1OIQ's controller.
// This section hasn't been tested on the hardware.
//int dit_len = 60 ; //milliseconds; https://morsecode.world/international/timing.html
//================================================================================
// stop_26(): set GPIO 26 to LOW. Used for dot(), dash().
//================================================================================
bool stop_26(void *){
output26State = "off";
digitalWrite(output26, LOW);
return false; // keep timer active? true
}
//================================================================================
// dit(): transmit a single dit
//================================================================================
void dit(int dit_len = 1000) {
output26State = "on";
digitalWrite(output26, HIGH);
timer.in(dit_len, stop_26);
}
//================================================================================
// dah(): transmit a single dah
//================================================================================
void dah(int dit_len = 1000) {
output26State = "on";
digitalWrite(output26, HIGH);
timer.in(dit_len * 3, stop_26);
}
//================================================================================
// char_space()): transmit a character space
//================================================================================
// A function that does nothing except (hopefully) block the timer.
bool empty(void *) {
return false;
}
void char_space(int dit_len = 1000) {
timer.in(dit_len, empty);
}
void k(){
Serial.println("K");
dah();
char_space();
dit();
char_space();
dah();
}
// ^^^^
//telegraph
//Telegraph telegraph(LED_BUILTIN, 10, HIGH);
Telegraph telegraph26(output26, 10, HIGH);
//arduinomorse
//LEDMorseSender sender(LED_BUILTIN);
//Etherkit Morse
//Morse morse(LED_BUILTIN, 15);
//jled from https://github.com/jandelgado/jled/blob/master/examples/morse/morse_effect.h
class MorseEffect : public jled::BrightnessEvaluator {
Morse morse_;
// duration of a single 'dit' in ms
const uint16_t speed_;
public:
explicit MorseEffect(const char* message, uint16_t speed = 200)
: morse_(message), speed_(speed) {}
uint8_t Eval(uint32_t t) const override {
const auto pos = t / speed_;
if (pos >= morse_.size()) return 0;
return morse_.test(pos) ? 255 : 0;
}
uint16_t Period() const override { return (morse_.size() + 1) * speed_; }
};
// Speed is milliseconds per dit, which is 1000 * (60 / (50 * WPM))
// 60 is 20 wpm, 120 is 10 wpm, 90 is 15 wpm, etc.
// https://morsecode.world/international/timing.html
2023-08-31 21:35:49 -05:00
int wpm = 10;
int ms_per_dit = 120; //1000 * (60 / (50 * wpm));
int word_space_ms = ms_per_dit * 7;
// Hardcoding these for now, will come back and make it more flexible.
2023-08-31 23:09:21 -05:00
MorseEffect morseEffectCQ("CQ CQ CQ DE W1CDN", ms_per_dit);
2023-08-31 21:35:49 -05:00
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);
auto morseCQ =
2023-08-31 23:09:21 -05:00
JLed(output26).UserFunc(&morseEffectCQ).DelayAfter(word_space_ms).Forever();
2023-08-31 21:35:49 -05:00
auto morseMOE =
JLed(output26).UserFunc(&morseEffectMOE).DelayAfter(word_space_ms).Forever();
auto morseMOI =
JLed(output26).UserFunc(&morseEffectMOI).DelayAfter(word_space_ms).Forever();
auto morseMOS =
JLed(output26).UserFunc(&morseEffectMOS).DelayAfter(word_space_ms).Forever();
auto morseMOH =
JLed(output26).UserFunc(&morseEffectMOH).DelayAfter(word_space_ms).Forever();
auto morseMO5 =
JLed(output26).UserFunc(&morseEffectMO5).DelayAfter(word_space_ms).Forever();
2023-09-01 10:01:49 -05:00
auto morseToSend = morseCQ; // set this up to overwrite later
2023-08-27 22:35:16 -05:00
//================================================================================
// setup(): stuff that only gets done once, after power up (KB1OIQ's description)
//================================================================================
2023-08-22 12:23:47 -05:00
void setup() {
Serial.begin(115200);
2023-08-22 14:13:47 -05:00
2023-08-26 20:55:28 -05:00
// Timer example, blink main LED
pinMode(LED_BUILTIN, OUTPUT); // set LED pin to OUTPUT
2023-08-26 21:08:35 -05:00
// call the toggle_led function every 10000 millis (10 second)
2023-08-27 22:35:16 -05:00
//timer.every(10000, toggle_led);
2023-08-26 21:08:35 -05:00
// call the toggle_gpio_26 function
2023-08-27 22:35:16 -05:00
//timer.every(1000, toggle_gpio_26);
2023-08-26 20:55:28 -05:00
2023-08-22 14:13:47 -05:00
// Initialize the output variables as outputs
pinMode(output26, OUTPUT);
pinMode(output27, OUTPUT);
// Set outputs to LOW
digitalWrite(output26, LOW);
digitalWrite(output27, LOW);
2023-08-22 12:23:47 -05:00
// Initialize SPIFFS
SPIFFS.begin(true);
//#ifdef ESP32
2023-08-22 12:23:47 -05:00
if(!SPIFFS.begin(true)){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
//#else
2023-08-22 12:23:47 -05:00
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
//#endif
2023-08-22 12:23:47 -05:00
2023-08-31 21:35:49 -05:00
// Make sure files exist, maybe with defaults here
// if(SPIFFS.exists("/inputString.txt") == 0){
// writeFile(SPIFFS, "/inputString.txt", "CQ");
// }
// if(SPIFFS.exists("/inputSend.txt") == 0){
// writeFile(SPIFFS, "/inputSend.txt", "0");
// }
// if(SPIFFS.exists("/inputWPM.txt") == 0){
// writeFile(SPIFFS, "/inputWPM.txt", "10");
// }
// if(SPIFFS.exists("/inputMsg.txt") == 0){
// writeFile(SPIFFS, "/inputMsg.txt", "0");
// }
// if(SPIFFS.exists("/inputFloat.txt") == 0){
// writeFile(SPIFFS, "/inputFloat.txt", "1.1");
// }
2023-08-26 20:55:28 -05:00
// Read in existing data
yourInputString = readFile(SPIFFS, "/inputString.txt");
yourInputSend = readFile(SPIFFS, "/inputSend.txt").toInt();
2023-08-31 21:35:49 -05:00
yourInputWPM = readFile(SPIFFS, "/inputWPM.txt").toInt();
yourInputMsg = readFile(SPIFFS, "/inputMsg.txt").toInt();
yourInputFloat = readFile(SPIFFS, "/inputFloat.txt").toFloat();
2023-08-26 20:55:28 -05:00
2023-09-01 10:01:49 -05:00
// On restart, keep doing what you were doing before
yourInputMsg_old = yourInputMsg;
if(yourInputMsg == 0){
morseToSend = morseCQ;
} else if(yourInputMsg == 1){
morseToSend = morseMOE;
} else if(yourInputMsg == 2){
morseToSend = morseMOI;
} else if(yourInputMsg == 3){
morseToSend = morseMOS;
} else if(yourInputMsg == 4){
morseToSend = morseMOH;
} else if(yourInputMsg == 5){
morseToSend = morseMO5;
}
2023-08-22 12:23:47 -05:00
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Send web page with input fields to client
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/get?inputString=<inputMessage>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
// GET inputString value on <ESP_IP>/get?inputString=<inputMessage>
if (request->hasParam(PARAM_STRING)) {
inputMessage = request->getParam(PARAM_STRING)->value();
writeFile(SPIFFS, "/inputString.txt", inputMessage.c_str());
yourInputString = inputMessage;
2023-08-22 12:23:47 -05:00
}
// GET inputSend value on <ESP_IP>/get?inputSend=<inputMessage>
if (request->hasParam(PARAM_SEND)) {
inputMessage = request->getParam(PARAM_SEND)->value();
writeFile(SPIFFS, "/inputSend.txt", inputMessage.c_str());
yourInputSend = inputMessage.toInt();
2023-08-22 12:23:47 -05:00
}
2023-08-31 21:35:49 -05:00
// GET inputWPM value on <ESP_IP>/get?inputWPM=<inputMessage>
if (request->hasParam(PARAM_WPM)) {
inputMessage = request->getParam(PARAM_WPM)->value();
writeFile(SPIFFS, "/inputWPM.txt", inputMessage.c_str());
yourInputWPM = inputMessage.toInt();
}
// GET inputMsg value on <ESP_IP>/get?inputMsg=<inputMessage>
if (request->hasParam(PARAM_MSG)) {
inputMessage = request->getParam(PARAM_MSG)->value();
writeFile(SPIFFS, "/inputMsg.txt", inputMessage.c_str());
// save previous state
yourInputMsg_old = yourInputMsg;
2023-08-31 21:35:49 -05:00
yourInputMsg = inputMessage.toInt();
}
2023-08-22 12:23:47 -05:00
// GET inputFloat value on <ESP_IP>/get?inputFloat=<inputMessage>
if (request->hasParam(PARAM_FLOAT)) {
2023-08-22 12:23:47 -05:00
inputMessage = request->getParam(PARAM_FLOAT)->value();
writeFile(SPIFFS, "/inputFloat.txt", inputMessage.c_str());
yourInputFloat = inputMessage.toFloat();
2023-08-22 12:23:47 -05:00
}
// else {
// inputMessage = "No message sent";
// }
2023-08-22 12:23:47 -05:00
request->send(200, "text/plain", inputMessage);
2023-08-26 20:55:28 -05:00
2023-08-22 12:23:47 -05:00
});
server.onNotFound(notFound);
server.begin();
2023-08-27 22:35:16 -05:00
//telegraph
//telegraph.send("CQ CQ CQ");
//telegraph26.send("CQ CQ CQ DE W1CDN K");
// arduinomorse
// sender.setup();
// sender.setMessage(String("73 de kb3jcy "));
// sender.startSending();
2023-08-27 22:35:16 -05:00
2023-08-22 12:23:47 -05:00
}
void loop() {
2023-08-26 20:55:28 -05:00
// Timers
time_until_start.tick();
timer.tick();
//arduinomorse
//sender.continueSending();
2023-08-31 21:35:49 -05:00
// 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 = morseCQ;
} else if(yourInputMsg == 1){
morseToSend = morseMOE;
} else if(yourInputMsg == 2){
morseToSend = morseMOI;
} else if(yourInputMsg == 3){
morseToSend = morseMOS;
} else if(yourInputMsg == 4){
morseToSend = morseMOH;
} else if(yourInputMsg == 5){
morseToSend = morseMO5;
}
}
2023-08-31 21:35:49 -05:00
// 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();
//morse.send("CQ CQ CQ DE W1CDN K"); //etherkit morse
//telegraph26.send("CQ CQ CQ DE W1CDN K"); //telegraph
2023-08-30 22:10:29 -05:00
// if you want to send continuous code, and it is sending, keep sending
} else if((yourInputSend == 1) & (morseToSend.IsRunning() == true)){
morseToSend.Update();
// if you want to send cycle code and it is sending, keep sending
} else if((yourInputSend == 2) & (morseToSend.IsRunning() == true)){
morseToSend.Update();
// if you want to send cycle code and it's not sending, then start it up
} else if((yourInputSend == 2) & (morseToSend.IsRunning() == true)){
morseToSend.Reset().Update();
// 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.Update();
// Blink LED according to seconds entered
// if (yourInputInt > 0) {
// Serial.println("GPIO 26 on");
// output26State = "on";
// digitalWrite(output26, HIGH);
// delay(yourInputInt * 1000);
// Serial.println(yourInputInt);
// Serial.println("GPIO 26 off");
// output26State = "off";
// digitalWrite(output26, LOW);
// delay(yourInputInt * 1000);
// } else {
// output26State = "off";
// }
2023-08-22 12:23:47 -05:00
}