温度ロガーを作成しました

3Dプリンタ用保温ボックスを作って、ABSでの出力がうまく行くようになったのはいいのですが、ボックス内がかなり高温になっている(あたりまえですが・・・)のが気になります。

実際どのくらいなのか、手持ちの部品で温度を測ってみることにしました。

PCへの取り込みはシリアルで、秋月で以前購入したFTDI USBシリアル変換ケーブル(5V)を使いました。マイコンはPIC12F1822、温度センサーはLM35DZを使っています。最初はESP8266かESP32でAmbientに直接送信させようかとも思ったのですが、今回は測定気温が80℃を超えているかもしれませんし、場合によっては100℃を超えているかもしれません。なので、自己発熱が少なく(=消費電力を抑えることができる)壊れてもすぐ交換可能なPICとデータシート上150℃まで動作可能なLM35を使用することにしました。

ソフトはMPLAB Code Configuratorで以下のように設定しました。

  • CPUの動作周波数は初期設定の内部クロック500kHz
  • RA4/RA5にEUSARTを割付け、通信パラメータは初期設定の9600bpsのままで、Enable EUSART Interrupts で割り込みを許可。送受信バッファは各16バイト(受信は機能として使ってませんが)
  • TMR1はプリスケーラを1:2に設定変更して、Timer Periodを1秒に設定、Enable Timer Interrupt でタイマ割り込みを発生させます。
  • FVRで1.024VのADC用の基準電圧を発生させます。
  • ADCはPositive ReferenceをFVRにして、基準電圧を1.024Vにします。これで10bit分解能なので、入力電圧換算で10mVの分解能となります。LM35DZは100mV/1℃なので、0.1℃単位でAD変換できることになります。Result Alignmentはrightに変更して、読んだ値を1/10すれば温度直読になるようにします。
  • ピンマネージャでADCはAN2を設定、EUSARTはRX/TXをRA5/RA4に割り付けます。

これでMPLAB Code Configuratorにソースを吐かせて、main.cを作成しました。

#include "mcc_generated_files/mcc.h"
#include <stdlib.h>

bool exec=false;
unsigned long int time=0;
void TMR1int(void){
    exec = true;
    time++;
}

/*
                         Main application
 */
inline void EUSART_putchar(unsigned char ch){
    EUSART_Write(ch);
}

void EUSART_puts(char *str){
    uint16_t p=0;
    while(str[p]!=0) EUSART_putchar(str[p++]);    // Write data
}

void EUSART_putdec(unsigned long int num){
    char buf[10];
    unsigned long int t,u;
    int8_t i=0;

    while(1){
        buf[i]=(num % 10)+'0';
        num = num / 10;
        if(num == 0)break;
        i++;
    }
    while(i>=0){
        EUSART_putchar(buf[i]);
        i--;
    }
}

void EUSART_putLM35(unsigned long int num){
    char buf[10];
    unsigned long int t,u;
    int8_t i=0;

    while(1){
        buf[i]=(num % 10)+'0';
        num = num / 10;
        if(num == 0)break;
        i++;
    }
    while(i>=0){
        if(i==0) EUSART_putchar('.');
        EUSART_putchar(buf[i]);
        i--;
    }
}

void main(void)
{
    // initialize the device
    SYSTEM_Initialize();

//    EUSART_Initialize();
    TMR1_SetInterruptHandler(TMR1int);
    exec = false;
    time=0;
    
    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
        if(exec){
            // Read LM35DZ
            uint16_t convertedValue;
            ADC_Initialize();
            convertedValue = ADC_GetConversion(channel_AN2);
            // Send to HOST
            EUSART_putdec(time);
            EUSART_putchar(' ');
            EUSART_putLM35((unsigned long int)convertedValue);
            EUSART_putchar(0x0d);
            EUSART_putchar(0x0a);
            exec = false;
        }
        // Add your application code
    }
}

ビルドして、PICkit3で書き込みます。書き込む際はICクリップを使うことで、基板上の書き込みに関する信号の配線作業を省略しています。DIPパッケージで20ピンまでの8bitのPICは電源/GNDを含めて1ピン側の8ピンに書き込みに必要な信号が共通で出ています。ですので、この8ピンのICクリップは20ピンまでの8ビットのPICで共通に使用することができます。(例外はあるかもしれませんが・・・)

