ラベル マイコン の投稿を表示しています。 すべての投稿を表示
ラベル マイコン の投稿を表示しています。 すべての投稿を表示

2024年4月20日土曜日

RP2040でR-2R DACを試してみる

I2Sの勉強をしていたら、R-2RというDACのことを知った。
せっかくなので、試しに作って遊んでみる。

参考文献はこちら→https://www.mech.tohoku-gakuin.ac.jp/rde/contents/course/mechatronics/anadigi.html

上記サイトの上のほうの回路図にあるように、正確な抵抗のセットが2n本と、n個のDOがあれば、n bitのアナログ出力が作れるということ。
今回はDO4つ+R 3つ+2R 5つで、4bitを作ってみる。
Rの値は電源とマイコンの電流入出力能力を超えない範囲で選べばよいが、今回はわかりやすく1kΩと2kΩで作ってみた。

お行儀はよくないがGP18をLOWにしてGND代わりとしている。
GP21-24がDOで、A0で結果を読み取っている。

結果は以下のようになった。
ガタガタしているのは、抵抗のバラつき起因と思われる。
オーディオ用途で拘って作る人々は、チップ抵抗で16bit, 24bitを作って、抵抗器をやすりで削って微調整しているらしい。
一度トライしてみたい。

今回作ったものの精度はともかく、こんな簡単な回路でアナログ出力ができるというのは本当に面白い。


オマケで、結果の取得に使ったThonnyのコード。
汚いけどそのまま貼ります。
整数0~15を2bitの01の0埋め文字列に変換する部分、もっといいやり方がある気がするけど動けばいいやの精神。

import time

LED = machine.Pin(25, machine.Pin.OUT) # GP25をLEDとして出力端子に設定
GND = machine.Pin(18, machine.Pin.OUT)
GND.off()
bits = [
    machine.Pin(24, machine.Pin.OUT)
    ,machine.Pin(23, machine.Pin.OUT)
    ,machine.Pin(22, machine.Pin.OUT)
    ,machine.Pin(21, machine.Pin.OUT)
]
VIN = machine.ADC(0)
conv = 3.3/65535
conv = 1

print("start")
print("----")

def v(order):
    print(order, end="")
    print(": ", end="")
    for i in range(4):
        if order[i] is "0":
            bits[i].off()
        else:
            bits[i].on()
    time.sleep_ms(10)
    print(VIN.read_u16()*conv)

for i in range(2**4):
    t = ("000" + str(bin(i)).replace("0b", ""))[-4:]
    v(t)  

2024年4月13日土曜日

トランジスタ2石とDO 1つでマイコンの電源自己保持

待機中のマイコンの消費電力を極限まで下げるため、トランジスタ2石とDOを1つ使い、電源に自己保持回路を加えることを考えてみる。

何か動作させるときは、モメンタリスイッチなどでワンショットの入力を受け付ける。マイコンが起動すると、電源用のDOをHIGHにして、電源をONにしつづける。
仕事が終わったら、電源用のDOをLOWにして、自己保持を解いて電流を消費しないようにする。

マイコンにもスリープモードがあるのでそちらで十分な時は無駄に部品が増えるだけだが、うまく作ればスリープモード以上の省電力化ができるのでは?

考えた回路がこちら。
2つのスイッチが、マイコンのDO相当。右端の100Ωがマイコンの電源部分。

この回路は、NPNのベース抵抗の反対側にHIGHが印加されると電源ONで、LOWもしくはハイインピーダンスだと電源OFFになる。

シミュレータは以下のサイトを利用。

Import用のテキスト列。
$ 1 0.000005 10.20027730826997 50 5 43 5e-11
r 384 272 384 208 0 1200
s 208 256 272 256 0 1 false
v 144 336 144 160 0 0 40 3.7 0 0 0.5
r 464 160 464 336 0 100
w 320 160 368 160 0
w 208 160 320 160 0
w 144 336 208 336 0
w 400 160 464 160 0
w 384 336 464 336 0
t 352 288 384 288 0 1 -3.161620436472443 0.20923865338699582 100 default
t 384 208 384 160 1 -1 3.370856126826202 -0.32914051091822083 100 default
w 384 336 208 336 0
r 272 288 352 288 0 1200
w 208 160 208 256 0
w 256 192 320 192 0
w 144 160 208 160 0
w 384 336 384 304 0
s 208 288 272 288 0 1 false
w 272 256 272 288 0
w 208 288 208 336 0
o 0 64 0 4099 0.0000762939453125 0.00009765625 0 2 0 3
o 3 64 0 4099 0.0000762939453125 0.00009765625 1 2 3 3
38 0 F1 0 1 101 -1 Resistance


2024年3月17日日曜日

