2018年9月26日水曜日

スピード計の製作(ハブ巻き付け型)

GARMINの自転車用スピードセンサーで、ハブに巻き付けるタイプがあります。

これを真似てBLE送信するものを製作してみました。
これはマグネット不要で、簡単に他のホイールに付け替えられるので、従来のマグネットセンサーのものより便利です。
精度は劣るようですが、私は、スピード計に精度は求めないので、こちらの方がお気に入りです。

GARMINの偽物のようなものが格安で売られたりしており、経済的には自作するより購入した方がよいのですが、自己満足度が高いので、製作してみました。

ハード構成は、BL652無線マイコンに、秋月の3軸加速度センサモジュール ADXL335を取り付けました。
配線は、BL652のアナログ入力4番ピンとADXL335のY軸出力を結線しただけです。
tmpdata=GpioRead(4)
この一行で、ADXL335のアナログ出力値をtmpdataに代入できます。
システム全体の消費電力が低いのでボタン電池CR2032で駆動可能です。

消費電力は、無線送信込みで、1秒間に100回ADコンバートして、0.8mA、
2000回ADコンバートすると、1.7mA程度でした。
1秒間に100回のサンプリングでは、明らかに精度が足りませんが、電池寿命を優先しています。センサー類の電池切れは、自転車でもっとも残念なイベントの一つと思ております。

ADXL335は、加速度をアナログ出力するので、BL652のADコンバータでデジタル値に変換し、角度情報を得ます。

実験中、スピード計をハブにつけたり、はがしたりが面倒なので、「ハブ回転シミュレータ」を開発しました。
↓こちらです。サラダ水切り器です。

これは、上のハンドルを回すと、中身のザルが回転し、遠心力で、サラダの水切りができます、というデバイスです。
これに今回のスピード計をいれてテープで固定、ハブ回転方向にグルグルすると、センサー角度に応じて、センサーから値が得られました。
1回転すると、最大値が490以上、最小値が340以下でした。最大値、最小値ともにけっこうブレ幅があります。この辺を詰めないと、精度はでないと思いますが、とりあえずテストしてみます。
アルゴリズムは、ADXL335の読み取り値が345を下回ったら、1回転したと判定します。
1回転したら、1回転に要した時間をミリ秒単位で算出し、
BLEデバイスのデバイス名の最後に、16進数の文字列として設定し、
デバイス名をBLEのAdvertise送信します。
これをBLE対応のAndroid端末で独自アプリで受信、デコードして、スピードとして表示します。
受信側はAndroidでなく、BL652でも可能です。
BL652で受信して、Nokia5110液晶に表示すれば省電力スピードメーターの完成です。

システム全体が小さいので100均のLEDライトに仕込むとキレイに収まりそうです。

まだ、収納できる段階でないので、
とりあえず、ハブにガムテープで巻いて走行しました。

結果・・・そこそこ動きました。
サラダ水切り器シミュレータでも40Km/h以上では不安定でしたが、
実走でも、かなり不安定で、しばしば10Km/h以上低く表示されたりと
アルゴリズムを考え直す必要がありそうです。
個人的には、このくらいでも、無いよりマシと感じます。

スマホ表示

0x:0000019A0000000B7C
と数字ばかり並んでますが、これが今回のスピードセンサーから無線Advertise送信されたBLEデバイス名です。
最後のB7Cは、10進数に直すと2940、つまり1回転に2940ミリ秒を要した、という意味です。
これをもとにスピードを算出してます。

このAndroidアプリは、AndroidStudioのBluetooth Le Gattというサンプル・プログラムを改変してます。

AndroidStudioでダウンロードしたサンプルコードですが、Android6以上では、なんと動きません。
サンプル・コードが動かないとか、意味が分からないのですが、なぜかAndroidでは、頻発するので、敷居が高く感じます。
以下のPermissionをmanifestとアクティビティのOnCreateにそれぞれ追加するとAndroid6でも動作しました
<uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    if(Build.VERSION.SDK_INT >= 23)
    {
        this.requestBlePermission();    }




@TargetApi(23)
private void requestBlePermission(){
    if(checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
        requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION        },1);    }
}

今回は、暫定的にAndroidアプリを作りましたが、Androidアプリは、スマホをサイクル・コンピュータとして利用する際に汎用的に利用できるよう、もっと汎用性のあるつくりにしたいと考えております。

今回のスピードセンサー用BL652のソースコード、および
Androidのスピード表示アプリのソースコードを
以下のサイトに置きました(本日の日付をファイル名としています)。