実行すると1秒単位に電源投入からの経過秒数と温度を出力します。

 :
 :
898 29.7
899 29.8
900 29.6
901 29.7
902 29.6
903 29.7
904 29.6
905 29.8
906 29.7

測定したら、そのテキストファイルをLibre Officeに読み込ませてグラフ化させるつもりです。

ABS出力に成功しました

3D Benchyの出力風景です。

ABSのデフォルト設定のノズル温度 230℃、ベッド温度 80℃、ファン 100%、積層ピッチ 0.15mmだと1~2mm出力したところでベッドから剥がれてしまったのですが、ノズル温度 240℃、ベッド温度 100℃、ファン 60%、積層ピッチ 0.15mmだと最後まで剥がれずに出力できました。

しかし、かなり保温ボックスの中の温度が上がっているので、いったい何度になっているのかチェックしておかないと危険かもしれません。

3Dプリンタ用保温ボックスを作ってみた

以前試してみて全然ダメだったABSでの出力ですが、あきらめきれないので再度試してみました。

今度は、MDFとプラ段で保温ボックスを作ってみました。

底板はMDFで、側面と天板はクリアのプラ段で、結合は白色の養生テープで行っています。

  • MDF 9mm厚 560mm×480mm ×1枚
  • MDF 9mm厚 568mm×488mm ×1枚
  • プラ段 4mm厚 クリア 560mm×480mm ×3枚(両側面、天面用)
  • プラ段 4mm厚 クリア 480mm×480mm ×2枚(背面、前面用)

MDFは2枚重ねて底に敷いています。寸法を4mm差をつけることにより、ここにプラ段を載せています。本当はプラ段は前面用、背面用は488mm×484mmにするべきだったかもしれません。

プラ段はクリアを使うことにより外につけたLEDライトの光が中に入るので作業性の悪化は避けることができました。また、以前設置したTTGO T-Cameraもそのまま使うことができました。

ちょっとファンのカバーが白のせいで白飛び気味なので、ファンカバーを黒で作り直した方がよさそうですが。

写真はスプールにつけるホルダー用の柱をPLAで出力しているところですが、200℃/60℃でも内部はすごい熱気です。これで電源ユニットやコントローラが耐えられるのか心配ですので、今度温度計を放り込んでみます。

肝心のABSでの出力ですが、ノズル230℃、ベッド80℃では途中でベッドから剥がれてしまいます。現在、ノズル240℃、ベッド100℃、ファン速度60%でトライ中です。

内部はさらに熱気が高まると思いますので、その対策が必要かもしれません。

ESP8266+Ambientを試す

ESP8266/ESP32自体でWebサーバを持つのではなく、外部にWebサーバを持つ環境を考えていたのですが、「Ambientでいいんじゃない?」という気がして、ESP8266とAmbientを試してみました。環境はいつもの如く、LinutMint19 + Arduino環境です。Ambientのユーザー登録は別途してあるものとします。

1.Ambientライブラリのインストール

Zipで持ってきてインストールする方法もあるのですが、Linuxの場合はgitで持ってくるほうが簡単です。持ってきてからArduino IDEを起動します。

~$ cd Arduino/libraries/
~/Arduino/libraries$ git clone https://github.com/AmbientDataInc/Ambient_ESP8266_lib
Cloning into 'Ambient_ESP8266_lib'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 101 (delta 0), reused 1 (delta 0), pack-reused 98
Receiving objects: 100% (101/101), 44.32 KiB | 242.00 KiB/s, done.
Resolving deltas: 100% (37/37), done.
~/Arduino/libraries$ 

2.ソースコードの作成

ソースコードは smartconfig が入っているものをベースにしました。

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "Ambient.h"

/*****************************************************************************/
/* NTP Client                                                                */
#include <time.h>
#define JST     3600*9

/*****************************************************************************/
/* Ambient                                                                   */
unsigned int channelId =  xxxxx;
const char* writeKey = "xxxxxxxxxxxxxxxx";
WiFiClient client;
Ambient ambient;

/*****************************************************************************/
/* Timer interrupt                                                           */
#define MS2CLK(ms)    (ms * 80000L)
uint32_t nxTim;
uint32_t numTim = 0;
bool exec = false;