RP2040のUARTで全文を途切れさせずに読み切る

GPSのNMEAを読み出すとき、文章が長かったりタイミングが悪いと、途中で途切れてしまうことがある。

以下のようにany()を使うと解決する。

■BEFORE

msg = uart.read()

■AFTER

msg = ""
while uart.any():
    msg = msg + str(uart.read())[2:-1]

スライサーで2文字目以降を取ることで、バイト列を文字列に変換したときの先頭の「'b」をスキップしている。-2にして文末もスキップすべき?

RP2040-ZERO+micropythonにてGPS AT6558の日時緯度経度を丸形液晶GC9A01に表示してみる

以前の記事。
RP2040-ZEROとGC9A01の試験→https://matsu-log.blogspot.com/2024/03/aitendom128gc9a01a-gc9a01rp2040-zero.html
GPS AT6558→https://matsu-log.blogspot.com/2024/03/gpsat6558.html

今度は、RP2040-ZEROに液晶とGPSを繋いで、緯度経度を液晶に表示してみる。


USART0を使いたいので、GP0,1を空ける。DCとRSTは、代わりにGP3,4に繋いでおく。

■液晶

  1. GND → GND
  2. VCC → 3V3
  3. SCL → GP6/SPI0 SCK
  4. SDA → GP5/SPI0 TX
  5. RES → GP3
  6. DC → GP4
  7. CS → GP5/SPI0 CSn
  8. BLK → 未使用
■GPS
  1. GND → GND
  2. VCC → 3V3
  3. TX → GP1
    ※RXは繋がなくてもいいので無視

Fritzingで作った回路図。探してみると、RP2040-ZEROなどたくさんのAdd-onパーツが公開されていて非常に有難い。さすがにGPSは無かったので、似たような部品と汎用ヘッダで代用。

以下のようなコードで、$GNGGAと$GNRMCから日時と緯度経度を吸い上げて、液晶に表示した。液晶、GPSをうまく繋げられていたので、これはすんなり作れた。

以下、micropythonコード。

from machine import Pin, SPI
import gc9a01py as gc9a01
import time

import vga2_8x16 as font

from machine import UART
uart = UART(0, baudrate=9600, tx=0, rx=1)

# GPSの結果から日時と緯度経度を抜き出す
def GPS(input):
    if input is None:
        return ("","","","")
    msgs = str(input)[2:-1].split("\\r\\n")
    lat = ""
    lgt = ""
    date = ""
    time = ""
    for msg in msgs:
        if msg[0:6] is "$GNGGA":
            item = msg.split(",")
            if len(item) <= 4:
                continue
            #print(item[1], item[2], item[4])
            time = item[1]
            lat = item[2]
            lgt = item[4]
        elif msg[0:6] is "$GNRMC":
            item = msg.split(",")
            if len(item) <= 10:
                continue
            time = item[1]
            lat = item[3]
            lgt = item[5]
            date = item[9]
    return (date, time, lat, lgt)

# GC9A01初期化
SPI_PORT = 0
SPI_BAUDRATE = 60000000
RESET_PIN = Pin(3, Pin.OUT)
DC_PIN = Pin(4, Pin.OUT)
CS_PIN = Pin(5, Pin.OUT)

spi = SPI(SPI_PORT, baudrate=SPI_BAUDRATE, sck=Pin(6), mosi=Pin(7))
tft = gc9a01.GC9A01(spi=spi, dc=DC_PIN, cs=CS_PIN, reset=RESET_PIN, backlight=None, rotation=0)

bgcolor = gc9a01.color565(0x00, 0x44, 0x22)
tft.fill(bgcolor)

tft.text(font,"GPS Tracker",80,20,gc9a01.BLACK,bgcolor)
while True:
    msg = uart.read()
    ret = GPS(msg)
    pos = 40
    for item in ret:
        if not item is "":
            tft.text(font,item,80,pos,gc9a01.BLACK,bgcolor)
        pos += 20
    tft.fill_rect(88,80,24,40, gc9a01.GREEN)
    time.sleep(0.5)

2024年3月16日土曜日

aitendoの丸形液晶(M128GC9A01A, GC9A01)をRP2040-ZEROで試す

aitendoの丸形液晶 M128GC9A01Aについて、前回、M5+UI FLOWで挑戦するも、失敗。
商品リンク:https://www.aitendo.com/product/20938

今度は、raspberry pi pico互換のRP2040-ZEROで挑戦する。
まずはSPI接続。

以下、スイッチサイエンスさんより画像を借用。
RP2040-ZEROのSPIは2系統あり、片方は裏面のパッドや左右のピンに点在してしまっているので、今回はSPI0を使い右側で固めて配線することにする。電源は左側固定で、さらにジャンパ使うのであまり意味はないが。