(実際にアクセスする場合、末尾の000を削除して1138で終わるようURLを修正する必要があります。ロボット除けのためで、お手数をお掛けします)

ttps://sites.google.com/site/myfiles1138000

公開ソースコードは備忘録として利用しているため、スピードセンサーに関係ないコード(実験中のパワーメーターのコード)も混ざっており大変読みにくくなっております。











2018年9月20日木曜日

BL652 - BLEマイコンで温度センサー(ソフトウェア編)

BLEモジュールのBL652を用いて、温度を計測しスマホにBLEで送るデバイスを作りました。
BL652は、Noridic社のSDKを使わずにsmartBASICと称する BASIC言語でソフト開発が可能で、開発時間を短縮できます。

今回の温度計測は、BL652のベースであるnRF528チップの内蔵温度計を使用しています。
データシート上、計測誤差が±4度と、温度計としては実用範囲外ですが、Garmin等のサイコンの温度表示もこのレベルかと思います。

smartBASICでは、簡単に温度を計測できる関数がありました。
i=SYSINFO(2024)
これで変数iに温度(摂氏×10倍)が代入されます。



1.任意のテキスト エディタでBASICコードを書きます。
特に開発環境に指定はありません。

















  1. // Definitions
  2. //******************************************************************************
  3. //#define TEMP_MANTISSA 2711
  4. #define TEMP_EXPONENT -2
  5. #define UUID_HEALTH_THERMOMETER_SERVICE 0x1809
  6.  
  7. //#define UUID_HEALTH_THERMOMETER_SERVICE 0x1818
  8.  
  9. #define ENABLE_DEBUG_PRINTS 1
  10. #define BLE_DISCOVERABILITY_GENERAL 2
  11. #define BLE_APPEARANCE 0
  12. #define MAX_DEVNAME_CHRS 20
  13. #define BLE_CHARVAL_AD_TAG 0x16
  14.  
  15. #define ADV_SCAN_IND 2
  16. #define ADV_FILTERPOLICY_ANY 0
  17.  
  18. //Advertise interval
  19. #define ADV_INTERVAL_MS 250
  20. //Advertise timeout
  21. #define ADV_TIMEOUT_MS 0
  22. //Whitelist Policy in Adverts
  23. #define ADV_WHITELIST_FILTER_POLICY ADV_FILTERPOLICY_ANY
  24.  
  25. //******************************************************************************
  26. // Global Variable Declarations
  27. //******************************************************************************
  28. dim rc //Result code
  29. dim devName$ //Device name
  30. dim hChar //Characteristic handle
  31. dim hSvc //Service handle
  32. DIM TEMP_MANTISSA
  33.  
  34. //Example :: BleVSpWrite.sb (See in BL652CodeSnippets.zip)
  35. #define GPIO_TEMP_SENS 3
  36.  
  37. DIM tx$,scRpt$,adRpt$,addr$,hndl,cnt,iToggle,adc
  38. DIM iInterval,iTimerCount,startTick,prevTick,veloTick,iGcounter
  39. iInterval=1
  40. iGcounter=0
  41. DIM tmpdata, data
  42. DIM i
  43. DIM iCrankPass, iPower
  44. //------------------------------------------------------------------------------
  45. // For debugging
  46. // --- rc = result code
  47. // --- ln = line number
  48. //------------------------------------------------------------------------------
  49. Sub AssertRC(rc,ln)
  50. if rc!=0 then
  51. print "\nFail :";integer.h' rc;" at tag ";ln
  52. endif
  53. EndSub
  54. //------------------------------------------------------------------------------
  55. // Register Error Handler as early as possible
  56. //------------------------------------------------------------------------------
  57. sub HandlerOnErr()
  58. if (ENABLE_DEBUG_PRINTS!=0) then
  59. print "\n OnErr - ";GetLastError();"\n"
  60. endif
  61. endsub
  62.  
  63. //------------------------------------------------------------------------------
  64. // This subroutine gets called first
  65. //------------------------------------------------------------------------------
  66. sub OnStartup()
  67.  
  68. endsub
  69.  
  70. //******************************************************************************
  71. // Handler definitions
  72. //******************************************************************************
  73.  
  74. //Example :: TimerRunning.sb
  1. FUNCTION HandlerTimer0()
  2. DIM rc,dvcNme$,nmeWrtble,apprnce,MinConnInt,MaxConnInt,ConnSupTO,sL,sTgt$,tmp$
  3. DIM addr$ : addr$=""
  4.  
  5. DIM discovMode : discovMode=0
  6. DIM advAppearance : advAppearance = 1
  7. DIM maxDevName : maxDevName = 22
  8. DIM advRpt$ : advRpt$=""
  9. DIM scRpt$ : scRpt$=""
  10.  
  11. dim scnRpt$ //Empty scan report
  12.  
  13. //----------------------
  14. nmeWrtble = 0 //Device name will not be writable by peer
  15. apprnce = 768 //The device will appear as a Generic Thermometer
  16. MinConnInt = 500000 //Minimum acceptable connection interval is 0.5 seconds
  17. MaxConnInt = 1000000 //Maximum acceptable connection interval is 1 second
  18. ConnSupTO = 4000000 //Connection supervisory timeout is 4 seconds
  19. sL = 0 //Slave latency--number of conn events that can be missed
  20.  
  21. PRINT "\nSystmp 2024 = ";SYSINFO(2024)
  22.  
  23. i=SYSINFO(2024)
  24.  
  25. //---Advertise
  26.  
  27. SPRINT #tmp$, " ";integer.h' i
  28. tmp$=RIGHT$(tmp$, strlen(tmp$)-4)
  29. sTgt$=tmp$
  30. dvcNme$="temp.="+sTgt$
  31.  
  32. nmeWrtble = 0
  33. apprnce = 768
  34. MinConnInt = 500000
  35. MaxConnInt = 1000000
  36. ConnSupTO = 4000000
  37. sL = 0
  38.  
  39. rc = BleGapSvcInit(dvcNme$,nmeWrtble,apprnce,MinConnInt,MaxConnInt,ConnSupTO,sL)
  40.  
  41. IF BleAdvRptInit(advRpt$, discovMode, advAppearance, maxDevName)==0 THEN
  42. PRINT "\nAdvert report initialised"
  43. endif
  44.  
  45. PRINT BleAdvRptAddUuid16(advRpt$, 0x180F,0x180A, -1, -1, -1, -1)
  46. PRINT BleAdvRptsCommit(advRpt$, scRpt$)
  47.  
  48. //rc =BleScanStart(20000, 0)
  49. PRINT "\n --- New DevName : "; BleGetDeviceName$()
  50. IF BleAdvertStart(0,addr$,25,60000,0)==0 THEN
  51. PRINT "\nAdverts Started\n"
  52. endif
  53.  
  54. ENDFUNC 1 //remain blocked in WAITEVENT
  55. //exit from WAITEVEN if end func ZERO0
  56. //----------------------------MAIN C----------------------------------
  57. ONEVENT EVTMR0 CALL HandlerTimer0
  58.  
  59. //setups------------------------------------------------------------------------------------------------
  60. TIMERSTART(0,1000,1) //4 NG 14 work //start a 1000 millisecond recurring timer
  61. PRINT "\nWaiting for Timer 0"
  62. //Remove resistor
  63. PRINT GpioSetFunc(GPIO_TEMP_SENS, 1, 2)
  64. //Analogue in
  65. PRINT GpioSetFunc(GPIO_TEMP_SENS, 3, 0)
  66.  
  67. WAITEVENT
  68.  
  69. PRINT "\nExiting..."
