diff --git a/.vscode/settings.json b/.vscode/settings.json index 9b72a2a2fa..8ea4339da6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -56,6 +56,7 @@ "HFSR", "Meshtastic", "NEMAGPS", + "RDEF", "Ublox", "bkpt", "cfsr", diff --git a/docs/_config.yml b/docs/_config.yml index 9c1ecd436e..1cb1a55bae 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,7 +1,7 @@ theme: jekyll-theme-cayman title: Meshtastic -description: An opensource hiking, pilot, skiing, Signal-App-extending GPS mesh communicator +description: An opensource hiking, pilot, skiing, secure GPS mesh communicator google_analytics: G-DRZ5H5EXHV include: [".well-known"] diff --git a/docs/software/power.md b/docs/software/power.md index cd0d2c9139..e98e5bd0df 100644 --- a/docs/software/power.md +++ b/docs/software/power.md @@ -32,11 +32,15 @@ From lower to higher power consumption. onEntry: setBluetoothOn(true) onExit: -- full on (ON) - Everything is on +- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off + onEntry: setBluetooth off, screen on + onExit: + +- full on (ON) - Everything is on, can eventually timeout and lower to a lower power state onEntry: setBluetoothOn(true), screen.setOn(true) onExit: screen.setOn(false) -- serial API usage (SERIAL) - Screen is on, device doesn't sleep, bluetooth off +- has power (POWER) - Screen is on, device doesn't sleep, bluetooth on, will stay in this state as long as we have power onEntry: setBluetooth off, screen on onExit: @@ -56,9 +60,11 @@ From lower to higher power consumption. - While in NB/DARK/ON: If we receive EVENT_NODEDB_UPDATED we transition to ON (so the new screen can be shown) - While in DARK: While the phone talks to us over BLE (EVENT_CONTACT_FROM_PHONE) reset any sleep timers and stay in DARK (needed for bluetooth sw update and nice user experience if the user is reading/replying to texts) - while in LS/NB/DARK: if SERIAL_CONNECTED, go to serial +- while in any state: if we have AC power, go to POWER ### events that decrease cpu activity +- While in POWER: if lose AC go to ON - While in SERIAL: if SERIAL_DISCONNECTED, go to NB - While in ON: If PRESS event occurs, reset screen_on_secs timer and tell the screen to handle the pess - While in ON: If it has been more than screen_on_secs since a press, lower to DARK diff --git a/proto b/proto index 4e431c8410..5cdd7bff56 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 4e431c841015edfdde925acf5ee4ac0a2272edff +Subproject commit 5cdd7bff56b0ea54351e5ea0e358e864b061078f diff --git a/src/Power.cpp b/src/Power.cpp index 1145c51c4f..7d032b6661 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -119,6 +119,7 @@ void Power::readPowerStatus() const PowerStatus powerStatus = PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse, batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent); + DEBUG_MSG("Read power stat %d\n", powerStatus.getHasUSB()); newStatus.notifyObservers(&powerStatus); // If we have a battery at all and it is less than 10% full, force deep sleep @@ -237,9 +238,11 @@ void Power::loop() } if (axp.isVbusRemoveIRQ()) { DEBUG_MSG("USB unplugged\n"); + powerFSM.trigger(EVENT_POWER_DISCONNECTED); } if (axp.isVbusPlugInIRQ()) { DEBUG_MSG("USB plugged In\n"); + powerFSM.trigger(EVENT_POWER_CONNECTED); } if (axp.isBattPlugInIRQ()) { DEBUG_MSG("Battery inserted\n"); diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 128947bcd2..f72e5842a4 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -4,8 +4,8 @@ #include "MeshService.h" #include "NodeDB.h" #include "configuration.h" -#include "main.h" #include "graphics/Screen.h" +#include "main.h" #include "sleep.h" #include "target_specific.h" @@ -123,6 +123,12 @@ static void serialEnter() screen.setOn(true); } +static void powerEnter() +{ + screen.setOn(true); + setBluetoothEnable(true); +} + static void onEnter() { screen.setOn(true); @@ -155,16 +161,20 @@ State stateDARK(darkEnter, NULL, NULL, "DARK"); State stateSERIAL(serialEnter, NULL, NULL, "SERIAL"); State stateBOOT(bootEnter, NULL, NULL, "BOOT"); State stateON(onEnter, NULL, NULL, "ON"); +State statePOWER(powerEnter, NULL, NULL, "POWER"); Fsm powerFSM(&stateBOOT); void PowerFSM_setup() { - powerFSM.add_timed_transition(&stateBOOT, &stateON, 3 * 1000, NULL, "boot timeout"); + // If we already have AC power go to POWER state after init, otherwise go to ON + bool hasPower = powerStatus && powerStatus->getHasUSB(); + DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower); + powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout"); powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, wakeForPing, "Wake timer"); - // Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB and then it - // handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet"); + // Note we don't really use this transition, because when we wake from light sleep we _always_ transition to NB and then + // it handles things powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet"); powerFSM.add_transition(&stateNB, &stateNB, EVENT_RECEIVED_PACKET, NULL, "Received packet, resetting win wake"); @@ -172,7 +182,10 @@ void PowerFSM_setup() powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press"); powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL, "Press"); + powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press"); powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers + powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress, + "Press"); // Allow button to work while in serial API // Handle critically low power battery by forcing deep sleep powerFSM.add_transition(&stateBOOT, &stateSDS, EVENT_LOW_BATTERY, NULL, "LowBat"); @@ -199,6 +212,14 @@ void PowerFSM_setup() powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); + powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); + + powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); + powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); + powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect"); powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); diff --git a/src/PowerFSM.h b/src/PowerFSM.h index c89ad9148e..4a219f5704 100644 --- a/src/PowerFSM.h +++ b/src/PowerFSM.h @@ -13,9 +13,11 @@ #define EVENT_BLUETOOTH_PAIR 7 #define EVENT_NODEDB_UPDATED 8 // NodeDB has a big enough change that we think you should turn on the screen #define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth -#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep +#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep #define EVENT_SERIAL_CONNECTED 11 #define EVENT_SERIAL_DISCONNECTED 12 +#define EVENT_POWER_CONNECTED 13 +#define EVENT_POWER_DISCONNECTED 14 extern Fsm powerFSM; diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index 683886ed5b..f246b607c9 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -40,6 +40,8 @@ void SerialConsole::onConnectionChanged(bool connected) if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api powerFSM.trigger(EVENT_SERIAL_CONNECTED); } else { + // FIXME, we get no notice of serial going away, we should instead automatically generate this event if we haven't + // received a packet in a while powerFSM.trigger(EVENT_SERIAL_DISCONNECTED); } } \ No newline at end of file diff --git a/src/esp32/WiFiServerAPI.cpp b/src/esp32/WiFiServerAPI.cpp index 974b0f3c12..109340bff4 100644 --- a/src/esp32/WiFiServerAPI.cpp +++ b/src/esp32/WiFiServerAPI.cpp @@ -52,6 +52,14 @@ void WiFiServerPort::loop() { auto client = available(); if (client) { - new WiFiServerAPI(client); + // Close any previous connection (see FIXME in header file) + if (openAPI) + delete openAPI; + + openAPI = new WiFiServerAPI(client); } + + if (openAPI) + // Allow idle processing so the API can read from its incoming stream + openAPI->loop(); } \ No newline at end of file diff --git a/src/esp32/WiFiServerAPI.h b/src/esp32/WiFiServerAPI.h index dcafa419d4..a9b1e39b44 100644 --- a/src/esp32/WiFiServerAPI.h +++ b/src/esp32/WiFiServerAPI.h @@ -29,6 +29,13 @@ class WiFiServerAPI : public StreamAPI */ class WiFiServerPort : public WiFiServer { + /** The currently open port + * + * FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to + * delegate to the worker. Once coroutines are implemented we can relax this restriction. + */ + WiFiServerAPI *openAPI = NULL; + public: WiFiServerPort(); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 0b57bfb3e6..14a2024580 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -31,8 +31,8 @@ along with this program. If not, see . #include "graphics/images.h" #include "main.h" #include "mesh-pb-constants.h" -#include "target_specific.h" #include "meshwifi/meshwifi.h" +#include "target_specific.h" #include "utils.h" using namespace meshtastic; /** @todo remove */ @@ -809,18 +809,18 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 { concurrency::LockGuard guard(&lock); snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str()); - - // Display power status - if (powerStatus->getHasBattery()) - drawBattery(display, x, y + 2, imgBattery, powerStatus); - else if (powerStatus->knowsUSB()) - display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); - // Display nodes status - drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); - // Display GPS status - drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); } + // Display power status + if (powerStatus->getHasBattery()) + drawBattery(display, x, y + 2, imgBattery, powerStatus); + else if (powerStatus->knowsUSB()) + display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); + // Display nodes status + drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); + // Display GPS status + drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); + // Draw the channel name display->drawString(x, y + FONT_HEIGHT, channelStr); // Draw our hardware ID to assist with bluetooth pairing diff --git a/src/main.cpp b/src/main.cpp index 4c4bff0f62..f5626e4552 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -215,9 +215,9 @@ void setup() // Currently only the tbeam has a PMU power = new Power(); - power->setup(); power->setStatusHandler(powerStatus); powerStatus->observe(&power->newStatus); + power->setup(); // Must be after status handler is installed, so that handler gets notified of the initial configuration #ifdef NRF52_SERIES nrf52Setup(); diff --git a/src/mesh/MeshRadio.h b/src/mesh/MeshRadio.h index 070c7c9a74..2cee8f89a1 100644 --- a/src/mesh/MeshRadio.h +++ b/src/mesh/MeshRadio.h @@ -6,87 +6,14 @@ #include "configuration.h" #include "mesh.pb.h" -// US channel settings -#define CH0_US 903.08f // MHz -#define CH_SPACING_US 2.16f // MHz -#define NUM_CHANNELS_US 13 - -// EU433 channel settings -#define CH0_EU433 433.175f // MHz -#define CH_SPACING_EU433 0.2f // MHz -#define NUM_CHANNELS_EU433 8 - -// EU865 channel settings -#define CH0_EU865 865.2f // MHz -#define CH_SPACING_EU865 0.3f // MHz -#define NUM_CHANNELS_EU865 10 - -// CN channel settings -#define CH0_CN 470.0f // MHz -#define CH_SPACING_CN 2.0f // MHz FIXME, this is just a guess for 470-510 -#define NUM_CHANNELS_CN 20 - -// JP channel settings (AS1 bandplan) -#define CH0_JP 920.0f // MHz -#define CH_SPACING_JP 0.5f -#define NUM_CHANNELS_JP 10 - -// TW channel settings (AS2 bandplan 923-925MHz) -#define CH0_TW 923.0f // MHz -#define CH_SPACING_TW 0.2 -#define NUM_CHANNELS_TW 10 - -// AU/NZ channel settings 915-928MHz -#define CH0_ANZ 916.0f // MHz - avoid overcrowding on 915.0 -#define CH_SPACING_ANZ 0.5f -#define NUM_CHANNELS_ANZ 20 - -// KR channel settings (KR920-923) -// Start from TTN download channel freq. (921.9f is for download, others are for uplink) -#define CH0_KR 921.9f // MHz -#define CH_SPACING_KR 0.2f -#define NUM_CHANNELS_KR 8 - -// FIXME add defs for other regions and use them here -#ifdef HW_VERSION_US -#define CH0 CH0_US -#define CH_SPACING CH_SPACING_US -#define NUM_CHANNELS NUM_CHANNELS_US -#elif defined(HW_VERSION_EU433) -#define CH0 CH0_EU433 -#define CH_SPACING CH_SPACING_EU433 -#define NUM_CHANNELS NUM_CHANNELS_EU433 -#elif defined(HW_VERSION_EU865) -#define CH0 CH0_EU865 -#define CH_SPACING CH_SPACING_EU865 -#define NUM_CHANNELS NUM_CHANNELS_EU865 -#elif defined(HW_VERSION_CN) -#define CH0 CH0_CN -#define CH_SPACING CH_SPACING_CN -#define NUM_CHANNELS NUM_CHANNELS_CN -#elif defined(HW_VERSION_JP) -// Also called AS1 bandplan -#define CH0 CH0_JP -#define CH_SPACING CH_SPACING_JP -#define NUM_CHANNELS NUM_CHANNELS_JP -#elif defined(HW_VERSION_TW) -// Also called AS2 bandplan -#define CH0 CH0_TW -#define CH_SPACING CH_SPACING_TW -#define NUM_CHANNELS NUM_CHANNELS_TW -#elif defined(HW_VERSION_ANZ) -// Australia and NZ -#define CH0 CH0_ANZ -#define CH_SPACING CH_SPACING_ANZ -#define NUM_CHANNELS NUM_CHANNELS_ANZ -#elif defined(HW_VERSION_KR) -// Republic of Korea -#define CH0 CH0_KR -#define CH_SPACING CH_SPACING_KR -#define NUM_CHANNELS NUM_CHANNELS_KR -#else -// HW version not set - assume US -#define CH0 CH0_US -#define CH_SPACING CH_SPACING_US -#define NUM_CHANNELS NUM_CHANNELS_US -#endif \ No newline at end of file +// Map from old region names to new region enums +struct RegionInfo { + RegionCode code; + uint8_t numChannels; + uint8_t powerLimit; // Or zero for not set + float freq; + float spacing; + const char *name; // EU433 etc +}; + +extern const RegionInfo regions[]; \ No newline at end of file diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 3356e09224..8baed24c18 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -197,12 +197,14 @@ void MeshService::loop() } /// The radioConfig object just changed, call this to force the hw to change to the new settings -void MeshService::reloadConfig() +bool MeshService::reloadConfig() { // If we can successfully set this radio to these settings, save them to disk - nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings + bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings configChanged.notifyObservers(NULL); nodeDB.saveToDisk(); + + return didReset; } /// The owner User record just got updated, update our node DB and broadcast the info into the mesh diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index a12f087b36..a30f3d4ecd 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -63,8 +63,10 @@ class MeshService */ void handleToRadio(MeshPacket &p); - /// The radioConfig object just changed, call this to force the hw to change to the new settings - void reloadConfig(); + /** The radioConfig object just changed, call this to force the hw to change to the new settings + * @return true if client devices should be sent a new set of radio configs + */ + bool reloadConfig(); /// The owner User record just got updated, update our node DB and broadcast the info into the mesh void reloadOwner(); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index bf82fe10a9..1f34eb5df5 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -6,6 +6,7 @@ #include "CryptoEngine.h" #include "GPS.h" +#include "MeshRadio.h" #include "NodeDB.h" #include "PacketHistory.h" #include "PowerFSM.h" @@ -13,9 +14,9 @@ #include "configuration.h" #include "error.h" #include "mesh-pb-constants.h" +#include "meshwifi/meshwifi.h" #include #include -#include "meshwifi/meshwifi.h" NodeDB nodeDB; @@ -103,14 +104,21 @@ const char *getChannelName() NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {} -void NodeDB::resetRadioConfig() +bool NodeDB::resetRadioConfig() { + bool didFactoryReset = false; + /// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128) static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf}; - if (radioConfig.preferences.sds_secs == 0) { - DEBUG_MSG("RadioConfig reset!\n"); + if (radioConfig.preferences.factory_reset) { + DEBUG_MSG("Performing factory reset!\n"); + installDefaultDeviceState(); + didFactoryReset = true; + } else if (radioConfig.preferences.sds_secs == 0) { + DEBUG_MSG("Fixing bogus RadioConfig!\n"); + radioConfig.preferences.send_owner_interval = 4; // per sw-design.md radioConfig.preferences.position_broadcast_secs = 15 * 60; radioConfig.preferences.wait_bluetooth_secs = 120; @@ -124,8 +132,8 @@ void NodeDB::resetRadioConfig() radioConfig.has_preferences = true; // radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast - // channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide bandwidth - // so incompatible radios can talk together + // channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide + // bandwidth so incompatible radios can talk together channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range channelSettings.tx_power = 0; // default @@ -148,12 +156,20 @@ void NodeDB::resetRadioConfig() radioConfig.preferences.position_broadcast_secs = 6 * 60; radioConfig.preferences.ls_secs = 60; } + + return didFactoryReset; } void NodeDB::installDefaultDeviceState() { + // We try to preserve the region setting because it will really bum users out if we discard it + String oldRegion = myNodeInfo.region; + RegionCode oldRegionCode = radioConfig.preferences.region; + memset(&devicestate, 0, sizeof(devicestate)); + *numNodes = 0; // Forget node DB + // init our devicestate with valid flags so protobuf writing/reading will work devicestate.has_my_node = true; devicestate.has_radio = true; @@ -182,6 +198,12 @@ void NodeDB::installDefaultDeviceState() // owner.short_name now sprintf(owner.long_name, "Unknown %02x%02x", ourMacAddr[4], ourMacAddr[5]); sprintf(owner.short_name, "?%02X", (unsigned)(myNodeInfo.my_node_num & 0xff)); + + // Restore region if possible + if (oldRegionCode != RegionCode_Unset) + radioConfig.preferences.region = oldRegionCode; + if (oldRegion.length()) + strcpy(myNodeInfo.region, oldRegion.c_str()); } void NodeDB::init() @@ -214,13 +236,29 @@ void NodeDB::init() // We set these _after_ loading from disk - because they come from the build and are more trusted than // what is stored in flash - strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region)); + if (xstr(HW_VERSION)[0]) + strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region)); + else + DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag + + // Check for the old style of region code strings, if found, convert to the new enum. + // Those strings will look like "1.0-EU433" + if (radioConfig.preferences.region == RegionCode_Unset && strncmp(myNodeInfo.region, "1.0-", 4) == 0) { + const char *regionStr = myNodeInfo.region + 4; // EU433 or whatever + for (const RegionInfo *r = regions; r->code != RegionCode_Unset; r++) + if (strcmp(r->name, regionStr) == 0) { + radioConfig.preferences.region = r->code; + break; + } + } + strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version)); strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model)); resetRadioConfig(); // If bogus settings got saved, then fix them - DEBUG_MSG("NODENUM=0x%x, dbsize=%d\n", myNodeInfo.my_node_num, *numNodes); + DEBUG_MSG("legacy_region=%s, region=%d, NODENUM=0x%x, dbsize=%d\n", myNodeInfo.region, radioConfig.preferences.region, + myNodeInfo.my_node_num, *numNodes); } // We reserve a few nodenums for future use @@ -408,11 +446,11 @@ void NodeDB::updateFrom(const MeshPacket &mp) powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); notifyObservers(true); // Force an update whether or not our node counts have changed -// This is going into the wifidev feature branch -// Only update the WebUI if WiFi is enabled -//#if WiFi_MODE != 0 -// notifyWebUI(); -//#endif + // This is going into the wifidev feature branch + // Only update the WebUI if WiFi is enabled + //#if WiFi_MODE != 0 + // notifyWebUI(); + //#endif } } break; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index bd22f730f4..e1a1b77d61 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -46,8 +46,13 @@ class NodeDB /// write to flash void saveToDisk(); - // Reinit radio config if needed, because sometimes a buggy android app might send us bogus settings - void resetRadioConfig(); + /** Reinit radio config if needed, because either: + * a) sometimes a buggy android app might send us bogus settings or + * b) the client set factory_reset + * + * @return true if the config was completely reset, in that case, we should send it back to the client + */ + bool resetRadioConfig(); /// given a subpacket sniffed from the network, update our DB state /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index caadf8563a..9ae5955d59 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -243,7 +243,10 @@ void PhoneAPI::handleSetRadio(const RadioConfig &r) { radioConfig = r; - service.reloadConfig(); + bool didReset = service.reloadConfig(); + if (didReset) { + state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client + } } /** diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 1a08f282e4..1fcee0dc37 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -10,6 +10,24 @@ #include #include +#define RDEF(name, freq, spacing, num_ch, power_limit) \ + { \ + RegionCode_##name, num_ch, power_limit, freq, spacing, #name \ + } + +const RegionInfo regions[] = { + RDEF(US, 903.08f, 2.16f, 13, 0), RDEF(EU433, 433.175f, 0.2f, 8, 0), RDEF(EU865, 865.2f, 0.3f, 10, 0), + RDEF(CN, 470.0f, 2.0f, 20, 0), + RDEF(JP, 920.0f, 0.5f, 10, 13), // See https://github.com/meshtastic/Meshtastic-device/issues/346 power level 13 + RDEF(ANZ, 916.0f, 0.5f, 20, 0), // AU/NZ channel settings 915-928MHz + RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel + // freq. (921.9f is for download, others are for uplink) + RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz) + RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last +}; + +static const RegionInfo *myRegion; + /** * ## LoRaWAN for North America @@ -77,7 +95,15 @@ RadioInterface::RadioInterface() { assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected - myNodeInfo.num_channels = NUM_CHANNELS; + if (!myRegion) { + const RegionInfo *r = regions; + for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++) + ; + myRegion = r; + DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name); + + myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have + } // Can't print strings this early - serial not setup yet // DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name); @@ -127,9 +153,12 @@ void RadioInterface::applyModemConfig() power = channelSettings.tx_power; + assert(myRegion); // Should have been found in init + // If user has manually specified a channel num, then use that, otherwise generate one by hashing the name - int channel_num = (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % NUM_CHANNELS; - freq = CH0 + CH_SPACING * channel_num; + int channel_num = + (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % myRegion->numChannels; + freq = myRegion->freq + myRegion->spacing * channel_num; DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num, power); @@ -143,9 +172,9 @@ void RadioInterface::limitPower() { uint8_t maxPower = 255; // No limit -#ifdef HW_VERSION_JP - maxPower = 13; // See https://github.com/meshtastic/Meshtastic-device/issues/346 -#endif + if (myRegion->powerLimit) + maxPower = myRegion->powerLimit; + if (power > maxPower) { DEBUG_MSG("Lowering transmit power because of regulatory limits\n"); power = maxPower; diff --git a/src/mesh/mesh.pb.c b/src/mesh/mesh.pb.c index ddc720cf33..11ec3f49f5 100644 --- a/src/mesh/mesh.pb.c +++ b/src/mesh/mesh.pb.c @@ -59,3 +59,4 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO) + diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 64b2758c3c..b726ab6e1c 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -25,6 +25,18 @@ typedef enum _Constants { Constants_Unused = 0 } Constants; +typedef enum _RegionCode { + RegionCode_Unset = 0, + RegionCode_US = 1, + RegionCode_EU433 = 2, + RegionCode_EU865 = 3, + RegionCode_CN = 4, + RegionCode_JP = 5, + RegionCode_ANZ = 6, + RegionCode_KR = 7, + RegionCode_TW = 8 +} RegionCode; + typedef enum _Data_Type { Data_Type_OPAQUE = 0, Data_Type_CLEAR_TEXT = 1, @@ -108,6 +120,8 @@ typedef struct _RadioConfig_UserPreferences { char wifi_ssid[33]; char wifi_password[64]; bool wifi_ap_mode; + RegionCode region; + bool factory_reset; pb_size_t ignore_incoming_count; uint32_t ignore_incoming[3]; } RadioConfig_UserPreferences; @@ -230,6 +244,10 @@ typedef struct _ToRadio { #define _Constants_MAX Constants_Unused #define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1)) +#define _RegionCode_MIN RegionCode_Unset +#define _RegionCode_MAX RegionCode_TW +#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1)) + #define _Data_Type_MIN Data_Type_OPAQUE #define _Data_Type_MAX Data_Type_CLEAR_READACK #define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1)) @@ -248,7 +266,7 @@ typedef struct _ToRadio { #define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0} #define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} -#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}} +#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, 0, 0, {0, 0, 0}} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} #define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0} @@ -264,7 +282,7 @@ typedef struct _ToRadio { #define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0} #define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} -#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, {0, 0, 0}} +#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, 0, 0, {0, 0, 0}} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} #define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0} @@ -322,7 +340,9 @@ typedef struct _ToRadio { #define RadioConfig_UserPreferences_wifi_ssid_tag 12 #define RadioConfig_UserPreferences_wifi_password_tag 13 #define RadioConfig_UserPreferences_wifi_ap_mode_tag 14 -#define RadioConfig_UserPreferences_ignore_incoming_tag 102 +#define RadioConfig_UserPreferences_region_tag 15 +#define RadioConfig_UserPreferences_factory_reset_tag 100 +#define RadioConfig_UserPreferences_ignore_incoming_tag 103 #define RouteDiscovery_route_tag 2 #define User_id_tag 1 #define User_long_name_tag 2 @@ -477,7 +497,9 @@ X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \ X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \ X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \ X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \ -X(a, STATIC, REPEATED, UINT32, ignore_incoming, 102) +X(a, STATIC, SINGULAR, UENUM, region, 15) \ +X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \ +X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) #define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_DEFAULT NULL @@ -613,11 +635,11 @@ extern const pb_msgdesc_t ManufacturingData_msg; #define SubPacket_size 274 #define MeshPacket_size 313 #define ChannelSettings_size 84 -#define RadioConfig_size 277 -#define RadioConfig_UserPreferences_size 188 +#define RadioConfig_size 282 +#define RadioConfig_UserPreferences_size 193 #define NodeInfo_size 132 #define MyNodeInfo_size 110 -#define DeviceState_size 5429 +#define DeviceState_size 5434 #define DebugString_size 258 #define FromRadio_size 322 #define ToRadio_size 316