配線表とmicropython用のライブラリや使用例を見比べて、以下のつなぎ方に決定。

  1. GND → GND
  2. VCC → 3V3
  3. SCL → GP6/SPI0 SCK
  4. SDA → GP5/SPI0 TX
  5. RES → GP0
  6. DC → GP1
  7. CS → GP5/SPI0 CSn
  8. BLK → 未使用
試しては無いが、たぶん3, 4以外は任意のピンでよいとは思う。
ライブラリはこちら:https://github.com/russhughes/gc9a01py/tree/mainを使わせていただく。
libのgc9a01py.pyをダウンロードし、ThonnyでRP2040-ZEROのルートにこのライブラリのpyファイルを格納すればOK。
下図のように、デバイスのルート直下にファイルが表示されていれば大丈夫。これで、「import gc9a01py as GC9A01」というようにして呼び出せるようになる。

本当に大したコードではないけど、私自身がこのディスプレイをRP2040-ZERO+micropythonで利用するのに結構苦労したので残しておきます。

from machine import Pin, SPI
import gc9a01py as GC9A01

# GC9A01初期化
SPI_PORT = 0
SPI_BAUDRATE = 60000000
RESET_PIN = Pin(0, Pin.OUT)
CS_PIN = Pin(5, Pin.OUT)
DC_PIN = Pin(1, Pin.OUT)

spi = SPI(SPI_PORT, baudrate=SPI_BAUDRATE, sck=Pin(6), mosi=Pin(7))
tft = GC9A01.GC9A01(spi=spi, dc=DC_PIN, cs=CS_PIN, reset=RESET_PIN, backlight=None, rotation=0) 
 
tft.fill(GC9A01.RED)
for i in range(240):
    tft.pixel(i, i, GC9A01.GREEN)
    tft.pixel(120, i, GC9A01.BLUE)
    tft.pixel(i, 120, GC9A01.WHITE)

これがうまく動くと、冒頭の画像のようになる。赤背景に、緑青白の線を引いただけ。
速度を気にするなら矩形や線描写のメソッドを使ったほうがいいはず。

以上。

2024年3月5日火曜日

地磁気センサQMC5883Lにハマりかける

