2023-07-26 16:48:04 -07:00
|
|
|
/**
|
|
|
|
|
* @file Power.cpp
|
2023-07-26 16:58:14 -07:00
|
|
|
* @brief This file contains the implementation of the Power class, which is responsible for managing power-related functionality
|
|
|
|
|
* of the device. It includes battery level sensing, power management unit (PMU) control, and power state machine management. The
|
|
|
|
|
* Power class is used by the main device class to manage power-related functionality.
|
|
|
|
|
*
|
|
|
|
|
* The file also includes implementations of various battery level sensors, such as the AnalogBatteryLevel class, which assumes
|
|
|
|
|
* the battery voltage is attached via a voltage-divider to an analog input.
|
|
|
|
|
*
|
2023-07-26 16:48:04 -07:00
|
|
|
* This file is part of the Meshtastic project.
|
|
|
|
|
* For more information, see: https://meshtastic.org/
|
|
|
|
|
*/
|
2020-06-28 19:03:39 -07:00
|
|
|
#include "power.h"
|
2021-01-16 23:10:08 -08:00
|
|
|
#include "NodeDB.h"
|
2020-06-27 21:19:49 -07:00
|
|
|
#include "PowerFSM.h"
|
2023-01-21 14:34:29 +01:00
|
|
|
#include "buzz/buzz.h"
|
2022-05-07 20:31:21 +10:00
|
|
|
#include "configuration.h"
|
2020-06-27 21:19:49 -07:00
|
|
|
#include "main.h"
|
2023-08-17 20:22:34 -05:00
|
|
|
#include "meshUtils.h"
|
2020-06-27 21:19:49 -07:00
|
|
|
#include "sleep.h"
|
|
|
|
|
|
2023-11-22 09:30:55 -07:00
|
|
|
// Working USB detection for powered/charging states on the RAK platform
|
|
|
|
|
#ifdef NRF_APM
|
|
|
|
|
#include "nrfx_power.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-03-25 05:33:57 -06:00
|
|
|
#if defined(DEBUG_HEAP_MQTT) && !MESHTASTIC_EXCLUDE_MQTT
|
2023-02-04 14:56:04 +01:00
|
|
|
#include "mqtt/MQTT.h"
|
|
|
|
|
#include "target_specific.h"
|
Sync Wio lr1110 refresh with master (#4251)
* Fix protobuf structs handling (#4140)
* Fix protobuf structs handling
* Log instead of assert
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* BLE based logging (#4146)
* WIP log characteristic
* Bluetooth logging plumbing
* Characteristic
* Callback
* Check for nullptr
* Esp32 bluetooth impl
* Formatting
* Add thread name and log level
* Add settings guard
* Remove comments
* Field name
* Fixes esp32
* Open it up
* Whoops
* Move va_end past our logic
* Use `upload_protocol = esptool` as with the other heltec devices instead of `esp-builtin` (#4151)
* Standardize lat/lon position logs (#4156)
* Standardize lat/lon position logs
* Missed sone and condensed logs
* [create-pull-request] automated change (#4157)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
* Pause BLE logging during want_config flow (#4162)
* Update NimBLE to 1.4.2 (#4163)
* Implement replies for all telemetry types based on variant tag (#4164)
* Implement replies for all telemetry types based on variant tag
* Remove check for `ignoreRequest`: modules can set this, don't need to check
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Esptool is better
* Explicitly set characteristic
* fix INA3221 sensor (#4168)
- pass wire to begin()
- remove redundant setAddr() (already set in header)
* Show compass on waypoint frame; clear when waypoint deleted (#4116)
* Clear expired or deleted waypoint frame
* Return 0 to CallbackObserver
* Add a missing comment
* Draw compass for waypoint frame
* Display our own waypoints
* [create-pull-request] automated change (#4171)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
* Add semihosting support for nrf52 devices (#4137)
* Turn off vscode cmake prompt - we don't use cmake on meshtastic
* Add rak4631_dap variant for debugging with NanoDAP debug probe device.
* The rak device can also run freertos (which is underneath nrf52 arduino)
* Add semihosting support for nrf52840 devices
Initial platformio.ini file only supports rak4630
Default to non TCP for the semihosting log output for now...
Fixes https://github.com/meshtastic/firmware/issues/4135
* fix my botched merge - keep board_level = extra flag for rak3631_dbg
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* [create-pull-request] automated change (#4174)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
* Display alerts (#4170)
* Move static functions into Screen.h, show compass during calibration
* Move to _fontHeight macro to avoid collision
* Move some alert functions to new alert handler
* Catch missed reboot code
* ESP32 fixes
* Bump esp8266-oled-ssd1306
* Fixes for when a device has no screen
* Use new startAlert(char*) helper class
* Add EINK bits back to alert handling
* Add noop class for no-display devices
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Send file system manifest up on want_config (#4176)
* Send file system manifest up on want_config
* Platform specific methods
* Helps to actually make the change
* Clear
* tell vscode, if formatting, use whatever our trunk formatter wants (#4186)
without this flag if the user has set some other formatter (clang)
in their user level settings, it will be looking in the wrong directory
for the clang options (we want the options in .trunk/clang)
Note: formatOnSave is true in master, which means a bunch of our older
files are non compliant and if you edit them it will generate lots of
formatting related diffs. I guess I'll start letting that happen with
my future commits ;-).
* fix the build - would loop forever if there were no files to send (#4188)
* Show owner.short_name on boot (and E-Ink sleep screen) (#4134)
* Show owner.short_name on boot and sleep screen (on e-ink)
* Update Screen.cpp - new line for short_name
Boot screen short_name now below the region setting.
Looks better on small screens.
* Draw short_name on right
---------
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: todd-herbert <herbert.todd@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* nrf52 soft device will watchdog if you use ICE while BT on... (#4189)
so have debugger disable bluetooth.
* correct xiao_ble build preventing sx1262 init (#4191)
* Force a compile time failur if FromRadio or ToRadio get larger than (#4190)
a BLE packet size. We are actually very close to this threshold so
important to make sure we don't accidentally pass it.
* Clear vector after complete config state (#4194)
* Clear after complete config
* Don't collect . entries
* Log file name and size
* [create-pull-request] automated change (#4200)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
* Make the logs Colorful! (#4199)
* Squash needlessly static functions (#4183)
* Trim extra vprintf and filter for unprintable characters
* Deprecate Router Client role (and make it Client) (#4201)
* [create-pull-request] automated change (#4205)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
* Move waypoint (#4202)
* Move waypoint screen draw into the waypoint module
* Get the observer set up for the waypoint screen draw
* Static squashing: screen dimensions
Macros moved back to Screen.cpp, as a band-aid until we eventually move all those static functions into the Screen class.
* Move getCompassDiam into Screen class
(supress compiler warnings)
At this stage, the method is still static, because it's used by drawNodeInfo, which has no tidy reference to our screen instance.
This is probably just another band-aid until these static functions all move.
* Use new getCompassDiam function in AccelerometerThread
* Properly gate display code in WaypointModule
---------
Co-authored-by: Todd Herbert <herbert.todd@gmail.com>
* Fix flakey phone api transition from file manifest to complete (#4209)
* Try fix flakey phone api transition from file manifest to complete
* Skip
* enable colors in platformio serial monitor (#4217)
* When talking via serial, encapsulate log messages in protobufs if necessary (#4187)
* clean up RedirectablePrint::log so it doesn't have three very different implementations inline.
* remove NoopPrint - it is no longer needed
* when talking to API clients via serial, don't turn off log msgs instead encapsuate them
* fix the build - would loop forever if there were no files to send
* don't use Segger code if not talking to a Segger debugger
* when encapsulating logs, make sure the strings always has nul terminators
* nrf52 soft device will watchdog if you use ICE while BT on...
so have debugger disable bluetooth.
* Important to not print debug messages while writing to the toPhone scratch buffer
* don't include newlines if encapsulating log records as protobufs
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* [create-pull-request] automated change (#4218)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
* Fix SHT41 support (#4222)
* Add SHT41 Serial to I2c Detection Code
On the Seeed Wio-WM1110 Dev Kit board, the SHT41 chip was being
incorrectly detected as SHT31.
This patch adds the necessary serial number for the SHT41 chip to
be correctly detected.
fixes meshtastic/firmware#4221
* Add missing sensor read for SHT41
* Typo fix in logs - mhz - MHz (#4225)
As reported by karamo, a few different places in our logs had
incorrect capitalization of MHz.
fixes meshtastic/firmware#4126
* New new BLE logging characteristic with LogRecord protos (#4220)
* New UUID
* New log radio characteristic with LogRecord protobuf
* LogRecord
* Merge derp
* How did you get there
* Trunk
* Fix length
* Remove assert
* minor cleanup proposal (#4169)
* MESHTASTIC_EXCLUDE_WIFI and HAS_WIFI cleanup...
Our code was checking HAS_WIFI and the new MESHTASTIC_EXCLUDE_WIFI
flags in various places (even though EXCLUDE_WIFI forces HAS_WIFI
to 0). Instead just check HAS_WIFI, only use EXCLUDE_WIFI inside
configuration.h
* cleanup: use HAS_NETWORKING instead of HAS_WIFI || HAS_ETHERNET
We already had HAS_NETWORKING as flag in MQTT to mean 'we have
tcpip'. Generallize that and move it into configuration.h so that
we can use it elsewhere.
* Use #pragma once, because supported by gcc and all modern compilers
instead of #ifdef DOTHFILE_H etc...
---------
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
* Add PowerMon support (#4155)
* Turn off vscode cmake prompt - we don't use cmake on meshtastic
* Add rak4631_dap variant for debugging with NanoDAP debug probe device.
* The rak device can also run freertos (which is underneath nrf52 arduino)
* Add semihosting support for nrf52840 devices
Initial platformio.ini file only supports rak4630
Default to non TCP for the semihosting log output for now...
Fixes https://github.com/meshtastic/firmware/issues/4135
* powermon WIP (for https://github.com/meshtastic/firmware/issues/4136 )
* oops - mean't to mark the _dbg variant as an 'extra' board.
* powermon wip
* Make serial port on wio-sdk-wm1110 board work
By disabling the (inaccessible) adafruit USB
* Instrument (radiolib only for now) lora for powermon
per https://github.com/meshtastic/firmware/issues/4136
* powermon gps support
https://github.com/meshtastic/firmware/issues/4136
* Add CPU deep and light sleep powermon states
https://github.com/meshtastic/firmware/issues/4136
* Change the board/swversion bootstring so it is a new "structured" log msg.
* powermon wip
* add example script for getting esp S3 debugging working
Not yet used but I didn't want these nasty tricks to get lost yet.
* Add PowerMon reporting for screen and bluetooth pwr.
* make power.powermon_enables config setting work.
* update to latest protobufs
* fix bogus shellcheck warning
* make powermon optional (but default enabled because tiny and no runtime impact)
* tell vscode, if formatting, use whatever our trunk formatter wants
without this flag if the user has set some other formatter (clang)
in their user level settings, it will be looking in the wrong directory
for the clang options (we want the options in .trunk/clang)
Note: formatOnSave is true in master, which means a bunch of our older
files are non compliant and if you edit them it will generate lots of
formatting related diffs. I guess I'll start letting that happen with
my future commits ;-).
* add PowerStress module
* nrf52 arduino is built upon freertos, so let platformio debug it
* don't accidentally try to Segger ICE if we are using another ICE
* clean up RedirectablePrint::log so it doesn't have three very different implementations inline.
* remove NoopPrint - it is no longer needed
* when talking to API clients via serial, don't turn off log msgs instead encapsuate them
* fix the build - would loop forever if there were no files to send
* don't use Segger code if not talking to a Segger debugger
* when encapsulating logs, make sure the strings always has nul terminators
* nrf52 soft device will watchdog if you use ICE while BT on...
so have debugger disable bluetooth.
* Important to not print debug messages while writing to the toPhone scratch buffer
* don't include newlines if encapsulating log records as protobufs
* update to latest protobufs (needed for powermon goo)
* PowerStress WIP
* fix linter warning
* Cleanup buffer
* Merge hex for wm1110 target(s)
* Only sdk
* Sudo
* Fix exclude macros (#4233)
* fix MESHTASTIC_EXCLUDE_BLUETOOTH
* fix HAS_SCREEN=0
* fix MESHTASTIC_EXCLUDE_GPS
* fix typo in build-nrf52.sh (#4231)
chmod is the command, '+x' is the argument.
* Tidy Wireless Paper variant files (#4238)
* Quick tidy of pins_arduino.h
Matches requests made at https://github.com/meshtastic/firmware/pull/4226#discussion_r1664183480)
* Tidy variant.h
* Change deprecated ADC attenuation parameter
From 11dB to 12dB. Resolves compiler warning. Allegly, no impact on function: `This is deprecated, it behaves the same as `ADC_ATTEN_DB_12`
* Updated raspbian CI to update apt repository ahead of libbluetooth. (#4243)
* Fix BLE logging on nrf52 (#4244)
* allow ble logrecords to be fetched either by NOTIFY or INDICATE ble types
This allows 'lossless' log reading. If client has requested INDICATE
(rather than NOTIFY) each log record emitted via log() will have to fetched
by the client device before the meshtastic node can continue.
* Fix serious problem with nrf52 BLE logging.
When doing notifies of LogRecords it is important to use the
binary write routines - writing using the 'string' write won't work.
Because protobufs can contain \0 nuls inside of them which if being
parsed as a string will cause only a portion of the protobuf to be sent.
I noticed this because some log messages were not getting through.
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Fix build when HAS_NETWORKING is false on nrf52 (#4237)
(tested on a rak4631 by setting HAS_ETHERNET false when shrinking
image)
* If `toPhoneQueue` is full, still increment `fromNum` to avoid client never getting packets (#4246)
* Update to SoftDevice 7.3.0 for wio-sdk-wm1110 and wio-tracker-wm1110 (#4248)
* Update variant.h
* Update wio-tracker-wm1110.json
* Update wio-sdk-wm1110.json
* Update platformio.ini
* Update platformio.ini
* Add files via upload
* Add files via upload
* Update variant.h
---------
Co-authored-by: Mike <mikhael.skvortsov@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Mike G <mkgin@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Co-authored-by: Warren Guy <5602790+warrenguy@users.noreply.github.com>
Co-authored-by: todd-herbert <herbert.todd@gmail.com>
Co-authored-by: geeksville <kevinh@geeksville.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Alexander <156134901+Dorn8010@users.noreply.github.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: quimnut <github@dopegoat.com>
Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
Co-authored-by: Agent Blu, 006 <blu006@ucr.edu>
Co-authored-by: Mark Trevor Birss <markbirss@gmail.com>
2024-07-08 19:03:23 +08:00
|
|
|
#if HAS_WIFI
|
2023-02-04 14:56:04 +01:00
|
|
|
#include <WiFi.h>
|
|
|
|
|
#endif
|
2024-03-25 05:33:57 -06:00
|
|
|
#endif
|
2023-02-04 14:56:04 +01:00
|
|
|
|
2023-03-11 22:40:33 +01:00
|
|
|
#ifndef DELAY_FOREVER
|
2023-03-11 22:48:08 +01:00
|
|
|
#define DELAY_FOREVER portMAX_DELAY
|
2023-03-11 22:40:33 +01:00
|
|
|
#endif
|
|
|
|
|
|
2023-05-16 01:40:42 -04:00
|
|
|
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
|
2023-05-13 20:38:37 -04:00
|
|
|
|
2023-05-14 19:49:08 -04:00
|
|
|
#ifndef BAT_MEASURE_ADC_UNIT // ADC1 is default
|
2023-05-13 20:38:37 -04:00
|
|
|
static const adc1_channel_t adc_channel = ADC_CHANNEL;
|
2023-05-14 19:49:08 -04:00
|
|
|
static const adc_unit_t unit = ADC_UNIT_1;
|
|
|
|
|
#else // ADC2
|
2023-05-13 20:38:37 -04:00
|
|
|
static const adc2_channel_t adc_channel = ADC_CHANNEL;
|
2023-05-14 19:49:08 -04:00
|
|
|
static const adc_unit_t unit = ADC_UNIT_2;
|
2023-05-13 20:38:37 -04:00
|
|
|
RTC_NOINIT_ATTR uint64_t RTC_reg_b;
|
2023-05-14 19:49:08 -04:00
|
|
|
|
2023-05-16 01:40:42 -04:00
|
|
|
#endif // BAT_MEASURE_ADC_UNIT
|
2023-05-13 20:38:37 -04:00
|
|
|
|
|
|
|
|
esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
|
2023-05-26 11:16:40 +00:00
|
|
|
#ifndef ADC_ATTENUATION
|
2024-05-22 05:02:09 +03:00
|
|
|
static const adc_atten_t atten = ADC_ATTEN_DB_12;
|
2023-05-26 11:16:40 +00:00
|
|
|
#else
|
|
|
|
|
static const adc_atten_t atten = ADC_ATTENUATION;
|
|
|
|
|
#endif
|
2023-05-16 01:40:42 -04:00
|
|
|
#endif // BATTERY_PIN && ARCH_ESP32
|
2023-05-13 20:38:37 -04:00
|
|
|
|
2024-03-12 17:55:31 +01:00
|
|
|
#ifdef EXT_CHRG_DETECT
|
|
|
|
|
#ifndef EXT_CHRG_DETECT_MODE
|
|
|
|
|
static const uint8_t ext_chrg_detect_mode = INPUT;
|
|
|
|
|
#else
|
|
|
|
|
static const uint8_t ext_chrg_detect_mode = EXT_CHRG_DETECT_MODE;
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef EXT_CHRG_DETECT_VALUE
|
|
|
|
|
static const uint8_t ext_chrg_detect_value = HIGH;
|
|
|
|
|
#else
|
|
|
|
|
static const uint8_t ext_chrg_detect_value = EXT_CHRG_DETECT_VALUE;
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-04-29 22:10:49 +12:00
|
|
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
|
2023-06-02 06:32:34 -05:00
|
|
|
INA260Sensor ina260Sensor;
|
|
|
|
|
INA219Sensor ina219Sensor;
|
2023-11-17 05:46:59 -07:00
|
|
|
INA3221Sensor ina3221Sensor;
|
2023-06-02 06:32:34 -05:00
|
|
|
#endif
|
|
|
|
|
|
2024-06-16 14:24:36 +08:00
|
|
|
#if HAS_RAKPROT && !defined(ARCH_PORTDUINO)
|
|
|
|
|
RAK9154Sensor rak9154Sensor;
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-09-08 10:36:53 +08:00
|
|
|
#ifdef HAS_PMU
|
2022-09-06 15:58:33 +08:00
|
|
|
#include "XPowersAXP192.tpp"
|
2023-01-21 14:34:29 +01:00
|
|
|
#include "XPowersAXP2101.tpp"
|
|
|
|
|
#include "XPowersLibInterface.hpp"
|
2022-09-06 15:58:33 +08:00
|
|
|
XPowersLibInterface *PMU = NULL;
|
2021-03-14 19:00:20 -07:00
|
|
|
#else
|
2023-06-02 06:32:34 -05:00
|
|
|
|
2022-05-07 20:31:21 +10:00
|
|
|
// Copy of the base class defined in axp20x.h.
|
2023-07-14 17:25:20 -04:00
|
|
|
// I'd rather not include axp20x.h as it brings Wire dependency.
|
2022-05-07 20:31:21 +10:00
|
|
|
class HasBatteryLevel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* Battery state of charge, from 0 to 100 or -1 for unknown
|
|
|
|
|
*/
|
2022-09-06 15:58:33 +08:00
|
|
|
virtual int getBatteryPercent() { return -1; }
|
2022-05-07 20:31:21 +10:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The raw voltage of the battery or NAN if unknown
|
|
|
|
|
*/
|
2023-05-16 02:43:00 -04:00
|
|
|
virtual uint16_t getBattVoltage() { return 0; }
|
2022-05-07 20:31:21 +10:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return true if there is a battery installed in this unit
|
|
|
|
|
*/
|
|
|
|
|
virtual bool isBatteryConnect() { return false; }
|
|
|
|
|
|
2022-09-06 15:58:33 +08:00
|
|
|
virtual bool isVbusIn() { return false; }
|
|
|
|
|
virtual bool isCharging() { return false; }
|
2021-03-14 19:00:20 -07:00
|
|
|
};
|
2020-08-12 17:03:36 -07:00
|
|
|
#endif
|
|
|
|
|
|
2020-06-27 21:19:49 -07:00
|
|
|
bool pmu_irq = false;
|
|
|
|
|
|
|
|
|
|
Power *power;
|
|
|
|
|
|
2020-08-25 12:48:47 -07:00
|
|
|
using namespace meshtastic;
|
|
|
|
|
|
2021-07-24 19:44:27 +02:00
|
|
|
#ifndef AREF_VOLTAGE
|
2022-07-31 07:11:47 -05:00
|
|
|
#if defined(ARCH_NRF52)
|
2020-10-16 17:00:27 +08:00
|
|
|
/*
|
|
|
|
|
* Internal Reference is +/-0.6V, with an adjustable gain of 1/6, 1/5, 1/4,
|
|
|
|
|
* 1/3, 1/2 or 1, meaning 3.6, 3.0, 2.4, 1.8, 1.2 or 0.6V for the ADC levels.
|
|
|
|
|
*
|
|
|
|
|
* External Reference is VDD/4, with an adjustable gain of 1, 2 or 4, meaning
|
|
|
|
|
* VDD/4, VDD/2 or VDD for the ADC levels.
|
|
|
|
|
*
|
|
|
|
|
* Default settings are internal reference with 1/6 gain (GND..3.6V ADC range)
|
|
|
|
|
*/
|
|
|
|
|
#define AREF_VOLTAGE 3.6
|
|
|
|
|
#else
|
|
|
|
|
#define AREF_VOLTAGE 3.3
|
|
|
|
|
#endif
|
2021-07-24 19:44:27 +02:00
|
|
|
#endif
|
2020-10-16 17:00:27 +08:00
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
/**
|
|
|
|
|
* If this board has a battery level sensor, set this to a valid implementation
|
|
|
|
|
*/
|
|
|
|
|
static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input
|
|
|
|
|
*/
|
|
|
|
|
class AnalogBatteryLevel : public HasBatteryLevel
|
2020-06-27 21:19:49 -07:00
|
|
|
{
|
2020-08-12 17:03:36 -07:00
|
|
|
/**
|
|
|
|
|
* Battery state of charge, from 0 to 100 or -1 for unknown
|
|
|
|
|
*/
|
2022-09-06 15:58:33 +08:00
|
|
|
virtual int getBatteryPercent() override
|
2020-08-12 17:03:36 -07:00
|
|
|
{
|
2024-06-16 14:24:36 +08:00
|
|
|
#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU)
|
|
|
|
|
if (hasRAK()) {
|
|
|
|
|
return rak9154Sensor.getBusBatteryPercent();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-14 19:00:20 -07:00
|
|
|
float v = getBattVoltage();
|
2020-08-12 17:03:36 -07:00
|
|
|
|
2020-10-18 09:44:29 +08:00
|
|
|
if (v < noBatVolt)
|
2020-08-27 14:46:59 -07:00
|
|
|
return -1; // If voltage is super low assume no battery installed
|
2020-08-12 17:03:36 -07:00
|
|
|
|
2024-02-16 13:09:57 +01:00
|
|
|
#ifdef NO_BATTERY_LEVEL_ON_CHARGE
|
2021-08-10 09:23:26 +02:00
|
|
|
// This does not work on a RAK4631 with battery connected
|
2020-10-18 09:44:29 +08:00
|
|
|
if (v > chargingVolt)
|
|
|
|
|
return 0; // While charging we can't report % full on the battery
|
2021-08-10 09:23:26 +02:00
|
|
|
#endif
|
2024-02-16 13:09:57 +01:00
|
|
|
/**
|
|
|
|
|
* @brief Battery voltage lookup table interpolation to obtain a more
|
|
|
|
|
* precise percentage rather than the old proportional one.
|
|
|
|
|
* @author Gabriele Russo
|
|
|
|
|
* @date 06/02/2024
|
|
|
|
|
*/
|
|
|
|
|
float battery_SOC = 0.0;
|
|
|
|
|
uint16_t voltage = v / NUM_CELLS; // single cell voltage (average)
|
|
|
|
|
for (int i = 0; i < NUM_OCV_POINTS; i++) {
|
|
|
|
|
if (OCV[i] <= voltage) {
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
battery_SOC = 100.0; // 100% full
|
|
|
|
|
} else {
|
|
|
|
|
// interpolate between OCV[i] and OCV[i-1]
|
|
|
|
|
battery_SOC = (float)100.0 / (NUM_OCV_POINTS - 1.0) *
|
|
|
|
|
(NUM_OCV_POINTS - 1.0 - i + ((float)voltage - OCV[i]) / (OCV[i - 1] - OCV[i]));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return clamp((int)(battery_SOC), 0, 100);
|
2020-08-12 17:03:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-08-13 11:23:27 -07:00
|
|
|
* The raw voltage of the batteryin millivolts or NAN if unknown
|
2020-08-12 17:03:36 -07:00
|
|
|
*/
|
2023-05-16 02:43:00 -04:00
|
|
|
virtual uint16_t getBattVoltage() override
|
2020-08-12 17:03:36 -07:00
|
|
|
{
|
2021-03-14 19:00:20 -07:00
|
|
|
|
2024-06-16 14:24:36 +08:00
|
|
|
#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU)
|
|
|
|
|
if (hasRAK()) {
|
|
|
|
|
return getRAKVoltage();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-04-29 22:10:49 +12:00
|
|
|
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
2023-06-02 06:32:34 -05:00
|
|
|
if (hasINA()) {
|
|
|
|
|
LOG_DEBUG("Using INA on I2C addr 0x%x for device battery voltage\n", config.power.device_battery_ina_address);
|
|
|
|
|
return getINAVoltage();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-14 19:00:20 -07:00
|
|
|
#ifndef ADC_MULTIPLIER
|
|
|
|
|
#define ADC_MULTIPLIER 2.0
|
2022-05-07 20:31:21 +10:00
|
|
|
#endif
|
2021-03-14 19:00:20 -07:00
|
|
|
|
2022-12-05 16:40:23 +01:00
|
|
|
#ifndef BATTERY_SENSE_SAMPLES
|
2024-01-29 12:14:21 +00:00
|
|
|
#define BATTERY_SENSE_SAMPLES \
|
2024-02-16 13:09:57 +01:00
|
|
|
15 // Set the number of samples, it has an effect of increasing sensitivity in complex electromagnetic environment.
|
2022-12-05 16:40:23 +01:00
|
|
|
#endif
|
|
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
#ifdef BATTERY_PIN
|
2022-05-01 14:21:30 -05:00
|
|
|
// Override variant or default ADC_MULTIPLIER if we have the override pref
|
2023-01-21 14:34:29 +01:00
|
|
|
float operativeAdcMultiplier =
|
|
|
|
|
config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
|
2022-05-07 20:31:21 +10:00
|
|
|
// Do not call analogRead() often.
|
2021-03-14 19:00:20 -07:00
|
|
|
const uint32_t min_read_interval = 5000;
|
|
|
|
|
if (millis() - last_read_time_ms > min_read_interval) {
|
|
|
|
|
last_read_time_ms = millis();
|
2022-07-30 20:12:28 +08:00
|
|
|
|
|
|
|
|
uint32_t raw = 0;
|
2024-01-29 12:14:21 +00:00
|
|
|
float scaled = 0;
|
|
|
|
|
|
|
|
|
|
#ifdef ARCH_ESP32 // ADC block for espressif platforms
|
|
|
|
|
raw = espAdcRead();
|
|
|
|
|
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
|
|
|
|
|
scaled *= operativeAdcMultiplier;
|
|
|
|
|
#else // block for all other platforms
|
2023-05-16 01:40:42 -04:00
|
|
|
for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
|
|
|
|
raw += analogRead(BATTERY_PIN);
|
|
|
|
|
}
|
2023-01-21 14:34:29 +01:00
|
|
|
raw = raw / BATTERY_SENSE_SAMPLES;
|
2024-01-29 12:14:21 +00:00
|
|
|
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
|
|
|
|
|
#endif
|
2024-05-03 00:14:44 +12:00
|
|
|
|
|
|
|
|
if (!initial_read_done) {
|
|
|
|
|
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
|
|
|
|
|
if (scaled > last_read_value)
|
|
|
|
|
last_read_value = scaled;
|
|
|
|
|
initial_read_done = true;
|
|
|
|
|
} else {
|
|
|
|
|
// Already initialized - filter this reading
|
|
|
|
|
last_read_value += (scaled - last_read_value) * 0.5; // Virtual LPF
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-16 13:09:57 +01:00
|
|
|
// LOG_DEBUG("battery gpio %d raw val=%u scaled=%u filtered=%u\n", BATTERY_PIN, raw, (uint32_t)(scaled), (uint32_t)
|
|
|
|
|
// (last_read_value));
|
2021-03-14 19:00:20 -07:00
|
|
|
}
|
2024-02-16 13:09:57 +01:00
|
|
|
return last_read_value;
|
2023-05-13 20:38:37 -04:00
|
|
|
#endif // BATTERY_PIN
|
2024-01-29 12:14:21 +00:00
|
|
|
return 0;
|
2020-08-12 17:03:36 -07:00
|
|
|
}
|
2020-06-27 21:19:49 -07:00
|
|
|
|
2024-01-29 12:14:21 +00:00
|
|
|
#if defined(ARCH_ESP32) && !defined(HAS_PMU) && defined(BATTERY_PIN)
|
|
|
|
|
/**
|
|
|
|
|
* ESP32 specific function for getting calibrated ADC reads
|
|
|
|
|
*/
|
|
|
|
|
uint32_t espAdcRead()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
uint32_t raw = 0;
|
2024-02-16 13:09:57 +01:00
|
|
|
uint8_t raw_c = 0; // raw reading counter
|
2024-01-29 12:14:21 +00:00
|
|
|
|
|
|
|
|
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
2024-02-16 13:09:57 +01:00
|
|
|
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
|
|
|
|
|
pinMode(ADC_CTRL, OUTPUT);
|
|
|
|
|
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
|
|
|
|
|
delay(10);
|
2024-01-29 12:14:21 +00:00
|
|
|
#endif
|
|
|
|
|
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
2024-02-16 13:09:57 +01:00
|
|
|
int val_ = adc1_get_raw(adc_channel);
|
|
|
|
|
if (val_ >= 0) { // save only valid readings
|
|
|
|
|
raw += val_;
|
|
|
|
|
raw_c++;
|
|
|
|
|
}
|
|
|
|
|
// delayMicroseconds(100);
|
2024-01-29 12:14:21 +00:00
|
|
|
}
|
2024-02-16 13:09:57 +01:00
|
|
|
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
|
|
|
|
|
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
|
2024-01-29 12:14:21 +00:00
|
|
|
#endif
|
2024-02-14 05:20:48 +13:00
|
|
|
#else // ADC2
|
|
|
|
|
#ifdef ADC_CTRL
|
|
|
|
|
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
|
|
|
|
pinMode(ADC_CTRL, OUTPUT);
|
|
|
|
|
digitalWrite(ADC_CTRL, LOW); // ACTIVE LOW
|
|
|
|
|
delay(10);
|
|
|
|
|
#endif
|
|
|
|
|
#endif // End ADC_CTRL
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3
|
|
|
|
|
// ADC2 wifi bug workaround not required, breaks compile
|
|
|
|
|
// On ESP32S3, ADC2 can take turns with Wifi (?)
|
|
|
|
|
|
|
|
|
|
int32_t adc_buf;
|
|
|
|
|
esp_err_t read_result;
|
|
|
|
|
|
|
|
|
|
// Multiple samples
|
|
|
|
|
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
|
|
|
|
adc_buf = 0;
|
|
|
|
|
read_result = -1;
|
|
|
|
|
|
|
|
|
|
read_result = adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
|
|
|
|
|
if (read_result == ESP_OK) {
|
|
|
|
|
raw += adc_buf;
|
|
|
|
|
raw_c++; // Count valid samples
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DEBUG("An attempt to sample ADC2 failed\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-16 13:09:57 +01:00
|
|
|
#else // Other ESP32
|
2024-01-29 12:14:21 +00:00
|
|
|
int32_t adc_buf = 0;
|
|
|
|
|
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
|
|
|
|
// ADC2 wifi bug workaround, see
|
|
|
|
|
// https://github.com/espressif/arduino-esp32/issues/102
|
|
|
|
|
WRITE_PERI_REG(SENS_SAR_READ_CTRL2_REG, RTC_reg_b);
|
|
|
|
|
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV);
|
|
|
|
|
adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
|
|
|
|
|
raw += adc_buf;
|
2024-02-14 05:20:48 +13:00
|
|
|
raw_c++;
|
2024-01-29 12:14:21 +00:00
|
|
|
}
|
2024-02-16 13:09:57 +01:00
|
|
|
#endif // BAT_MEASURE_ADC_UNIT
|
2024-02-14 05:20:48 +13:00
|
|
|
|
|
|
|
|
#ifdef ADC_CTRL
|
|
|
|
|
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
|
|
|
|
digitalWrite(ADC_CTRL, HIGH);
|
|
|
|
|
#endif
|
|
|
|
|
#endif // End ADC_CTRL
|
|
|
|
|
|
|
|
|
|
#endif // End BAT_MEASURE_ADC_UNIT
|
|
|
|
|
return (raw / (raw_c < 1 ? 1 : raw_c));
|
2024-01-29 12:14:21 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
/**
|
|
|
|
|
* return true if there is a battery installed in this unit
|
|
|
|
|
*/
|
2023-08-17 20:22:34 -05:00
|
|
|
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
|
2020-10-18 09:44:29 +08:00
|
|
|
|
|
|
|
|
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
|
|
|
|
/// in power
|
2023-03-31 13:43:42 +03:00
|
|
|
/// On some boards we don't have the power management chip (like AXPxxxx)
|
|
|
|
|
/// so we use EXT_PWR_DETECT GPIO pin to detect external power source
|
2023-01-21 14:34:29 +01:00
|
|
|
virtual bool isVbusIn() override
|
|
|
|
|
{
|
2023-03-31 13:43:42 +03:00
|
|
|
#ifdef EXT_PWR_DETECT
|
2024-06-16 16:29:45 +02:00
|
|
|
#ifdef HELTEC_CAPSULE_SENSOR_V3
|
|
|
|
|
// if external powered that pin will be pulled down
|
|
|
|
|
if (digitalRead(EXT_PWR_DETECT) == LOW) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// if it's not LOW - check the battery
|
|
|
|
|
#else
|
|
|
|
|
// if external powered that pin will be pulled up
|
|
|
|
|
if (digitalRead(EXT_PWR_DETECT) == HIGH) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// if it's not HIGH - check the battery
|
|
|
|
|
#endif
|
2023-03-31 13:43:42 +03:00
|
|
|
#endif
|
2023-01-21 14:34:29 +01:00
|
|
|
return getBattVoltage() > chargingVolt;
|
|
|
|
|
}
|
2020-10-18 09:44:29 +08:00
|
|
|
|
|
|
|
|
/// Assume charging if we have a battery and external power is connected.
|
|
|
|
|
/// we can't be smart enough to say 'full'?
|
2024-03-12 17:55:31 +01:00
|
|
|
virtual bool isCharging() override
|
|
|
|
|
{
|
2024-06-16 14:24:36 +08:00
|
|
|
#if defined(HAS_RAKPROT) && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU)
|
|
|
|
|
if (hasRAK()) {
|
|
|
|
|
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-03-12 17:55:31 +01:00
|
|
|
#ifdef EXT_CHRG_DETECT
|
|
|
|
|
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
|
|
|
|
|
#else
|
|
|
|
|
return isBatteryConnect() && isVbusIn();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2020-10-18 09:44:29 +08:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
|
|
|
|
/// in power
|
2021-05-09 10:31:42 +08:00
|
|
|
|
2024-02-16 13:09:57 +01:00
|
|
|
/// For heltecs with no battery connected, the measured voltage is 2204, so
|
|
|
|
|
// need to be higher than that, in this case is 2500mV (3000-500)
|
|
|
|
|
const uint16_t OCV[NUM_OCV_POINTS] = {OCV_ARRAY};
|
|
|
|
|
const float chargingVolt = (OCV[0] + 10) * NUM_CELLS;
|
|
|
|
|
const float noBatVolt = (OCV[NUM_OCV_POINTS - 1] - 500) * NUM_CELLS;
|
|
|
|
|
// Start value from minimum voltage for the filter to not start from 0
|
|
|
|
|
// that could trigger some events.
|
2024-05-03 00:14:44 +12:00
|
|
|
// This value is over-written by the first ADC reading, it the voltage seems reasonable.
|
|
|
|
|
bool initial_read_done = false;
|
2024-02-16 13:09:57 +01:00
|
|
|
float last_read_value = (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS);
|
2021-03-14 19:00:20 -07:00
|
|
|
uint32_t last_read_time_ms = 0;
|
2023-06-02 06:32:34 -05:00
|
|
|
|
2024-06-16 14:24:36 +08:00
|
|
|
#if defined(HAS_RAKPROT)
|
|
|
|
|
|
|
|
|
|
uint16_t getRAKVoltage() { return rak9154Sensor.getBusVoltageMv(); }
|
|
|
|
|
|
|
|
|
|
bool hasRAK()
|
|
|
|
|
{
|
|
|
|
|
if (!rak9154Sensor.isInitialized())
|
|
|
|
|
return rak9154Sensor.runOnce() > 0;
|
|
|
|
|
return rak9154Sensor.isRunning();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2024-04-29 22:10:49 +12:00
|
|
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
|
2023-06-02 06:32:34 -05:00
|
|
|
uint16_t getINAVoltage()
|
|
|
|
|
{
|
2023-10-15 02:33:45 +02:00
|
|
|
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
2023-06-02 06:32:34 -05:00
|
|
|
return ina219Sensor.getBusVoltageMv();
|
2023-10-15 02:33:45 +02:00
|
|
|
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
|
|
|
|
|
config.power.device_battery_ina_address) {
|
2023-06-02 06:32:34 -05:00
|
|
|
return ina260Sensor.getBusVoltageMv();
|
2023-11-17 05:46:59 -07:00
|
|
|
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first ==
|
|
|
|
|
config.power.device_battery_ina_address) {
|
|
|
|
|
return ina3221Sensor.getBusVoltageMv();
|
2023-06-02 06:32:34 -05:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool hasINA()
|
|
|
|
|
{
|
|
|
|
|
if (!config.power.device_battery_ina_address) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-10-15 02:33:45 +02:00
|
|
|
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
|
2023-06-02 06:32:34 -05:00
|
|
|
if (!ina219Sensor.isInitialized())
|
|
|
|
|
return ina219Sensor.runOnce() > 0;
|
|
|
|
|
return ina219Sensor.isRunning();
|
2023-10-15 02:33:45 +02:00
|
|
|
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
|
|
|
|
|
config.power.device_battery_ina_address) {
|
2023-06-02 06:32:34 -05:00
|
|
|
if (!ina260Sensor.isInitialized())
|
|
|
|
|
return ina260Sensor.runOnce() > 0;
|
|
|
|
|
return ina260Sensor.isRunning();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-07-24 19:44:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AnalogBatteryLevel analogLevel;
|
2020-06-27 21:19:49 -07:00
|
|
|
|
2022-05-07 20:31:21 +10:00
|
|
|
Power::Power() : OSThread("Power")
|
|
|
|
|
{
|
2022-01-24 07:00:14 +00:00
|
|
|
statusHandler = {};
|
|
|
|
|
low_voltage_counter = 0;
|
2022-12-16 20:46:43 +01:00
|
|
|
#ifdef DEBUG_HEAP
|
2023-02-17 12:31:51 +01:00
|
|
|
lastheap = memGet.getFreeHeap();
|
2022-12-16 20:46:43 +01:00
|
|
|
#endif
|
2022-01-24 07:00:14 +00:00
|
|
|
}
|
2020-10-09 14:16:51 +08:00
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
bool Power::analogInit()
|
|
|
|
|
{
|
2023-03-31 14:42:50 +03:00
|
|
|
#ifdef EXT_PWR_DETECT
|
2024-06-16 16:29:45 +02:00
|
|
|
#ifdef HELTEC_CAPSULE_SENSOR_V3
|
|
|
|
|
pinMode(EXT_PWR_DETECT, INPUT_PULLUP);
|
|
|
|
|
#else
|
|
|
|
|
pinMode(EXT_PWR_DETECT, INPUT);
|
|
|
|
|
#endif
|
2023-03-31 14:42:50 +03:00
|
|
|
#endif
|
2024-03-12 17:55:31 +01:00
|
|
|
#ifdef EXT_CHRG_DETECT
|
|
|
|
|
pinMode(EXT_CHRG_DETECT, ext_chrg_detect_mode);
|
|
|
|
|
#endif
|
2023-03-31 14:42:50 +03:00
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
#ifdef BATTERY_PIN
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Using analog input %d for battery level\n", BATTERY_PIN);
|
2020-10-16 17:00:27 +08:00
|
|
|
|
|
|
|
|
// disable any internal pullups
|
2023-05-16 01:40:42 -04:00
|
|
|
pinMode(BATTERY_PIN, INPUT);
|
2023-05-13 20:38:37 -04:00
|
|
|
|
|
|
|
|
#ifndef BATTERY_SENSE_RESOLUTION_BITS
|
2023-05-16 01:40:42 -04:00
|
|
|
#define BATTERY_SENSE_RESOLUTION_BITS 10
|
2023-05-13 20:38:37 -04:00
|
|
|
#endif
|
2020-10-16 17:00:27 +08:00
|
|
|
|
2023-05-16 01:40:42 -04:00
|
|
|
#ifdef ARCH_ESP32 // ESP32 needs special analog stuff
|
|
|
|
|
|
|
|
|
|
#ifndef ADC_WIDTH // max resolution by default
|
|
|
|
|
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
|
|
|
|
|
#else
|
|
|
|
|
static const adc_bits_width_t width = ADC_WIDTH;
|
|
|
|
|
#endif
|
2023-05-13 20:38:37 -04:00
|
|
|
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
|
|
|
|
adc1_config_width(width);
|
|
|
|
|
adc1_config_channel_atten(adc_channel, atten);
|
|
|
|
|
#else // ADC2
|
|
|
|
|
adc2_config_channel_atten(adc_channel, atten);
|
2024-02-14 05:20:48 +13:00
|
|
|
#ifndef CONFIG_IDF_TARGET_ESP32S3
|
2023-05-14 19:49:08 -04:00
|
|
|
// ADC2 wifi bug workaround
|
2024-02-14 05:20:48 +13:00
|
|
|
// Not required with ESP32S3, breaks compile
|
2023-05-13 20:38:37 -04:00
|
|
|
RTC_reg_b = READ_PERI_REG(SENS_SAR_READ_CTRL2_REG);
|
2024-02-14 05:20:48 +13:00
|
|
|
#endif
|
2020-09-26 18:13:16 -07:00
|
|
|
#endif
|
2023-05-13 20:38:37 -04:00
|
|
|
// calibrate ADC
|
|
|
|
|
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_characs);
|
|
|
|
|
// show ADC characterization base
|
|
|
|
|
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
2023-05-24 17:29:50 +02:00
|
|
|
LOG_INFO("ADCmod: ADC characterization based on Two Point values stored in eFuse\n");
|
2023-05-13 20:38:37 -04:00
|
|
|
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
2023-05-24 17:29:50 +02:00
|
|
|
LOG_INFO("ADCmod: ADC characterization based on reference voltage stored in eFuse\n");
|
2024-02-14 05:20:48 +13:00
|
|
|
}
|
|
|
|
|
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
|
|
|
|
// ESP32S3
|
|
|
|
|
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
|
|
|
|
|
LOG_INFO("ADCmod: ADC Characterization based on Two Point values and fitting curve coefficients stored in eFuse\n");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else {
|
2023-05-24 17:29:50 +02:00
|
|
|
LOG_INFO("ADCmod: ADC characterization based on default reference voltage\n");
|
2023-05-13 20:38:37 -04:00
|
|
|
}
|
|
|
|
|
#endif // ARCH_ESP32
|
|
|
|
|
|
2022-07-31 07:11:47 -05:00
|
|
|
#ifdef ARCH_NRF52
|
2021-07-24 19:44:27 +02:00
|
|
|
#ifdef VBAT_AR_INTERNAL
|
2022-05-07 20:31:21 +10:00
|
|
|
analogReference(VBAT_AR_INTERNAL);
|
2021-07-24 19:44:27 +02:00
|
|
|
#else
|
2020-10-16 17:00:27 +08:00
|
|
|
analogReference(AR_INTERNAL); // 3.6V
|
|
|
|
|
#endif
|
2023-05-13 20:38:37 -04:00
|
|
|
#endif // ARCH_NRF52
|
2024-02-11 19:20:20 +01:00
|
|
|
|
|
|
|
|
#ifndef ARCH_ESP32
|
2024-02-08 20:46:22 +00:00
|
|
|
analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS);
|
2024-02-11 19:20:20 +01:00
|
|
|
#endif
|
2024-02-08 14:56:46 -06:00
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
batteryLevel = &analogLevel;
|
|
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
return false;
|
2023-05-16 13:05:45 -04:00
|
|
|
#endif
|
2020-08-12 17:03:36 -07:00
|
|
|
}
|
|
|
|
|
|
2023-07-26 16:48:04 -07:00
|
|
|
/**
|
|
|
|
|
* Initializes the Power class.
|
2023-07-26 16:58:14 -07:00
|
|
|
*
|
2023-07-26 16:48:04 -07:00
|
|
|
* @return true if the setup was successful, false otherwise.
|
|
|
|
|
*/
|
2020-08-12 17:03:36 -07:00
|
|
|
bool Power::setup()
|
|
|
|
|
{
|
2023-12-14 07:35:46 -06:00
|
|
|
bool found = axpChipInit() || analogInit();
|
2020-08-12 17:03:36 -07:00
|
|
|
|
2020-10-09 14:16:51 +08:00
|
|
|
enabled = found;
|
2021-09-19 15:03:37 +02:00
|
|
|
low_voltage_counter = 0;
|
2020-08-12 17:03:36 -07:00
|
|
|
|
|
|
|
|
return found;
|
2020-06-27 21:19:49 -07:00
|
|
|
}
|
|
|
|
|
|
2020-11-22 19:01:48 -08:00
|
|
|
void Power::shutdown()
|
|
|
|
|
{
|
2022-12-30 10:27:07 -06:00
|
|
|
LOG_INFO("Shutting down\n");
|
2023-03-11 22:40:33 +01:00
|
|
|
|
2024-04-12 02:39:07 +02:00
|
|
|
#if defined(ARCH_NRF52) || defined(ARCH_ESP32)
|
2023-03-11 22:40:33 +01:00
|
|
|
#ifdef PIN_LED1
|
2022-06-06 18:48:22 +02:00
|
|
|
ledOff(PIN_LED1);
|
2023-03-11 22:40:33 +01:00
|
|
|
#endif
|
|
|
|
|
#ifdef PIN_LED2
|
2022-06-06 18:48:22 +02:00
|
|
|
ledOff(PIN_LED2);
|
2023-03-11 22:40:33 +01:00
|
|
|
#endif
|
|
|
|
|
#ifdef PIN_LED3
|
2023-12-02 12:30:00 +00:00
|
|
|
ledOff(PIN_LED3);
|
2023-03-11 22:40:33 +01:00
|
|
|
#endif
|
2023-09-30 21:09:17 -05:00
|
|
|
doDeepSleep(DELAY_FOREVER, false);
|
2020-11-22 19:01:48 -08:00
|
|
|
#endif
|
2020-11-22 18:50:14 -08:00
|
|
|
}
|
|
|
|
|
|
2020-06-27 21:19:49 -07:00
|
|
|
/// Reads power status to powerStatus singleton.
|
|
|
|
|
//
|
|
|
|
|
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
|
|
|
|
|
void Power::readPowerStatus()
|
|
|
|
|
{
|
2020-08-12 17:03:36 -07:00
|
|
|
if (batteryLevel) {
|
|
|
|
|
bool hasBattery = batteryLevel->isBatteryConnect();
|
2023-05-13 20:38:37 -04:00
|
|
|
uint32_t batteryVoltageMv = 0;
|
2020-08-25 12:48:47 -07:00
|
|
|
int8_t batteryChargePercent = 0;
|
2020-08-12 17:03:36 -07:00
|
|
|
if (hasBattery) {
|
|
|
|
|
batteryVoltageMv = batteryLevel->getBattVoltage();
|
|
|
|
|
// If the AXP192 returns a valid battery percentage, use it
|
2022-09-06 15:58:33 +08:00
|
|
|
if (batteryLevel->getBatteryPercent() >= 0) {
|
|
|
|
|
batteryChargePercent = batteryLevel->getBatteryPercent();
|
2020-08-12 17:03:36 -07:00
|
|
|
} else {
|
|
|
|
|
// If the AXP192 returns a percentage less than 0, the feature is either not supported or there is an error
|
2024-02-16 13:09:57 +01:00
|
|
|
// In that case, we compute an estimate of the charge percent based on open circuite voltage table defined
|
|
|
|
|
// in power.h
|
|
|
|
|
batteryChargePercent = clamp((int)(((batteryVoltageMv - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS)) * 1e2) /
|
|
|
|
|
((OCV[0] * NUM_CELLS) - (OCV[NUM_OCV_POINTS - 1] * NUM_CELLS))),
|
|
|
|
|
0, 100);
|
2020-08-12 17:03:36 -07:00
|
|
|
}
|
2020-06-27 21:19:49 -07:00
|
|
|
}
|
2020-06-28 18:17:52 -07:00
|
|
|
|
2023-11-22 09:30:55 -07:00
|
|
|
OptionalBool NRF_USB = OptFalse;
|
|
|
|
|
|
|
|
|
|
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
|
|
|
|
|
// changes.
|
|
|
|
|
|
2024-05-22 08:58:05 +12:00
|
|
|
static nrfx_power_usb_state_t prev_nrf_usb_state = (nrfx_power_usb_state_t)-1; // -1 so that state detected at boot
|
2023-11-22 09:30:55 -07:00
|
|
|
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
|
|
|
|
|
|
2024-05-22 08:58:05 +12:00
|
|
|
// If state changed
|
|
|
|
|
if (nrf_usb_state != prev_nrf_usb_state) {
|
|
|
|
|
// If changed to DISCONNECTED
|
|
|
|
|
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
|
|
|
|
|
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
|
|
|
|
NRF_USB = OptFalse;
|
|
|
|
|
}
|
|
|
|
|
// If changed to CONNECTED / READY
|
|
|
|
|
else {
|
|
|
|
|
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
|
|
|
|
NRF_USB = OptTrue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cache the current state
|
|
|
|
|
prev_nrf_usb_state = nrf_usb_state;
|
2023-11-22 09:30:55 -07:00
|
|
|
}
|
|
|
|
|
#endif
|
2020-08-12 17:03:36 -07:00
|
|
|
// Notify any status instances that are observing us
|
2023-11-22 09:30:55 -07:00
|
|
|
const PowerStatus powerStatus2 = PowerStatus(
|
|
|
|
|
hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() || NRF_USB == OptTrue ? OptTrue : OptFalse,
|
|
|
|
|
batteryLevel->isCharging() || NRF_USB == OptTrue ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
|
2022-01-24 18:39:17 +00:00
|
|
|
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
|
|
|
|
|
newStatus.notifyObservers(&powerStatus2);
|
2022-11-30 09:52:28 +01:00
|
|
|
#ifdef DEBUG_HEAP
|
2023-02-17 12:31:51 +01:00
|
|
|
if (lastheap != memGet.getFreeHeap()) {
|
2022-12-30 20:18:19 +01:00
|
|
|
LOG_DEBUG("Threads running:");
|
2022-12-30 17:03:48 +01:00
|
|
|
int running = 0;
|
2023-01-21 14:34:29 +01:00
|
|
|
for (int i = 0; i < MAX_THREADS; i++) {
|
2022-12-30 17:03:48 +01:00
|
|
|
auto thread = concurrency::mainController.get(i);
|
2023-01-21 14:34:29 +01:00
|
|
|
if ((thread != nullptr) && (thread->enabled)) {
|
2022-12-30 20:18:19 +01:00
|
|
|
LOG_DEBUG(" %s", thread->ThreadName.c_str());
|
2022-12-30 17:03:48 +01:00
|
|
|
running++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-30 20:18:19 +01:00
|
|
|
LOG_DEBUG("\n");
|
2023-02-17 12:31:51 +01:00
|
|
|
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(),
|
|
|
|
|
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
|
|
|
|
|
lastheap = memGet.getFreeHeap();
|
2022-12-16 20:25:51 +01:00
|
|
|
}
|
2023-02-04 14:56:04 +01:00
|
|
|
#ifdef DEBUG_HEAP_MQTT
|
|
|
|
|
if (mqtt) {
|
|
|
|
|
// send MQTT-Packet with Heap-Size
|
|
|
|
|
uint8_t dmac[6];
|
|
|
|
|
getMacAddr(dmac); // Get our hardware ID
|
|
|
|
|
char mac[18];
|
|
|
|
|
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
|
2023-04-13 23:50:21 +02:00
|
|
|
|
2023-02-17 12:31:51 +01:00
|
|
|
auto newHeap = memGet.getFreeHeap();
|
2023-04-18 14:08:06 +02:00
|
|
|
std::string heapTopic =
|
|
|
|
|
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
|
2023-02-04 14:56:04 +01:00
|
|
|
std::string heapString = std::to_string(newHeap);
|
|
|
|
|
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
|
|
|
|
|
auto wifiRSSI = WiFi.RSSI();
|
2023-04-18 14:08:06 +02:00
|
|
|
std::string wifiTopic =
|
|
|
|
|
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
|
2023-02-04 14:56:04 +01:00
|
|
|
std::string wifiString = std::to_string(wifiRSSI);
|
2023-04-18 13:37:50 +02:00
|
|
|
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
|
2023-02-04 14:56:04 +01:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-11-30 09:52:28 +01:00
|
|
|
#endif
|
2020-06-27 21:19:49 -07:00
|
|
|
|
2024-02-16 13:09:57 +01:00
|
|
|
// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
|
|
|
|
|
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
|
|
|
|
|
//
|
2022-05-07 20:31:21 +10:00
|
|
|
if (powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
|
2024-02-16 13:09:57 +01:00
|
|
|
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) {
|
2021-09-19 15:03:37 +02:00
|
|
|
low_voltage_counter++;
|
2023-04-22 00:22:09 +02:00
|
|
|
LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter);
|
2022-12-14 10:13:23 +01:00
|
|
|
if (low_voltage_counter > 10) {
|
2023-04-22 00:22:09 +02:00
|
|
|
#ifdef ARCH_NRF52
|
2022-12-14 09:36:25 +01:00
|
|
|
// We can't trigger deep sleep on NRF52, it's freezing the board
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n");
|
2023-04-22 00:22:09 +02:00
|
|
|
#else
|
|
|
|
|
LOG_INFO("Low voltage detected, triggering deep sleep\n");
|
|
|
|
|
powerFSM.trigger(EVENT_LOW_BATTERY);
|
|
|
|
|
#endif
|
2022-12-14 10:29:45 +01:00
|
|
|
}
|
2021-09-19 15:03:37 +02:00
|
|
|
} else {
|
|
|
|
|
low_voltage_counter = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-25 12:48:47 -07:00
|
|
|
} else {
|
|
|
|
|
// No power sensing on this board - tell everyone else we have no idea what is happening
|
2022-01-24 18:39:17 +00:00
|
|
|
const PowerStatus powerStatus3 = PowerStatus(OptUnknown, OptUnknown, OptUnknown, -1, -1);
|
|
|
|
|
newStatus.notifyObservers(&powerStatus3);
|
2020-08-12 17:03:36 -07:00
|
|
|
}
|
2020-06-27 21:19:49 -07:00
|
|
|
}
|
|
|
|
|
|
2020-10-10 09:57:57 +08:00
|
|
|
int32_t Power::runOnce()
|
2020-06-27 21:19:49 -07:00
|
|
|
{
|
|
|
|
|
readPowerStatus();
|
2020-08-12 17:03:36 -07:00
|
|
|
|
2022-09-08 10:36:53 +08:00
|
|
|
#ifdef HAS_PMU
|
2020-10-12 09:27:07 +08:00
|
|
|
// WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll
|
|
|
|
|
// the IRQ status by reading the registers over I2C
|
2023-01-21 14:34:29 +01:00
|
|
|
if (PMU) {
|
2020-10-09 14:16:51 +08:00
|
|
|
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->getIrqStatus();
|
2020-10-09 14:16:51 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
if (PMU->isVbusRemoveIrq()) {
|
2022-12-30 10:27:07 -06:00
|
|
|
LOG_INFO("USB unplugged\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PMU->isVbusInsertIrq()) {
|
2022-12-30 10:27:07 -06:00
|
|
|
LOG_INFO("USB plugged In\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Other things we could check if we cared...
|
|
|
|
|
|
|
|
|
|
if (PMU->isBatChagerStartIrq()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Battery start charging\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isBatChagerDoneIrq()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Battery fully charged\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isBatInsertIrq()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Battery inserted\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isBatRemoveIrq()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("Battery removed\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
*/
|
2023-07-22 09:26:54 -05:00
|
|
|
#ifndef T_WATCH_S3 // FIXME - why is this triggering on the T-Watch S3?
|
2022-10-30 20:40:30 -05:00
|
|
|
if (PMU->isPekeyLongPressIrq()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("PEK long button press\n");
|
2022-10-30 20:40:30 -05:00
|
|
|
screen->setOn(false);
|
|
|
|
|
}
|
2023-07-22 09:26:54 -05:00
|
|
|
#endif
|
2022-09-06 15:58:33 +08:00
|
|
|
|
|
|
|
|
PMU->clearIrqStatus();
|
2020-10-12 09:27:07 +08:00
|
|
|
}
|
2020-10-12 09:33:15 +08:00
|
|
|
#endif
|
2020-06-27 21:19:49 -07:00
|
|
|
// Only read once every 20 seconds once the power status for the app has been initialized
|
2020-10-10 09:57:57 +08:00
|
|
|
return (statusHandler && statusHandler->isInitialized()) ? (1000 * 20) : RUN_SAME;
|
2020-06-27 21:19:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Init the power manager chip
|
|
|
|
|
*
|
|
|
|
|
* axp192 power
|
2023-05-16 01:40:42 -04:00
|
|
|
DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the OLED and the
|
|
|
|
|
axp192 share the same i2c bus, instead use ssd1306 sleep mode DCDC2 -> unused DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this
|
|
|
|
|
on!) LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS to power the GPS ram (for a couple of
|
|
|
|
|
days), can not be turned off LDO2 200mA -> LORA LDO3 200mA -> GPS
|
2023-01-21 14:34:29 +01:00
|
|
|
*
|
2020-06-27 21:19:49 -07:00
|
|
|
*/
|
2022-09-06 15:58:33 +08:00
|
|
|
bool Power::axpChipInit()
|
2020-06-27 21:19:49 -07:00
|
|
|
{
|
2021-01-16 23:10:08 -08:00
|
|
|
|
2022-09-08 10:36:53 +08:00
|
|
|
#ifdef HAS_PMU
|
2020-06-27 21:19:49 -07:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
TwoWire *w = NULL;
|
2022-10-07 19:57:55 +08:00
|
|
|
|
|
|
|
|
// Use macro to distinguish which wire is used by PMU
|
|
|
|
|
#ifdef PMU_USE_WIRE1
|
2023-01-21 14:34:29 +01:00
|
|
|
w = &Wire1;
|
2022-10-07 19:57:55 +08:00
|
|
|
#else
|
2023-01-21 14:34:29 +01:00
|
|
|
w = &Wire;
|
2022-10-07 19:57:55 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/**
|
2023-01-21 14:34:29 +01:00
|
|
|
* It is not necessary to specify the wire pin,
|
2022-10-07 19:57:55 +08:00
|
|
|
* just input the wire, because the wire has been initialized in main.cpp
|
|
|
|
|
*/
|
2022-09-06 15:58:33 +08:00
|
|
|
if (!PMU) {
|
2022-10-07 19:57:55 +08:00
|
|
|
PMU = new XPowersAXP2101(*w);
|
2022-09-06 15:58:33 +08:00
|
|
|
if (!PMU->init()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_WARN("Failed to find AXP2101 power management\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
delete PMU;
|
|
|
|
|
PMU = NULL;
|
|
|
|
|
} else {
|
2022-12-30 10:27:07 -06:00
|
|
|
LOG_INFO("AXP2101 PMU init succeeded, using AXP2101 PMU\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-27 21:19:49 -07:00
|
|
|
|
2022-09-06 15:58:33 +08:00
|
|
|
if (!PMU) {
|
2022-10-07 19:57:55 +08:00
|
|
|
PMU = new XPowersAXP192(*w);
|
2022-09-06 15:58:33 +08:00
|
|
|
if (!PMU->init()) {
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_WARN("Failed to find AXP192 power management\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
delete PMU;
|
|
|
|
|
PMU = NULL;
|
2020-06-27 21:19:49 -07:00
|
|
|
} else {
|
2022-12-30 10:27:07 -06:00
|
|
|
LOG_INFO("AXP192 PMU init succeeded, using AXP192 PMU\n");
|
2020-06-27 21:19:49 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-08-12 17:03:36 -07:00
|
|
|
|
2022-09-06 15:58:33 +08:00
|
|
|
if (!PMU) {
|
2023-01-21 14:34:29 +01:00
|
|
|
/*
|
|
|
|
|
* In XPowersLib, if the XPowersAXPxxx object is released, Wire.end() will be called at the same time.
|
|
|
|
|
* In order not to affect other devices, if the initialization of the PMU fails, Wire needs to be re-initialized once,
|
|
|
|
|
* if there are multiple devices sharing the bus.
|
|
|
|
|
* * */
|
2022-10-07 19:57:55 +08:00
|
|
|
#ifndef PMU_USE_WIRE1
|
|
|
|
|
w->begin(I2C_SDA, I2C_SCL);
|
|
|
|
|
#endif
|
2022-09-06 15:58:33 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
batteryLevel = PMU;
|
|
|
|
|
|
|
|
|
|
if (PMU->getChipModel() == XPOWERS_AXP192) {
|
2023-01-21 14:34:29 +01:00
|
|
|
|
2022-09-06 15:58:33 +08:00
|
|
|
// lora radio power channel
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_LDO2);
|
|
|
|
|
|
|
|
|
|
// oled module power channel,
|
2023-01-21 14:34:29 +01:00
|
|
|
// disable it will cause abnormal communication between boot and AXP power supply,
|
2022-09-06 15:58:33 +08:00
|
|
|
// do not turn it off
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
|
|
|
|
|
// enable oled power
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_DCDC1);
|
|
|
|
|
|
|
|
|
|
// gnss module power channel - now turned on in setGpsPower
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300);
|
|
|
|
|
// PMU->enablePowerOutput(XPOWERS_LDO3);
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// protected oled power source
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->setProtectedChannel(XPOWERS_DCDC1);
|
2023-01-21 14:34:29 +01:00
|
|
|
// protected esp32 power source
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->setProtectedChannel(XPOWERS_DCDC3);
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// disable not use channel
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC2);
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// disable all axp chip interrupt
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ);
|
|
|
|
|
|
|
|
|
|
// Set constant current charging current
|
2022-09-09 21:49:54 +02:00
|
|
|
PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_450MA);
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// Set up the charging voltage
|
2022-12-06 16:56:38 +01:00
|
|
|
PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2);
|
2022-09-06 15:58:33 +08:00
|
|
|
} else if (PMU->getChipModel() == XPOWERS_AXP2101) {
|
|
|
|
|
|
2023-03-27 10:50:17 +02:00
|
|
|
/*The alternative version of T-Beam 1.1 differs from T-Beam V1.1 in that it uses an AXP2101 power chip*/
|
2023-05-27 10:03:02 +02:00
|
|
|
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
|
2023-05-27 10:17:37 +08:00
|
|
|
// Unuse power channel
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC2);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC3);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC4);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC5);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_ALDO1);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_ALDO4);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_BLDO1);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_BLDO2);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DLDO1);
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DLDO2);
|
|
|
|
|
|
|
|
|
|
// GNSS RTC PowerVDD 3300mV
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_VBACKUP, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_VBACKUP);
|
|
|
|
|
|
|
|
|
|
// ESP32 VDD 3300mV
|
|
|
|
|
// ! No need to set, automatically open , Don't close it
|
|
|
|
|
// PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300);
|
|
|
|
|
// PMU->setProtectedChannel(XPOWERS_DCDC1);
|
|
|
|
|
|
|
|
|
|
// LoRa VDD 3300mV
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
|
|
|
|
|
|
|
|
|
// GNSS VDD 3300mV
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_ALDO3);
|
2023-07-22 09:26:54 -05:00
|
|
|
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE ||
|
|
|
|
|
HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
|
2023-05-27 10:17:37 +08:00
|
|
|
// t-beam s3 core
|
|
|
|
|
/**
|
|
|
|
|
* gnss module power channel
|
|
|
|
|
* The default ALDO4 is off, you need to turn on the GNSS power first, otherwise it will be invalid during
|
|
|
|
|
* initialization
|
|
|
|
|
*/
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_ALDO4);
|
|
|
|
|
|
|
|
|
|
// lora radio power channel
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_ALDO3);
|
|
|
|
|
|
|
|
|
|
// m.2 interface
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_DCDC3);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ALDO2 cannot be turned off.
|
|
|
|
|
* It is a necessary condition for sensor communication.
|
|
|
|
|
* It must be turned on to properly access the sensor and screen
|
|
|
|
|
* It is also responsible for the power supply of PCF8563
|
|
|
|
|
*/
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
|
|
|
|
|
|
|
|
|
// 6-axis , magnetometer ,bme280 , oled screen power channel
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_ALDO1);
|
|
|
|
|
|
2023-07-14 17:25:20 -04:00
|
|
|
// sdcard power channel
|
2023-05-27 10:17:37 +08:00
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_BLDO1);
|
|
|
|
|
|
2023-07-22 09:26:54 -05:00
|
|
|
#ifdef T_WATCH_S3
|
|
|
|
|
// DRV2605 power channel
|
|
|
|
|
PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300);
|
|
|
|
|
PMU->enablePowerOutput(XPOWERS_BLDO2);
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-05-27 10:17:37 +08:00
|
|
|
// PMU->setPowerChannelVoltage(XPOWERS_DCDC4, 3300);
|
|
|
|
|
// PMU->enablePowerOutput(XPOWERS_DCDC4);
|
|
|
|
|
|
|
|
|
|
// not use channel
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC2); // not elicited
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DCDC5); // not elicited
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DLDO1); // Invalid power channel, it does not exist
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_DLDO2); // Invalid power channel, it does not exist
|
|
|
|
|
PMU->disablePowerOutput(XPOWERS_VBACKUP);
|
|
|
|
|
}
|
2023-03-27 10:33:26 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// disable all axp chip interrupt
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// Set the constant current charging current of AXP2101, temporarily use 500mA by default
|
2022-09-06 15:58:33 +08:00
|
|
|
PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// Set up the charging voltage
|
2022-12-06 16:56:38 +01:00
|
|
|
PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PMU->clearIrqStatus();
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// TBeam1.1 /T-Beam S3-Core has no external TS detection,
|
2022-09-06 15:58:33 +08:00
|
|
|
// it needs to be disabled, otherwise it will cause abnormal charging
|
|
|
|
|
PMU->disableTSPinMeasure();
|
|
|
|
|
|
|
|
|
|
// PMU->enableSystemVoltageMeasure();
|
|
|
|
|
PMU->enableVbusVoltageMeasure();
|
|
|
|
|
PMU->enableBattVoltageMeasure();
|
|
|
|
|
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("=======================================================================\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
if (PMU->isChannelAvailable(XPOWERS_DCDC1)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_DCDC1));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_DCDC2)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("DC2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_DCDC2));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_DCDC3)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("DC3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_DCDC3));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_DCDC4)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("DC4 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_DCDC4));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_LDO2)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("LDO2 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_LDO2));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_LDO3)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("LDO3 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_LDO3));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_ALDO1)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("ALDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_ALDO1));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_ALDO2)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("ALDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_ALDO2));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_ALDO3)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("ALDO3: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_ALDO3));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_ALDO4)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("ALDO4: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_ALDO4));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_BLDO1)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("BLDO1: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_BLDO1));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
|
|
|
|
if (PMU->isChannelAvailable(XPOWERS_BLDO2)) {
|
2023-01-21 14:34:29 +01:00
|
|
|
LOG_DEBUG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-",
|
|
|
|
|
PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
|
2022-09-06 15:58:33 +08:00
|
|
|
}
|
2022-12-29 20:41:37 -06:00
|
|
|
LOG_DEBUG("=======================================================================\n");
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2022-12-14 17:26:55 +01:00
|
|
|
// We can safely ignore this approach for most (or all) boards because MCU turned off
|
|
|
|
|
// earlier than battery discharged to 2.6V.
|
|
|
|
|
//
|
|
|
|
|
// Unfortanly for now we can't use this killswitch for RAK4630-based boards because they have a bug with
|
|
|
|
|
// battery voltage measurement. Probably it sometimes drops to low values.
|
|
|
|
|
#ifndef RAK4630
|
2022-09-06 15:58:33 +08:00
|
|
|
// Set PMU shutdown voltage at 2.6V to maximize battery utilization
|
|
|
|
|
PMU->setSysPowerDownVoltage(2600);
|
2022-12-14 14:36:15 +01:00
|
|
|
#endif
|
2022-09-06 15:58:33 +08:00
|
|
|
|
|
|
|
|
#ifdef PMU_IRQ
|
2023-01-21 14:34:29 +01:00
|
|
|
uint64_t pmuIrqMask = 0;
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
if (PMU->getChipModel() == XPOWERS_AXP192) {
|
|
|
|
|
pmuIrqMask = XPOWERS_AXP192_VBUS_INSERT_IRQ | XPOWERS_AXP192_BAT_INSERT_IRQ | XPOWERS_AXP192_PKEY_SHORT_IRQ;
|
|
|
|
|
} else if (PMU->getChipModel() == XPOWERS_AXP2101) {
|
|
|
|
|
pmuIrqMask = XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_PKEY_SHORT_IRQ;
|
|
|
|
|
}
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
pinMode(PMU_IRQ, INPUT);
|
|
|
|
|
attachInterrupt(
|
|
|
|
|
PMU_IRQ, [] { pmu_irq = true; }, FALLING);
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
// we do not look for AXPXXX_CHARGING_FINISHED_IRQ & AXPXXX_CHARGING_IRQ because it occurs repeatedly while there is
|
|
|
|
|
// no battery also it could cause inadvertent waking from light sleep just because the battery filled
|
|
|
|
|
// we don't look for AXPXXX_BATT_REMOVED_IRQ because it occurs repeatedly while no battery installed
|
|
|
|
|
// we don't look at AXPXXX_VBUS_REMOVED_IRQ because we don't have anything hooked to vbus
|
|
|
|
|
PMU->enableIRQ(pmuIrqMask);
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
PMU->clearIrqStatus();
|
2022-09-06 15:58:33 +08:00
|
|
|
#endif /*PMU_IRQ*/
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
readPowerStatus();
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2022-09-08 10:32:12 +08:00
|
|
|
pmu_found = true;
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2022-09-08 10:32:12 +08:00
|
|
|
return pmu_found;
|
2022-09-06 15:58:33 +08:00
|
|
|
|
2020-08-12 17:03:36 -07:00
|
|
|
#else
|
|
|
|
|
return false;
|
2020-06-27 21:19:49 -07:00
|
|
|
#endif
|
2024-02-08 20:46:22 +00:00
|
|
|
}
|