Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
458611e
Added setClockSource to retrieve custom millis() value
jmpmscorp Jan 29, 2019
0879f2d
Added clearClockSource
jmpmscorp Jan 29, 2019
bf13f84
Added ArduinoEthernetShieldSleep example
jmpmscorp Jan 30, 2019
d74891b
Update Readme
jmpmscorp Jan 30, 2019
cbbcd4d
MqttClientClockSourceCb to MqttClientClockSource Delete clearClockSource
jmpmscorp Feb 7, 2019
e204b43
MqttClientClockSourceCb to MqttClientClockSource
jmpmscorp Feb 7, 2019
4c6a58c
Delete clearClockSource
jmpmscorp Feb 7, 2019
fbdd93c
Merge remote-tracking branch 'origin/master'
jmpmscorp Feb 7, 2019
4e1cea4
Add ArduinoMKRGSM1400Sleep example
jmpmscorp Feb 8, 2019
f90b8d5
Amend EthernetShieldSleep example. Add didWakeup = false
jmpmscorp Feb 8, 2019
c8698e5
Update Readme
jmpmscorp Feb 8, 2019
10fa8c9
move implementation to cpp file
256dpi Feb 8, 2019
929919c
Merge branch 'master' of https://github.com/256dpi/arduino-mqtt
256dpi Feb 8, 2019
75740e3
fmt
256dpi Feb 8, 2019
eae351a
added setClockSource to enable a custom clock source for deep sleep a…
jmpmscorp Jan 29, 2019
d982bab
updated lwmqtt
256dpi Feb 8, 2019
c36e561
bump version
256dpi Feb 8, 2019
13b75e5
Added setClockSource to retrieve custom millis() value
jmpmscorp Jan 29, 2019
641e4d3
Added ArduinoEthernetShieldSleep example
jmpmscorp Jan 30, 2019
eb59f4e
Update Readme
jmpmscorp Jan 30, 2019
e848349
Add ArduinoMKRGSM1400Sleep example
jmpmscorp Feb 8, 2019
ef1dd25
Amend EthernetShieldSleep example. Add didWakeup = false
jmpmscorp Feb 8, 2019
92f9272
Update Readme
jmpmscorp Feb 8, 2019
f77f12f
Merge remote-tracking branch 'origin/master'
jmpmscorp Feb 8, 2019
b5f7ba5
Added setClockSource, ethernet and mkr1400 examples
jmpmscorp Feb 9, 2019
4e9f650
Merge remote-tracking branch 'origin/master'
jmpmscorp Feb 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .development
Empty file.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
.DS_Store
cmake-build-debug/

\.development

*\.vscode/
11 changes: 6 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# Uncompilable CMake File to enable project editing with CLion IDE

cmake_minimum_required(VERSION 2.8.4)
cmake_minimum_required(VERSION 3.13)
project(arduino-mqtt)

include_directories(
/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/
/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino
/Users/256dpi/Development/Arduino/libraries/Ethernet/src
/Users/256dpi/Development/Arduino/libraries/WiFi101/src
/Users/256dpi/Development/Arduino/libraries/MKRGSM/src
/Applications/Arduino.app/Contents/Java/libraries/Bridge/src
/Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFi/src
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFiClientSecure/src
src/)
src)

include_directories(src/)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS -std=c++11)

set(SOURCE_FILES
examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino
Expand All @@ -33,6 +33,7 @@ set(SOURCE_FILES
examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino
src/lwmqtt
src/MQTT.h
src/MQTTClient.h)
src/MQTTClient.h
src/MQTTClient.cpp)

add_executable(arduino-mqtt ${SOURCE_FILES})
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
all: fmt

fmt:
clang-format -i src/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}"
clang-format -i src/*.h src/*.cpp -style="{BasedOnStyle: Google, ColumnLimit: 120}"

update:
rm -rf ./lwmqtt
git clone --branch v0.6.2 https://github.com/256dpi/lwmqtt.git ./lwmqtt
git clone --branch v0.6.4 https://github.com/256dpi/lwmqtt.git ./lwmqtt
mkdir -p ./src/lwmqtt
cp -r ./lwmqtt/src/*.c ./src/lwmqtt/
cp -r ./lwmqtt/src/*.h ./src/lwmqtt/
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,29 @@ void setOptions(int keepAlive, bool cleanSession, int timeout);

- The `keepAlive` option controls the keep alive interval in seconds (default: 10).
- The `cleanSession` option controls the session retention on the broker side (default: true).
- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000).
- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000).

Set a custom clock source "custom millis" callback to enable deep sleep applications:

```c++
void setClockSource(MQTTClientClockSource);
// Callback signature: uint32_t clockSource() {}
```

- The specified callback is used by the internal timers to get a monotonic time in milliseconds. Since the clock source for the built-in `millis` is stopped when the the Arduino goes into deep sleep, you need to provide a custom callback that first syncs with a built-in or external Real Time Clock (RTC). You can pass `NULL` to reset to the default implementation.

Connect to broker using the supplied client id and an optional username and password:

Register a custom clock source ("millis source") callback:
```c++
void setClockSource(MQTTClientClockSource);
// MQTTClientClockSource callback signature: uint32_t customMillis (void) {}

```

- If no callback is registered `millis()` function is used. Otherwise, passed function is used.
- You can clear callback at any time. At this moment, `millis()` will be use again as "millis source"

```c++
bool connect(const char clientId[], bool skip = false);
bool connect(const char clientId[], const char username[], bool skip = false);
Expand Down
204 changes: 204 additions & 0 deletions examples/ArduinoEthernetShieldSleep/ArduinoEthernetShieldSleep.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/**
* Hardware Used:
* - Arduino UNO
* - Ethernet Shield (W5100 model)
* - DS1307 RTC
*
* I have tested it with mosquitto and emqx brokers installed in my local
* network but it should work with any broker in cloud.
*
* Device will go to sleep when it detects no MQTT activity during 2.5 seconds.
*
* Device will wake up periodically by WDT, in order to, if no MQTT activity,
* do keep_alive if necessary. You should take care of keep alive time and wdt
* period configuration. If your wdt period is too much high than keep alive, you
* could lost connection because keep alive is not sent.
*
* Furthermore, device could be wake up if ethernet traffic is detected. Ethernet
* shield Interrupt is enabled (ensure you have soldered ethernet shield /INT to INT0 or INT1).
*
* For testing purpose, every minute, a packet will be publish to test/period
* topic. If you want to test ethernet interruption, you could publish any message
* through test/receive topic.
*
*
* by Jose Manuel Perez
* https://github.com/jmpmscorp *
*/

