Merge branch 'eink' into power

This commit is contained in:
geeksville
2020-10-01 07:51:01 -07:00
18 changed files with 378 additions and 119 deletions

View File

@@ -77,6 +77,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN PIN_BUTTON1
#endif
#ifdef PIN_BUTTON2
#define BUTTON_PIN_ALT PIN_BUTTON2
#endif
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
#elif defined(CubeCell_BoardPlus)

87
src/gps/Air530GPS.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include "Air530GPS.h"
#include <assert.h>
/*
Helpful translations from the Air530 GPS datasheet
Sat acquision mode
捕获电流值@3.3v 42.6 mA
sat tracking mode
跟踪电流值@3.3v 36.7 mA
Low power mode
低功耗模式@3.3V 0.85 mA
(发送指令:$PGKC051,0)
Super low power mode
超低功耗模式@3.3V 31 uA
(发送指令:$PGKC105,4)
To exit sleep use WAKE pin
Commands to enter sleep
6、Command: 105
进入周期性低功耗模式
Arguments:
Arg1: “0”,正常运行模式 (normal mode)
“1”,周期超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (periodic low power tracking mode - keeps sat positions, use wake to wake up)
“2”,周期低功耗模式 (periodic low power mode)
“4”,直接进入超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (super low power consumption mode immediately, need WAKE to resume)
“8”,自动低功耗模式,可以通过串口唤醒 (automatic low power mode, wake by sending characters to serial port)
“9”, 自动超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (automatic low power tracking when possible, need wake pin to resume)
(Arg 2 & 3 only valid if Arg1 is "1" or "2")
Arg2:运行时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用
ON time in msecs
Arg3:睡眠时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用
Sleep time in msecs
Example:
$PGKC105,8*3F<CR><LF>
This will set automatic low power mode with waking when we send chars to the serial port. Possibly do this as soon as we get a
new location. When we wake again in a minute we send a character to wake up.
*/
void Air530GPS::sendCommand(const char *cmd) {
uint8_t sum = 0;
// Skip the $
assert(cmd[0] == '$');
const char *p = cmd + 1;
while(*p)
sum ^= *p++;
assert(_serial_gps);
_serial_gps->write(cmd);
_serial_gps->printf("*%02x\r\n", sum);
// DEBUG_MSG("xsum %02x\n", sum);
}
void Air530GPS::sleep() {
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, 0);
pinMode(PIN_GPS_WAKE, OUTPUT);
sendCommand("$PGKC105,4");
#endif
}
/// wake the GPS into normal operation mode
void Air530GPS::wake()
{
#if 1
#ifdef PIN_GPS_WAKE
digitalWrite(PIN_GPS_WAKE, 1);
pinMode(PIN_GPS_WAKE, OUTPUT);
#endif
#else
// For power testing - keep GPS sleeping forever
sleep();
#endif
}

22
src/gps/Air530GPS.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "NMEAGPS.h"
/**
* A gps class thatreads from a NMEA GPS stream (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class Air530GPS : public NMEAGPS
{
protected:
/// If possible force the GPS into sleep/low power mode
virtual void sleep();
/// wake the GPS into normal operation mode
virtual void wake();
private:
/// Send a NMEA cmd with checksum
void sendCommand(const char *str);
};

View File

@@ -85,3 +85,20 @@ uint32_t getValidTime()
{
return timeSetFromGPS ? getTime() : 0;
}
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
* calls sleep/wake
*/
void GPS::setWantLocation(bool on)
{
if (wantNewLocation != on) {
wantNewLocation = on;
DEBUG_MSG("WANT GPS=%d\n", on);
if (on)
wake();
else
sleep();
}
}

View File

@@ -30,6 +30,8 @@ class GPS
protected:
bool hasValidLocation = false; // default to false, until we complete our first read
bool wantNewLocation = false; // true if we want a location right now
public:
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
@@ -62,10 +64,24 @@ class GPS
/// Returns ture if we have acquired GPS lock.
bool hasLock() const { return hasValidLocation; }
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
* calls sleep/wake
*/
void setWantLocation(bool on);
/**
* Restart our lock attempt - try to get and broadcast a GPS reading ASAP
* called after the CPU wakes from light-sleep state */
virtual void startLock() {}
protected:
/// If possible force the GPS into sleep/low power mode
virtual void sleep() {}
/// wake the GPS into normal operation mode
virtual void wake() {}
};
extern GPS *gps;

View File