// 割り込みハンドラ
void timer0_ISR (void) {
  nxTim += MS2CLK(100); // 100msec 
  timer0_write( nxTim );
  
  // 割り込み処理
  numTim++;
  if( numTim % 10 == 0 ){
    exec = true;  // 1秒に1回処理するため
  }
}

/*************************** Sketch Code ************************************/
 
void setup() {
  uint8_t cnt = 0;  

  // set for STA mode
  WiFi.mode(WIFI_STA);
  
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.flush();
  Serial.println("\r\n");
    
  // sensor input
  pinMode(14,INPUT);
  
  // LED
  pinMode(12,OUTPUT);
  pinMode(13,OUTPUT);
  
  //configure pin0 
  pinMode(0, INPUT_PULLUP);

  // deplay for 2 sec for smartConfig
  Serial.println("2 sec before clear SmartConfig");
  delay(2000);
  
  // read pullup
  bool isSmartConfig = digitalRead(0);
  if (isSmartConfig == false) {
    // bink for clear config
    blinkClearConfig();
    Serial.println("clear config");
    // reset default config
    WiFi.disconnect();

  }

  // if wifi cannot connect start smartconfig
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if(cnt++ >= 15){
       WiFi.beginSmartConfig();
       while(1){
           delay(500);
           if(WiFi.smartConfigDone()){
             Serial.println("SmartConfig Success");
             blinkSmartConfig();
             break;
           }
       }
    }
  }

  Serial.println("");

  WiFi.printDiag(Serial);

  // Print the IP address
  Serial.println(WiFi.localIP());

  // Ambientへ接続
  ambient.begin(channelId, writeKey, &client);

  // タイマ割り込みの設定
  noInterrupts();
  timer0_isr_init();
  timer0_attachInterrupt(timer0_ISR);
  nxTim = ESP.getCycleCount() + MS2CLK(100); // 100msec
  timer0_write( nxTim );
  exec = false;
  interrupts();
  
  // time set with NTP
  configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

}


void blinkSmartConfig() {
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(50);              // wait for a second 
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
    delay(50);
}

void blinkClearConfig() {
  int i=0;
  while(i<=3) {
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(100);              // wait for a second 
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
    delay(100);
    i++;
  }
}

int detcnt=0;
uint16_t SW1=0;

void loop() {
  time_t t;
  struct tm *tm;
  char msg[64]="";
    
  bool isSW1 = digitalRead(0);

  // チャタリング除去
  if(isSW1 == false){
    SW1 = (SW1 << 1) | 0x0001;
  } else {
    SW1 = (SW1 << 1);
  }

  // SW1を押した回数を数える
  if(SW1 == 0x0001){
    detcnt++;
    Serial.print(".");
  }
    
  if(exec){       // 1秒間に1回処理
    digitalWrite(12, 1-digitalRead(12));   // Blink the LED
    /*******/
    /* NTP */
    t = time(NULL);
    tm = localtime(&t);
    sprintf(msg,"%04d/%02d/%02d %02d:%02d:%02d %ld",
        tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
        tm->tm_hour, tm->tm_min, tm->tm_sec,
        (long)t);    
    int s = tm->tm_sec;
    //
    if( s==0 ){   // 1分間に一回、毎分0秒にAmbientにデータ送信
      ambient.set(1,detcnt);
      ambient.send();
      Serial.println("");
      Serial.print(msg);
      Serial.print(" - ");
      Serial.print(detcnt);
      Serial.print(" : ");
      detcnt=0;
    }
    exec = false;
  }

  delay(5);
}

タクトスイッチを押した回数を1分間に1回送信するものですが、非常に簡単にグラフ表示できました。簡単でいいかもです。

Python3でMQTTクライアント

ESP8266でMQTTを喋れるようになったので、受ける側を考えます。自分用のサービスまでしか考えていないので、記述が楽なPythonで作ります。調べると paho というMQTTクライアントがあるようなので、こちらを参考にそれを試してみます。

Python3でvenvを使って仮想環境を作ります。