#include <Ethernet.h>
#include <utility/w5100.h>
#include <MQTT.h>

#include <Wire.h>
#include "RTClib.h" // From https://github.com/adafruit/RTClib

#include <avr/sleep.h>
#include <avr/wdt.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte ip[] = {192, 168, 1, 66}; // <- change to match your network

const char * mqttBrokerAddress = "192.168.1.65";
const char * mqttClientId = "arduino-ethernet";
const char * mqttUsername = "";
const char * mqttPassword = "";

EthernetClient net;
MQTTClient client;
RTC_DS1307 rtc;

volatile bool ethShieldInterruptFlag = false;
volatile bool wdtInterruptFlag = false;
bool didWakeup = true;

unsigned long mqttLastActivityMillis = 0;

void connect() {
Serial.print("connecting...");
while (!client.connect(mqttClientId, mqttUsername, mqttPassword)) {
Serial.print(".");
delay(1000);
}

Serial.println("\nconnected!");

client.subscribe("test/receive");
}

void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}

void setup() {
wdt_disable();
Serial.begin(19200);
Ethernet.begin(mac, ip);
while(!rtc.begin()) {
Serial.println(F("Couldn't find RTC"));
delay(2500);
}

rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

Serial.println(F("Start!"));

// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino.
// You need to set the IP address directly.
client.begin(mqttBrokerAddress, net);
client.setClockSource(customMillis);
client.setOptions(30, true, 1500);
client.onMessage(messageReceived);

// enable interrupts for Socket 0
W5100.writeIMR(0x01);
attachInterrupt(digitalPinToInterrupt(2), [](){ ethShieldInterruptFlag = true; }, FALLING );

connect();
enableWdt(WDTO_8S);
}

void loop() {
if(wdtInterruptFlag) {
wdt_reset();
// Clear WDT Int Flag
WDTCSR |= _BV(WDIE);
wdtInterruptFlag = false;
}

client.loop();

if (!client.connected()) {
connect();
}
else {
periodicallySent();
}

if(ethShieldInterruptFlag) {
ethShieldInterruptFlag = false;
//Serial.println("Ethernet Shield Interrupt");
uint8_t irState = W5100.readSnIR(0);
//Serial.println(irState, HEX);
W5100.writeSnIR(0, irState);
mqttLastActivityMillis = customMillis();
}

if(customMillis() - mqttLastActivityMillis > 2500) {
Serial.println(F("To Sleep!"));
Serial.flush();
enterSleep();
}
}

void periodicallySent() {
static unsigned long lastSentMillis = 0;

if(customMillis() - lastSentMillis > 60000) {
if(client.publish("test/period", "Periodically packet")) {
lastSentMillis = customMillis();
}
}
}

uint32_t customMillis() {
static uint32_t offset = 0;

if(didWakeup) {
offset = rtcMillis() - millis();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you set didWakeup = false after setting the offset, customMillis will call rtcMillis only once after wakeup, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, of course! I forgot it. Feel free to add it.

didWakeUp = false;
}

return millis() + offset;
}

uint32_t rtcMillis() {
return rtc.now().secondstime() * 1000; //We return epoch time in milliseconds
}

void enterSleep() {
ADCSRA &= ~_BV(ADEN);

set_sleep_mode(SLEEP_MODE_STANDBY);
noInterrupts();
sleep_enable();
interrupts();
sleep_mode();

sleep_disable();


ADCSRA |= _BV(ADEN);

doAfterSleep();
}

void doAfterSleep() {
didWakeup = true;
Serial.println(F("Wake Up!"));
}

void enableWdt(uint16_t period) {
#ifdef ARDUINO_ARCH_AVR

// Both WDE and WDIE
__asm__ __volatile__ ( \
"in __tmp_reg__,__SREG__" "\n\t" \
"cli" "\n\t" \
"wdr" "\n\t" \
"sts %0,%1" "\n\t" \
"out __SREG__,__tmp_reg__" "\n\t" \
"sts %0,%2" "\n\t" \
: /* no outputs */ \
: "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
"r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
"r" ((uint8_t) (((period & 0x08) ? _WD_PS3_MASK : 0x00) | \
_BV(WDE) | _BV(WDIE) | (period & 0x07)) ) \
: "r0" \
);
#endif
}

ISR(WDT_vect) {
wdtInterruptFlag = true;
}
Loading