diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 2f209c54..09d04266 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -19,6 +19,7 @@ struct NodePrefs { // persisted to file uint8_t tx_power_dbm; uint8_t telemetry_mode_base; uint8_t telemetry_mode_loc; + uint8_t telemetry_mode_env; float rx_delay_base; uint32_t ble_pin; }; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 5293c0c7..f97b47f4 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -53,6 +53,29 @@ void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs, const char* bu // v1.2.3 (1 Jan 2025) sprintf(_version_info, "%s (%s)", version, build_date); + +#ifdef PIN_BUZZER + buzzer.begin(); +#endif +} + +void UITask::soundBuzzer(UIEventType bet) { +#if defined(PIN_BUZZER) +switch(bet){ + case UIEventType::contactMessage: + // gemini's pick + buzzer.play("MsgRcv3:d=4,o=6,b=200:32e,32g,32b,16c7"); + break; + case UIEventType::channelMessage: + case UIEventType::roomMessage: + case UIEventType::newContactMessage: + case UIEventType::none: + default: + break; +} +#endif +// Serial.print("DBG: Buzzzzzz -> "); +// Serial.println((int) bet); } void UITask::msgRead(int msgcount) { @@ -248,6 +271,10 @@ void UITask::loop() { buttonHandler(); userLedHandler(); +#ifdef PIN_BUZZER + if (buzzer.isPlaying()) buzzer.loop(); +#endif + if (_display != NULL && _display->isOn()) { static bool _firstBoot = true; if(_firstBoot && millis() >= BOOT_SCREEN_MILLIS) { diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 58d68564..134b5a16 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -4,11 +4,27 @@ #include #include +#ifdef PIN_BUZZER + #include +#endif + #include "NodePrefs.h" + enum class UIEventType +{ + none, + contactMessage, + channelMessage, + roomMessage, + newContactMessage +}; + class UITask { DisplayDriver* _display; mesh::MainBoard* _board; +#ifdef PIN_BUZZER + genericBuzzer buzzer; +#endif unsigned long _next_refresh, _auto_off; bool _connected; uint32_t _pin_code; @@ -24,6 +40,7 @@ class UITask { void userLedHandler(); void renderBatteryIndicator(uint16_t batteryMilliVolts); + public: UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { @@ -37,5 +54,6 @@ public: void clearMsgPreview(); void msgRead(int msgcount); void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount); + void soundBuzzer(UIEventType bet = UIEventType::none); void loop(); }; diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 2cfb833b..03a8c90a 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -271,8 +271,8 @@ class MyMesh : public BaseChatMesh { void saveContacts() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/contacts3"); File file = _fs->open("/contacts3", FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open("/contacts3", "w"); #else @@ -336,8 +336,8 @@ class MyMesh : public BaseChatMesh { void saveChannels() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/channels2"); File file = _fs->open("/channels2", FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open("/channels2", "w"); #else @@ -393,8 +393,8 @@ class MyMesh : public BaseChatMesh { sprintf(path, "/bl/%s", fname); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove(path); File f = _fs->open(path, FILE_O_WRITE); - if (f) { f.seek(0); f.truncate(); } #elif defined(RP2040_PLATFORM) File f = _fs->open(path, "w"); #else @@ -483,10 +483,6 @@ class MyMesh : public BaseChatMesh { return 0; // queue is empty } - void soundBuzzer() { - // TODO - } - protected: float getAirtimeBudgetFactor() const override { return _prefs.airtime_factor; @@ -523,7 +519,9 @@ protected: _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } } else { - soundBuzzer(); + #ifdef DISPLAY_CLASS + ui_task.soundBuzzer(UIEventType::newContactMessage); + #endif } saveContacts(); @@ -584,7 +582,9 @@ protected: frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); } else { - soundBuzzer(); + #ifdef DISPLAY_CLASS + ui_task.soundBuzzer(UIEventType::contactMessage); + #endif } #ifdef DISPLAY_CLASS ui_task.newMsg(path_len, from.name, text, offline_queue_len); @@ -635,7 +635,9 @@ protected: frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle' _serial->writeFrame(frame, 1); } else { - soundBuzzer(); + #ifdef DISPLAY_CLASS + ui_task.soundBuzzer(UIEventType::channelMessage); + #endif } #ifdef DISPLAY_CLASS ui_task.newMsg(path_len, "Public", text, offline_queue_len); @@ -659,6 +661,12 @@ protected: permissions |= cp & TELEM_PERM_LOCATION; } + if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_ALL) { + permissions |= TELEM_PERM_ENVIRONMENT; + } else if (_prefs.telemetry_mode_env == TELEM_MODE_ALLOW_FLAGS) { + permissions |= cp & TELEM_PERM_ENVIRONMENT; + } + if (permissions & TELEM_PERM_BASE) { // only respond if base permission bit is set telemetry.reset(); telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); @@ -822,7 +830,7 @@ public: file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 file.read((uint8_t *) &_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 file.read((uint8_t *) &_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 - file.read(pad, 1); // 71 + file.read((uint8_t *) &_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 file.read((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 file.read(pad, 4); // 76 file.read((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 @@ -861,6 +869,11 @@ public: mesh::Utils::toHex(pub_key_hex, self_id.pub_key, 4); strcpy(_prefs.node_name, pub_key_hex); + // if name is provided as a build flag, use that as default node name instead + #ifdef ADVERT_NAME + strcpy(_prefs.node_name, ADVERT_NAME); + #endif + // load persisted prefs if (_fs->exists("/new_prefs")) { loadPrefsInt("/new_prefs"); // new filename @@ -913,8 +926,8 @@ public: void savePrefs() { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove("/new_prefs"); File file = _fs->open("/new_prefs", FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open("/new_prefs", "w"); #else @@ -938,7 +951,7 @@ public: file.write((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68 file.write((uint8_t *) &_prefs.telemetry_mode_base, sizeof(_prefs.telemetry_mode_base)); // 69 file.write((uint8_t *) &_prefs.telemetry_mode_loc, sizeof(_prefs.telemetry_mode_loc)); // 70 - file.write(pad, 1); // 71 + file.write((uint8_t *) &_prefs.telemetry_mode_env, sizeof(_prefs.telemetry_mode_env)); // 71 file.write((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72 file.write(pad, 4); // 76 file.write((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80 @@ -983,7 +996,7 @@ public: memcpy(&out_frame[i], &lon, 4); i += 4; out_frame[i++] = 0; // reserved out_frame[i++] = 0; // reserved - out_frame[i++] = (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ + out_frame[i++] = (_prefs.telemetry_mode_env << 4) | (_prefs.telemetry_mode_loc << 2) | (_prefs.telemetry_mode_base); // v5+ out_frame[i++] = _prefs.manual_add_contacts; uint32_t freq = _prefs.freq * 1000; @@ -1275,6 +1288,7 @@ public: if (len >= 3) { _prefs.telemetry_mode_base = cmd_frame[2] & 0x03; // v5+ _prefs.telemetry_mode_loc = (cmd_frame[2] >> 2) & 0x03; + _prefs.telemetry_mode_env = (cmd_frame[2] >> 4) & 0x03; } savePrefs(); writeOKFrame(); diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 1d0d847f..63ff20da 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -127,8 +127,8 @@ class MyMesh : public BaseChatMesh, ContactVisitor { void saveContacts() { #if defined(NRF52_PLATFORM) + _fs->remove("/contacts"); File file = _fs->open("/contacts", FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open("/contacts", "w"); #else @@ -341,8 +341,8 @@ public: void savePrefs() { #if defined(NRF52_PLATFORM) + _fs->remove("/node_prefs"); File file = _fs->open("/node_prefs", FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open("/node_prefs", "w"); #else diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 29b3a4e2..8b8296f5 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -74,8 +74,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { void CommonCLI::savePrefs(FILESYSTEM* fs) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + fs->remove("/com_prefs"); File file = fs->open("/com_prefs", FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = fs->open("/com_prefs", "w"); #else diff --git a/src/helpers/IdentityStore.cpp b/src/helpers/IdentityStore.cpp index 85f146c5..dc85d69c 100644 --- a/src/helpers/IdentityStore.cpp +++ b/src/helpers/IdentityStore.cpp @@ -47,8 +47,8 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id) { sprintf(filename, "%s/%s.id", _dir, name); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove(filename); File file = _fs->open(filename, FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open(filename, "w"); #else @@ -69,8 +69,8 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id, const sprintf(filename, "%s/%s.id", _dir, name); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + _fs->remove(filename); File file = _fs->open(filename, FILE_O_WRITE); - if (file) { file.seek(0); file.truncate(); } #elif defined(RP2040_PLATFORM) File file = _fs->open(filename, "w"); #else diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp new file mode 100644 index 00000000..6d39e905 --- /dev/null +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -0,0 +1,90 @@ +#include "EnvironmentSensorManager.h" + +static Adafruit_AHTX0 AHTX0; +static Adafruit_BME280 BME280; +static Adafruit_INA3221 INA3221; +static Adafruit_INA219 INA219(TELEM_INA219_ADDRESS); + +bool EnvironmentSensorManager::begin() { + + if (INA3221.begin(TELEM_INA3221_ADDRESS, &Wire)) { + MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); + MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); + + for(int i = 0; i < 3; i++) { + INA3221.setShuntResistance(i, TELEM_INA3221_SHUNT_VALUE); + } + INA3221_initialized = true; + } else { + INA3221_initialized = false; + MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS); + } + + if (INA219.begin(&Wire)) { + MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); + INA219_initialized = true; + } else { + INA219_initialized = false; + MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS); + } + + if (AHTX0.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { + MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); + AHTX0_initialized = true; + } else { + AHTX0_initialized = false; + MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS); + } + + if (BME280.begin(TELEM_BME280_ADDRESS, &Wire)) { + MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); + MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID); + BME280_initialized = true; + } else { + BME280_initialized = false; + MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS); + } + + return true; +} + +bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + next_available_channel = TELEM_CHANNEL_SELF + 1; + if (requester_permissions & TELEM_PERM_ENVIRONMENT) { + if (INA3221_initialized) { + for(int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) { + // add only enabled INA3221 channels to telemetry + if (INA3221.isChannelEnabled(i)) { + float voltage = INA3221.getBusVoltage(i); + float current = INA3221.getCurrentAmps(i); + telemetry.addVoltage(next_available_channel, voltage); + telemetry.addCurrent(next_available_channel, current); + telemetry.addPower(next_available_channel, voltage * current); + next_available_channel++; + } + } + } + if (INA219_initialized) { + telemetry.addVoltage(next_available_channel, INA219.getBusVoltage_V()); + telemetry.addCurrent(next_available_channel, INA219.getCurrent_mA() / 1000); + telemetry.addPower(next_available_channel, INA219.getPower_mW() / 1000); + next_available_channel++; + } + if (AHTX0_initialized) { + sensors_event_t humidity, temp; + AHTX0.getEvent(&humidity, &temp); + + telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity); + } + + if (BME280_initialized) { + telemetry.addTemperature(TELEM_CHANNEL_SELF, BME280.readTemperature()); + telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME280.readHumidity()); + telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME280.readPressure()); + telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA)); + } + } + + return true; +} \ No newline at end of file diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h new file mode 100644 index 00000000..fa3a1a1e --- /dev/null +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address +#define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address +#define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address +#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address + +#define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts +#define TELEM_INA3221_NUM_CHANNELS 3 + +#define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level + +class EnvironmentSensorManager : public SensorManager { +protected: + int next_available_channel = TELEM_CHANNEL_SELF + 1; + + bool INA3221_initialized = false; + bool INA219_initialized = false; + bool BME280_initialized = false; + bool AHTX0_initialized = false; +public: + EnvironmentSensorManager(){}; + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; +}; diff --git a/src/helpers/ui/buzzer.cpp b/src/helpers/ui/buzzer.cpp new file mode 100644 index 00000000..ccc18cd3 --- /dev/null +++ b/src/helpers/ui/buzzer.cpp @@ -0,0 +1,54 @@ +#ifdef PIN_BUZZER +#include "buzzer.h" + +void genericBuzzer::begin() { +// Serial.print("DBG: Setting up buzzer on pin "); +// Serial.println(PIN_BUZZER); + #ifdef PIN_BUZZER_EN + pinMode(PIN_BUZZER_EN, OUTPUT); + digitalWrite(PIN_BUZZER_EN, HIGH); + #endif + + quiet(false); + pinMode(PIN_BUZZER, OUTPUT); + startup(); +} + +void genericBuzzer::play(const char *melody) { + if (isPlaying()) // interrupt existing + { + rtttl::stop(); + } + + if (_is_quiet) return; + + rtttl::begin(PIN_BUZZER,melody); +// Serial.print("DBG: Playing melody - isQuiet: "); +// Serial.println(isQuiet()); +} + +bool genericBuzzer::isPlaying() { + return rtttl::isPlaying(); +} + +void genericBuzzer::loop() { + if (!rtttl::done()) rtttl::play(); +} + +void genericBuzzer::startup() { + play(startup_song); +} + +void genericBuzzer::shutdown() { + play(shutdown_song); +} + +void genericBuzzer::quiet(bool buzzer_state) { + _is_quiet = buzzer_state; +} + +bool genericBuzzer::isQuiet() { + return _is_quiet; +} + +#endif // ifdef PIN_BUZZER \ No newline at end of file diff --git a/src/helpers/ui/buzzer.h b/src/helpers/ui/buzzer.h new file mode 100644 index 00000000..0a500552 --- /dev/null +++ b/src/helpers/ui/buzzer.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +/* class abstracts underlying RTTTL library + + Just a simple imlementation to start. At the moment use same + melody for message and discovery + Suggest enum type for different sounds + - on message + - on discovery + + TODO + - make message ring tone configurable + +*/ + +class genericBuzzer +{ + public: + void begin(); // set up buzzer port + void play(const char *melody); // Generic play function + void loop(); // loop driven-nonblocking + void startup(); // play startup sound + void shutdown(); // play shutdown sound + bool isPlaying(); // returns true if a sound is still playing else false + void quiet(bool buzzer_state); // enables or disables the buzzer + bool isQuiet(); // get buzzer state on/off + + private: + // gemini's picks: + const char *startup_song = "Startup:d=4,o=5,b=160:16c6,16e6,8g6"; + const char *shutdown_song = "Shutdown:d=4,o=5,b=100:8g5,16e5,16c5"; + + bool _is_quiet = true; +}; diff --git a/variants/lilygo_tbeam/platformio.ini b/variants/lilygo_tbeam/platformio.ini index 14ce144e..c471e44c 100644 --- a/variants/lilygo_tbeam/platformio.ini +++ b/variants/lilygo_tbeam/platformio.ini @@ -7,6 +7,7 @@ build_flags = -D LILYGO_TBEAM -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper + -D SX127X_CURRENT_LIMIT=120 -D LORA_TX_POWER=20 -D P_LORA_TX_LED=4 -D PIN_BOARD_SDA=21 @@ -31,8 +32,6 @@ build_flags = -D BLE_PIN_CODE=123456 -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -; -D MESH_PACKET_LOGGING=1 -; -D MESH_DEBUG=1 ; -D RADIOLIB_DEBUG_BASIC=1 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 0316bef2..4367dc15 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -14,6 +14,7 @@ build_flags = -D DISPLAY_CLASS=SH1106Display -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 + -D SX126X_CURRENT_LIMIT=140 build_src_filter = ${esp32_base.build_src_filter} +<../variants/lilygo_tbeam_supreme_SX1262> + diff --git a/variants/nano_g2_ultra/nano-g2.cpp b/variants/nano_g2_ultra/nano-g2.cpp index 731fa873..8088ddd0 100644 --- a/variants/nano_g2_ultra/nano-g2.cpp +++ b/variants/nano_g2_ultra/nano-g2.cpp @@ -27,11 +27,10 @@ void NanoG2Ultra::begin() // for future use, sub-classes SHOULD call this from their begin() startup_reason = BD_STARTUP_NORMAL; + // set user button pinMode(PIN_BUTTON1, INPUT); + // the external notification circuit is shared for both buzzer and led - // need to find out the switch state or somehow write a function that can - // sound the buzzer or signal the led. the led will stay on once brought HIGH - // and can be then brought LOW. It turns off with a hardware btn. pinMode(EXT_NOTIFY_OUT, OUTPUT); digitalWrite(EXT_NOTIFY_OUT, LOW); diff --git a/variants/nano_g2_ultra/platformio.ini b/variants/nano_g2_ultra/platformio.ini index dc8e81ac..98feb35c 100644 --- a/variants/nano_g2_ultra/platformio.ini +++ b/variants/nano_g2_ultra/platformio.ini @@ -40,6 +40,7 @@ build_flags = -D BLE_DEBUG_LOGGING=1 -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display + -D PIN_BUZZER=4 ; -D ENABLE_PRIVATE_KEY_IMPORT=1 ; -D ENABLE_PRIVATE_KEY_EXPORT=1 ; -D MESH_PACKET_LOGGING=1 @@ -47,10 +48,12 @@ build_flags = build_src_filter = ${Nano_G2_Ultra.build_src_filter} + + + + +<../examples/companion_radio> lib_deps = ${Nano_G2_Ultra.lib_deps} densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SH110X @ ~2.1.13 adafruit/Adafruit GFX Library @ ^1.12.1 - + stevemarple/MicroNMEA @ ^2.0.6 + end2endzone/NonBlockingRTTTL@^1.3.0 diff --git a/variants/nano_g2_ultra/target.cpp b/variants/nano_g2_ultra/target.cpp index d8c5ef8e..33824f62 100644 --- a/variants/nano_g2_ultra/target.cpp +++ b/variants/nano_g2_ultra/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" #include +#include NanoG2Ultra board; @@ -10,10 +11,11 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +NanoG2UltraSensorManager sensors = NanoG2UltraSensorManager(nmea); #ifdef DISPLAY_CLASS - DISPLAY_CLASS display; +DISPLAY_CLASS display; #endif #ifndef LORA_CR @@ -73,6 +75,107 @@ void radio_set_tx_power(uint8_t dbm) radio.setOutputPower(dbm); } +void NanoG2UltraSensorManager::start_gps() +{ + if (!gps_active) + { + MESH_DEBUG_PRINTLN("starting GPS"); + digitalWrite(PIN_GPS_STANDBY, HIGH); + gps_active = true; + } +} + +void NanoG2UltraSensorManager::stop_gps() +{ + if (gps_active) + { + MESH_DEBUG_PRINTLN("stopping GPS"); + digitalWrite(PIN_GPS_STANDBY, LOW); + gps_active = false; + } +} + +bool NanoG2UltraSensorManager::begin() +{ + Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); // be sure to tx into rx and rx into tx + Serial1.begin(115200); + + pinMode(PIN_GPS_STANDBY, OUTPUT); + digitalWrite(PIN_GPS_STANDBY, HIGH); // Wake GPS from standby + delay(500); + + // We'll consider GPS detected if we see any data on Serial1 + if (Serial1.available() > 0) + { + MESH_DEBUG_PRINTLN("GPS detected"); + } + else + { + MESH_DEBUG_PRINTLN("No GPS detected"); + } + digitalWrite(GPS_EN, LOW); // Put GPS back into standby mode + return true; +} + +bool NanoG2UltraSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP &telemetry) +{ + if (requester_permissions & TELEM_PERM_LOCATION) + { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void NanoG2UltraSensorManager::loop() +{ + static long next_gps_update = 0; + _location->loop(); + if (millis() > next_gps_update && gps_active) // don't bother if gps position is not enabled + { + if (_location->isValid()) + { + node_lat = ((double)_location->getLatitude()) / 1000000.; + node_lon = ((double)_location->getLongitude()) / 1000000.; + node_altitude = ((double)_location->getAltitude()) / 1000.0; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + } + next_gps_update = millis() + (1000 * 60); // after initial update, only check every minute TODO: should be configurable + } +} + +int NanoG2UltraSensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) + +const char *NanoG2UltraSensorManager::getSettingName(int i) const +{ + return i == 0 ? "gps" : NULL; +} + +const char *NanoG2UltraSensorManager::getSettingValue(int i) const +{ + if (i == 0) + { + return gps_active ? "1" : "0"; + } + return NULL; +} + +bool NanoG2UltraSensorManager::setSettingValue(const char *name, const char *value) +{ + if (strcmp(name, "gps") == 0) + { + if (strcmp(value, "0") == 0) + { + stop_gps(); + } + else + { + start_gps(); + } + return true; + } + return false; // not supported +} + mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); diff --git a/variants/nano_g2_ultra/target.h b/variants/nano_g2_ultra/target.h index b0af0a8d..e0d891e9 100644 --- a/variants/nano_g2_ultra/target.h +++ b/variants/nano_g2_ultra/target.h @@ -8,16 +8,36 @@ #include #include #ifdef DISPLAY_CLASS - #include +#include #endif +#include + +class NanoG2UltraSensorManager : public SensorManager +{ + bool gps_active = false; + LocationProvider *_location; + + void start_gps(); + void stop_gps(); + +public: + NanoG2UltraSensorManager(LocationProvider &location) : _location(&location) {} + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP &telemetry) override; + void loop() override; + int getNumSettings() const override; + const char *getSettingName(int i) const override; + const char *getSettingValue(int i) const override; + bool setSettingValue(const char *name, const char *value) override; +}; extern NanoG2Ultra board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern NanoG2UltraSensorManager sensors; #ifdef DISPLAY_CLASS - extern DISPLAY_CLASS display; +extern DISPLAY_CLASS display; #endif bool radio_init(); diff --git a/variants/nano_g2_ultra/variant.h b/variants/nano_g2_ultra/variant.h index b0cc3100..b8a53fcf 100644 --- a/variants/nano_g2_ultra/variant.h +++ b/variants/nano_g2_ultra/variant.h @@ -130,11 +130,15 @@ External serial flash W25Q16JV_IQ * GPS pins */ +#define HAS_GPS 1 #define GPS_L76K #define PIN_GPS_STANDBY (0 + 13) // An output to wake GPS, low means allow sleep, high means force wake STANDBY #define PIN_GPS_TX (0 + 9) // This is for bits going TOWARDS the CPU #define PIN_GPS_RX (0 + 10) // This is for bits going TOWARDS the GPS +#define GPS_RX_PIN PIN_GPS_RX +#define GPS_TX_PIN PIN_GPS_TX + // #define GPS_THREAD_INTERVAL 50 diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 51fbc92f..568e20d7 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -15,12 +15,14 @@ build_flags = ${nrf52840_base.build_flags} -D PIN_USER_BTN=6 build_src_filter = ${nrf52840_base.build_src_filter} + + + +<../variants/promicro> lib_deps= ${nrf52840_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 - robtillaart/INA3221 @ ^0.4.1 - robtillaart/INA219 @ ^0.4.1 - adafruit/Adafruit AHTX0@^2.0.5 + adafruit/Adafruit INA3221 Library @ ^1.0.1 + adafruit/Adafruit INA219 @ ^1.2.3 + adafruit/Adafruit AHTX0 @ ^2.0.5 + adafruit/Adafruit BME280 Library @ ^2.3.0 [env:Faketec_Repeater] extends = Faketec @@ -120,11 +122,13 @@ build_flags = ${nrf52840_base.build_flags} build_src_filter = ${nrf52840_base.build_src_filter} + + + +<../variants/promicro> lib_deps= ${nrf52840_base.lib_deps} - robtillaart/INA3221 @ ^0.4.1 - robtillaart/INA219 @ ^0.4.1 - adafruit/Adafruit AHTX0@^2.0.5 + adafruit/Adafruit INA3221 Library @ ^1.0.1 + adafruit/Adafruit INA219 @ ^1.2.3 + adafruit/Adafruit AHTX0 @ ^2.0.5 + adafruit/Adafruit BME280 Library @ ^2.3.0 [env:ProMicroLLCC68_Repeater] extends = ProMicroLLCC68 diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index 6e3dc938..841bd1dc 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -10,7 +10,7 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -PromicroSensorManager sensors; +EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; @@ -79,120 +79,3 @@ mesh::LocalIdentity radio_new_identity() { return mesh::LocalIdentity(&rng); // create new random identity } -static INA3221 INA_3221(TELEM_INA3221_ADDRESS, &Wire); -static INA219 INA_219(TELEM_INA219_ADDRESS, &Wire); -static Adafruit_AHTX0 AHTX; - -bool PromicroSensorManager::begin() { - initINA3221(); - initINA219(); - initAHTX(); - - return true; -} - -bool PromicroSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { - int nextAvalableChannel = TELEM_CHANNEL_SELF + 1; - if (requester_permissions & TELEM_PERM_ENVIRONMENT) { - if (INA3221initialized) { - for(int i = 0; i < 3; i++) { - // add only enabled INA3221 channels to telemetry - if (INA3221_CHANNEL_ENABLED[i]) { - telemetry.addVoltage(nextAvalableChannel, INA_3221.getBusVoltage(i)); - telemetry.addCurrent(nextAvalableChannel, INA_3221.getCurrent(i)); - telemetry.addPower(nextAvalableChannel, INA_3221.getPower(i)); - nextAvalableChannel++; - } - } - } - if (INA219initialized) { - telemetry.addVoltage(nextAvalableChannel, INA_219.getBusVoltage()); - telemetry.addCurrent(nextAvalableChannel, INA_219.getCurrent()); - telemetry.addPower(nextAvalableChannel, INA_219.getPower()); - nextAvalableChannel++; - } - if (AHTXinitialized) { - sensors_event_t humidity, temp; - AHTX.getEvent(&humidity, &temp); - telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature); - telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity); - } - } - - return true; -} - -int PromicroSensorManager::getNumSettings() const { - return NUM_SENSOR_SETTINGS; -} - -const char* PromicroSensorManager::getSettingName(int i) const { - if (i >= 0 && i < NUM_SENSOR_SETTINGS) { - return INA3221_CHANNEL_NAMES[i]; - } - return NULL; -} - -const char* PromicroSensorManager::getSettingValue(int i) const { - if (i >= 0 && i < NUM_SENSOR_SETTINGS) { - return INA3221_CHANNEL_ENABLED[i] ? "1" : "0"; - } - return NULL; -} - -bool PromicroSensorManager::setSettingValue(const char* name, const char* value) { - for (int i = 0; i < NUM_SENSOR_SETTINGS; i++) { - if (strcmp(name, INA3221_CHANNEL_NAMES[i]) == 0) { - int channelEnabled = INA_3221.getEnableChannel(i); - if (strcmp(value, "1") == 0) { - INA3221_CHANNEL_ENABLED[i] = true; - if (!channelEnabled) { - INA_3221.enableChannel(i); - } - } else { - INA3221_CHANNEL_ENABLED[i] = false; - if (channelEnabled) { - INA_3221.disableChannel(i); - } - } - return true; - } - } - return false; -} - -void PromicroSensorManager::initINA3221() { - if (INA_3221.begin()) { - MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", INA_3221.getAddress()); - MESH_DEBUG_PRINTLN("%04X %04X %04X", INA_3221.getDieID(), INA_3221.getManufacturerID(), INA_3221.getConfiguration()); - - for(int i = 0; i < 3; i++) { - INA_3221.setShuntR(i, TELEM_INA3221_SHUNT_VALUE); - } - INA3221initialized = true; - } else { - INA3221initialized = false; - MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS); - } -} - -void PromicroSensorManager::initINA219() { - if (INA_219.begin()) { - MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", INA_219.getAddress()); - INA_219.setMaxCurrentShunt(TELEM_INA219_MAX_CURRENT, TELEM_INA219_SHUNT_VALUE); - INA219initialized = true; - } else { - INA219initialized = false; - MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS); - } -} - -void PromicroSensorManager::initAHTX() { - if (AHTX.begin(&Wire, 0, TELEM_AHTX_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); - AHTXinitialized = true; - } else { - AHTXinitialized = false; - MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS); - } -} diff --git a/variants/promicro/target.h b/variants/promicro/target.h index b7475b4a..c634d18a 100644 --- a/variants/promicro/target.h +++ b/variants/promicro/target.h @@ -7,19 +7,16 @@ #include #include #include -#include -#include -#include -#include #ifdef DISPLAY_CLASS #include #endif -#define NUM_SENSOR_SETTINGS 3 +#include extern PromicroBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; @@ -31,39 +28,3 @@ void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); void radio_set_tx_power(uint8_t dbm); mesh::LocalIdentity radio_new_identity(); -#define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address -#define TELEM_INA219_ADDRESS 0x40 // INA219 single channel current sensor I2C address -#define TELEM_AHTX_ADDRESS 0x38 // AHT10, AHT20 temperature and humidity sensor I2C address - -#define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts -#define TELEM_INA3221_SETTING_CH1 "INA3221-1" -#define TELEM_INA3221_SETTING_CH2 "INA3221-2" -#define TELEM_INA3221_SETTING_CH3 "INA3221-3" - -#define TELEM_INA219_SHUNT_VALUE 0.100 // shunt value in ohms (may differ between manufacturers) -#define TELEM_INA219_MAX_CURRENT 5 - -class PromicroSensorManager: public SensorManager { - bool INA3221initialized = false; - bool INA219initialized = false; - bool AHTXinitialized = false; - - // INA3221 channels in telemetry - const char * INA3221_CHANNEL_NAMES[NUM_SENSOR_SETTINGS] = { TELEM_INA3221_SETTING_CH1, TELEM_INA3221_SETTING_CH2, TELEM_INA3221_SETTING_CH3}; - bool INA3221_CHANNEL_ENABLED[NUM_SENSOR_SETTINGS] = {true, true, true}; - - void initINA3221(); - void initINA219(); - void initAHTX(); -public: - PromicroSensorManager(){}; - bool begin() override; - bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; - int getNumSettings() const override; - const char* getSettingName(int i) const override; - const char* getSettingValue(int i) const override; - bool setSettingValue(const char* name, const char* value) override; -}; - - -extern PromicroSensorManager sensors; \ No newline at end of file diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index 927655af..00974208 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -47,9 +47,13 @@ build_flags = ${t1000-e.build_flags} -D RX_BOOSTED_GAIN=true -D RF_SWITCH_TABLE -D DISPLAY_CLASS=NullDisplayDriver + -D PIN_BUZZER=25 + -D PIN_BUZZER_EN=37 ; P1/5 - required for T1000-E build_src_filter = ${t1000-e.build_src_filter} + + + +<../examples/companion_radio/*.cpp> lib_deps = ${t1000-e.lib_deps} densaugeo/base64 @ ~1.4.0 stevemarple/MicroNMEA @ ^2.0.6 + end2endzone/NonBlockingRTTTL@^1.3.0 \ No newline at end of file diff --git a/variants/t114/target.cpp b/variants/t114/target.cpp index fe3d14f9..a57a55e2 100644 --- a/variants/t114/target.cpp +++ b/variants/t114/target.cpp @@ -97,7 +97,7 @@ bool T114SensorManager::begin() { digitalWrite(GPS_EN, HIGH); // Power on GPS // Give GPS a moment to power up and send data - delay(500); + delay(1500); // We'll consider GPS detected if we see any data on Serial1 gps_detected = (Serial1.available() > 0); diff --git a/variants/t114/variant.h b/variants/t114/variant.h index d4802341..a0fd2e4f 100644 --- a/variants/t114/variant.h +++ b/variants/t114/variant.h @@ -109,7 +109,7 @@ //////////////////////////////////////////////////////////////////////////////// // Buzzer -#define PIN_BUZZER (46) +// #define PIN_BUZZER (46) ////////////////////////////////////////////////////////////////////////////////