@@ -1,50 +1,6 @@
#include "NMEAGPS.h"
#include "configuration.h"
/*
Helpful translations from the Air530 GPS datasheet
Sat acquision mode
捕获电流值@3.3v 42.6 mA
sat tracking mode
跟踪电流值@3.3v 36.7 mA
Low power mode
低功耗模式@3.3V 0.85 mA
(发送指令:$PGKC051,0)
Super low power mode
超低功耗模式@3.3V 31 uA
(发送指令:$PGKC105,4)
To exit sleep use WAKE pin
Commands to enter sleep
6、Command: 105
进入周期性低功耗模式
Arguments:
Arg1: “0”,正常运行模式 (normal mode)
“1”,周期超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (periodic low power tracking mode - keeps sat positions, use wake to wake up)
“2”,周期低功耗模式 (periodic low power mode)
“4”,直接进入超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (super low power consumption mode immediately, need WAKE to resume)
“8”,自动低功耗模式,可以通过串口唤醒 (automatic low power mode, wake by sending characters to serial port)
“9”, 自动超低功耗跟踪模式,需要拉高 WAKE 来唤醒 (automatic low power tracking when possible, need wake pin to resume)
(Arg 2 & 3 only valid if Arg1 is "1" or "2")
Arg2:运行时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用
ON time in msecs
Arg3:睡眠时间(毫秒),在 Arg1 为 1、2 的周期模式下,此参数起作用
Sleep time in msecs
Example:
$PGKC105,8*3F<CR><LF>
This will set automatic low power mode with waking when we send chars to the serial port. Possibly do this as soon as we get a new
location. When we wake again in a minute we send a character to wake up.
*/
static int32_t toDegInt(RawDegrees d)
{
@@ -68,6 +24,7 @@ bool NMEAGPS::setup()
void NMEAGPS::loop()
{
// First consume any chars that have piled up at the receiver
while (_serial_gps->available() > 0) {
int c = _serial_gps->read();
// DEBUG_MSG("%c", c);
@@ -78,11 +35,18 @@ void NMEAGPS::loop()
isConnected = true;
}
// If we are overdue for an update, turn on the GPS and at least publish the current status
uint32_t now = millis();
if ((now - lastUpdateMsec) > 20 * 1000) { // Ugly hack for now - limit update checks to once every 20 secs (but still consume
// serial chars at whatever rate)
lastUpdateMsec = now;
bool mustPublishUpdate = false;
if ((now - lastUpdateMsec) > 30 * 1000 && !wantNewLocation) {
// Ugly hack for now - limit update checks to once every 30 secs
setWantLocation(true);
mustPublishUpdate =
true; // Even if we don't have an update this time, we at least want to occasionally publish the current state
}
// Only bother looking at GPS state if we are interested in what it has to say
if (wantNewLocation) {
auto ti = reader.time;
auto d = reader.date;
if (ti.isUpdated() && ti.isValid() && d.isValid()) {
@@ -105,6 +69,8 @@ void NMEAGPS::loop()
hasValidLocation = ((fixtype >= 1) && (fixtype <= 5));
if (reader.location.isUpdated()) {
lastUpdateMsec = now;
if (reader.altitude.isValid())
altitude = reader.altitude.meters();
@@ -112,6 +78,9 @@ void NMEAGPS::loop()
auto loc = reader.location.value();
latitude = toDegInt(loc.lat);
longitude = toDegInt(loc.lng);
// Once we get a location we no longer desperately want an update
setWantLocation(false);
}
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
if (reader.hdop.isValid()) {
@@ -128,11 +97,14 @@ void NMEAGPS::loop()
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7,
altitude, dop * 1e-2, heading * 1e-5);
mustPublishUpdate = true;
}
// Notify any status instances that are observing us
const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
if (mustPublishUpdate) {
// Notify any status instances that are observing us
const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
}
}
}

View File

@@ -183,11 +183,11 @@ void UBloxGPS::doTask()
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only
{
if (hasValidLocation) {
wantNewLocation = false;
setWantLocation(false);
// ublox.powerOff();
}
} else // we didn't get a location update, go back to sleep and hope the characters show up
wantNewLocation = true;
setWantLocation(true);
// Notify any status instances that are observing us
const meshtastic::GPSStatus status =

View File

@@ -14,8 +14,6 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask
{
SFE_UBLOX_GPS ublox;
bool wantNewLocation = true;
CallbackObserver<UBloxGPS, void *> notifySleepObserver = CallbackObserver<UBloxGPS, void *>(this, &UBloxGPS::prepareSleep);
public:

View File

@@ -56,12 +56,13 @@ uint32_t lastDrawMsec;
// Write the buffer to the display memory
void EInkDisplay::display(void)
{
concurrency::LockGuard g(spiLock);
// No need to grab this lock because we are on our own SPI bus
// concurrency::LockGuard g(spiLock);
uint32_t now = millis();
uint32_t sinceLast = now - lastDrawMsec;
if (framePtr && (sinceLast > 30 * 1000 || lastDrawMsec == 0)) {
if (framePtr && (sinceLast > 60 * 1000 || lastDrawMsec == 0)) {
lastDrawMsec = now;
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
@@ -76,10 +77,14 @@ void EInkDisplay::display(void)
}
}
updateDisplay(); // Send image to display and refresh
ePaper.Reset(); // wake the screen from sleep
// Put screen to sleep to save power (if wanted)
// ePaper.Sleep();
DEBUG_MSG("Updating eink... ");
updateDisplay(); // Send image to display and refresh
DEBUG_MSG("done\n");
// Put screen to sleep to save power
ePaper.Sleep();
}
}
@@ -95,6 +100,11 @@ bool EInkDisplay::connect()
{
DEBUG_MSG("Doing EInk init\n");
#ifdef PIN_EINK_PWR_ON
digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals
pinMode(PIN_EINK_PWR_ON, OUTPUT);
#endif
#ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, HIGH);
pinMode(PIN_EINK_EN, OUTPUT);

View File

@@ -241,18 +241,17 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
}
}
//asdf
static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
{
String displayLine = "";
if (!gps->getIsConnected()) {
//displayLine = "No GPS Module";
//display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
// displayLine = "No GPS Module";
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else if (!gps->getHasLock()) {
//displayLine = "No GPS Lock";
//display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
// displayLine = "No GPS Lock";
// display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
} else {
displayLine = "Altitude: " + String(gps->getAltitude()) + "m";
display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
}
@@ -905,8 +904,8 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Lost");
} else if (WiFi.status() == WL_CONNECT_FAILED) {
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
//} else if (WiFi.status() == WL_DISCONNECTED) {
// display->drawString(x, y + FONT_HEIGHT * 1, "Disconnected");
//} else if (WiFi.status() == WL_DISCONNECTED) {
// display->drawString(x, y + FONT_HEIGHT * 1, "Disconnected");
} else if (WiFi.status() == WL_IDLE_STATUS) {
display->drawString(x, y + FONT_HEIGHT * 1, "Idle ... Reconnecting");
} else {
@@ -1009,10 +1008,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->drawString(x, y, String("USB"));
}
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)),
y, "Mode " + String(channelSettings.modem_config));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y,
"Mode " + String(channelSettings.modem_config));
// Line 2
uint32_t currentMillis = millis();
@@ -1029,6 +1026,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
// Line 3
drawGPSAltitude(display, x, y + FONT_HEIGHT * 2, gpsStatus);
// Line 4

