STM32F103を使ってみる

STANDBY中のタイマー精度


STM32 にはいくつかの省電力機能が有ります。
こ ちらこちらに詳しい解説が有りま す。
こ ちらの解説書(5.2.3章)に各モードの消費電流が記載されています。
SLEEP mode(72Mhz)
5.5から14.4mA
STOP mode
14から24μA
STANDBY mode
2μA

こちらにSTOP mode/STANDBY modeに移行するためのライブラリが公開されています。
そこで消費電流が一番小さいSTANDBY mode時のタイマー精度を測定してみました。
なお、STANDBY modeに移行するには以下の関数を使用しますが、指定できる秒数の精度が8ビット(最大255秒)なので
この関数は使わずに、直接createAlarmを使います。

void sleepAndWakeUp(SleepMode mode, RTClock *rt, uint8_t seconds) {
  rt->createAlarm(&noop, rt->getTime() + seconds);
  goToSleep(mode);
}

STANDBY modeからの復帰方法についてはこち らに解説が公開されています。
いずれの方法で復帰してもスケッチの先頭から再開しますが、このときシリアルモニターの初期化がうまく行われず、
シリアルモニターには何も表示されません。
そこで、再開したことが分かるように、W5500イーサネットモジュールを使って、MQTTのPublishを行います。
Sleep時間は3600秒とし、起動直後からPublish実行までの処理時間(大体1秒)を補正しています。
#include <SPI.h>
#include <Ethernet_STM.h> // https://github.com/rogerclarkmelbourne/Arduino_STM32
#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient
#include <STM32Sleep.h>   // https://github.com/chacal/stm32sleep
#include <RTClock.h>

#define MQTT_SERVER     "192.168.10.40"
//#define MQTT_SERVER     "broker.hivemq.com"
//#define MQTT_SERVER     "iot.eclipse.org"
#define MQTT_PORT       1883
#define MQTT_KEEP_ALIVE 60
#define MQTT_TOPIC      "stm32f103/STANDBY" // You can change
//#define MQTT_WILL_MSG   "I am leaving..." // You can change
#define MQTT_WILL_MSG   "" // You can change
#define STOP_BUTTON     PB0 // 0: Disable STOP_BUTTON
#define RUNNING_LED     PB1 // 0: Disable RUNNING_LED

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
#if defined(WIZ550io_WITH_MACADDRESS) // Use assigned MAC address of WIZ550io
;
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
#endif 

EthernetClient ethClient;
PubSubClient pubsubClient(ethClient);

RTClock rt(RTCSEL_LSE);
long alarmDelay = 3600;
//long alarmDelay = 30;

static void noop() {};

void putChar(char c) {
  char tmp[10];
  if ( c == 0x0a) {
    Serial.println();
  } else if (c == 0x0d) {
   
  } else if ( c < 0x20) {
    uint8_t cc = c;
    sprintf(tmp,"[0x%.2X]",cc);
    Serial.print(tmp);
  } else {
    Serial.print(c);
  }
}

//Wait for specific input string until timeout runs out
bool waitForString(char* input, int length, unsigned int timeout) {
  unsigned long end_time = millis() + timeout;
  char current_byte = 0;
  int index = 0;

   while (end_time >= millis()) {
   
      if(Serial2.available()) {
       
        //Read one byte from serial port
        current_byte = Serial2.read();
//        Serial.print(current_byte);
        putChar(current_byte);
        if (current_byte != -1) {
          //Search one character at a time
          if (current_byte == input[index]) {
            index++;
           
            //Found the string
            if (index == length) {             
              return true;
            }
          //Restart position of character to look for
          } else {
            index = 0;
          }
        }
      }
  } 
  //Timed out
  return false;
}

void getResponse(int timeout){
  char c;
  bool flag = false;
  char tmp[10];
 
  long int time = millis() + timeout;
  while( time > millis()) {
    if (Serial2.available()) {
      flag = true;
      c = Serial2.read();
      if (c == 0x0d) {
          
      } else if (c == 0x0a) {
        Serial.println();
      } else if ( c < 0x20) {
        uint8_t cc = c;
        sprintf(tmp,"[0x%.2X]",cc);
        Serial.print(tmp);
      } else {
        Serial.print(c);
      }
    } // end if
  } // end while
  if (flag) Serial.println();
}