~$ mkdir python
~$ cd python
~/python$ sudo apt install python3-venv
~/python$ python3 -m venv pytest
~/python$ source ./pytest/bin/activate
(pytest) ~/python$ cd pytest/
(pytest) ~/python/pytest$ 
(pytest) ~/python/pytest$ python -V
Python 3.6.8

pipをバージョンアップします。

(pytest) ~/python/pytest$ pip install --upgrade pip
Collecting pip
  Downloading https://files.pythonhosted.org/packages/5c/e0/be401c003291b56efc55aeba6a80ab790d3d4cece2778288d65323009420/pip-19.1.1-py2.py3-none-any.whl (1.4MB)
    100% |████████████████████████████████| 1.4MB 672kB/s 
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Successfully installed pip-19.1.1
(pytest) ~/python/pytest$ 

pahoをインストールします。

(pytest) ~/python/pytest$ pip install paho-mqtt
Collecting paho-mqtt
  Downloading https://files.pythonhosted.org/packages/25/63/db25e62979c2a716a74950c9ed658dce431b5cb01fde29eb6cba9489a904/paho-mqtt-1.4.0.tar.gz (88kB)
     |████████████████████████████████| 92kB 12.6MB/s 
Installing collected packages: paho-mqtt
  Running setup.py install for paho-mqtt ... done
Successfully installed paho-mqtt-1.4.0
(pytest) ~/python/pytest$ 

Subscriberをテストしてみます。ソースコードは以下のとおりです。

# -*- coding:utf-8 -*-
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, respons_code):
    topic = '/feeds/sensor/#'
    print('watch %s' % topic)
    client.subscribe(topic)

def on_message(client, userdata, msg):
    print(msg.topic + ' ' + str(msg.payload))

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect('XXX.XXX.XXX.XXX', 1883, keepalive=60)
client.loop_forever()

結果は、

(pytest) ~/python/pytest$ python scbscriber.py 
watch /feeds/sensor/#
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 1970/01/01 08:00:02 28802 0'
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 2019/07/16 01:39:52 1563241192 1'
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 2019/07/16 01:39:53 1563241193 2'
/feeds/sensor/0001 b'0001 18FE34XXXXXX 1970/01/01 08:00:02 28802 0'
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 2019/07/16 01:39:54 1563241194 3'
/feeds/sensor/0001 b'0001 18FE34XXXXXX 2019/07/16 01:39:55 1563241195 1'
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 2019/07/16 01:39:55 1563241195 4'
/feeds/sensor/0001 b'0001 18FE34XXXXXX 2019/07/16 01:39:56 1563241196 2'
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 2019/07/16 01:39:56 1563241196 5'
/feeds/sensor/0001 b'0001 18FE34XXXXXX 2019/07/16 01:39:57 1563241197 3'
/feeds/sensor/0001 b'0001 4C11AEXXXXXX 2019/07/16 01:39:57 1563241197 6'
/feeds/sensor/0001 b'0001 18FE34XXXXXX 2019/07/16 01:39:58 1563241198 4'

ということで、無事に動作しました。

smartconfig+MQTT

ESP8266を便利な(笑)MQTTクライアントにするため、smartconfigと組み合わせてみます。センサーの情報をMQTTで一箇所に集めて、表示できるようにするのが目標です。

1.smartconfigの動作確認

まずベースは以前の記事のsmartconfigです。Androidのアプリ側はESP8266 smartconfigで動作することは確認しました。

2.ライブラリのインストール

ArduinoIDEで「ツール→ライブラリを管理」でライブラリマネージャーから「Adafruit MQTT Library」をインストールします。

3.ソースの結合

ソースを結合して以下の通りとしました。難しいところはありません。ついでに、NTPでの時刻合わせ、タイマ割り込みの処理も記述も追加しておきました。
トピックは /feed/sensor/番号 にすることにしました。
できればこの際、SSL化もしたかったのですが、mosquittoのブローカ側のSSL化すらうまく行っていないので今回は見送りです。どちらにせよ、プライベートIPアドレスの中での運用なのであまり気にする必要はないのですが。
※あちこちからの切り貼りです。

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/*****************************************************************************/
/* MQTT Client                                                               */