前回、ATD5883Lを1つ焼いてしまったので、Amazonで格安の類似品を注文。3つで\769と格安。(https://www.amazon.co.jp/dp/B0CFQSW2TG

aitendoで買ったATD5883Lでうまくいったコードを動かしてみると、そもそもI2Cのアドレスが0x0D (13)と表示され、アドレスを変えて動かすも、なぜかセンサの値が3軸とも0になる。

よくよく調べてみると、このチップは「QMC5883L」というレジスタアドレスが互換ではない似て非なるチップとのこと。
https://leico.github.io/TechnicalNote/Arduino/esp32-qmc5883l-basicにて先人がレジスタをまとめてくださっているので、有難く参照する。

要所は以下の通り。

  • 出力レジスタが異なる
    • MAG_X:0x00-01
    • MAG_Y:0x02-03
    • MAG_Z:0x04-05
  • 出力レジスタ16bitがリトルエンディアンの並び順
  • 連続モードの設定方法が異なる
    • 0x0Bに0x01を書き込み
    • 0x09に0x1Dを書き込み


UI FLOWだと上記の通り。
以下はmicropython。

pin0 = machine.Pin(22, mode=machine.Pin.OUT, pull=0x00)
pin0.off()
i2c0 = i2c_bus.easyI2C((23, 19), 0x00, freq=10000)
wait_ms(100)
print('----start')
print(i2c0.scan())
print(i2c0.available())
wait_ms(100)
i2c0.addr=(0x0d)
wait_ms(100)
i2c0.write_u8(0x0b, 0x01)
i2c0.write_u8(0x09, 0x1d)
print(i2c0.read_reg(0x0D, 1))
while True:
  print(str([i2c0.read_u16(0x00, byteorder="little"), i2c0.read_u16(0x02, byteorder="little"), i2c0.read_u16(0x04, byteorder="little")]))
  wait_ms(100)
  wait_ms(2)

この例では、m5atom liteの3.3V側の端子5pinにセンサを直結している。G22を0V出力にして、G23, 19をI2Cに割り当てればOK。

これでうまく地磁気を読み取れて、めでたしめでたし。

2024年3月4日月曜日

aitendoの丸形液晶(M128GC9A01A, GC9A01)をm5で試す<失敗>

追記:この後UI FLOWでこの液晶を動かそうとしたが、ライブラリを読み込むとメモリ溢れで落ちることが判明。UI FLOWは諦めて、raspberry pi picoで動かすよう方針転換。

後日、RP2040-ZERO+Thonnyでうまく動いたので、動作例が知りたい方はこちらをご覧ください。
https://matsu-log.blogspot.com/2024/03/aitendom128gc9a01a-gc9a01rp2040-zero.html

-------

aitendoの丸形液晶 M128GC9A01Aも買ったので、m5atom matrixで表示できるよう試験する。
商品リンク:https://www.aitendo.com/product/20938


この液晶、中身はGC9A01というコントローラを使ったもので、価格もサイズも手ごろで見た目も綺麗なためか、aitendoだけでなくadafruitやsparkfunなどいろいろな会社からdip化基板セットやタッチパネル、rp2040と組み合わせたセットが出ていて面白い。

■SPIとI2Cのどちらで繋ぐか?

このボードはSPIとI2Cの両方で繋げるようだが、グラフィック関係は帯域の問題からSPIを使うのがよさそう。このサイト(https://emb.macnica.co.jp/articles/8191/)で分かりやすくまとめられているが、bpsの桁が違う。SPIは50Mbpsを狙えるが、I2Cは1Mbpsとのこと。

atom liteは240MHzで、SPIは最大80MHz対応とのこと。
例えば128x128dot、フルカラー8+8+8=24bitを20Hzで更新したいとき、1フレームは128*128*3Byte=49,152Byte=48kBとなり、これを1秒に5枚転送するので、240kB/s=1,920kbps=1.875Mbpsとなる。
I2Cだと1MHzでも1Mbps未満なので、完全に帯域不足。

I2Cは帯域で劣るものの、2線で済むのが一番のメリットで、バイト単位のR/Wで十分なセンサ類と繋ぐのがよい。

■m5atomとの接続

先人のコードを参考に、どこに何を繋げばいいのか読み解いていく。
m5側はUIFLOWだとこんな名前になっているので、それぞれどれと繋げばよいかが問題。


他の液晶の例だが、https://lang-ship.com/blog/work/m5stickc-spi/を参考に何を繋げばよいか検討する。
  1. BLK … 不要 ※バックライトの制御用
  2. CS … GND ※他に繋ぐものが無ければ、L固定でOK
  3. DC … MISO ※RのみでOK
  4. RES … 任意のW ※画面リセット用
  5. SDA … MOSI ※WのみでOK
  6. SCL … SCK
  7. VCC … 3.3V
  8. GND … GND
今回は順番に以下のように接続した。
  1. BLK … n/a
  2. CS … GND
  3. DC … G22
  4. RES … G19
  5. SDA … G23
  6. SCL … G33
  7. VCC … 3.3V
  8. GND … GND
atomはRead Onlyなどで迷うことが無いので楽。
結果、ブロックの設定は次のようになる

そうこうするうちに遅くなってしまったので、続きはまた次回。

2024年3月2日土曜日

地磁気センサ(電子コンパス)ATD5883Lを試す2

前の投稿の続き(https://matsu-log.blogspot.com/2024/03/atd5883l.html)

aitendoのATD5883Lを試したが、うまくいかなかったのでリトライ。
結論、i2cアドレスと周波数設定と電圧ミスが原因だった。
センサ1個目は、うかつにportAにつないだせいで5Vで焼けてしまったよう…無念。安くない勉強料だ。
2個買っておいたので何とか助かった。
これ、シルクをよく見るとGY-271という基板名っぽい。この名前だとAmazonで安いコピー品が大量にヒットする。

私の買ったデバイスは、100kHz 3.3Vでアクセスすると30, 104という二つのIDがヒットする。
104ではうまく動かず、30でA/B/CのWHO AM Iを読むと、以下の値が読みだされる。
 b'H', b'4', b'3'

ハネウェルの正規品とは異なるので、要注意。

出力結果だが、以下のように、x,y,zを読み取っていい感じにプロットできた。

※ちなみに、この3次元プロットにはhttps://qiita.com/yo16/items/b5af6d2e9d1bf5e8ab51を活用させていただいた


焼けていないほうのチップをm5atomの3.3V, G, 21, 25につなぎ直して、上記のプログラムを走らせれば無事動いた。

x,y,zの生値を見ると、以下の範囲になっている。
 x: -435~649
 y: -556~540
 z: -585~409

乱暴だが、それぞれ中点を取ると107, -8, -88となるので、これをオフセットとして使う。
それなりの精度になっていそうなので、オモチャレベルではこれでよしとしておく。

2024年3月1日金曜日

地磁気センサ(電子コンパス)ATD5883Lを試す

(成功事例はこちら)

今度は、ATD5883L。
秋月の9軸センサを使っていたが、実際IMUは無くても必要十分な精度が出せることがわかったので、コンパスだけで安いこちらを使ってみる。
未だとaitendoで税抜\695。
https://www.aitendo.com/product/9545

小さくて安い

まずはGROVE端子経由のPORTAにつないでアドレスダンプしてみる。



結果、このICは13 = 0x0Dのよう。
このICは「HMC5883L」の互換品らしいので、このワードで使用例を探してみる。

raspiで繋いでいる例が以下のリンクにある。
0x0A,0B,0Cを見れば、識別コードが書いてあるっぽい。
さくっと読み出してみる。


これは正しい値なのか・・・?


strawberryリナックスのサイトを見るに、明らかに間違っていそう。
https://strawberry-linux.com/catalog/items?code=12105
原因もわからないし、今日はここまで。GPSが動いたのでよしとする。

GPSモジュールAT6558を試す

 m5のGPSユニットは安くなく、サイズも大きいので、aitendoの安いモジュール(AT6558)を使えないか試してみる。
https://www.aitendo.com/product/17122
今は税抜き\900と、m5の半額で買える。
ただし、アンテナは別で必要。

chinaの主張を感じる

aitendoから拝借
この画像がめちゃくちゃ役に立つ

汚い配線だが気にしない
とりあえずV,G,RX,TXだけ繋ぐ

早速仮配線し、atom matrixで繋いでみる。

小さくて便利
このチップは3.3Vでないとダメなので、5Vのボードとは繋げない

極々シンプルなプログラム

まずはアンテナ無しで試してみる。
結果、以下のようなメッセージが出る。
電波が拾えずうまく繋げていないことがわかる。

$GNGGA,,,,,,0,00,25.5,,,,,,*64
$GNGLL,,,,,,V,M*79
$GPGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*02
$BDGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*13
$GPGSV,1,1,00*79
$BDGSV,1,1,00*68
$GNRMC,,V,,,,,,,,,,M*4E$GNVTG,,,,,,,,,M*2D
$GNZDA,,,,,,*56
$GPTXT,01,01,01,ANTENNA OPEN*25

続いて、アンテナを繋いでリトライ。


ちなみに、このアンテナは特価品で\199のもの。
もうすぐ無くなりそう。
https://www.aitendo.com/product/17558

アンテナを繋ぐと、起動して少し待てば衛星と繋がり、時間や座標を正しく知らせてくれるようになる。

$GNGGA,142138.000,3xxx.1177,N,1xxxx.6385,E,1,10,1.1,4.0,M,0.0,M,,*79
$GNGLL,3xxx.1177,N,1xxxx.6385,E,142138.000,A,A*49
$GPGSA,A,3,13,11,06,29,30,07,36,20,35,,,,2.1,1.1,1.8*32
$BDGSA,A,3,19,,,,,,,,,,,,2.1,1.1,1.8*21
$GPGSV,3,1,11,06,38,135,15,07,30,071,18,09,,,25,11,76,126,17*42
$GPGSV,3,2,11,13,46,214,37,15,14,233,,20,66,000,24,29,22,308,22*7C
$GPGSV,3' 
※xで緯度経度の一部を塗りつぶしています

思ったより簡単に動く。
これで小型化・安価化を達成できそう。

2024年2月25日日曜日

GPSと地磁気センサで、目的地までの距離と方角だけを出す簡易ナビを作る

タイトルの通り。
LSM9DS1をいじっていたのは、これを作るため。

いいアイデアだと思っていたが、既に先駆者がいて製品化までされている。(https://www.plotonline.com/motor/beeline/
先を越されたのは悔しいから、こっちはネット環境無しでも使えるものを目指したい。
今悩んでいるのは、目的地の緯度・経度をどうやって入力するか?
今のバージョンは、プログラム中に直打ちしてしまっている。

プロトタイプ
左上のマスキングテープの下にLSM9DS1がある
GPSはm5純正品、U032というやつ
完成したら円形液晶とか使って格好よく仕上げたい

コード実行だらけのUI FLOW
素直にmicropythonやArduino C++を使ったほうがいいのかもしれない

初代m5stickc用の.m5fをgithubに上げたので、詳細な中身が知りたいときはそちらをご覧ください。
https://github.com/matsuura-h/lsm9ds1_micropython/tree/main
■方角検出
地味に苦労したのは、磁気センサの値を方角に変換する部分。
atan2に行きつくまでに相当時間がかかった。
地磁気のx, yをプロットしてみると、中心が相当ずれていることがわかる。
現物合わせだが、コード内に直書きでx+0.03, y-0.25の補正を加えている。
本来はoffsetをセンサに書き込むべき。
補正後のx,yを、参考URLのように位相を横軸にプロットしてみる。
それなりのsin, cos波になった。

ちなみに、LSM9DS1をGPSやm5stickcの傍で動かすと、磁界の影響でめちゃくちゃな値になるので注意。

atan2で処理した後も、まずラジアン角から度に直し、±180度から0-360度レンジに変換し、さらに東西南北=90度-270度-180度-0度になるように補正する必要がある。

コード例は以下の通り。
mx0,my0,mz0 = lsm.read_magnet()
deg = math.atan2(my0-0.25, mx0+0.03)*180/math.pi
deg = deg if deg >= 0 else 360 + deg
deg = (deg + 180) % 360

1行目は、自作ライブラリで磁気センサを読み取る部分。
2行目でオフセット補正+角度算出+ラジアン→度への変換をやっている。
続いて、3行目で±180から0-360度に変換。
最後に、このままだと南が0度なので、180度回転させている。
もしセンサを裏表逆に使う場合、最初に正負逆転させるか、atan2の引数の符号を変えることで対応可能。

実際に動かしてみたときの動画
薄い赤の△が北、黒の▲が目的地の方角を示している
m5stickcの描画が遅いのでブレがあるが、ロジック自体は問題無し
あとはフィールド試験




2024年2月24日土曜日

m5シリーズのUI FLOW, micropythonでLSM9DS1を使う

m5のUI FLOWやmicropythonでLSM9DS1を使えるlibrayを作成。
手っ取り早く使いたい方は、githubに説明とコードをupしたので以下を見てください。

秋月のLSM9DS1使用9軸センサモジュール AE-LSM9DS1-I2Cを買ってきて、m5stickCやm5atom matrix, liteで使おうとしたらひとしきりハマりました。

LSM9DS1は、1つのチップにそれぞれ3次元の加速度センサ、ジャイロセンサ、地磁気センサが詰まっていて、9軸自由度=9DoFセンサと呼ばれるもの。
今回はこのうち地磁気センサ、つまり電子コンパスを使う予定。
そもそも6軸imuなら多くのm5デバイスやスマートウォッチにも入っていて、作例も豊富だけど、意外と地磁気センサを使う作例は少ない気がする。

(画像は秋月電子より借用)



LSM9DS1は秋月やスイッチサイエンスでも手に入り、値段もたぶん一番安い。
しかしながら真面目に使おうとすると、分解能を設定し、各センサ値を2byte リトルエンディアンで読み取らないといけず、ちゃんとドキュメントを読まないと手も足も出ない。
自ずから既存のライブラリに頼りたくなるのだが、そのライブラリがArduino C++やcircuitpython向けしかない。

micropython、m5用は全く無い
タコ猫が出てしまう

また、既存のライブラリを移植しようにも、UI FLOWで簡単に使えるI2Cと微妙に読み書き関数の仕様が異なるので、そこの読み解きが難しい。
そして、UI FLOWで使われているi2c_bus.easyI2Cもまたリファレンスや作例がないので模索しながら構築した。

3日以上格闘した結果何とか動くものができたので公開しました。

2017年4月22日土曜日

フィラメント管で時計を作る(準備編)

昔買ったフィラメント管:IV-9で時計のような数字表示機を作った。
↓完成したものの動画。ケースはレゴで作成。



■フィラメント管IV-9
購入元は秋葉原の真空管屋さん。(サンエイ電機)
今売っているかはわからないけど、1個300円くらいだった。

データシートもネットですぐ見つかる。
 http://www.tube-tester.com/sites/nixie/data/IV-9/iv-9.htm

ニキシー管と違って数Vの低圧で動き、1セグは20mAくらい。
Typの電圧は4.5Vらしいが、アルカリ2本の3Vでも十分光る。
手持ちの4つだと、ピンに限らず3V~5Vの範囲では、消費電流はほとんど変わらなかった。

一般的には共通の1ピンをGNDにし、7セグ相当の3~9ピンと右下のドットの2ピンに正の電圧をかけるらしい。
ダイナミック制御でもうまく光ったが、ネットの作例を見るとスタティックのものが多い。
ロングランでは試していないけど、ダイナミックだとフィラメントにダメージがあるのだろうか?

■PICによるスタティックドライブ
スタティックで点灯させるなら、駆動は1管8個なので4つなら32個必要。
20mA×32個なので最大640mAになる。1管あたりは180mA。
Trをいっぱい並べるのも面倒なので普通は7セグドライバIC(74HC4511)とかTrアレイを使うけど、手持ちで古いPIC(PIC16F648A)が大量に余っていたのでこれを活用する。

このPICは1ピンでぎりぎり20mA出せて、チップ単位で200mAまでOK。
IOもDOがRA0~4, 6, 7とRB0~7の15本がある。
ただし、RA4はオープンドレインなのでDOだと電流吸い込みしかできないので注意。
また、シリアルを使うならRB1:RX, RB2:TXも埋まる。

今回は、アノードコモンとしRA系で7セグを点灯、RB0で2ピンのドットを点灯、baud 9600のUARTで数値を受けることにした。
ネットで作例を探すと、1つのTXから複数のRXを繋いでもうまくいくようだ。

最初の1字でA~Dを送り、管を区別させることにした。
2文字目で表示内容を送る。

2文字ファンクション
0~9数字表示
!全消灯
スペースドット消灯(2ピン)
.ドット表示(2ピン)

例えば「1234.」としたければ、シリアルで以下のコマンドを送る。
 [A1B2C3D4A B C D.]
すべて消灯なら次のコマンド。
 [A!B!C!D!]

■RX並列のテスト
基板を組んでからでは遅いので、RX並列でうまく管の切り分けが可能か確かめる。
上記ファンクションのうち、RB0にLEDを繋いでテストした。

左から順にA, B, C, Dで、[A!B.C.D!]とコマンドを送ると・・・。

結果、問題なく成功。
本当はPICごとに抵抗を繋いだほうがいいかもしれないけど、手抜きでPC側にだけ100Ωを繋いでいる。



あとは、基板を作ってIV-9とPICを繋ぐのみ。

ESP8266でデータロガー(UDP Client)

Switch ScienceのESPr Developperを使えばブレッドボードで即席ロガーが作れる。
左真ん中に繋がっている汚い線が秋月のドア開閉センサで、
左上のICが温度センサのLM61。


ESPr Developperは大事な端子が片側に集まっているので、凝ったことをしなければ小さいブレッドボードで十分。
左側には3.3Vがないので、2つのDiodeで5V⇒4.2V⇒3.4Vを作成。

・ケース入りリードスイッチ
 http://akizukidenshi.com/catalog/g/gP-04025/
・LM61BIZ(4個入りのほうがオススメ)
 http://akizukidenshi.com/catalog/g/gI-09691/

センサデータは、UDPを使ってRasPiで動くRubyのサーバアプリに送信、テキストに直接保存する。


■温度センサ
LM61はリニアな電圧で温度が測れる便利なICで、LM35と違って負電圧にならないので使いやすい。

↓データシートのTyp値。
温度電圧
+100℃+1600mV
+85℃+1450mV
+25℃+850mV
0℃+600mV
-25℃+350mV
-35℃+300mV

式を整理すると、次のようになる。
 <温度>[℃] = (<電圧>[V] - 0.6)×0.1

ESP8266のHTTP Clientと割り込み(Ticker, Timer)でハマる。

ESPr Developer / ESP8266 / ESP-WROOM-02でThingSpeakにデータ送信。
ThingSpeakは15秒の制限があるので、30秒周期でデータを送ることに。

自然な発想でTimerを仕込むが、何故かうまく通信できない。
UDPならうまく行ったのに何故??

参考にしたのは以下のサイト。
 http://www.esp8266.com/viewtopic.php?p=62981

// ソースはこんな感じ
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Ticker.h>
extern "C" {
  #include "user_interface.h"
}

const char* SSID = "<SSID>", PASS= "<PASS>";
Ticker tic1;
void setup() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(SSID, PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  IPAddress myIP = WiFi.localIP();
  tic1.attach_ms(30 * 1000, tic1_loop); // 30秒に1回、tic1_loopを実行
}

void sendMessage(String buf) {
  HTTPClient http;
  String url = "http://api.thingspeak.com/update?key=";
  String key = <ThingSpeak API KEY>;
  http.begin(url + key + buf);
  int httpCode = http.GET();
  if(httpCode) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      if(httpCode == 200) {
          String payload = http.getString();
          Serial.println(payload);
      }
  } else {
      Serial.printf("[HTTP] GET... failed, error: %d\n", httpCode);
  }
  http.end();
}

void tic1_loop(){
  sendMessage(<センサデータ>);
}
void loop{}


いろいろ試行錯誤して気づいたのだが、HTTPClientも割り込みを使うので割り込みの中では使えない。
検証は以下の通り。
1)sendMessage()を単体で呼ぶ ⇒OK
2)loop内でdelayでsendMessage()を何度も動かす ⇒OK
3)Timer割り込みでsendMessage()を何度も動かす ⇒NG

