2020-02-01 16:30:53 +00:00
/*
Main module
# Modified by Kyle T. Gabriel to fix issue with incorrect GPS data for TTNMapper
Copyright ( C ) 2018 by Xose Pérez < xose dot perez at gmail dot com >
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "configuration.h"
# include "rom/rtc.h"
2020-02-02 03:45:12 +00:00
# include <driver/rtc_io.h>
2020-02-01 16:30:53 +00:00
# include <TinyGPS++.h>
# include <Wire.h>
# include "BluetoothUtil.h"
2020-02-02 00:14:34 +00:00
# include "MeshBluetoothService.h"
2020-02-02 20:45:32 +00:00
# include "MeshService.h"
2020-02-06 15:39:21 +00:00
# include "GPS.h"
2020-02-07 21:51:17 +00:00
# include "screen.h"
2020-02-08 04:59:21 +00:00
# include "NodeDB.h"
2020-02-15 19:15:43 +00:00
# include "Periodic.h"
2020-02-19 15:58:51 +00:00
# include "esp32/pm.h"
# include "esp_pm.h"
2020-02-21 16:09:07 +00:00
# include "MeshRadio.h"
2020-02-22 01:01:26 +00:00
# include "sleep.h"
2020-02-22 20:01:59 +00:00
# include "PowerFSM.h"
2020-02-01 16:30:53 +00:00
# ifdef T_BEAM_V10
# include "axp20x.h"
AXP20X_Class axp ;
bool pmu_irq = false ;
# endif
2020-02-12 17:13:49 +00:00
bool isCharging = false ;
2020-02-14 22:00:08 +00:00
bool isUSBPowered = false ;
2020-02-12 17:13:49 +00:00
2020-02-01 16:30:53 +00:00
bool ssd1306_found = false ;
bool axp192_found = false ;
2020-02-21 12:57:08 +00:00
# define xstr(s) str(s)
# define str(s) #s
2020-02-20 20:49:34 +00:00
2020-02-01 16:30:53 +00:00
// -----------------------------------------------------------------------------
// Application
// -----------------------------------------------------------------------------
void scanI2Cdevice ( void )
{
2020-02-02 00:14:34 +00:00
byte err , addr ;
int nDevices = 0 ;
for ( addr = 1 ; addr < 127 ; addr + + )
{
Wire . beginTransmission ( addr ) ;
err = Wire . endTransmission ( ) ;
if ( err = = 0 )
{
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " I2C device found at address 0x%x \n " , addr ) ;
2020-02-02 00:14:34 +00:00
nDevices + + ;
if ( addr = = SSD1306_ADDRESS )
{
ssd1306_found = true ;
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " ssd1306 display found \n " ) ;
2020-02-02 00:14:34 +00:00
}
# ifdef T_BEAM_V10
if ( addr = = AXP192_SLAVE_ADDRESS )
{
axp192_found = true ;
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " axp192 PMU found \n " ) ;
2020-02-02 00:14:34 +00:00
}
# endif
2020-02-01 16:30:53 +00:00
}
2020-02-02 00:14:34 +00:00
else if ( err = = 4 )
{
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " Unknow error at address 0x%x \n " , addr ) ;
2020-02-02 00:14:34 +00:00
}
}
if ( nDevices = = 0 )
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " No I2C devices found \n " ) ;
2020-02-02 00:14:34 +00:00
else
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " done \n " ) ;
2020-02-01 16:30:53 +00:00
}
/**
* Init the power manager chip
*
* axp192 power
DCDC1 0.7 - 3.5 V @ 1200 mA 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.5 V @ 700 mA max - > ESP32 ( keep this on ! )
LDO1 30 mA - > 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 200 mA - > LORA
LDO3 200 mA - > GPS
*/
2020-02-02 00:14:34 +00:00
void axp192Init ( )
{
# ifdef T_BEAM_V10
if ( axp192_found )
{
if ( ! axp . begin ( Wire , AXP192_SLAVE_ADDRESS ) )
{
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " AXP192 Begin PASS \n " ) ;
// axp.setChgLEDMode(LED_BLINK_4HZ);
DEBUG_MSG ( " DCDC1: %s \n " , axp . isDCDC1Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " DCDC2: %s \n " , axp . isDCDC2Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " LDO2: %s \n " , axp . isLDO2Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " LDO3: %s \n " , axp . isLDO3Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " DCDC3: %s \n " , axp . isDCDC3Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " Exten: %s \n " , axp . isExtenEnable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " ---------------------------------------- \n " ) ;
axp . setPowerOutPut ( AXP192_LDO2 , AXP202_ON ) ; // LORA radio
axp . setPowerOutPut ( AXP192_LDO3 , AXP202_ON ) ; // GPS main power
axp . setPowerOutPut ( AXP192_DCDC2 , AXP202_ON ) ;
axp . setPowerOutPut ( AXP192_EXTEN , AXP202_ON ) ;
axp . setPowerOutPut ( AXP192_DCDC1 , AXP202_ON ) ;
axp . setDCDC1Voltage ( 3300 ) ; // for the OLED power
DEBUG_MSG ( " DCDC1: %s \n " , axp . isDCDC1Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " DCDC2: %s \n " , axp . isDCDC2Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " LDO2: %s \n " , axp . isLDO2Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " LDO3: %s \n " , axp . isLDO3Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " DCDC3: %s \n " , axp . isDCDC3Enable ( ) ? " ENABLE " : " DISABLE " ) ;
DEBUG_MSG ( " Exten: %s \n " , axp . isExtenEnable ( ) ? " ENABLE " : " DISABLE " ) ;
2020-02-17 00:03:16 +00:00
#if 0
// cribbing from https://github.com/m5stack/M5StickC/blob/master/src/AXP192.cpp to fix charger to be more like 300ms.
// I finally found an english datasheet. Will look at this later - but suffice it to say the default code from TTGO has 'issues'
axp . adc1Enable ( 0xff , 1 ) ; // turn on all adcs
uint8_t val = 0xc2 ;
axp . _writeByte ( 0x33 , 1 , & val ) ; // Bat charge voltage to 4.2, Current 280mA
val = 0 b11110010 ;
// Set ADC sample rate to 200hz
// axp._writeByte(0x84, 1, &val);
// Not connected
//val = 0xfc;
//axp._writeByte(AXP202_VHTF_CHGSET, 1, &val); // Set temperature protection
//not used
//val = 0x46;
//axp._writeByte(AXP202_OFF_CTL, 1, &val); // enable bat detection
# endif
2020-02-04 16:17:44 +00:00
axp . debugCharging ( ) ;
2020-02-12 17:13:49 +00:00
# ifdef PMU_IRQ
2020-02-04 16:17:44 +00:00
pinMode ( PMU_IRQ , INPUT_PULLUP ) ;
attachInterrupt ( PMU_IRQ , [ ] {
pmu_irq = true ;
} ,
2020-02-21 19:39:10 +00:00
RISING ) ;
2020-02-04 16:17:44 +00:00
axp . adc1Enable ( AXP202_BATT_CUR_ADC1 , 1 ) ;
axp . enableIRQ ( AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ , 1 ) ;
axp . clearIRQ ( ) ;
2020-02-12 17:13:49 +00:00
# endif
2020-02-04 16:17:44 +00:00
2020-02-12 17:13:49 +00:00
isCharging = axp . isChargeing ( ) ;
2020-02-14 22:00:08 +00:00
isUSBPowered = axp . isVBUSPlug ( ) ;
2020-02-02 00:14:34 +00:00
}
else
{
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " AXP192 Begin FAIL \n " ) ;
2020-02-01 16:30:53 +00:00
}
2020-02-02 00:14:34 +00:00
}
else
{
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " AXP192 not found \n " ) ;
2020-02-02 00:14:34 +00:00
}
# endif
2020-02-01 16:30:53 +00:00
}
2020-02-02 20:55:26 +00:00
const char * getDeviceName ( )
{
uint8_t dmac [ 6 ] ;
assert ( esp_efuse_mac_get_default ( dmac ) = = ESP_OK ) ;
// Meshtastic_ab3c
static char name [ 20 ] ;
2020-02-04 15:31:32 +00:00
sprintf ( name , " Meshtastic_%02x%02x " , dmac [ 4 ] , dmac [ 5 ] ) ;
2020-02-02 20:55:26 +00:00
return name ;
}
2020-02-02 00:14:34 +00:00
void setup ( )
{
// Debug
# ifdef DEBUG_PORT
2020-02-01 16:30:53 +00:00
DEBUG_PORT . begin ( SERIAL_BAUD ) ;
2020-02-02 00:14:34 +00:00
# endif
2020-02-01 16:30:53 +00:00
initDeepSleep ( ) ;
2020-02-01 22:23:21 +00:00
# ifdef VEXT_ENABLE
pinMode ( VEXT_ENABLE , OUTPUT ) ;
digitalWrite ( VEXT_ENABLE , 0 ) ; // turn on the display power
2020-02-02 00:14:34 +00:00
# endif
2020-02-01 22:23:21 +00:00
# ifdef RESET_OLED
pinMode ( RESET_OLED , OUTPUT ) ;
digitalWrite ( RESET_OLED , 1 ) ;
2020-02-02 00:14:34 +00:00
# endif
2020-02-01 22:23:21 +00:00
2020-02-04 15:31:32 +00:00
# ifdef I2C_SDA
2020-02-01 16:30:53 +00:00
Wire . begin ( I2C_SDA , I2C_SCL ) ;
scanI2Cdevice ( ) ;
2020-02-04 15:31:32 +00:00
# endif
2020-02-01 16:30:53 +00:00
axp192Init ( ) ;
// Buttons & LED
2020-02-01 22:23:21 +00:00
# ifdef BUTTON_PIN
2020-02-01 16:30:53 +00:00
pinMode ( BUTTON_PIN , INPUT_PULLUP ) ;
2020-02-01 22:23:21 +00:00
digitalWrite ( BUTTON_PIN , 1 ) ;
# endif
2020-02-01 16:30:53 +00:00
# ifdef LED_PIN
pinMode ( LED_PIN , OUTPUT ) ;
2020-02-02 00:05:12 +00:00
digitalWrite ( LED_PIN , 1 ) ; // turn on for now
2020-02-01 16:30:53 +00:00
# endif
// Hello
2020-02-20 20:49:34 +00:00
DEBUG_MSG ( " %s %s \n " , xstr ( APP_NAME ) , str ( APP_VERSION ) ) ;
2020-02-01 16:30:53 +00:00
// Don't init display if we don't have one or we are waking headless due to a timer event
2020-02-02 00:14:34 +00:00
if ( wakeCause = = ESP_SLEEP_WAKEUP_TIMER )
2020-02-01 16:30:53 +00:00
ssd1306_found = false ; // forget we even have the hardware
2020-02-02 00:14:34 +00:00
if ( ssd1306_found )
2020-02-21 18:51:36 +00:00
screen . setup ( ) ;
2020-02-01 16:30:53 +00:00
// Init GPS
2020-02-06 15:39:21 +00:00
gps . setup ( ) ;
2020-02-01 16:30:53 +00:00
2020-02-08 01:48:12 +00:00
screen_print ( " Started... \n " ) ;
2020-02-01 16:30:53 +00:00
2020-02-02 20:45:32 +00:00
service . init ( ) ;
2020-02-05 05:24:11 +00:00
bool useBluetooth = true ;
if ( useBluetooth )
{
DEBUG_MSG ( " Starting bluetooth \n " ) ;
2020-02-20 20:49:34 +00:00
BLEServer * serve = initBLE ( getDeviceName ( ) , HW_VENDOR , str ( APP_VERSION ) ) ; // FIXME, use a real name based on the macaddr
2020-02-05 05:24:11 +00:00
createMeshBluetoothService ( serve ) ;
2020-02-13 19:53:46 +00:00
// Start advertising - this must be done _after_ creating all services
serve - > getAdvertising ( ) - > start ( ) ;
2020-02-05 05:24:11 +00:00
}
2020-02-12 00:04:25 +00:00
2020-02-21 16:09:07 +00:00
setBluetoothEnable ( false ) ;
2020-02-21 19:32:33 +00:00
setCPUFast ( false ) ; // 80MHz is fine for our slow peripherals
2020-02-22 21:14:10 +00:00
PowerFSM_setup ( ) ;
powerFSM . trigger ( EVENT_BOOT ) ; // transition to ON, FIXME, only do this for cold boots, not waking from SDS
2020-02-01 16:30:53 +00:00
}
2020-02-15 19:15:43 +00:00
uint32_t ledBlinker ( )
{
static bool ledOn ;
ledOn ^ = 1 ;
2020-02-21 12:57:08 +00:00
setLed ( ledOn ) ;
2020-02-15 19:15:43 +00:00
2020-02-17 00:03:16 +00:00
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return isCharging ? 1000 : ( ledOn ? 2 : 1000 ) ;
2020-02-15 19:15:43 +00:00
}
Periodic ledPeriodic ( ledBlinker ) ;
2020-02-17 00:03:16 +00:00
#if 0
// Turn off for now
uint32_t axpReads ( )
{
axp . debugCharging ( ) ;
DEBUG_MSG ( " vbus current %f \n " , axp . getVbusCurrent ( ) ) ;
DEBUG_MSG ( " charge current %f \n " , axp . getBattChargeCurrent ( ) ) ;
DEBUG_MSG ( " bat voltage %f \n " , axp . getBattVoltage ( ) ) ;
DEBUG_MSG ( " batt pct %d \n " , axp . getBattPercentage ( ) ) ;
return 30 * 1000 ;
}
Periodic axpDebugOutput ( axpReads ) ;
2020-02-21 12:57:08 +00:00
# endif
2020-02-17 00:03:16 +00:00
2020-02-02 00:14:34 +00:00
void loop ( )
{
2020-02-08 00:12:55 +00:00
uint32_t msecstosleep = 1000 * 30 ; // How long can we sleep before we again need to service the main loop?
2020-02-22 21:14:10 +00:00
powerFSM . run_machine ( ) ;
2020-02-06 15:39:21 +00:00
gps . loop ( ) ;
2020-02-21 18:51:36 +00:00
screen . loop ( ) ;
2020-02-02 20:45:32 +00:00
service . loop ( ) ;
2020-02-21 18:51:36 +00:00
2020-02-15 19:15:43 +00:00
ledPeriodic . loop ( ) ;
2020-02-17 00:03:16 +00:00
// axpDebugOutput.loop();
2020-02-01 16:30:53 +00:00
loopBLE ( ) ;
2020-02-03 02:33:46 +00:00
# ifdef T_BEAM_V10
2020-02-04 15:31:32 +00:00
if ( axp192_found )
{
2020-02-12 17:13:49 +00:00
# ifdef PMU_IRQ
2020-02-08 15:55:12 +00:00
if ( pmu_irq )
{
pmu_irq = false ;
axp . readIRQ ( ) ;
2020-02-12 17:13:49 +00:00
2020-02-21 19:39:10 +00:00
DEBUG_MSG ( " pmu irq! \n " ) ;
2020-02-14 22:00:08 +00:00
isCharging = axp . isChargeing ( ) ;
isUSBPowered = axp . isVBUSPlug ( ) ;
2020-02-12 22:24:57 +00:00
2020-02-14 22:00:08 +00:00
axp . clearIRQ ( ) ;
2020-02-08 15:55:12 +00:00
}
2020-02-12 17:13:49 +00:00
# endif
2020-02-02 21:55:44 +00:00
}
2020-02-03 02:33:46 +00:00
# endif
2020-02-02 21:55:44 +00:00
2020-02-01 22:23:21 +00:00
# ifdef BUTTON_PIN
2020-02-01 16:30:53 +00:00
// if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of this boilerplate)
static bool wasPressed = false ;
static uint32_t minPressMs ; // what tick should we call this press long enough
2020-02-22 20:01:59 +00:00
static uint32_t lastPingMs ;
2020-02-22 21:50:08 +00:00
if ( ! digitalRead ( BUTTON_PIN ) )
2020-02-02 00:14:34 +00:00
{
if ( ! wasPressed )
{ // just started a new press
2020-02-04 16:17:44 +00:00
DEBUG_MSG ( " pressing \n " ) ;
2020-02-21 12:57:08 +00:00
//doLightSleep();
2020-02-12 03:06:12 +00:00
// esp_pm_dump_locks(stdout); // FIXME, do this someplace better
2020-02-01 16:30:53 +00:00
wasPressed = true ;
2020-02-12 22:24:57 +00:00
uint32_t now = millis ( ) ;
minPressMs = now + 3000 ;
if ( now - lastPingMs > 60 * 1000 )
{ // if more than a minute since our last press, ask other nodes to update their state
service . sendNetworkPing ( ) ;
lastPingMs = now ;
}
2020-02-22 20:01:59 +00:00
powerFSM . trigger ( EVENT_PRESS ) ;
2020-02-02 00:14:34 +00:00
}
}
else if ( wasPressed )
{
2020-02-01 16:30:53 +00:00
// we just did a release
wasPressed = false ;
2020-02-02 00:14:34 +00:00
if ( millis ( ) > minPressMs )
{
2020-02-01 16:30:53 +00:00
// held long enough
screen_print ( " Erasing prefs " ) ;
delay ( 5000 ) ; // Give some time to read the screen
// ESP.restart();
}
}
2020-02-22 21:50:08 +00:00
# endif
2020-02-01 16:30:53 +00:00
2020-02-02 03:09:17 +00:00
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
// i.e. don't just keep spinning in loop as fast as we can.
2020-02-08 01:26:42 +00:00
//DEBUG_MSG("msecs %d\n", msecstosleep);
2020-02-08 01:48:12 +00:00
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons feel slow
msecstosleep = 10 ;
2020-02-21 12:57:08 +00:00
2020-02-22 20:01:59 +00:00
delay ( msecstosleep ) ;
2020-02-01 16:30:53 +00:00
}