PlatformIOを試してみる on LinuxMint

こちらの記事でESP32(など)の開発にとても魅力的な文言が並んでいたので、PlatformIOを試してみました。ちょうど、Visual Studio Codeも試してみたいと思っていたところでもあります。

環境はもちろんいつものLinuxMint19、64bit版です。こちらの手順に沿ってすすめます。

  1. 案内に沿ってVS Codeの.debパッケージをダウンロードしてパッケージシンストーラでインストールします。
  2. 完了したら、メニューの中のプログラミングの中に Visual Studio CODE をクリックします。
  3. 起動したら、左のメニューの中のExtensionsをクリックします。
  4. 上の方の検索ボックスの中に「platformio」と入れると、候補としてPlatfomIO IDEが出てくるので、そのInstallボタンをクリックします。
  5. 完了すると、VSCodeの再起動を促されるので、「Reload Now」をクリックして再起動します。

あっという間にインストールが完了しました。10分もかかってないと思います。

上記画面の右上の「Import Arduino Project」をクリックしてESP32のプロジェクトをインポートしてみます。

ボードはとりあえず、Espressif ESP32 Dev Module を選んでみます。Use libraries installed by Arduino IDEはチェックせず、Arduinoで保存したESP32のNTPのサンプル(SSID/パスワードは記入済)をインポートしてみます。

しばらくすると、インポートが完了しますので、上の方のOpen Projectでインポートしたプロジェクトを開いてみます。

左の方の src にインポートしたソースが表示されるので、これをクリックすると、ソースコードが開きました。’Arduino’ Extension をインストールするよう表示が出るので、Install をクリックしてインストールします。

さらに、.ino ファイルでは何やら支障がある旨表示されて、.cpp に書き換えることが推奨されるので、クリックして表示されるWebページに従って書き換えます。内容としては、

  • 「#include <Arduino.h>」の追加
  • プロトタイプ宣言の追加
  • 拡張子を .cpp に変更して保存し、元の .ino ファイルを削除

というものです。

下の方の水色のバー(ステータスバーのようなもの)に色々メニューが入っているようです。シェルやシリアルモニタもあります。シリアルモニタを開いたら、「CTRL-T b」で速度を変更できますので、115200に変更しました。

左の方のメニューのPlatform IOアイコン(マウスオンで少し静止するとアイコン名が表示される)をクリックすると、PlatformIOのタスクが表示されるので、Buildをクリックするとコンパイルが実行されます。さらに、Uploadをクリックすると、ファームが書き込まれて実行されました。

書き込み後、シリアルモニタに戻るのですが、設定が元に戻ってしまうようです。

プロジェクトフォルダの中の platformio.ini をこちらに従って書き換えると、初期値を変更できそうです。具体的には、

monitor_speed = 115200

を最後に書き足すだけで大丈夫でした。

確かに簡単に導入できて、便利そうです。

12回は存在しない気がします

「任意の自然数の各桁を、一桁になるまで掛け算する回数の最大回数とその数を示せ」

という問題で、昨日からCore2 Q6600を動かし続けて電力を消費しまくっています。13回のパターンが存在しないことは数学的に証明されているらしく、一方で277777788888899が11回というのは容易に見つかりましたが、そこから先、12回が見つかりません。

で、高速化の手法を調べているうちに、桁数が多いほど回数が少なくなっていることに気づきました。で、こんなプログラムを作って動かしてみました。(concurrent.futuresを使っていますが、意味はありませんw)

import concurrent.futures

def check(m,n):
	s = list(str(m**n))
	return('0' in s)

def show(k):
	r = [i for i in range(1,10001) if not check(k,i)]
	print(k,':',r)

if __name__ == "__main__":
	kl = [7,8,9]
	with concurrent.futures.ProcessPoolExecutor(max_workers=4) as excuter:
		result_list = list(excuter.map(show,kl))

結果は、

7 : [1, 2, 3, 6, 7, 10, 11, 19, 35]
8 : [1, 2, 3, 5, 6, 8, 9, 11, 12, 13, 17, 24, 27]
9 : [1, 2, 3, 4, 6, 7, 12, 13, 14, 17, 34]

こうなりましたが、こんなめんどくさいことしなくても、

>>> [i for i in range(1,10001) if not '0' in list(str(7**i))]
[1, 2, 3, 6, 7, 10, 11, 19, 35]
>>> [i for i in range(1,10001) if not '0' in list(str(8**i))]
[1, 2, 3, 5, 6, 8, 9, 11, 12, 13, 17, 24, 27]
>>> [i for i in range(1,10001) if not '0' in list(str(9**i))]
[1, 2, 3, 4, 6, 7, 12, 13, 14, 17, 34]
>>> 