/************************* Adafruit.io Setup *********************************/
#define AIO_SERVER      "10.89.105.1"
#define AIO_SERVERPORT  1883                   // use 8883 for SSL
#define AIO_USERNAME    "username"  // 適当に変更(未使用)
#define AIO_KEY         "password"  // 適当に変更(未使用)
#define SERIALNO        "0001"

/************ Global State (you don't need to change this!) ******************/
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiFlientSecure for SSL
//WiFiClientSecure client;
 
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

/****************************** Feeds ***************************************/
Adafruit_MQTT_Publish motion = Adafruit_MQTT_Publish(&mqtt, "/feeds/sensor/" SERIALNO);
Adafruit_MQTT_Subscribe command = Adafruit_MQTT_Subscribe(&mqtt, "/feeds/command/" SERIALNO);
char macstr[13];    // Station mode MAC address 

/*****************************************************************************/
/* NTP Client                                                                */
#include <time.h>
#define JST     3600*9

/*****************************************************************************/
/* Timer interrupt                                                           */
#define MS2CLK(ms)    (ms * 80000L)
uint32_t nxTim;

// 割り込みハンドラ
void timer0_ISR (void) {
  nxTim += MS2CLK(100); // 100msec 
  timer0_write( nxTim );
  
  // 割り込み処理
  digitalWrite(13, 1-digitalRead(13));   // Blink the LED
}

/*************************** Sketch Code ************************************/
 
void setup() {
  uint8_t cnt = 0;  

  // set for STA mode
  WiFi.mode(WIFI_STA);
  
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.flush();
  Serial.println("\r\n");
    
  // led status at pin4
  pinMode(12,OUTPUT);
  digitalWrite(12, HIGH);   // turn the LED on (HIGH is the voltage level)
  
  // led status at pin5
  pinMode(13,OUTPUT);
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  
  //configure pin0 
  pinMode(0, INPUT_PULLUP);

  // deplay for 2 sec for smartConfig
  Serial.println("2 sec before clear SmartConfig");
  delay(2000);
  
  // read pullup
  bool isSmartConfig = digitalRead(0);
  if (isSmartConfig == false) {
    // bink for clear config
    blinkClearConfig();
    Serial.println("clear config");
    // reset default config
    WiFi.disconnect();

  }

  // if wifi cannot connect start smartconfig
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if(cnt++ >= 15){
       WiFi.beginSmartConfig();
       while(1){
           delay(500);
           if(WiFi.smartConfigDone()){
             Serial.println("SmartConfig Success");
             blinkSmartConfig();
             break;
           }
       }
    }
  }

  Serial.println("");

  WiFi.printDiag(Serial);

  // Print the IP address
  Serial.println(WiFi.localIP());

  // タイマ割り込みの設定
  noInterrupts();
  timer0_isr_init();
  timer0_attachInterrupt(timer0_ISR);
  nxTim = ESP.getCycleCount() + MS2CLK(100); // 100msec
  timer0_write( nxTim );
  interrupts();
  
  // time set with NTP
  configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

  // Setup MQTT subscription for command feed.
  mqtt.subscribe(&command);

  //  
  uint8_t mac0[6];
  WiFi.macAddress(mac0);
  sprintf(macstr,"%02X%02X%02X%02X%02X%02X", mac0[0], mac0[1], mac0[2], mac0[3], mac0[4], mac0[5]);
}


void blinkSmartConfig() {
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(50);              // wait for a second 
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
    delay(50);
}

