From 910d50f11e9498cd6a50ee9adcff79bec738d1a5 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 7 Jan 2023 16:35:07 +0100 Subject: [PATCH] Added WiFi AP console mode --- Bluetooth.h | 2 +- Config.h | 5 ++ Console.h | 178 +++++++++++++++++++++++++++++++++++++++++++++ Console/index.html | 14 ++++ LoRa.cpp | 35 ++++++--- LoRa.h | 2 + RNode_Firmware.ino | 66 ++++++++++++----- Release/spiffs.bin | Bin 0 -> 2031616 bytes Utilities.h | 38 ++++++++++ 9 files changed, 313 insertions(+), 27 deletions(-) create mode 100644 Console.h create mode 100644 Console/index.html create mode 100644 Release/spiffs.bin diff --git a/Bluetooth.h b/Bluetooth.h index 099a442..a22e5dc 100644 --- a/Bluetooth.h +++ b/Bluetooth.h @@ -109,7 +109,7 @@ char bt_devname[11]; bool bt_init() { bt_state = BT_STATE_OFF; if (bt_setup_hw()) { - if (bt_enabled) bt_start(); + if (bt_enabled && !console_active) bt_start(); return true; } else { return false; diff --git a/Config.h b/Config.h index b3beb4b..23424b4 100644 --- a/Config.h +++ b/Config.h @@ -42,6 +42,11 @@ bool bt_enabled = false; bool bt_allow_pairing = false; + #define M_FRQ_S 27388122 + #define M_FRQ_R 27388061 + bool console_active = false; + bool sx1276_installed = false; + #if defined(__AVR_ATmega1284P__) #define PLATFORM PLATFORM_AVR #define MCU_VARIANT MCU_1284P diff --git a/Console.h b/Console.h new file mode 100644 index 0000000..e51b5bb --- /dev/null +++ b/Console.h @@ -0,0 +1,178 @@ +#include +#include +#include +#include + +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/rtc.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif + +// Replace with your network credentials +const char* ssid = "RNode Test"; +const char* password = "somepass"; + +WebServer server(80); + +void console_dbg(String msg) { + Serial.print("[Webserver] "); + Serial.println(msg); +} + +bool exists(String path){ + bool yes = false; + File file = SPIFFS.open(path, "r"); + if(!file.isDirectory()){ + yes = true; + } + file.close(); + return yes; +} + +String console_get_content_type(String filename) { + if (server.hasArg("download")) { + return "application/octet-stream"; + } else if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } + return "text/plain"; +} + +bool console_serve_file(String path) { + console_dbg("Request for: "+path); + if (path.endsWith("/")) { + path += "index.html"; + } + + String content_type = console_get_content_type(path); + String pathWithGz = path + ".gz"; + if (exists(pathWithGz) || exists(path)) { + if (exists(pathWithGz)) { + path += ".gz"; + } + + File file = SPIFFS.open(path, "r"); + console_dbg("Serving file to client"); + server.streamFile(file, content_type); + file.close(); + + console_dbg("File serving done"); + return true; + } + + console_dbg("Error: Could not open file for serving"); + return false; +} + +void console_register_pages() { + server.onNotFound([]() { + if (!console_serve_file(server.uri())) { + server.send(404, "text/plain", "Not Found"); + } + }); +} + +void console_start() { + Serial.println(""); + console_dbg("Starting Access Point..."); + // WiFi.softAP(ssid, password); + WiFi.softAP(bt_devname, password); + delay(150); + IPAddress ip(10, 0, 0, 1); + IPAddress nm(255, 255, 255, 0); + WiFi.softAPConfig(ip, ip, nm); + + if(!SPIFFS.begin(true)){ + console_dbg("Error: Could not mount SPIFFS"); + return; + } else { + console_dbg("SPIFFS Ready"); + } + + console_register_pages(); + server.begin(); + led_indicate_console(); +} + +void console_loop(){ + server.handleClient(); + + // Internally, this yields the thread and allows + // other tasks to run. + delay(5); +} + +// void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ +// Serial.printf("Listing directory: %s\r\n", dirname); + +// File root = fs.open(dirname); +// if(!root){ +// Serial.println("- failed to open directory"); +// return; +// } +// if(!root.isDirectory()){ +// Serial.println(" - not a directory"); +// return; +// } + +// File file = root.openNextFile(); +// while(file){ +// if(file.isDirectory()){ +// Serial.print(" DIR : "); +// Serial.println(file.name()); +// if(levels){ +// listDir(fs, file.path(), levels -1); +// } +// } else { +// Serial.print(" FILE: "); +// Serial.print(file.name()); +// Serial.print("\tSIZE: "); +// Serial.println(file.size()); +// } +// file = root.openNextFile(); +// } +// } + +// void readFile(fs::FS &fs, const char * path){ +// Serial.printf("Reading file: %s\r\n", path); + +// File file = fs.open(path); +// if(!file || file.isDirectory()){ +// Serial.println("− failed to open file for reading"); +// return; +// } + +// Serial.println("− read from file:"); +// while(file.available()){ +// Serial.write(file.read()); +// } +// } diff --git a/Console/index.html b/Console/index.html new file mode 100644 index 0000000..b1c0e90 --- /dev/null +++ b/Console/index.html @@ -0,0 +1,14 @@ + + + + + +RNode Bootstrap Console + + + + + + \ No newline at end of file diff --git a/LoRa.cpp b/LoRa.cpp index ca28ab4..08df454 100644 --- a/LoRa.cpp +++ b/LoRa.cpp @@ -85,6 +85,8 @@ #define MAX_PKT_LENGTH 255 +bool lora_preinit_done = false; + LoRaClass::LoRaClass() : _spiSettings(8E6, MSBFIRST, SPI_MODE0), _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), @@ -97,13 +99,26 @@ LoRaClass::LoRaClass() : setTimeout(0); } -int LoRaClass::begin(long frequency) -{ +bool LoRaClass::preInit() { // setup pins pinMode(_ss, OUTPUT); // set SS high digitalWrite(_ss, HIGH); + + SPI.begin(); + // check version + uint8_t version = readRegister(REG_VERSION); + if (version != 0x12) { + return false; + } + + lora_preinit_done = true; + return true; +} + +int LoRaClass::begin(long frequency) +{ if (_reset != -1) { pinMode(_reset, OUTPUT); @@ -114,13 +129,10 @@ int LoRaClass::begin(long frequency) delay(10); } - // start SPI - SPI.begin(); - - // check version - uint8_t version = readRegister(REG_VERSION); - if (version != 0x12) { - return 0; + if (!lora_preinit_done) { + if (!preInit()) { + return false; + } } // put in sleep mode @@ -433,6 +445,11 @@ void LoRaClass::setTxPower(int level, int outputPin) { } } +uint8_t LoRaClass::getTxPower() { + byte txp = readRegister(REG_PA_CONFIG); + return txp; +} + void LoRaClass::setFrequency(long frequency) { _frequency = frequency; diff --git a/LoRa.h b/LoRa.h index 02a48c9..1df158d 100644 --- a/LoRa.h +++ b/LoRa.h @@ -54,6 +54,8 @@ public: void idle(); void sleep(); + bool preInit(); + uint8_t getTxPower(); void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); uint32_t getFrequency(); void setFrequency(long frequency); diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino index 1f5c132..51945c4 100644 --- a/RNode_Firmware.ino +++ b/RNode_Firmware.ino @@ -32,9 +32,6 @@ char sbuf[128]; bool packet_ready = false; #endif -// TODO: Implement -bool console_active = true; - void setup() { #if MCU_VARIANT == MCU_ESP32 boot_seq(); @@ -75,6 +72,26 @@ void setup() { // Set chip select, reset and interrupt // pins for the LoRa module LoRa.setPins(pin_cs, pin_reset, pin_dio); + + if (LoRa.preInit()) { + sx1276_installed = true; + uint32_t lfr = LoRa.getFrequency(); + if (lfr == 0) { + // Normal boot + } else if (lfr == M_FRQ_R) { + // Quick reboot + #if HAS_CONSOLE + if (rtc_get_reset_reason(0) == POWERON_RESET) { + console_active = true; + } + #endif + } else { + // Unknown boot + } + LoRa.setFrequency(M_FRQ_S); + } else { + sx1276_installed = false; + } #if HAS_DISPLAY disp_ready = display_init(); @@ -91,15 +108,17 @@ void setup() { bt_init_ran = true; #endif - kiss_indicate_reset(); - if (console_active) { console_start(); + } else { + kiss_indicate_reset(); } #endif // Validate board health, EEPROM and config validate_status(); + + LoRa.setFrequency(0); } void lora_receive() { @@ -864,16 +883,27 @@ void validate_status() { if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) { if (eeprom_checksum_valid()) { eeprom_ok = true; - #if PLATFORM == PLATFORM_ESP32 - if (device_init()) { + if (sx1276_installed) { + #if PLATFORM == PLATFORM_ESP32 + if (device_init()) { + hw_ready = true; + } else { + hw_ready = false; + } + #else hw_ready = true; - } else { - hw_ready = false; - } - #else - hw_ready = true; - #endif - + #endif + } else { + hw_ready = false; + Serial.write("No SX1276/SX1278 radio module found\r\n"); + #if HAS_DISPLAY + if (disp_ready) { + device_init_done = true; + update_display(); + } + #endif + } + if (hw_ready && eeprom_have_conf()) { eeprom_conf_load(); op_mode = MODE_TNC; @@ -969,8 +999,10 @@ void loop() { } #if MCU_VARIANT == MCU_ESP32 - buffer_serial(); - if (!fifo_isempty(&serialFIFO)) serial_poll(); + // if (!console_active) { + buffer_serial(); + if (!fifo_isempty(&serialFIFO)) serial_poll(); + // } #else if (!fifo_isempty_locked(&serialFIFO)) serial_poll(); #endif @@ -984,7 +1016,7 @@ void loop() { #endif #if HAS_BLUETOOTH - if (bt_ready) update_bt(); + if (!console_active && bt_ready) update_bt(); #endif } diff --git a/Release/spiffs.bin b/Release/spiffs.bin new file mode 100644 index 0000000000000000000000000000000000000000..909ec9f7717eec7ca03bf96d3b5815b3267b87eb GIT binary patch literal 2031616 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%0DluDfoh!(AKg&CNzSHKDiHj~`k}@94ei{6u>1 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 led_console_wait) { + // led_standby_ticks = 0; + + // if (led_standby_value <= led_standby_min) { + // led_standby_direction = 1; + // } else if (led_standby_value >= led_standby_max) { + // led_standby_direction = -1; + // } + + // uint8_t led_standby_intensity; + // led_standby_value += led_standby_direction; + // int led_standby_ti = led_standby_value - led_standby_lng; + + // if (led_standby_ti < 0) { + // led_standby_intensity = 0; + // } else if (led_standby_ti > led_standby_cut) { + // led_standby_intensity = led_standby_cut; + // } else { + // led_standby_intensity = led_standby_ti; + // } + // npset(led_standby_intensity, 0x00, led_standby_intensity); + // } + } + #else void led_indicate_standby() { led_standby_ticks++; @@ -418,6 +447,10 @@ int8_t led_standby_direction = 0; #endif } } + + void led_indicate_console() { + led_indicate_standby(); + } #endif #endif @@ -804,6 +837,11 @@ void set_implicit_length(uint8_t len) { } } +int getTxPower() { + uint8_t txp = LoRa.getTxPower(); + return (int)txp; +} + void setTXPower() { if (radio_online) { if (model == MODEL_A2) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);