で十分ですね。

これは、7のみ36個以上(1万個以下)」、8のみ27個以上(1万個以下)、9のみ34個以上(1万個以下)の数値は各桁を乗算すると、出てきた数値の中に0の桁が含まれることを示します。

・・・ということはある桁数以上の検証は無意味かも、と思ったのですが、さらに他の数字をいくつか掛けたときに0の桁が残るかまではわからないのか・・・。

いずれにせよ、桁数がふえると、各桁の乗算結果に0だったり、5と2だったりという桁が出てくる確率が高くなりそうなので、桁数が増えるほど実は回数が少なくなりそうです。

Python処理の高速化

昨日の頭の体操は現在もCore2 Q6600で黙々と処理をしていて、10^617まできましたが、相変わらず12回のパターンはみつかりません。(たぶん、ないのだと思う)

それはさておき concurrent.futures を使用して、複数CPUコアで処理を高速化してみました。他にもちまちまと高速化の工夫を入れました。結果、Core i5-3570K(定格動作)で180桁まで5分かかったのが1分17秒と4倍近く早くなりました。(その過程で、4回のパターンをうまく抽出的できなくなったので誤魔化してあります^^;)

import datetime
import concurrent.futures

keta2 = [2,3,4,5,6,34,35]

def check(n):	# 引数の数値が何回かチェックする
	k = 1	# ループした回数を保持
	t = n	# 検査する対象をセット
	while True:
		u = 1
		for i in list(map(int,list(str(t)))) :
			u = u * i
		if u < 10 :
			break
		t = u
		k += 1
	return(k)

def kensho(keta):	# その桁数の数値の検証を行う
	maxk = 1		# 桁数を保持
	maxn = None		# 数値を保持
	now = datetime.datetime.now()
	print(&quot;keta: &quot;,keta,&quot; / time: &quot;,now-start)
	keta789 = keta - 2	# 
	for s1s2 in ['7'*i+'8'*j+'9'*(keta789-j-i) for i in range(0,keta789+1) for j in range(0,keta789-i+1)] :
		for k in keta2:	# 23456で構成される数値
			s3=str(k)
			n = int(s3+s1s2)
			r = check(n)
			if r > maxk :
				maxk = r
				maxn = n
	# print(keta,maxk,maxn)
	return(keta,maxk,maxn)

if __name__ == &quot;__main__&quot;:
	maxk = 1		# 桁数を保持
	maxn = None		# 数値を保持	
	start = datetime.datetime.now()
	print('start: ',start)
	n = 2
	maxlist = {}
	while True:
		kl = range(n,n+50)
		n += 50
		with concurrent.futures.ProcessPoolExecutor(max_workers=4) as excuter:
			result_list = list(excuter.map(kensho,kl))
		# print(result_list)
		for k,mk,mn in result_list:
			if mk not in maxlist:
				maxlist[mk] = mn
			elif maxlist[mk] > mn :
				maxlist[mk] = mn
		print(&quot;result&quot;)
		for i in sorted(maxlist.keys()):
			if i>4 : print(i,maxlist[i])

頭の体操

Twitterで見かけた

を考えてみました。

最初はPython3で力ずくで試してみて、9回の34888999まではすぐに見つかりました。しかし、ここからは時間がかかります。傾向をみると、8と9がたくさん含まれる数値ということで、そこにターゲットを絞ってみたところ、277777788888899が11回というのも比較的短時間でわかりました。13回のものは存在しないことが証明されているらしいので、残りは12回のものということになります。

これを効率よく探す方法を考えてみました。

数値の並び順が結果には影響しないのはすぐにわかるので、個々の数値がどんな意味があるか考えてみました。もちろん、探すのは最短桁数のものです。

  • 0は乗算するといきなり0になってしまうので、候補になりえません。
  • 1は桁数を増やす効果しかないので、最小桁数のものを考える上では探索不要です。
  • 4は2x2、6は2x3、8は2x2x2、9は3x3なので、桁数を短縮する効果があります。
  • 7は素数ですから、桁数を短縮する効果はありません。
  • 5は2が出てこないケースにおいてのみ意味があります。2があると、結果に0が含まれてしまい、2回で終わってしまいます。
  • 2と3は素数ですから、これ以上桁数を短縮する効果はありません。

これらのことから、候補となる数値は7がx個、8がy個、9がz個とそれに付加して2が0〜2個、3が0〜1個、5が0〜1個登場するパターンと思われます。

5は2が0個のときのみ意味があるので、2356で構成される桁は最大3桁、4が2x2であることを考えると、23456で構成される最大2桁の範囲で探索すればよいということがわかります。