noInterrupts()なのが問題なのでinterrupts()を明示的に呼び出せばHTTPClientも動くはずだが、今回はシンプルにフラグ処理で逃げることにした。


char flag = 0;void interruptFunc(){
  flag = 1;
}

void loop{
  if(flag){
    sendMessage(<センサデータ>);
    flag = 0;
  }
}

これでOKだった。
ESP8266は1か月以上動かしているけど、元気に温度とドアの開閉を記録してくれている。


2017年2月25日土曜日

無線LANマイコンの決定版 ESP-WROOM-02 / ESP8266(1/2)

遅ればせながら、無線LANマイコンを使ってみた。
ArduinoのIDEで開発できるところが素晴らしい。
簡単に無線LANに接続し、マイコン⇒Raspberry PiへUDPのデータ送信ができた。
こんな強力なデバイスを個人が数百円で買えるなんてすごい時代だ。

本稿では、幾つかハマったところを中心に記録します。

<前半>
①買い物
②基板概要
③ATコマンド実行
④ファームウェア焼き直し

<後半:次記事へ>
⑤Arduino環境構築
⑥UDPクライアント
⑦UDPサーバ(Raspberry Pi+Ruby)

#####################

①買い物
スイッチサイエンスか、秋月でESP8266でググればOK。
自前でピッチ変換やUSB⇔シリアル変換やスイッチ実装できるなら、秋月だと割安。
(狭ピッチのマイコンのみで550円、ピッチ変換実装済で650円くらい)

