Skip to content

Commit 81594e7

Browse files
committed
MCU8MASS-852 MCU8MASS-949 Lower power consumption and fix bug where MQTT prevented low power
1 parent fdd073d commit 81594e7

File tree

6 files changed

+172
-76
lines changed

6 files changed

+172
-76
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* @brief This example demonstrates how to use MQTT with low power.
3+
*/
4+
5+
#include <Arduino.h>
6+
7+
#include <ecc608.h>
8+
#include <led_ctrl.h>
9+
#include <log.h>
10+
#include <low_power.h>
11+
#include <lte.h>
12+
#include <mcp9808.h>
13+
#include <mqtt_client.h>
14+
#include <veml3328.h>
15+
16+
#define USE_PSM true
17+
18+
static char mqtt_pub_topic[128];
19+
20+
bool initMQTTTopics() {
21+
ECC608.begin();
22+
23+
// Find the thing ID and set the publish and subscription topics
24+
uint8_t thingName[128];
25+
uint8_t thingNameLen = sizeof(thingName);
26+
27+
// -- Get the thingname
28+
uint8_t err = ECC608.getThingName(thingName, &thingNameLen);
29+
if (err != ECC608.ERR_OK) {
30+
Log.error("Could not retrieve thingname from the ECC");
31+
return false;
32+
}
33+
34+
sprintf(mqtt_pub_topic, "%s/sensors", thingName);
35+
36+
return true;
37+
}
38+
39+
void setup() {
40+
Log.begin(115200);
41+
LedCtrl.begin();
42+
LedCtrl.startupCycle();
43+
44+
Log.info("Starting MQTT with low power\r\n");
45+
46+
// First we retrieve the topics we're going to publish to, here using the
47+
// ECC thingname with AWS
48+
if (initMQTTTopics() == false) {
49+
Log.error("Unable to initialize the MQTT topics. Stopping...");
50+
while (1) {}
51+
}
52+
53+
// Configure low power depending on whether to use power save mode or just a
54+
// complete power down.
55+
#if USE_PSM
56+
LowPower.configurePeriodicPowerSave(
57+
PowerSaveModePeriodMultiplier::ONE_MINUTE,
58+
1);
59+
#else
60+
LowPower.configurePowerDown();
61+
#endif
62+
63+
Lte.begin();
64+
65+
// If we're using PSM, we only need to connect to the MQTT broker at the
66+
// beginning, since the connection will remain active
67+
#if USE_PSM
68+
MqttClient.beginAWS();
69+
70+
Log.infof("Connecting to broker");
71+
72+
while (!MqttClient.isConnected()) {
73+
Log.rawf(".");
74+
delay(500);
75+
}
76+
77+
Log.raw(" OK!\r\n");
78+
#endif
79+
80+
Mcp9808.begin();
81+
Veml3328.begin();
82+
}
83+
84+
static uint32_t counter = 0;
85+
86+
void loop() {
87+
88+
Mcp9808.wake();
89+
Veml3328.wake();
90+
91+
// If we're not using PSM, all connections will be terminated when power
92+
// down is issued, so we need to re-establish s connection
93+
#if !USE_PSM
94+
MqttClient.beginAWS();
95+
96+
Log.infof("Connecting to broker");
97+
98+
while (!MqttClient.isConnected()) {
99+
Log.rawf(".");
100+
delay(500);
101+
}
102+
103+
Log.raw(" OK!\r\n");
104+
#endif
105+
106+
char message_to_publish[8] = {0};
107+
sprintf(message_to_publish, "%lu", counter);
108+
109+
bool published_successfully =
110+
MqttClient.publish(mqtt_pub_topic, message_to_publish, AT_LEAST_ONCE);
111+
112+
if (published_successfully) {
113+
Log.infof("Published message: %s.\r\n", message_to_publish);
114+
} else {
115+
Log.info("Failed to publish");
116+
}
117+
118+
counter++;
119+
120+
Mcp9808.shutdown();
121+
Veml3328.shutdown();
122+
123+
Log.info("Entering low power");
124+
delay(10);
125+
126+
#if USE_PSM
127+
LowPower.powerSave();
128+
#else
129+
LowPower.powerDown(60);
130+
#endif
131+
132+
Log.info("Woke up!");
133+
delay(10000);
134+
}

examples/power_down/power_down.ino

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,12 @@
1111
#include <mcp9808.h>
1212
#include <veml3328.h>
1313

