BaliseHF/src/wsprPico_GPS_RP2350/lib/Menu/Menu.cpp

475 lines
15 KiB
C++
Raw Normal View History

2026-07-25 20:30:01 +02:00
/*
* File: Menu.cpp
*
* Created on 22 avril 2022, 17:11
*/
#include "Menu.h"
2026-07-25 20:37:32 +02:00
const char* Menu::modeStr[7] = {"WSPR", "RTTY", "FT8", "HELL", "CW", "IAMBIC", "ALL"};
2026-07-25 20:30:01 +02:00
const char* Menu::bandStr[7] = {"160m", "80m", "60m", "40m", "30m", "20m", "free frequency"};
Menu::Menu() :
exitFlag(false),
con(new Console())
{
anchor = this;
pinMode(MENU_PIN, INPUT_PULLUP); // GP6 avec résistance interne activée
tabfreq[0] = {1838100, -50};
tabfreq[1] = {3570100, -150};
tabfreq[2] = {5288700, -200};
tabfreq[3] = {7040100, -300};
tabfreq[4] = {10140200, -400};
tabfreq[5] = {14097100, -550};
}
Menu::Menu(const Menu& orig) {
}
Menu::~Menu() {
anchor = NULL;
}
void Menu::run() {
exitFlag = false;
if (digitalRead(MENU_PIN) == 0) {
EEPROM.begin(sizeof (config));
EEPROM.get(0, cfg);
Serial.println(F("help command for info"));
while (exitFlag == false) {
con->run();
}
}
Serial.println(F("\nConsole exit"));
}
void Menu::setup() {
con->onCmd("call", _call_);
con->onCmd("freq", _freq_);
con->onCmd("minute",_minute_);
con->onCmd("offset", _offset_);
con->onCmd("gpsbaud", _gpsbaud_);
con->onCmd("dbm", _dbm_);
con->onCmd("mail", _mail_);
con->onCmd("show", _show_);
con->onCmd("raz", _raz_);
con->onCmd("restart", _restart_);
con->onCmd("save", _save_);
con->onCmd("exit", _exit_);
con->onCmd("help", _help_);
con->onCmd("scan", _scan_);
con->onCmd("loc", _loc_);
con->onCmd("mode", _mode_);
con->onCmd("band", _band_);
con->onCmd("iambic", _iambic_);
con->onCmd("nmea", _nmea_);
con->onCmd("wpm", _wpm_);
con->onCmd("follow", _follow_);
2026-07-25 20:37:32 +02:00
2026-07-25 20:30:01 +02:00
con->onUnknown(_unknown);
con->start();
this->run();
}
void Menu::_exit_(ArgList& L, Stream& S) {
anchor->exitFlag = true;
}
void Menu::_help_(ArgList& L, Stream& S) {
S.println(F("Available commands"));
2026-07-25 20:37:32 +02:00
S.println(F("Set transmission frequency : freq 7040100"));
S.println(F("Set offset value : offset -200"));
S.println(F("Set callsign : call F4XYZ"));
S.println(F("Set locator : loc JN07"));
S.println(F("Set mode wspr rtty ft8 hell cw all : mode wspr"));
S.println(F("Set band 160,80m,60m,40m,30m,20m,freq : band 40m"));
S.println(F("Set modulo in minutes : minute 10"));
S.println(F("Set follow mode : follow 1"));
S.println(F("Set cw words per minute : wpm 15"));
S.println(F("Set iambic mode : iambic B"));
S.println(F("Set transmission power (dBm) : dbm 10"));
S.println(F("Set email address : mail f4xyz at example.com"));
S.println(F("Set gps baud rate : gpsbaud 9600"));
S.println(F("Scan I2C bus : scan"));
S.println(F("Show nmea frame : nmea 1"));
S.println(F("Save current configuration to EEPROM : save"));
S.println(F("Display current configuration : show"));
S.println(F("Reset all parameters to default : raz"));
S.println(F("Restart RPI pico : restart"));
S.println(F("Show this help message : help"));
S.println(F("Exit menu : exit"));
2026-07-25 20:30:01 +02:00
}
bool Menu::acceptCmd(String cmd, int longMin, int longMax) {
if (cmd.length() >= longMin && cmd.length() <= longMax) {
return true;
} else {
Serial.println("Erreur");
return false;
}
}
void Menu::_unknown(String& L, Stream& S) {
S.print(L);
S.println(" : command not found");
}
void Menu::_call_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 4, 9);
if (ret == true) {
p.toUpperCase();
size_t len = strlen(p.c_str());
if (len < sizeof (cfg.call)) {
//memcpy(anchor->cfg.call, p.c_str(), len);
//anchor->cfg.call[len] = '\0'; // Ajout manuel du terminateur
strcpy(anchor->cfg.call, p.c_str());
S.printf("Callsign set to: %s\n\r", anchor->cfg.call);
} else {
S.println(F("Callsign too long. Max 9 characters."));
}
} else {
S.println(F("Invalid callsign format."));
}
}
void Menu::_loc_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 4, 4);
if (ret == true) {
p.toUpperCase();
strcpy(anchor->cfg.locator, p.c_str());
S.printf("Locator set to: %s\n\r", anchor->cfg.locator);
} else {
S.println(F("Invalid locator format ex JN07"));
}
}
void Menu::_mode_(ArgList& L, Stream& S) {
String p = L.getNextArg();
p.toLowerCase(); // Normalise la casse pour comparaison
if (p == "wspr") {
anchor->cfg.mode = WSPR;
} else if (p == "rtty") {
anchor->cfg.mode = RTTY;
} else if (p == "ft8") {
anchor->cfg.mode = FT8;
} else if (p == "hell") {
anchor->cfg.mode = HELL;
} else if (p == "cw") {
anchor->cfg.mode = CW;
} else if (p == "iambic") {
anchor->cfg.mode = IAMBIC;
2026-07-25 20:37:32 +02:00
} else if (p == "all") {
anchor->cfg.mode = ALL;
2026-07-25 20:30:01 +02:00
} else {
S.println(F("Invalid mode syntax. Example: mode wspr"));
return;
}
// Affiche le mode sélectionné
S.printf("Mode set to: %s\n\r", modeStr[anchor->cfg.mode]);
}
void Menu::_band_(ArgList& L, Stream& S) {
String p = L.getNextArg();
p.toLowerCase(); // Normalise la casse pour comparaison
if (p == "160m") {
anchor->cfg.band = M160;
anchor->cfg.freq = anchor->tabfreq[0].freq;
anchor->cfg.offset = anchor->tabfreq[0].offset;
} else if (p == "80m") {
anchor->cfg.band = M80;
anchor->cfg.freq = anchor->tabfreq[1].freq;
anchor->cfg.offset = anchor->tabfreq[1].offset;
} else if (p == "60m") {
anchor->cfg.band = M60;
anchor->cfg.freq = anchor->tabfreq[2].freq;
anchor->cfg.offset = anchor->tabfreq[2].offset;
} else if (p == "40m") {
anchor->cfg.band = M40;
anchor->cfg.freq = anchor->tabfreq[3].freq;
anchor->cfg.offset = anchor->tabfreq[3].offset;
} else if (p == "30m") {
anchor->cfg.band = M30;
anchor->cfg.freq = anchor->tabfreq[4].freq;
anchor->cfg.offset = anchor->tabfreq[4].offset;
} else if (p == "20m") {
anchor->cfg.band = M20;
anchor->cfg.freq = anchor->tabfreq[5].freq;
anchor->cfg.offset = anchor->tabfreq[5].offset;
} else if (p == "freq") {
anchor->cfg.band = FREQ;
} else {
S.println(F("Invalid mode syntax. Example: band 40m"));
return;
}
// Affiche le mode sélectionné
S.printf("Band set to: %s\n\r", bandStr[anchor->cfg.band]);
}
//manque le test de la fréquence max supportée par le RPI
void Menu::_freq_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 6, 8);
if (ret == true) {
uint32_t freqValue = p.toInt(); // Conversion de la chaîne en entier
anchor->cfg.freq = freqValue; // Sauvegarde dans la structure
S.printf("Frequency set to: %lu Hz\n\r", anchor->cfg.freq);
}
}
void Menu::_minute_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 1, 2);
if (ret == true) {
uint8_t minuteValue = (uint8_t) p.toInt(); // Conversion en entier 8 bits
anchor->cfg.minute = minuteValue; // Sauvegarde dans la structure
S.printf("Minute modulo set to: %u\n\r", anchor->cfg.minute);
}
}
void Menu::_wpm_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 1, 2);
if (ret == true) {
uint8_t wpm = (uint8_t) p.toInt(); // Conversion en entier 8 bits
anchor->cfg.wpm = wpm; // Sauvegarde dans la structure
S.printf("WPM set to: %u\n\r", anchor->cfg.wpm);
}
}
//facteur de correction
void Menu::_offset_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 1, 5);
if (ret == true) {
int32_t offsetValue = p.toInt(); // Conversion en entier signé
anchor->cfg.offset = offsetValue; // Sauvegarde dans la structure
S.printf("Offset set to: %ld\n\r", anchor->cfg.offset);
}
}
void Menu::_gpsbaud_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 4, 5);
if (ret == true) {
uint32_t baudValue = p.toInt(); // Conversion en entier non signé
anchor->cfg.baud = baudValue; // Sauvegarde dans la structure
S.printf("GPS baudrate set to: %lu\n\r", anchor->cfg.baud);
}
}
//les valeurs de dbm sont normalisées la table pour vérification
void Menu::_dbm_(ArgList& L, Stream& S) {
String p = L.getNextArg();
bool ret = anchor->acceptCmd(p, 1, 2);
if (ret == true) {
int inputValue = p.toInt();
// Tableau des puissances disponibles
const uint8_t dbmTable[] = {
0, 3, 7, 10, 13, 17, 20, 23, 27, 30,
33, 37, 40, 43, 47, 50, 53, 57, 60
};
const size_t tableSize = sizeof(dbmTable) / sizeof(dbmTable[0]);
// Recherche de la valeur la plus proche
uint8_t closest = dbmTable[0];
int minDiff = abs(inputValue - dbmTable[0]);
for (size_t i = 1; i < tableSize; ++i) {
int diff = abs(inputValue - dbmTable[i]);
if (diff < minDiff) {
minDiff = diff;
closest = dbmTable[i];
}
}
// Mise à jour de la configuration
anchor->cfg.dbm = closest;
S.printf("Transmission power set to: %u dBm (closest to %d)\n\r", closest, inputValue);
}
}
void Menu::_mail_(ArgList& L, Stream& S) {
String arg;
bool ret;
String p = "";
while (!(arg = L.getNextArg()).isEmpty()) {
p = p + arg + " ";
}
ret = anchor->acceptCmd(p, 3, 30);
p.toUpperCase();
size_t len = strlen(p.c_str());
if (len < sizeof (anchor->cfg.mail)) {
//memcpy(anchor->cfg.mail, p.c_str(), len);
//anchor->cfg.mail[len] = '\0'; // Ajout manuel du terminateur
strcpy(anchor->cfg.mail, p.c_str());
S.printf("Email set to: %s\n\r", anchor->cfg.mail);
} else {
S.println(F("Email too long. Max 30 characters."));
}
}
void Menu::_nmea_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 1, 1);
if (ret == true) {
int val = p.toInt(); // Conversion en entier
if (val == 0 || val == 1) {
anchor->cfg.nmeaEnabled = (val == 1); // Affectation booléenne
S.printf("Nmea frame debug %s\n\r", anchor->cfg.nmeaEnabled ? "enabled" : "disabled");
} else {
S.println(F("Invalid value. Use 0 to disable or 1 to enable nmea debug."));
}
}
}
void Menu::_iambic_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 1, 1);
if (ret == true) {
char val = toupper(p[0]); // Conversion en char
if (val == 'A' || val == 'B') {
anchor->cfg.bicmd = val; // Affectation booléenne
S.printf("Iambic type %c\n\r", anchor->cfg.bicmd);
} else {
S.println(F("Invalid value. Use A or B type for iambic paddle."));
}
}
}
void Menu::_follow_(ArgList& L, Stream& S) {
String p;
bool ret;
p = L.getNextArg();
ret = anchor->acceptCmd(p, 1, 1);
if (ret == true) {
int val = p.toInt(); // Conversion en entier
if (val == 0 || val == 1) {
anchor->cfg.follow = (val == 1); // Affectation booléenne
S.printf("Follow is %s\n\r", anchor->cfg.follow ? "enabled" : "disabled");
} else {
S.println(F("Invalid value. Use 0 to disable or 1 to enable follow mode."));
}
}
}
void Menu::_show_(ArgList& L, Stream& S) {
S.println("Current configuration :");
S.printf(" freq : %lu Hz\n\r", anchor->cfg.freq);
S.printf(" offset : %ld\n\r", anchor->cfg.offset);
S.printf(" call : %s\n\r", anchor->cfg.call);
S.printf(" locator : %s\n\r", anchor->cfg.locator);
S.printf(" dbm : %u dBm\n\r", anchor->cfg.dbm);
S.printf(" mode : %s\n\r", modeStr[anchor->cfg.mode]);
S.printf(" band : %s\n\r", bandStr[anchor->cfg.band]);
S.printf(" wpm : %u\n\r", anchor->cfg.wpm);
2026-07-25 20:37:32 +02:00
S.printf(" iambic mode : %c\n\r", anchor->cfg.bicmd);
2026-07-25 20:30:01 +02:00
S.printf(" mail : %s\n\r", anchor->cfg.mail);
S.printf(" minute : %u min\n\r", anchor->cfg.minute);
S.printf(" Gps baud rate : %u\n\r", anchor->cfg.baud);
S.printf(" Nmea debug is : %s\n\r", anchor->cfg.nmeaEnabled ? "ON" : "OFF");
S.printf(" Follow is : %s\n\r", anchor->cfg.follow ? "ON" : "OFF");
}
void Menu::_save_(ArgList& L, Stream& S) {
EEPROM.put(0, anchor->cfg);
EEPROM.commit();
Serial.println("Configuration saved.");
}
void Menu::_raz_(ArgList& L, Stream& S) {
anchor->cfg.freq = 7040100; // Fréquence par défaut
anchor->cfg.offset = -200; // Fréquence par défaut
strcpy(anchor->cfg.call, "NOCALL"); // Indicatif par défaut
anchor->cfg.dbm = 10; // Puissance par défaut
strcpy(anchor->cfg.mail, "none@example.com"); // Email par défaut
strcpy(anchor->cfg.locator, "JN07"); // Locator par défaut
anchor->cfg.minute = 6; // Durée par défaut
anchor->cfg.baud=9600; //9600 bauds
anchor->cfg.mode=WSPR;
anchor->cfg.nmeaEnabled=false;
anchor->cfg.wpm=12;
anchor->cfg.follow=0;
anchor->cfg.band = FREQ;
Serial.println("Configuration has been reset to default values.");
}
void Menu::_restart_(ArgList& L, Stream& S) {
watchdog_reboot(0, 0, 0); // Redémarre immédiatement
}
void Menu::_scan_(ArgList& L, Stream& S) {
anchor->scanI2C();
}
void Menu::scanI2C() {
Serial.println(" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f");
for (uint8_t row = 0; row < 8; row++) {
Serial.printf("%02x: ", row << 4);
for (uint8_t col = 0; col < 16; col++) {
uint8_t address = (row << 4) | col;
Wire.beginTransmission(address);
uint8_t error = Wire.endTransmission();
if (error == 0) {
Serial.printf("%02x ", address);
} else if (error == 4) {
Serial.print("UU "); // Bus error
} else {
Serial.print("-- ");
}
}
Serial.println();
}
}
2026-07-25 20:37:32 +02:00
2026-07-25 20:30:01 +02:00
Menu* Menu::anchor = NULL;