「23456で構成される最大2桁」+「7がx桁、8がy桁、9がz桁の組み合わせ」で、2桁の部分をさらに候補を絞ります。2が0〜2桁、3が0〜1桁、5が0〜1桁登場するパターンで、2が2桁=4が1桁であることを考えると、2が1桁=2、2が2桁=22=4と同じ、3が1桁=3、5が1桁=5、2が1桁+3が1桁=6と同じ、2が2桁+3が1桁=34と同じ、3が1桁+5が1桁=35と同じ、ということから、探索するのは[2,4,3,5,6,34,35]だけで良いと思われます。

そこで、[2,4,3,5,6,34,35]+(7,8,9の組み合わせからなる多数桁)をチェックするPython3プログラムを作成しました。(見直すと、もっときれいに書けそうですが・・・)

import datetime

keta2 = [2,3,4,5,6,34,35]

maxk = 1		# 桁数を保持
maxn = None		# 数値を保持

def once(n):	# 1回の計算を行う
	t = 1
	for i in list(str(n)) :
		t = t * int(i)
	return(t)

def check(n):	# 引数の数値が何回かチェックする
	global maxk,maxn
	k = 1	# ループした回数を保持
	t = n	# 検査する対象をセット
	while True:
		r = once(t)
		if r < 10 :
			break
		t = r
		k += 1
	if k == maxk :
		print('同等 ',n,k)
	if k > maxk :
		maxk = k
		maxn = n
		print('更新 ',n,k)

def check_print(n):	# 引数の数値が何回か出力しながらチェックする
	global maxk,maxn
	k = 1	# ループした回数を保持
	t = n	# 検査する対象をセット
	while True:
		r = once(t)
		if r < 10 :
			break
		t = r
		k += 1
		print(t,' ',end='')

def kensho(keta):	# その桁数の数値の検証を行う
	keta789 = keta - 2	# 
	for i in range(0,keta789+1):
		s1 = '7'*i
		keta89 = keta789 - i;
		for j in range(0,keta89+1):
			s2 = '8'*j + '9'*(keta89-j)
			for k in keta2:	# 23456で構成される数値
				s3=str(k)
				n = int(s3+s1+s2)
				check(n)

if __name__ == &quot;__main__&quot;:
	start = datetime.datetime.now()
	print('start: ',start)
#	for keta in range(2,100+1) :
	keta = 2
	while True:
		now = datetime.datetime.now()
		print(&quot;keta: &quot;,keta,&quot; / time: &quot;,now-start,&quot; / record: &quot;,maxn,maxk)
		kensho(keta)
		keta += 1
	print(&quot;result &quot;,maxn,maxk)
	check_print(maxn)

これを Core2 Q6600(古い・・)で実行すると、

keta:  2  / time:  0:00:00.000048  / record:  None 1
同等  2 1
同等  3 1
同等  4 1
同等  5 1
同等  6 1
更新  34 2
同等  35 2
keta:  3  / time:  0:00:00.000200  / record:  34 2
同等  29 2
更新  39 3
同等  49 3
同等  59 3
同等  69 3
同等  359 3
同等  68 3
更新  348 4
keta:  4  / time:  0:00:00.000449  / record:  348 4
同等  699 4
同等  3499 4
同等  489 4
同等  3489 4
更新  688 5
同等  3488 5
同等  679 5
keta:  5  / time:  0:00:00.000870  / record:  688 5
同等  6999 5
同等  34999 5
同等  34888 5
更新  6788 6
keta:  6  / time:  0:00:00.001480  / record:  6788 6
同等  49999 6
更新  68889 7
同等  347799 7
keta:  7  / time:  0:00:00.002459  / record:  68889 7
同等  377889 7
更新  3477889 8
keta:  8  / time:  0:00:00.003920  / record:  3477889 8
同等  6999999 8
同等  6888999 8
更新  34888999 9
keta:  9  / time:  0:00:00.005924  / record:  34888999 9
keta:  10  / time:  0:00:00.008575  / record:  34888999 9
keta:  11  / time:  0:00:00.011963  / record:  34888999 9
更新  3778888999 10
keta:  12  / time:  0:00:00.016466  / record:  3778888999 10
keta:  13  / time:  0:00:00.022207  / record:  3778888999 10
keta:  14  / time:  0:00:00.029282  / record:  3778888999 10
同等  3888888888889 10
keta:  15  / time:  0:00:00.037926  / record:  3778888999 10
keta:  16  / time:  0:00:00.048363  / record:  3778888999 10
更新  277777788888899 11
keta:  17  / time:  0:00:00.060824  / record:  277777788888899 11
keta:  18  / time:  0:00:00.075424  / record:  277777788888899 11
同等  27777789999999999 11
keta:  19  / time:  0:00:00.092772  / record:  277777788888899 11
keta:  20  / time:  0:00:00.112918  / record:  277777788888899 11