14-
#define SW0 PIN_PD2
15-
16-
void buttonPressedInterrupt(void) {
17-
if (PORTD.INTFLAGS & PIN2_bm) {
18-
// Reset the interupt flag so that we can process the next incoming
19-
// interrupt
20-
PORTD.INTFLAGS = PIN2_bm;
21-
}
22-
}
23-
2414
void setup() {
2515
Log.begin(115200);
2616

2717
LedCtrl.begin();
2818
LedCtrl.startupCycle();
2919

30-
// Configure SW0 for interrupt so we can wake the device up from sleep by
31-
// pressing the button
32-
pinConfigure(SW0, PIN_DIR_INPUT);
33-
attachInterrupt(SW0, buttonPressedInterrupt, FALLING);
34-
3520
// Now we configure the low power module for power down configuration, where
3621
// the cellular modem and the CPU will be powered down
3722
LowPower.configurePowerDown();

examples/power_save/power_save.ino

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,12 @@
1212
#include <sequans_controller.h>
1313
#include <veml3328.h>
1414

15-
#define SW0 PIN_PD2
16-
17-
void buttonPressedInterrupt(void) {
18-
if (PORTD.INTFLAGS & PIN2_bm) {
19-
PORTD.INTFLAGS = PIN2_bm;
20-
}
21-
}
22-
2315
void setup() {
2416
Log.begin(115200);
2517

2618
LedCtrl.begin();
2719
LedCtrl.startupCycle();
2820

29-
// Configure SW0 for interrupt so we can wake the device up from sleep by
30-
// pressing the button
31-
pinConfigure(SW0, PIN_DIR_INPUT);
32-
attachInterrupt(SW0, buttonPressedInterrupt, FALLING);
33-
3421
// Now we configure the power save configuration. Note that this has to be
3522
// done before calling Lte.begin().
3623
//

src/low_power.cpp

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,9 @@ static void restorePinState(void) {
436436
* @brief Sets the pin state in order to minimize the power consumption. Will
437437
* not enable LDO.
438438
*
439-
* @param keep_ring_active Whether to keep ring line active (used in PSM).
439+
* @param keep_modem_active Whether to keep modem lines active (used in PSM).
440440
*/
441-
static void powerDownPeripherals(const bool keep_ring_active) {
442-
443-
// TODO: might keep SW0 in order to not mess with interrupt
441+
static void powerDownPeripherals(const bool keep_modem_active) {
444442

445443
savePinState();
446444

@@ -508,14 +506,26 @@ static void powerDownPeripherals(const bool keep_ring_active) {
508506

509507
PORTA.DIR = 0x00;
510508
PORTB.DIR = PIN3_bm | PIN4_bm;
511-
PORTC.DIR = 0x00;
509+
510+
if (keep_modem_active) {
511+
PORTC.DIRCLR = PIN2_bm | PIN3_bm;
512+
} else {
513+
PORTC.DIR = 0x00;
514+
}
515+
512516
PORTD.DIR = 0x00;
513517
PORTE.DIR = 0x00;
514518
PORTF.DIR = 0x00;
515519

516520
PORTA.OUT = 0x00;
517521
PORTB.OUT = 0x00;
518-
PORTC.OUT = 0x00;
522+
523+
if (keep_modem_active) {
524+
PORTC.OUTCLR = PIN2_bm | PIN3_bm;
525+
} else {
526+
PORTC.OUT = 0x00;
527+
}
528+
519529
PORTD.OUT = 0x00;
520530
PORTE.OUT = 0x00;
521531
PORTF.OUT = 0x00;
@@ -538,18 +548,17 @@ static void powerDownPeripherals(const bool keep_ring_active) {
538548
PORTB.PIN6CTRL = 0x0C;
539549
PORTB.PIN7CTRL = 0x0C;
540550

541-
PORTC.PIN0CTRL = 0x0C;
542-
PORTC.PIN1CTRL = 0x0C;
543551
PORTC.PIN2CTRL = 0x04;
544552
PORTC.PIN3CTRL = 0x04;
545-
PORTC.PIN4CTRL = 0x0C;
546-
PORTC.PIN5CTRL = 0x04;
547-
if (keep_ring_active) {
548-
PORTC.PIN6CTRL = 0x01;
549-
} else {
553+
554+
if (!keep_modem_active) {
555+
PORTC.PIN0CTRL = 0x0C;
556+
PORTC.PIN1CTRL = 0x0C;
557+
PORTC.PIN4CTRL = 0x0C;
558+
PORTC.PIN5CTRL = 0x04;
550559
PORTC.PIN6CTRL = 0x04;
560+
PORTC.PIN7CTRL = 0x04;
551561
}
552-
PORTC.PIN7CTRL = 0x04;
553562

554563
PORTD.PIN0CTRL = 0x0C;
555564
PORTD.PIN1CTRL = 0x0C;
@@ -688,9 +697,6 @@ void LowPowerClass::configurePeriodicPowerSave(
688697

689698
void LowPowerClass::powerSave(void) {
690699

691-
const uint8_t cell_led_state = digitalRead(LedCtrl.getLedPin(Led::CELL));
692-
const uint8_t con_led_state = digitalRead(LedCtrl.getLedPin(Led::CON));
693-
694700
if (!retrieved_period) {
695701
// Retrieve the proper sleep time set by the operator, which may
696702
// deviate from what we requested
@@ -712,7 +718,7 @@ void LowPowerClass::powerSave(void) {
712718

713719
// Retrieving the operator sleep time will call CEREG, which will
714720
// trigger led ctrl, so we just disable it again.
715-
LedCtrl.off(Led::CELL, true);
721+
// LedCtrl.off(Led::CELL, true);
716722
}
717723
if (!attemptToEnterPowerSaveModeForModem(30000)) {
718724
Log.error(
@@ -724,55 +730,36 @@ void LowPowerClass::powerSave(void) {
724730
powerDownPeripherals(true);
725731
SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc | SLPCTRL_SEN_bm;
726732

727-
// enableLDO();
733+
enableLDO();
728734
sleep_cpu();
729735

730736
// Will sleep here until we get the RING line activity and wake up
731-
// disableLDO();
737+
disableLDO();
732738

733739
SLPCTRL.CTRLA &= ~SLPCTRL_SEN_bm;
734740
powerUpPeripherals();
735741

736742
modem_is_in_power_save = false;
737743
}
738744

739-
SequansController.setPowerSaveMode(0, NULL);
740-
741-
// Pins are active low
742-
if (!cell_led_state) {
743-
LedCtrl.on(Led::CELL, true);
744-
} else {
745-
LedCtrl.off(Led::CELL, true);
746-
}
745+
// LedCtrl.on(Led::CELL, true);
747746

748-
if (!con_led_state) {
749-
LedCtrl.on(Led::CON, true);
750-
} else {
751-
LedCtrl.off(Led::CON, true);
752-
}
747+
SequansController.setPowerSaveMode(0, NULL);
753748
}
754749

755750
void LowPowerClass::powerDown(const uint32_t power_down_time_seconds) {
756751

757-
const unsigned long start_time_ms = millis();
758-
759752
SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc | SLPCTRL_SEN_bm;
760753

761754
Lte.end();
762755

763-
uint32_t remaining_time_seconds =
764-
power_down_time_seconds -
765-
(uint32_t)(((millis() - start_time_ms) / 1000.0f));
766-
767-
// Need to power down the peripherals here as we want to grab the time
768-
// (millis()) from the timer before disabling it
756+
// TODO: somehow this prevents the modem to sleep...
769757
powerDownPeripherals(false);
770758

771759
enablePIT();
772760
enableLDO();
773761

774-
// TODO: might want to just have the remaining time in seconds to be the
775-
// power_down_time_seconds to prevent issues with millis() overflowing
762+
uint32_t remaining_time_seconds = power_down_time_seconds;
776763

777764
while (remaining_time_seconds > 0) {
778765

@@ -793,7 +780,6 @@ void LowPowerClass::powerDown(const uint32_t power_down_time_seconds) {
793780

794781
powerUpPeripherals();
795782

796-
// TODO: should we keep this here?
797783
while (!Lte.begin()) {}
798784
}
799785

src/lte.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
#include "lte.h"
12
#include "led_ctrl.h"
23
#include "log.h"
3-
#include "lte.h"
44
#include "mqtt_client.h"
55
#include "sequans_controller.h"
66

@@ -255,6 +255,9 @@ void LteClass::end(void) {
255255

256256
if (SequansController.isInitialized()) {
257257

258+
// Terminate active connections (if any)
259+
MqttClient.end();
260+
258261
SequansController.unregisterCallback(TIMEZONE_CALLBACK);
259262
SequansController.writeCommand(AT_DISCONNECT);
260263

@@ -300,8 +303,8 @@ String LteClass::getOperator(void) {
300303
}
301304

302305
// Remove the quotes
303-
char* buffer = id + 1;
304-
id[strnlen(buffer, 47)] = '\0';
306+
char* buffer = id + 1;
307+
id[strnlen(buffer, sizeof(id) - 1)] = '\0';
305308

306309
return String(buffer);
307310
}

src/lte.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef LTE_H
66
#define LTE_H
77

8+
#include <Arduino.h>
89
#include <stdint.h>
910

1011
class LteClass {
@@ -20,7 +21,7 @@ class LteClass {
2021
/**
2122
* @brief Singleton instance.
2223
*/
23-
static LteClass &instance(void) {
24+
static LteClass& instance(void) {
2425
static LteClass instance;
2526
return instance;
2627
}

0 commit comments

Comments
 (0)