Skip to content

Instantly share code, notes, and snippets.

@chaeplin
Last active March 17, 2017 22:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chaeplin/e78ecaa11ee05c62865c to your computer and use it in GitHub Desktop.
Save chaeplin/e78ecaa11ee05c62865c to your computer and use it in GitHub Desktop.
esp8266_rtc_mem_test.ino
/*
system_rtc_mem_write
Function:
During deep sleep, only RTC still working, so maybe we need to save some user data in RTC memory.
Only user data area can be used by user.
|<--------system data--------->|<-----------------user data--------------->|
| 256 bytes | 512 bytes |
Note:
RTC memory is 4 bytes aligned for read and write operations. Parameter des_addr means block number(4 bytes per block).
So, if we want to save some data at the beginning of user data area, des_addr will be 256/4 = 64, save_size will be data length.
Prototype:
bool system_rtc_mem_write (
uint32 des_addr,
void * src_addr,
uint32 save_size
)
Parameter:
uint32 des_addr : destination address (block number) in RTC memory, des_addr >=64
void * src_addr : data pointer.
uint32 save_size : data length ( byte)
Return:
true: succeed
false: fail
system_rtc_mem_read
Function:
Read user data from RTC memory. Only user data area should be accessed by the user.
|<--------system data--------->|<-----------------user data--------------->|
| 256 bytes | 512 bytes |
Note:
RTC memory is 4 bytes aligned for read and write operations.
Parameter src_addr means block number(4 bytes per block).
So, to read data from the beginning of user data area, src_addr will be 256/4=64, save_size will be data length.
Prototype:
bool system_rtc_mem_read (
uint32 src_addr,
void * des_addr,
uint32 save_size
)
Parameter:
uint32 src_addr : source address (block number) in rtc memory, src_addr >= 64
void * des_addr : data pointer
uint32 save_size : data length, byte
Return:
true: succeed
false: fail
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "/usr/local/src/ap_setting.h"
extern "C" {
#include "user_interface.h"
}
//--
#define RTC_MAGIC 12345678
typedef struct _tagPoint {
uint32 magic ;
uint32 salt;
uint32 nemo;
} RTC_TEST;
RTC_TEST rtc_mem_test;
#define IPSET_STATIC { 192, 168, 10, 7 }
#define IPSET_GATEWAY { 192, 168, 10, 1 }
#define IPSET_SUBNET { 255, 255, 255, 0 }
#define IPSET_DNS { 192, 168, 10, 10 }
IPAddress ip_static = IPSET_STATIC;
IPAddress ip_gateway = IPSET_GATEWAY;
IPAddress ip_subnet = IPSET_SUBNET;
IPAddress ip_dns = IPSET_DNS;
//---
String macToStr(const uint8_t* mac);
void sendmqttMsg(char* topictosend, String payload);
//---
const char* ssid = WIFI_SSID;
const char* password = WIFI_PASSWORD;
IPAddress mqtt_server = MQTT_SERVER;
char* topic = "pubtest";
String clientName;
long lastReconnectAttempt = 0;
long lastMsg = 0;
int test_para = 1;
unsigned long startMills;
WiFiClient wifiClient;
PubSubClient client(mqtt_server, 1883, wifiClient);
void goingToSleepNoRF()
{
// 3 sec
Serial.println("going to sleep with no rf");
//system_deep_sleep_set_option(4);
//system_deep_sleep(10000000);
ESP.deepSleep(3000000, WAKE_RF_DISABLED);
}
void goingToSleepWithRF()
{
// 3 sec
Serial.println("going to sleep with rf");
//system_deep_sleep_set_option(0);
//system_deep_sleep(10000000);
ESP.deepSleep(1, WAKE_RF_DEFAULT);
}
void rtc_count()
{
// system_rtc_mem_read(64... not work, use > 64
system_rtc_mem_read(100, &rtc_mem_test, sizeof(rtc_mem_test));
Serial.print("=================> rtc mem mgic / salt / nemo : ");
Serial.print(rtc_mem_test.magic);
Serial.print(" / ");
Serial.print(rtc_mem_test.salt);
Serial.print(" / ");
Serial.println(rtc_mem_test.nemo);
if (rtc_mem_test.magic != RTC_MAGIC) {
Serial.println("===============> rtc mem init...");
rtc_mem_test.magic = RTC_MAGIC;
rtc_mem_test.salt = 0;
rtc_mem_test.nemo = 0;
}
rtc_mem_test.salt++;
rtc_mem_test.nemo++;
boolean reportnow = false;
if ( rtc_mem_test.nemo > 10 || rtc_mem_test.nemo == 1) {
reportnow = true;
rtc_mem_test.nemo = 1;
}
if (system_rtc_mem_write(100, &rtc_mem_test, sizeof(rtc_mem_test))) {
Serial.println("rtc mem write is ok");
} else {
Serial.println("rtc mem write is fail");
}
if (reportnow == false) {
if ( rtc_mem_test.nemo == 10 ) {
goingToSleepWithRF();
} else {
goingToSleepNoRF();
}
}
}
boolean reconnect()
{
if (!client.connected()) {
if (client.connect((char*) clientName.c_str())) {
Serial.println("===> mqtt connected");
} else {
Serial.print("---> mqtt failed, rc=");
Serial.println(client.state());
}
}
return client.connected();
}
void wifi_connect()
{
if (WiFi.status() != WL_CONNECTED) {
// WIFI
Serial.println();
Serial.print("===> WIFI ---> Connecting to ");
Serial.println(ssid);
delay(10);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
WiFi.config(IPAddress(ip_static), IPAddress(ip_gateway), IPAddress(ip_subnet), IPAddress(ip_dns));
int Attempt = 0;
while (WiFi.status() != WL_CONNECTED) {
Serial.print(". ");
Serial.print(Attempt);
delay(100);
Attempt++;
if (Attempt == 250)
{
Serial.println();
Serial.println("-----> Could not connect to WIFI");
ESP.restart();
delay(200);
}
}
Serial.println();
Serial.print("===> WiFi connected");
Serial.print(" ------> IP address: ");
Serial.println(WiFi.localIP());
}
}
void setup()
{
startMills = millis();
Serial.begin(74880);
Serial.println("");
Serial.println("rtc mem test");
Serial.println(wifi_station_get_auto_connect());
WiFi.setAutoConnect(true);
rtc_count();
wifi_connect();
clientName += "esp8266-";
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
clientName += "-";
clientName += String(micros() & 0xff, 16);
}
void loop()
{
if (WiFi.status() == WL_CONNECTED) {
if (!client.connected()) {
long now = millis();
if (now - lastReconnectAttempt > 200) {
lastReconnectAttempt = now;
if (reconnect()) {
lastReconnectAttempt = 0;
}
}
} else {
long now = millis();
if (now - lastMsg > test_para) {
lastMsg = now;
String payload = "{\"startMills\":";
payload += (millis() - startMills);
payload += ",\"salt\":";
payload += rtc_mem_test.salt;
payload += ",\"FreeHeap\":";
payload += ESP.getFreeHeap();
payload += ",\"RSSI\":";
payload += WiFi.RSSI();
payload += "}";
sendmqttMsg(topic, payload);
}
client.loop();
goingToSleepNoRF();
}
} else {
wifi_connect();
}
}
void sendmqttMsg(char* topictosend, String payload)
{
if (client.connected()) {
Serial.print("Sending payload: ");
Serial.print(payload);
unsigned int msg_length = payload.length();
Serial.print(" length: ");
Serial.println(msg_length);
byte* p = (byte*)malloc(msg_length);
memcpy(p, (char*) payload.c_str(), msg_length);
if ( client.publish(topictosend, p, msg_length)) {
Serial.println("Publish ok");
free(p);
//return 1;
} else {
Serial.println("Publish failed");
free(p);
//return 0;
}
}
}
String macToStr(const uint8_t* mac)
{
String result;
for (int i = 0; i < 6; ++i) {
result += String(mac[i], 16);
if (i < 5)
result += ':';
}
return result;
}
@donnib
Copy link

donnib commented Jan 27, 2016

Hi, Thanks for posting this. I like your example, it's what i do. I took some of your code and replaced mine. I have implemented an error count to keep track if i get errors. I dunno, i have implemented following and now i'll see how long time it will survive on count 0.

Here is my full code :

#include <PubSubClient.h>

// Import required libraries
#include "ESP8266WiFi.h"
#include "DHT.h"
#include <Arduino.h>
#include <ArduinoJson.h>

extern "C" {
#include <user_interface.h>
}

const int bufSize = 201;
int count;

// WiFi parameters HOME
//const char* ssid = "xx";
//const char* password = "xx";

// Static IP for HOME
//IPAddress ip(10,10,0,101);
//IPAddress gateway(10,10,0,1);
//IPAddress subnet(255,255,255,0);

byte rtcStore[2];

// Pin
#define DHTPIN 4

// Use DHT11 sensor
#define DHTTYPE DHT11

// Initialize DHT sensor
DHT dht(DHTPIN, DHTTYPE, 15);

// Host
const char* host = "dweet.io";

const char* mqtt_server = "54.170.168.125";

WiFiClient espClient;
PubSubClient client(espClient);

String readDataFromRTC()
{
  const int BUFFER_SIZE_R = JSON_ARRAY_SIZE(10) + 10*JSON_OBJECT_SIZE(2);
  StaticJsonBuffer<BUFFER_SIZE_R> jsonReadBuffer;

  String datalog = "";
  char jsonText[bufSize] __attribute__((aligned(4)));
  system_rtc_mem_read(66, jsonText, bufSize);

  JsonArray& rtcArray = jsonReadBuffer.parseArray(jsonText);
  Serial.println("I've just read from RTC this JSON:");
  rtcArray.printTo(Serial);
  rtcArray.printTo(datalog);

  return datalog;
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client","xx","xx")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("sensor", "hello world");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void goingToSleepNoRF()
{
  // 3 sec
  Serial.println("going to sleep with no rf");
  ESP.deepSleep(3000000, WAKE_RF_DISABLED);
}

void goingToSleepWithRF()
{
  // 3 sec
  Serial.println("going to sleep with rf");
  ESP.deepSleep(3000000, WAKE_RF_DEFAULT);
}

void turnOffRF()
{
  ESP.deepSleep(1, WAKE_RF_DISABLED);
}

void SendDataToMQTT()
{
  client.setServer(mqtt_server, 19927);

  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  char message_buff[150];
  String pub_string = readDataFromRTC();
  pub_string.toCharArray(message_buff, pub_string.length()+1);
  //Serial.println(message_buff);
  char buf[10];
  client.publish("outTopic",message_buff);
  client.publish("error",itoa(rtcStore[0],buf,10));
}

void IncreaseCounter()
{
  Serial.println("Calling increaseCounter ");
  system_rtc_mem_read(65, rtcStore, 3);

  rtcStore[1] += 1;
  Serial.print("rtc = ");
  Serial.println(rtcStore[1]);

  system_rtc_mem_write(65, rtcStore, 3);
}

void SaveDataToRTCMem(int temperature, int humidity)
{
  //Write JSON data
  const int BUFFER_SIZE = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(2);
  StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;

  JsonObject& reading = jsonBuffer.createObject();
  reading["T"] = temperature;
  reading["H"] = humidity;

  //First time we take a measurement so the RTC Mem is empty
  if(rtcStore[1]==1)
  {
    JsonArray& dataArray = jsonBuffer.createArray();
    dataArray.add(reading);
    char jsonText[bufSize] __attribute__((aligned(4)));
    dataArray.printTo(jsonText,dataArray.measureLength()+1);
    system_rtc_mem_write(66, jsonText, bufSize);
  }
  else
  {
    //Read JSON before write
    const int BUFFER_SIZE_R = JSON_ARRAY_SIZE(10) + 10*JSON_OBJECT_SIZE(2);
    StaticJsonBuffer<BUFFER_SIZE_R> jsonReadBuffer;

    //Read from RTC
    char jsonText[bufSize] __attribute__((aligned(4)));
    system_rtc_mem_read(66, jsonText, bufSize);

    JsonArray& rtcArray = jsonReadBuffer.parseArray(jsonText);
    rtcArray.add(reading);
    char jsonTextWrite[bufSize] __attribute__((aligned(4)));
    rtcArray.printTo(jsonTextWrite,rtcArray.measureLength()+1);
    system_rtc_mem_write(66, jsonTextWrite, bufSize);
  }
}

void wifi_connect()
{
  if (WiFi.status() != WL_CONNECTED) {
    // WIFI
    Serial.println();
    Serial.print("===> WIFI ---> Connecting to ");
    Serial.println(ssid);
    delay(10);
    WiFi.mode(WIFI_STA);
    WiFi.config(ip,gateway,subnet);
    WiFi.begin(ssid, password);

    int Attempt = 0;
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(". ");
      Serial.print(Attempt);
      delay(100);
      ESP.wdtFeed();
      Attempt++;
      if (Attempt == 250)
      {
        Serial.println();
        Serial.println("-----> Could not connect to WIFI");
        ESP.restart();
        delay(200);
      }

    }
    Serial.println();
    Serial.print("===> WiFi connected");
    Serial.print(" ------> IP address: ");
    Serial.println(WiFi.localIP());
  }
}

void SendData()
{
  wifi_connect();
  SendDataToMQTT();
  Serial.println("We have sent data so let's start again from 0 and without wifi");
  //Set counter back to 0 in RTC
  rtcStore[1]=0;
  //write to RTC
  system_rtc_mem_write(65, rtcStore, 3);
  WiFi.disconnect(true);
  goingToSleepNoRF();
}

void setup() {

  // Start Serial
  Serial.begin(74880);
  delay(10);
  ESP.wdtDisable();

  rst_info* rinfo = ESP.getResetInfoPtr();
  int bReason = rinfo->reason;
  Serial.print("Reason for reboot ");
  Serial.println(bReason);

  switch (bReason) {
    case 0:
      Serial.println("We just been booted first time so disable WiFi to lower power usage");
      rtcStore[1] = 0;
      rtcStore[0] = 0;
      system_rtc_mem_write(65, rtcStore, 3);
      Serial.print("rtc = ");
      Serial.println(rtcStore[1]);
      turnOffRF();
      break;
    case 4:
      //We have issued an Reset because wifi did not work
      //Check that rtcStore is at 3
      system_rtc_mem_read(65, rtcStore, 3);

      if(rtcStore[1]==3)
      {
        //Send data we didn't get a change to send last time.
        Serial.println("WiFi did now work last time so we have made a reset and will try to send data now...");
        rtcStore[0] += 1;
        SendData();
      }
      break;
    case 5:
        break;
    default:
      rtcStore[0] += 1;
      rtcStore[1] = 0;
      system_rtc_mem_write(65, rtcStore, 3);
      turnOffRF();
    break;
  }

  // Init DHT
  dht.begin();
}

void loop() {
  delay(2000);
  // Reading temperature and humidity
  int h = dht.readHumidity();
  // Read temperature as Celsius
  int t = dht.readTemperature();
  Serial.println(String("H:")+h+String("T:")+t+String(";"));

  IncreaseCounter();
  SaveDataToRTCMem(t,h);

  //If counter is 3 then let's send data
  if(rtcStore[1]==3)
  {
    SendData();
  }

  //If we have taken 2 measurements then make sure that at next boot we have wifi on so we can send data to dweet
  if(rtcStore[1]==2)
  {
    Serial.println("We got to 2 measurements so let's turn wifi on next time");
    ESP.deepSleep(3000000, RF_DEFAULT);
  }
  else
  {
    Serial.println("We are under 2 so let's keep wifi off");
    goingToSleepNoRF();
  }
}

@chaeplin
Copy link
Author

@donnib store json array to rtcmem !!. thanks. I will use it.

@chaeplin
Copy link
Author

chaeplin commented Feb 1, 2016

http://lowreal.net/2016/01/10/1 --->
'''

include <Arduino.h>

extern "C" {

include <user_interface.h>

};

// system_rtc_mem_write() 先のブロックアドレス。
// 4 bytes で align されており、先頭256bytes はシステム予約領域
// 64 から書けるはずだが、65 以降でないとうまくいかなかった。。
static const uint32_t USER_DATA_ADDR = 66;

// ハッシュ関数 (FNV) CRC でいいけどコード的に短いのでFNV
static uint32_t fnv_1_hash_32(uint8_t *bytes, size_t length) {
static const uint32_t FNV_OFFSET_BASIS_32 = 2166136261U;
static const uint32_t FNV_PRIME_32 = 16777619U;
uint32_t hash = FNV_OFFSET_BASIS_32;;
for(size_t i = 0 ; i < length ; ++i) hash = (FNV_PRIME_32 * hash) ^ (bytes[i]);
return hash;
}

// struct の hash (先頭にあることを想定) を除くデータ部分のハッシュを計算する
template
uint32_t calc_hash(T& data) {
return fnv_1_hash_32(((uint8_t*)&data) + sizeof(data.hash), sizeof(T) - sizeof(data.hash));
}

struct {
// retain data
uint32_t hash;
uint16_t count;
uint8_t send;
uint16_t etc2;
} retain_data;

void post_sensor_data();

void setup() {
pinMode(13, OUTPUT);

Serial.begin(9600);
Serial.println("Initializing...");

// データ読みこみ
bool ok;
ok = system_rtc_mem_read(USER_DATA_ADDR, &retain_data, sizeof(retain_data));
if (!ok) {
    Serial.println("system_rtc_mem_read failed");
}
Serial.print("retain_data.count = ");
Serial.println(retain_data.count);

// ハッシュが一致していない場合、初期化されていないとみなし、初期化処理を行う
uint32_t hash = calc_hash(retain_data);
if (retain_data.hash != hash) {
    Serial.println("retain_data may be uninitialized");
    retain_data.count = 0;
    retain_data.send = 0;
}

// データの変更処理(任意)
retain_data.count++;
if (!retain_data.send) {
    // 4回に1度送信する
    retain_data.send = retain_data.count % 4 == 0;
} else {
    Serial.println("send data");
    retain_data.send = 0;
    // なんか定期的に書きこみたい処理
    post_sensor_data();
}

// 書きこみ処理。hash を計算していれておく
retain_data.hash = hash = calc_hash(retain_data);
ok = system_rtc_mem_write(USER_DATA_ADDR, &retain_data, sizeof(retain_data));
if (!ok) {
    Serial.println("system_rtc_mem_write failed");
}

// 動作確認用のダミー
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);

if (retain_data.send) {
    ESP.deepSleep(1e6, WAKE_RF_DEFAULT);
} else {
    // sendしない場合は WIFI をオフで起動させる
    ESP.deepSleep(1e6, WAKE_RF_DISABLED);
}

}

void loop() {
}

// dummy
void post_sensor_data() {
for (uint i = 0; i < 5; i++) {
digitalWrite(13, HIGH);
delay(300);
digitalWrite(13, LOW);
delay(300);
}
}
'''

@chaeplin
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment