雷センサーAS3935

ずいぶん前に秋月で買った雷センサーAS3935を使って、しばらく前に雷センサーロガーを作りました。

仕様としては、CPUにはPIC16F18857を使用、時計ICにRTC8564NBを使用したRTCモジュールに、液晶にI2C接続の液晶モジュールを使用、記録は24LC256のE2PROMに保存するというものになっています。電源は単三電池が3本ですので、持ち歩くにはやや大きいものになっています。電池の持ちとしては約3ヶ月程度くらいと思います。

制作後、最初は作業卓の近くの窓際に置いていたのですが、PCや3Dプリンタなどのノイズをかなり拾うようで、妨害イベントがかなり検知されました。弱い雷もあるにはあったのですが、鉄筋コンクリート造りでは肝心の落雷を検知せずに、ノイズが偽の落雷イベントとなっていると思われるものが少なくありませんでした。そこで、途中から部屋のもう一つの窓際に置いたところ、PCや3Dプリンタなどのノイズの影響は受けなく鳴りました。しかし、この窓も窓の外は2〜3mほど離れて隣の棟が建っており、電波状況としては良くなく、AMラジオも内蔵バーアンテナでは受信できません。(ちなみに、AS3935は雷が発する500kHz近傍のノイズを電波として受信して動作している)

今年は近所で雷が鳴るということもあまり多くなく、以降の動作確認が難しかったのですが、昨日は関東地方でかなり雷が鳴ったようで、自宅近くでもそこそこ雷が鳴りました。

やはり鉄筋コンクリート造の屋内設置で、窓際とはいえ窓の外も鉄筋コンクリート造の建物が建っているという条件はかなり厳しいようで、かなり反応はするものの、検知できる条件は雷の音がすでに聞こえる状況くらいでした。

屋外設置する方法を考えたいところですね。

ESP32 NTP時計にBME280を追加など

前回の記事のESP32 NTP時計にBME280を追加して気圧・温度・湿度を表示するようにしてみました。(台風15号が直撃するらしい、というのがきっかけです^^;)

ついでに、表示した値は1分間に1回、MQTTで自宅内のMQTTブローカに向けてsubscribeするのと、Ambientに送信するようにしました。

また、併せて、昔作ったLPS331APを使用した電池タイプの気圧計に3Dプリンタでケースを作ってみました。

青いほうがESP32+BME280、黒いほうがPIC1823+LPS331APです。LPS331APに比べて、BME280の方が1.5〜2hPaほど高めの値が出るようです。また、BME280の方が圧倒的に値が安定していて安定性では比較になりません。
気温については、置いている場所がルータや無線LANのアクセスポイント、スイッチングハブ、ポータブル温冷庫などが置いてるところの上なので、結構温度は高めに出ています。(温度は湿度にはもちろん影響していますが、気圧には影響はなさそうです)

Ambientに気圧をアップロードしてみた結果は以下のような感じです。

上の画像が送信し始めてしばらくの様子です。台風15号は小型なので、影響が出てくるのが遅いようです。また、このグラフでもBME280は値が安定していることがわかるかと思います。

しかし、接近し始めるとどんどん下がっていっています。

【追伸】夕方頃にはこんな感じになりました。もっとも低かったのは、朝4時38分の977.53hPaのようです。

ちなみに、天気図はこんな感じ。台風15号は小型で強い台風だったので、気圧が急激に下がり、急激に回復したようです。

Windows10 1903でgrubに対応・・・???

Jumper EZbook3 Pro はその後、増設したSSDを交換(32GB⇒240GB)して、Windows10をSSDに移動、標準のeMMCはLinux Mint19を入れ直して使っています。で、eMMCのLinux側からは事故等でWindows10を触ってしまわないように、ブートオプションでSATAを禁止してあります。

で、一昨日、翌日(つまり昨日ですね)のお出かけの前に Jumper Ezbook3 Pro のセキュリティアップデートをかけるために増設したSSD(240GB)に移してあるWindowsを起動したまま放置していたら、更新して〇〇になっていたので、更新して再起動を選択したら・・・1903のインストールを始めやがりました。