void errorDisplay(char* buff) {
  int stat = 0;
  Serial.print("Error:");
  Serial.println(buff);
  while(1) {
    if (RUNNING_LED) {
      digitalWrite(RUNNING_LED,stat);
      stat = !stat;
      delay(100);
    }
  }
}

void clearBuffer() {
  while (Serial2.available())
    Serial2.read();
//  Serial.println("");
}

int buildConnect(byte *buf, int keep_alive, char *client_id, char *will_topic, char *will_msg) {
  int rlen = 12;
  int pos = 14;

  int client_id_len = strlen(client_id);
//  Serial.println(client_id_len);
  buf[pos++] = 0x00;
  buf[pos++] = client_id_len;
  for(int i=0;i<client_id_len;i++) {
    buf[pos++] = client_id[i];
  }
  rlen = rlen + 2 + client_id_len;
 
  int will_topic_len = strlen(will_topic);
//  Serial.println(will_topic_len);
  int will_msg_len = strlen(will_msg);
//  Serial.println(will_msg_len);

  if (will_topic_len > 0 && will_msg_len > 0) {
    buf[pos++] = 0x00;
    buf[pos++] = will_topic_len;
    for(int i=0;i<will_topic_len;i++) {
      buf[pos++] = will_topic[i];
    }
    buf[pos++] = 0x00;
    buf[pos++] = will_msg_len;
    for(int i=0;i<will_msg_len;i++) {
      buf[pos++] = will_msg[i];
    }
    rlen = rlen + 2 + will_topic_len + 2 + will_msg_len; 
  }

  buf[0] = 0x10;
  buf[1] = rlen;
  buf[2] = 0x00;
  buf[3] = 0x06;
  buf[4] = 'M';
  buf[5] = 'Q';
  buf[6] = 'I';
  buf[7] = 's';
  buf[8] = 'd';
  buf[9] = 'p';
  buf[10] = 0x03;
  buf[11] = 0x02;
  if (will_topic_len > 0 && will_msg_len > 0) buf[11] = 0x06;
  buf[12] = 0x00;
  buf[13] = keep_alive;
  return buf[1] + 2; 
}

int buildPublish(byte *buf, char *topic, char *msg) {
  int tlen = strlen(topic);
  for(int i=0;i<tlen;i++) {
    buf[4+i] = topic[i];
  }
  int mlen = strlen(msg);
  for(int i=0;i<mlen;i++) {
    buf[4+tlen+i] = msg[i];
  }
  buf[0] = 0x30;
  buf[1] = tlen + mlen + 2;
  buf[2] = 0x00;
  buf[3] = tlen;
  return buf[1] + 2;  
}

int buildSubscribe(byte *buf, char *topic) {
  int tlen = strlen(topic);
  for(int i=0;i<tlen;i++) {
    buf[6+i] = topic[i];
  }
  buf[0] = 0x82;
  buf[1] = tlen + 5;
  buf[2] = 0x00;
  buf[3] = 0x01;
  buf[4] = 0x00;
  buf[5] = tlen;
  buf[tlen+6] = 0x00;
  return buf[1] + 2;  
}