void blinkClearConfig() {
  int i=0;
  while(i<=3) { digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level) delay(100); // wait for a second digitalWrite(13, LOW); // turn the LED off by making the voltage LOW delay(100); i++; } } uint32_t x=0; void loop() { time_t t; struct tm *tm; char msg[64]=""; bool isSW1 = digitalRead(0); if(isSW1 == false){ for(int i=5;i>0;i--){
      delay(1000);
      Serial.println(i);    
    }
    Serial.println("Power off");    
    delay(500);
    pinMode(15,OUTPUT);
    digitalWrite(15, HIGH);   // Power-off the board (HIGH is the voltage level)
    delay(5000);
  }
  digitalWrite(12, 1-digitalRead(12));   // Blink the LED

  /*******/
  /* NTP */
  t = time(NULL);
  tm = localtime(&t);
  sprintf(msg,"%s %s %04d/%02d/%02d %02d:%02d:%02d %ld %d",
        SERIALNO,macstr,
        tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
        tm->tm_hour, tm->tm_min, tm->tm_sec,
        (long)t,x++);

  /********/
  /* MQTT */
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();
 
  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here
 
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(1000))) {
    if (subscription == &command) {
      Serial.print(F("Got: "));
      Serial.println((char *)command.lastread);
    }
  }
 
  // Now we can publish stuff!
  Serial.print(F("\nSending message : "));
  Serial.print(msg);
  Serial.print("...");
  if (! motion.publish(msg)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }
 
  // ping the server to keep the mqtt connection alive
  // NOT required if you are publishing once every KEEPALIVE seconds
  /*
  if(! mqtt.ping()) {
    mqtt.disconnect();
  }
  */
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;
 
  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }
 
  Serial.print("Connecting to MQTT... ");
 
  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
       retries--;
       if (retries == 0) {
         // basically die and wait for WDT to reset me
         while (1);
       }
  }
  Serial.println("MQTT Connected!");
}

ついでに、基板の回路図も再掲載です。

実装部品

ミニマム実装:
 半田面
    GS5 2-3
    GS6 2-3
    C6  0.1u
    C7  22u
    R1  470
    R2  4.7k
    R3  4.7k
    R4  100
    R7  20k
    R8  4.7k 
    R11 0Ω
    R19 22k
    R20 33k
    R21 470
    R22 470
    R23 470
    R24 470
 部品面
    U3  AZ1117-ADJ
    D1  LED
    D2  LED
    D3  LED
    SW1 タクトSW
    SW2 タクトSW
    C5  33u

STM32F103C8でLチカ

STM32F103C8とArduino IDE環境で、とりあえずLチカしてみました。

この状態で消費電流は・・・・・・・・約40mA。動作周波数を72MHzから48MHzまで落としても約32mA。うーん、電池動作には使えん。Arduino環境じゃなければ減らせるんだろうか・・。

LEDについている抵抗は2.2kオームなのでLEDに流れる電流は大したことはありません。

STM32F103C8にArduinoのスケッチを書き込む

結局、ブートローダとか関係なくArduinoのスケッチを書き込む方法が一番簡単そうです。この方法ならブートローダの書き込みも要らないはずです。

1.環境準備

Arduino IDEを起動したら、ボードマネージャで「Arduino SAM Boards (32-bits ARM Cortex-M3) bu Arduino」をインストールします。その後、以下の手順で ~/Arduino/hartdware の下にライブラリを追加します。

~$ sudo apt install stm32flash 
~$ cd Arduino/hardware 
~/Arduino/hardware$ git clone https://github.com/rogerclarkmelbourne/Arduino_STM32

追加したら、 ~/Arduino/hardware/Arduino_STM32/tools/linux/serial_upload を修正します。

#!/bin/bash
#$(dirname $0)/stm32flash/stm32flash -g 0x8000000 -b 115200 -w "$4" /dev/"$1" 
stm32flash -g 0x8000000 -b 115200 -w "$4" /dev/"$1" 

2行目がもとの行で、3行目が修正した行です。要は、aptでインストールしたstm32flashを使うように修正します。

完了したら、スケッチ例で Blink を開きます。Blinkを開いたら、使用するポートをPC13に書き換えます。また、ボードマネージャではボードに合わせてGeneric STM32F103C Seriesを選択します。

  • ボード : STM32F103C8またはSTM32F103CBを選択
  • Upload method : Serial
  • シリアルポート : /dev/ttyUSB0

に設定します。

2.ボード側の準備

ボード側はUSBシリアルケーブルと接続します。

  • USBシリアルのVcc ⇔ STM32F103ボードの+5V
  • USBシリアルのGND ⇔ STM32F103ボードのGND
  • USBシリアルのTX ⇔ STM32F103ボードのA10
  • USBシリアルのRX ⇔ STM32F103ボードのA9

接続したら、BOOT0のジャンパを1側にセットして、PCに接続します。電源もUSBシリアルから供給します。

3.ビルドと書き込み

ボード上のリセットスイッチを押してブートローダを待機させます。その状態で Arduino IDE の書き込みボタンをクリックします。

しばらくすると書き込みに成功してプログラムが動作を始めるはず・・・・です。