スイッチサイエンスは秋月よりは高いが、開発ボードが優れている。
オススメは「ESPr Developer」で、必要なスイッチや抵抗やUSB⇔シリアル変換も実装済。
2017/2月時点で2,160円だが、お金や小型実装に拘らないならこれ一択。
少なくともH/Wでは困らないので、時間短縮できる。


②基板概略
・3.3V動作
・IEEE802.11a/b/g対応、技適OK!(日本国内で無線LANが合法利用できる)
・32bitマイコン(非ARM)
・UART/I2C/SPI等対応
・10bit ADC 1ch
・GPIO 9ch

ATコマンドだけで無線接続を構成できるので、親機Arduino+無線機ESP8266という構成も見かける。
これはESPのRAMを書き換えると技適に反する懸念があったためで、今は問題ない事を先人が確認してくれている。
スイッチサイエンス社の当該記事

ESP8266のIOで十分なら、わざわざ外部ボードを繋がなくてもよい。
もしくは、ESP8266を親とし、I2Cで周辺機器を繋げばよい。


③ATコマンド
ESP8266は、通常起動させるとシリアルでATコマンドを受け付けるモードになる。
Arduino IDEで書き込んで単体で動かすなら不要だが、初期不良のチェックも兼ねて一度はATコマンドで無線LANに繋いでおくほうがよい。