そしてこれが悪夢の始まりでした。

1903のインストール完了(かな?)の後の再起動をしたら、Windowsのブートローダがいません

当然、Windowsが起動できるはずもありません。しかたがないので、そのまま持参せずにおでかけ(何のためのノートPCなんだか・・・)しました。

そして、帰宅後に、修復を試み始めたのですが、ブートローダの修復にはWindowsのインストールメディアが必要なことがわかりました。で、1903のインストールイメージを持ってきたら・・・・4.8GBあって、1層のDVDメディアでは容量不足で書き込めません。BD-Rメディアにも書き込めません。

しかたがないので、別のPC上のWindows10でメディア作成ツールでUSB起動のメディアを作成しようとすると、長い長い作成過程のあと、「0x80042405 0xA001B」というエラーで作成ができません。このコードだけ表示するエラー、何が原因かさっぱりわからないので、ものすごく頭にきます。

ネット上にたくさんある、WindowsのHowTo的なページも1903でのメディア作成の方法については肝心な部分が暈してあるものが多く、実際のところはトラブルが多いのではないかという気がします。

悪戦苦闘して、なんとか起動メディアが作成できたのですが、どうも、USBメモリのパーティションテーブルがMBRになっているとダメで、GUIDパーティションテーブルでUSBメモリが初期化されていて、しかもWindows上で領域のフォーマットがされていないとダメな感じがします。(ひょっとしたら、NTFSも条件かもしれない)

しかし、これで起動してもインストール済みのWindows10のアップデートかクリーンインストールかを選択する画面しか出力されず、ブートローダのインストールはできませんでした。

結局、eMMC側のLinuxMint19でSATAを禁止するのをやめました。具体的には、/etc/default/grub に記述してあった、「GRUB_CMDLINE_LINUX_DEFAULT」の起動時オプションの「libata.force=disable」を削除しました。削除後は、

$ sudo grub-mkconfig -o /boot/grub/grub.cfg

として grub を更新して再起動、ここでSATA上のWindowsが見えるようになっているはずなので、再度

$ sudo grub-mkconfig -o /boot/grub/grub.cfg

として grub を更新して grub のエントリーにWindows10を追加させました。

すると、eMMCからの起動時に grub 上でWindowsが起動できるようになりました。実際には更新は完了しておらず、なんどかまた再起動した後に、無事に1903になりました。

結局のところ、grubを検出すると、Windows10の更新プログラムはブートローダをインストールせず(というか消去して)、grubに任せるような挙動になっているのかもしれません。自分はLinuxとWindowsで環境をキッチリ分けたいので、以前のやり方が気に入っていたのですが、今後はダメなのかもしれません。

ちなみに、壁紙が以前より明るくなっているのですが、自分は嫌いです。

ESP32 NTP時計

ESP32モジュールと昔買って余っていたグラフィック液晶でNTPで時刻取得する時計を作ってみました。部屋には電波時計があるのですが、鉄筋コンクリートの構造だと全然時刻合わせができませんでした。今だと電波時計よりも無線LAN+NTPの方が便利かもしれません。ファームウェアはPlatformIOで作ってみました。

今のところは画面は上半分しか使っていませんが、I2Cの端子は空けてあるので、下半分はBME280モジュールを追加して温湿度+気圧を表示するか、ネットワークの状態を監視して状況を表示するか、そのあたりで考えています。あるいは、アナログっぽい表示にするかもしれません。

それにしても昔の液晶モジュールなので、インタフェースがパラレルで配線本数が多くて大変です。あと、GPIO34以降は入力専用であることに気づかず、表示が出なくてハマりました。

ケースはこれから適当に現物合わせで設計して3Dプリンタで出力しようかと思います。

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("keta: ",keta," / time: ",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__ == "__main__":
	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("result")
		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__ == "__main__":
	start = datetime.datetime.now()
	print('start: ',start)
#	for keta in range(2,100+1) :
	keta = 2
	while True:
		now = datetime.datetime.now()
		print("keta: ",keta," / time: ",now-start," / record: ",maxn,maxk)
		kensho(keta)
		keta += 1
	print("result ",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);
}

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