2回目以降の書き込みはリセットボタンを押してブートローダを待機させて、Arduino側で書き込み操作をすれば行けるようです。

STM32F103C8ボードのSTM32duino化

いろいろ調べたところ、どうやらブートローダーは書き換える必要があるようです。以下に手順を記録します。

1.フラッシュ書き込みツールのインストールと準備

フラッシュへの書き込みツールは以下の手順でインストールします。

$ sudo apt install stm32flash

ツールの書き込みができたら、USBシリアルケーブルと接続します。

  • USBシリアルのVcc ⇔ STM32F103ボードの+5V
  • USBシリアルのGND ⇔ STM32F103ボードのGND
  • USBシリアルのTX ⇔ STM32F103ボードのA10
  • USBシリアルのRX ⇔ STM32F103ボードのA9

接続したら、BOOT0のジャンパを1側にセットして、PCに接続します。

2.ブートローダのダウンロードと書き込み

以下の手順でブートローダをダウンロードして書き込みます。

~$ git clone https://github.com/rogerclarkmelbourne/STM32duino-bootloader
Cloning into 'STM32duino-bootloader'...
remote: Enumerating objects: 796, done.
remote: Total 796 (delta 0), reused 0 (delta 0), pack-reused 796
Receiving objects: 100% (796/796), 905.37 KiB | 1.18 MiB/s, done.
Resolving deltas: 100% (548/548), done.
~$ cd STM32duino-bootloader/
~/STM32duino-bootloader$ cd binaries/
~/STM32duino-bootloader/binaries$ sudo stm32flash -f -v -w generic_boot20_pc13.bin /dev/ttyUSB0
stm32flash 0.5

http://stm32flash.sourceforge.net/

Using Parser : Raw BINARY
Interface serial_posix: 57600 8E1
Version      : 0x22
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0410 (STM32F10xxx Medium-density)
- RAM        : 20KiB  (512b reserved by bootloader)
- Flash      : 128KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB
Write to memory
Erasing memory
Wrote and verified address 0x08005294 (100.00%) Done.

~/STM32duino-bootloader/binaries$ 

書き込んだらPCからUSBシリアルを外して、ジャンパを戻します。

【参考】
https://ht-deko.com/arduino/stm32f103c8t6.html

ST-LINK V2 用Toolをインストール

stm32duinoでビルドが通らない件は、STM32 Cores by STMicroelectronicsのバージョンを1.6.1から1.6に落としたらビルドはできるようになりました。その後、アップデートで1.6.1にしたら今度はビルドが通ります。

・・・が、ST-LINK V2ではまだ書込みができません。(シリアルなら書けそうな気がしますが・・)

そこで、githubにあるOpenSource版のST-LINK V2用のツールをインストールしてみます。

~/$ git clone https://github.com/texane/stlink.git
~/$ cd stlink
~/stlink$ sudo apt install cmake libusb-1.0.0-dev
~/stlink$ make
~/stlink$ cd build/Release/
~/stlink/build/Release$ sudo make install
~/stlink/build/Release$ cd ../..
~/stlink$ cd etc/udev/rules.d
~/stlink/etc/udev/rules.d$ sudo cp 49-stlinkv2.rules /etc/udev/rules
~/stlink/etc/udev/rules.d$ sudo udevadm control --reload-rules
~/stlink/etc/udev/rules.d$ sudo udevadm trigge
~/stlink/etc/udev/rules.d$ ls /dev/stlink*
/dev/stlinkv2_2

でインストールすることはできました。

・・・が、arduinoは認識はしてくれないようです。

調べていくうちに、STM32F103C8をArduinoで扱う方法は少なくとも2種類あるようです。

【その1】stm32duinoで、追加のボードマネージャのパスを設定した後、ボードマネージャで「STM32 Cores by STMicroelectronics」を追加する方法。
前回の記事の方法)

【その2】ボードマネージャで「Arduino SAM Boards (32-bits ARM Cortex-M3) bu Arduino」をインストールした後、~/Arduino/hardware の下にライブラリを追加する方法。(Arduino_STM32)
https://github.com/rogerclarkmelbourne/Arduino_STM32

いずれにしても、まだうまく行きません・・・。