2.オンラインコンパイラで、BL652(とfirmwareのバージョン)を指定し、上記で書いたBASICのファイルを選択し、XCompileボタンを押すと、ソースがコンパイルされ、実行ファイルがダウンロードされます。
エラーがあると、ブラウザにエラー箇所が表示されます。




Laird社推奨のUwTerminalXを使用して、上記の実行ファイルをBL652に書き込みます。
まず、COMポートの設定を行います。


続いてTerminalタブで右クリックー>LOAD+RUNー>ファイル選択
でBL652に書き込む実行ファイルを指定すると、書き込めます。
 LOADとRUN・・・懐かしい。



書き込みが完了すると、自動的に実行ファイルがRUNされます。
PRINT文の出力は、Terminalに表示されます。


“Systmp 2024=”の値が温度です。(摂氏×10倍)
“New DevName” は、BL652モジュールがBLEデバイスとしてAdvertiseしてる名前です。


↓スマホ側の表示です。
BLEデバイスをスキャンすると、以下のようにtemp.=として温度(摂氏×10倍)が16進数でデバイス名として表示され、プログラム通りにスマホに無線送信(Advertise)されていることが確認できます。



BL652のsmartBASIC開発は、 オンライン・コンパイラを利用するため、面倒なSDKの開発環境を構築する必要がありません。
一見お手軽ですが、オンラインコンパイラが閉鎖されると、BASICでは何も開発できないリスクは感じます。
ただ、最後は、Nordic社の開発環境を利用して、ソフト開発はできるので、購入したBL652がゴミになるリスクはないと思います。