void hexDump(byte *buf, int msize) {
  Serial.print("\nmsize=");
  Serial.println(msize);
  for(int i=0;i<msize;i++) {
    Serial.print(buf[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
}

int getIpAddress(char *buf, int szbuf, int timeout) {
  Serial2.print("AT+CIPSTA?\r\n");
  int len=0;
  int pos=0;
  char line[128];
   
  long int time = millis();

  while( (time+timeout) > millis()) {
    while(Serial2.available())  {
      char c = Serial2.read(); // read the next character.
      if (c == 0x0d) {
         
      } else if (c == 0x0a) {
        Serial.print("Read=[");
        Serial.print(line);
        Serial.println("]");
        if (strncmp(line,"+CIPSTA:ip:",11) == 0) {
          strcpy(buf,&line[12]);
          len = strlen(buf) - 1;
          buf[len] = 0;
        }
        if (strcmp(line,"OK") == 0) return len;
        pos=0;
        line[pos]=0;
      } else {
        line[pos++]=c;
        line[pos]=0;
      }
    } 
  }
  return len;
}

int getMacAddress(char *buf, int szbuf, int timeout) {
  Serial2.print("AT+CIPSTAMAC?\r\n");
  int len=0;
  int pos=0;
  char line[128];
   
  long int time = millis();

  while( (time+timeout) > millis()) {
    while(Serial2.available())  {
      char c = Serial2.read(); // read the next character.
      if (c == 0x0d) {
         
      } else if (c == 0x0a) {
        Serial.print("Read=[");
        Serial.print(line);
        Serial.println("]");
        if (strncmp(line,"+CIPSTAMAC:",11) == 0) {
          strcpy(buf,&line[12]);
          len = strlen(buf) - 1;
          buf[len] = 0;
        }
        if (strcmp(line,"OK") == 0) return len;
        pos=0;
        line[pos]=0;
      } else {
        line[pos++]=c;
        line[pos]=0;
      }
    } 
  }
  return len;
}


void setup() {
  unsigned long Time1;
  unsigned long Time2;

  delay(1000);
  Time1 = millis();
  Serial.begin(9600);

  pinMode(STOP_BUTTON,INPUT);
  int wk = digitalRead(STOP_BUTTON);
  if (wk) {
    while(1) {
      Serial.println("HALT!!");
      delay(1000);
    }
  }

  if (RUNNING_LED) {
    pinMode(RUNNING_LED,OUTPUT);
    digitalWrite(RUNNING_LED,LOW);
  }

  // start Ethernet and UDP
#if defined(WIZ550io_WITH_MACADDRESS)
  if (Ethernet.begin() == 0) {
#else
  if (Ethernet.begin(mac) == 0) {
#endif 
    errorDisplay("Failed to configure Ethernet using DHCP");
  }

  Serial.print("My IP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("Netmask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("GW IP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("DNS IP: ");
  Serial.println(Ethernet.dnsServerIP());

  pubsubClient.setServer(MQTT_SERVER, MQTT_PORT);

  char clientid[30];
  IPAddress ip = Ethernet.localIP();
  Serial.print(ip[0]);
  Serial.print(".");
  Serial.print(ip[1]);
  Serial.print(".");
  Serial.print(ip[2]);
  Serial.print(".");
  Serial.println(ip[3]);
  sprintf(clientid,"STM32-%03d-%03d-%03d-%03d",(int)ip[0],(int)ip[1],(int)ip[2],(int)ip[3]);
  Serial.print("clientid=");
  Serial.println(clientid);
  Serial.print("Attempting MQTT connection...");
  // Attempt to connect
  if (!pubsubClient.connect(clientid,MQTT_TOPIC,0,0,MQTT_WILL_MSG)) {
    errorDisplay("connect Fail");
  }
  Serial.println("connected");

  char msg[50];
  snprintf (msg, 75, "hello world!! I'm STM32F103");
  Serial.print("Publish message: ");
  Serial.println(msg);
  if (!pubsubClient.publish(MQTT_TOPIC, msg)) {
    errorDisplay("publish fail");
  }

  Time2 = millis();
  Serial.print("Timee2 - Time1=");
  Serial.println(Time2-Time1);
  int procTime = (Time2-Time1)/1000;
  Serial.print("procTime=");
  Serial.println(procTime);

 
//  sleepAndWakeUp(STANDBY, &rt, alarmDelay); 
  rt.createAlarm(&noop, rt.getTime() + alarmDelay - procTime);
  goToSleep(STANDBY);
}

void loop() { }  // This is never run

しばらく動かすと、以下の様に9時8分16秒に始めた通信が、24時間後には9時9分24秒になっています。
24時間で1分ほど時計が進むことが分かりました。
STANDBY中のタイマーの精度はあまりよくないです。


次回はSPI-TFTライブラリを紹介します。

続く...