View File

@@ -23,7 +23,7 @@
#include "MeshRadio.h"
#include "MeshService.h"
#include "NMEAGPS.h"
#include "Air530GPS.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
@@ -259,10 +259,14 @@ void setup()
if (GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NMEA at 9600 baud.
// dumb NMEA access only work for serial GPSes)
DEBUG_MSG("Hoping that NMEA might work\n");
// dumb NMEA access only work for serial GPSes)
#ifdef HAS_AIR530_GPS
gps = new Air530GPS();
#else
gps = new NMEAGPS();
#endif
gps->setup();
}
}

View File

@@ -162,6 +162,11 @@ void RadioInterface::applyModemConfig()
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num,
power);
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing);
DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels);
DEBUG_MSG("Radio channel_num: %d\n", channel_num);
DEBUG_MSG("Radio frequency: %f\n", freq);
}
/**

View File

@@ -25,8 +25,8 @@ bool isWifiAvailable()
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;
//strcpy(radioConfig.preferences.wifi_ssid, "");
//strcpy(radioConfig.preferences.wifi_password, "");
// strcpy(radioConfig.preferences.wifi_ssid, "");
// strcpy(radioConfig.preferences.wifi_password, "");
if (*wifiName && *wifiPsw) {

View File

@@ -93,11 +93,6 @@ void nrf52Setup()
// This is the recommended setting for Monitor Mode Debugging
NVIC_SetPriority(DebugMonitor_IRQn, 6UL);
#ifdef PIN_PWR_ON
digitalWrite(PIN_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals
pinMode(PIN_PWR_ON, OUTPUT);
#endif
// Not yet on board
// pmu.init();