不用意にコマンドを打つと、シリアルで繋げなくなるので要注意。
どんな効力を持つコマンドかよく調べてから実行しないと危険。
そのときは④のファーム焼き直しへ。

シリアル設定は以下の通り。
・115200bps (最初のMessageは74800で送ってくるが、無視してよい)
・8bit
・パリティなし
・Stop bit = 1bit
・フロー制御なし

ESPr DeveloperならUSBでそのまま繋げる。
Arduino IDEのシリアルモニタを使うと楽。
うまくいかないときは、電源を指し直すかRESETスイッチを押すとよい。

ATのみもしくはAT+<COMMAND>という形式で制御する。

(1) AT
動作確認用。うまく認識できていれば、OKを返す。

(2) AT+GMR
バージョン確認。
当方の環境では以下の通りだった。
 AT version:1.3.0.0(Jul 14 2016 18:54:01)
 SDK version:2.0.0(656edbf)
 compile time:Jul 19 2016 18:44:22
 OK

(3) AT+CWMODE=1
無線LANのモードをクライアントにする。
これをやらないとAP設定受け付けない。
2にすると、APになる様子。


(4) AT+CWJAP=<SSID>,<PASSWORD>
無線LANのAP設定を書き込む。


(5) AT+CIFSR
今のIP等の設定を確認する。上のCWJAPがうまくできており、接続したAPでDHCPが動いていればIPが表示される。


検証のみなら(1)~(5)で事足りると思う。
他の機能が使いたければ、「ATコマンド」で調べるとよい。


④ファーム焼き直し
ここまでは順調だったが、以下のATコマンドを打ったらシリアルがうまくつながらなくなった。
・AT+UART_DEF=9600,8,1,0,0

以下のサイトを参考にし、ファームを元に戻せば復活した。
http://nemuisan.blog.bai.ne.jp/?eid=215341

ファームの書き込みは、ボードをSPI書き込みモードにする事で実行できる。
ESPr DeveloperならFLASHを押しながらリセットするとSPI書き込みモードになる
書き込みが始まるまではSWを押し続けないといけない。


続きは別投稿とします。


<以下、メモ>
Arduinoを対応させる
http://qiita.com/dz_/items/eb7c80d3caf4a5c84274

Arduino
最新の1.8.1をccから導入
zipがよい
http://arduino.esp8266.com/stable/package_esp8266com_index.json