2019/05/24追記

BL652を腕時計型デバイスに組み込めるか。
BL652は、以下のように小型です。
素手ではんだ付けができる限界サイズといえる小ささです。






腕時計型デバイスを作成するにあたり、
今回のようにBLEのAdvertiseによってデータを送信し、
XperiaZシリーズで受信するのであれば、
何ら問題ないとおもいます。
消費電力も小さくCR2032で賄える(2ma以下)で動作可能と記憶してます。

しかし、BL652でAdvertiseし、BL652でスキャン/受信すると
データの受信にタイムラグ(4秒程度)が生じます。
原因は調査中ですが、ややインパクトがあるので、
BL652間通信は、VSP(VirtualSerialProtocol)と称する
BLE上でUART通信する技術を用いることとなるかと思います。
この場合、マイコンのリソース/パワーの多くを無線通信にもっていかれるため、
心拍センサーとのI2C通信が安定的に行えるか、やや不安がございます。

BL652でI2Cは行ったことはございませんが、万一、安定しない場合、
I2Cの制御用にBL652を一つと、無線送信用にBL652をもう一つと、
2CPU体制にすることで解決は可能かと思います。








2018年9月15日土曜日

続・ANT+サイコンの製作

ANT+対応サイコンをDynastream社のD52無線マイコンを用いて組み立ててみました。

↓標準版のD52モジュール
 https://www.digikey.jp/product-detail/ja/dynastream-innovations-inc/D52QD2M4IA-TRAY/1094-1023-ND/6149451
 ↓加速度センサ内蔵バージョンです。
 https://www.digikey.jp/product-detail/ja/dynastream-innovations-inc/D52QD2M4IA-A-TRAY/1094-1024-ND/6149452


前回(2017年5月公開)は、ANTモジュールにBC-ANT-シリアルを利用しましたが、私のような素人には扱いが難しく、不安定になったり動作不能になったりと悩みました。

シリアル通信でコマンドを発行することによりANT+の信号を受信することができるモジュールは、以下の候補があります。
これらはNordic社の難解なSDK環境を構築することなく、シリアルコマンドでANT通信を実現できるので、私のような素人には、お手軽で、開発時間を短縮できるメリットがあります。
- BC-ANT-シリアル
- nRF24AP2
- D52(nRF528ベース)
今回は、D52を選択しました。
上の写真では、D52で受信したANT信号(パワー値283w)を秋月のArduinoUnoに送信、Nokia5110という200円液晶に表示しています。



便宜上、上記のような2システム構成です。
赤枠のシステムは、D52でANT+信号を受信し、ArduinoProMini8Mhz(3.3v動作)でテキストに変換してシリアル送信します。
オレンジ枠のシステムは、上記ArduinoProMiniからテキストを受信し、Nokia5110液晶に表示します。

D52とArduinoProMiniの接続図
電源Vcc, GNDとTx、Rxを接続するだけです。

D52のPINOUT

ArduinoUnoは5v動作のため、D52のような最大電圧3.7v程度のモジュールを接続するにはいちいち電圧を落とす必要があり面倒です。

私は5vが必要なデバイスをほとんともっていないので、秋月の基板のパターンを以下のようにカットして、5vラインを切断、代わりに3.3vを供給し、システム全体を3.3v動作に改造しています。3.3vで16Mhz動作となるので、オーバークロックとなりますが、短時間であれば、動作します。安定性が必要な場合は、クロックダウンが必要です。






今回のD52でANT受信を行うArduinoソースコード、および
Nokia5110で値を表示するArduinoソースコード(別途Nokia5110用ライブラリが必要)を以下のサイトに置きました(本日の日付をファイル名としています)。

(実際にアクセスする場合、末尾の000を削除して1138で終わるようURLを修正する必要があります。ロボット除けのためで、お手数をお掛けします)

ttps://sites.google.com/site/myfiles1138000

なお、ANT通信を行うには、NETWORK KEYを入手してソースコードに書き込む必要があります。
このあたりは信州Makers管理人様が詳しく公開されております。
いつもありがとうございます。
http://maru-yo.net/shinshu_makers/2017/08/15/%E3%80%90%E3%83%91%E3%83%AF%E3%83%BC%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E3%80%91antatmega328p%EF%BC%88%E5%86%85%E8%94%B58mhz%E6%8E%A5%E7%B6%9A%EF%BC%9C5v-3-3v%E3%82%82%EF%BD%8F%EF%BD%8B%EF%BC%9E/