ということで、60msほどで11回のものまで見つかりました。
これをひたすら回して、現在11時間で470桁くらいまで到達しているのですが、今の所12回のパターンは見つかっていません。横軸に桁数、縦軸にその桁数までの所要時間(秒)を取ると、

という感じで、概ね所要時間は 7.7×桁数^4×10^(-7) (秒)で近似できそうです。となると、1000桁までチェックすると9日くらいかかりそうです。

PIC12F1822でLチカ

PIC12F1822でLチカしたときの消費電流を測ってみました。どのくらい消費電流が抑えられるか(=どのくらい電池動作ができるか)を確認したいので。

レジスタの設定はすべてCode Cofiguratorにお任せ。
CPUのクロックは初期設定の500kHzです。
特にあれこれ設定はいじらず、TMR1でぼんやり明滅するタイミング(100ms周期)を生成、割り込みを発生させて、PWM2のデューティーを変動させます。TMR2は100us周期のクロックを生成して、ECCPでPWM波形を生成します。そして、PWM端子の出力に1kΩを通して高輝度LEDを接続して点灯させます。なお、不要な端子は入力端子として、貫通電流による消費電力増加を防ぐため内部プルアップを有効化してあります。

作成したソースコードは main.c の以下の部分だけです。見ての通り、初期化以外はすべて100msに1回の割り込みの際に処理しています。

#include "mcc_generated_files/mcc.h"
uint8_t c=0;
uint16_t pwmt[]={
       0, 2, 4, 6, 8,10,12,14,16,18,
      20,20,20,20,20,20,20,20,20,20,
      18,16,14,12,10, 8, 6, 4, 2, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0
};

void TMR1int(void){
    c++;
    if(c == sizeof(pwmt)/sizeof(uint16_t)) c=0;
    EPWM_LoadDutyValue(pwmt[c]);
}

void main(void)
{
    SYSTEM_Initialize();
    TMR1_SetInterruptHandler(TMR1int);
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
        // Add your application code
    }
}

単4電池2本で動かしてみると、消費電流は平均で0.2mA〜0.6mAなので、こちらの資料を参考にすると2,900時間くらい持つ計算です。つまり、約4ヶ月くらいでしょうか。ただ、電圧が落ちてくるとLEDが点灯しなくなる可能性があるので、実際にはそんなに持たないかもしれませんが。

<追伸>
ソースコードの中の EPWM_LoadDutyValue(pwmt) という部分は、実際には EPWM_LoadDutyValue(pwmt[c])です。SyntaxHighlighter を使っているのですが、半角の[c]がタグとして扱われるのか、消えてしまいます。orz

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

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 &quot;mcc_generated_files/mcc.h&quot;
#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 &quot;Ambient.h&quot;

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

/*****************************************************************************/
/* Ambient                                                                   */
unsigned int channelId =  xxxxx;
const char* writeKey = &quot;xxxxxxxxxxxxxxxx&quot;;
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(&quot;\r\n&quot;);
    
  // 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(&quot;2 sec before clear SmartConfig&quot;);
  delay(2000);
  
  // read pullup
  bool isSmartConfig = digitalRead(0);
  if (isSmartConfig == false) {
    // bink for clear config
    blinkClearConfig();
    Serial.println(&quot;clear config&quot;);
    // reset default config
    WiFi.disconnect();

  }

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

  Serial.println(&quot;&quot;);

  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, &quot;ntp.nict.jp&quot;, &quot;ntp.jst.mfeed.ad.jp&quot;);

}


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]=&quot;&quot;;
    
  bool isSW1 = digitalRead(0);

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

  // SW1を押した回数を数える
  if(SW1 == 0x0001){
    detcnt++;
    Serial.print(&quot;.&quot;);
  }
    
  if(exec){       // 1秒間に1回処理
    digitalWrite(12, 1-digitalRead(12));   // Blink the LED
    /*******/
    /* NTP */
    t = time(NULL);
    tm = localtime(&t);
    sprintf(msg,&quot;%04d/%02d/%02d %02d:%02d:%02d %ld&quot;,
        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(&quot;&quot;);
      Serial.print(msg);
      Serial.print(&quot; - &quot;);
      Serial.print(detcnt);
      Serial.print(&quot; : &quot;);
      detcnt=0;
    }
    exec = false;
  }

  delay(5);
}

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