obnizとGPSモジュールで地図(その3)

概要

前回の続き

Google Mapの表示

自分のページにGoogle Mapを埋め込むにはAPI Keyを取得し、使用時に設定する必要がある。 しかし、API Keyを使用しなくても実験用途には使用できる。

その際、次のように明らかに開発用途だとわかるようになっている。

f:id:mickey_happygolucky:20181209184140p:plain
API KeyなしでのGoogle Map

この時、「このページではGoogleマップが正しく読み込まれませんでした。」というダイアログが表示されるがOKをクリックする。

任意の場所を地図で表示

表示したい緯度と軽度がわかれば、次のようにするだけで地図は表示することができる。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Map Test</title>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js"></script>
</head>

<body>
<div id="sample" style="width: 700px; height: 400px"></div>
<script>
    var map;
    $(function () {
        map = new google.maps.Map($('#sample')[0], {
            center: {
                lat: 35.69496, // 緯度
                lng: 139.76746000000003 // 経度
            },
            zoom: 19
        });
    })
</script>

</body>
</html>

ぜんぶくっつけてみる

その1からここまで作ってきたサンプルを組み合わせて、 obnizに接続したGPSモジュールから取得したNMEAをGPS.jsでパースし、Google Mapで表示するプログラムを作ってみる。

obnizとGPSモジュールの接続についてはその1を参照。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Obniz GPS Google Map Sample</title>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://unpkg.com/obniz/obniz.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/infusion/GPS.js@v0.4.7/gps.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js"></script>
</head>
<body>

<div id="obniz-debug"></div>
<div id="sample" style="width: 700px; height: 400px"></div>

<script>
    let gps = new GPS;
    let obniz = new Obniz('OBNIZ_ID_HERE');
    let buffer = '';
    let counter = 0;
    let map = null;

    obniz.onconnect = async function () {
        obniz.io0.output(1); //VCC
        obniz.io3.output(0); //GND

        obniz.display.print('waiting GPS...');
        const uart = obniz.getFreeUart();
        uart.start({tx: 1, rx: 2, baud: 9600});

        uart.onreceive = function (data, text) {
            buffer += text;
            if (++counter % 3 === 0) {
                const lines = buffer.split(/\n/);
                for (const s of lines) {
                    if (s.indexOf('$GPGGA') !== -1) {
                        gps.update(s);
                    }
                }
                buffer = '';
            }
        };
    };

    gps.on('data', function(parsed) {
        const lat = parsed.lat;
        const lon = parsed.lon;
        if (lat != null && lon != null) {
            update_map(lat, lon);
        }
    });

    function update_map(lat, lon) {
        if (map == null) {
            map = new google.maps.Map($('#sample')[0], {
                center: {
                    lat: lat,
                    lng: lon
                },
                zoom: 19
            });
            obniz.display.clear();
            obniz.display.print('GPS ok');
        } else {
            map.setCenter({lat: lat, lng: lon});
        }
    }

</script>

</body>
</html>

UARTで受信したデータは、必ずしも切りの良いところまで受信できているわけではないため、 3回に1回だけパースを行なうようにしている。

実行画面を載せると、住所がモロバレするので今回は載せない。

GPSモジュールが衛星に捕捉され緯度経度が取れるようになるまで数分かかるので、 実行してから気長に待つ必要がある。

obnizとGPSモジュールで地図(その2)

概要

前回の続き

取得したNMEAをパース

GPSモジュールから取得できるのはNMEAというデータのかたまり。

こんな感じ。

$GPTXT,01,01,02,LLC FFFFFFFF-FFFFFFED-FFFFFFFF-FFFFFFFF-FFFFFFF9*50
$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGLL,,,,,,V,N*64
$GPTXT,01,01,02,ANTSTATUS=INIT*25
$GPRMC,,V,,,,,,,,,,N*53

GPSモジュールが衛星に捕捉されると、ここに緯度経度の情報が入ってくるのだが、 これを自前で解析するのは面倒。

そこで、GPS.jsというライブラリを使用させてもらう。

GPS.jsは基本的には、Node.jsで使用することを想定しているようだが、次の通りブラウザからも使用できるようになっている。

Using GPS.js with the browser
The use cases should be rare to parse NMEA directly inside the browser, but it works too.

<script src="gps.js"></script>
<script>
   var gps = new GPS;
   gps.update('...');
</script>

ただ、レアケースだと言っていることと、やはりダウンロードして使うことが前提となっている様子。

Webサーバを構築せずに手軽にやりたいので、これをCDNでアクセスできないか検討する。

jsDeliver

いろいろなJavaScriptのライブラリのCDNを提供しているjsDeliverというありがたいサイトがある。

ここによると、GithubにおいてあるJavaScriptCDNでアクセスできるようになるらしい。

// load any GitHub release, commit, or branch
// note: we recommend using npm for projects that support it
https://cdn.jsdelivr.net/gh/user/repo@version/file

バージョン情報が必要らしい。masterだと実行するタイミングで意図通りに動作するかわからないので、 リリースタイミングのタグかブランチを探してみる。

GPS.jsの人は、リリースごとにきちんとタグを打ってくれる人のようで、最新版は「v0.4.7」というタグが打たれていた。

jsDeliverのGithubルールにGPS.jsのURLを当てはめていくと次のようになる。

<script src="https://cdn.jsdelivr.net/gh/infusion/GPS.js@v0.4.7/gps.min.js"></script>

NMEAから緯度経度を取得してみる

GPS.jsは、GPSクラスを持っており、gps.on()にfunctionを登録しておくとデータが更新されたタイミングでコールバックしてくれる。 データの更新はgps.update()にNMEAを渡して呼び出すタイミングで行われる。 これら踏まえてサンプルを作ってみる。

NMEAの中で緯度経度の情報の取得に必要なのはGPGGAというデータらしい。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>GPS parse</title>
    <script src="https://cdn.jsdelivr.net/gh/infusion/GPS.js@v0.4.7/gps.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>

<div id="obniz-debug"></div>
<textarea id="text" style="width:100%; height:200px; resize:none;"></textarea>

<script>
   var gps = new GPS;

   // Add an event listener on all protocols
   gps.on('data', function(parsed) {
       var text = 'lat :' + parsed.lat + ', lon :' + parsed.lon;
       var textarea = $('#text');
       textarea.text(textarea.text() + text);
       textarea.scrollTop(textarea[0].scrollHeight);
   });

   gps.update('$GPGGA,224900.000,4832.3762,N,00903.5393,E,1,04,7.8,498.6,M,48.0,M,,0000*5E');
</script>

</body>
</html>

今回は適当なデータを直に埋め込んでいる。

実行画面

f:id:mickey_happygolucky:20181209093303p:plain
実行画面

緯度と経度がそれぞれ取得できた。

obnizとGPSモジュールで地図(その1)

obniz

obnizはWifi経由でクラウドに常時接続され、クラウド経由で操作するマイコンボード。 通常のマイコンボードと異なるのはクラウド経由で操作するため、obnizにプログラムを書き込んだりしないということ。

JavaScript SDKを使用すると、ブラウザやNode.jsでobnizを操作することができる。 また、RESTFulなAPIやWeb Socketで、JSONを送ることで操作することもできるので、 PythonRubyなどJavaScript以外の言語でも使用することができる。

obnizは12のフレキシブルなIOを持っており、LEDやUART、SPIなどの機器を接続することができる。 各IOは最大1A流せるため、モータードライバなしでモーターを直に制御することができる。

その他、BLEやオンボードのOLEDを操作することもできる。

詳細はここを参照されたい。

概要

雑誌向けネタがボツったのでブログネタに。

でも近いうちに、obnizを使った記事は少しだけ書かせてもらう予定。(その時は宣伝します)

  • GPSから緯度経度を取得(イマココ)
  • GPSから取得したNMEAはGPS.jsでパース
  • 取得した緯度経度でGoogle Mapに表示

obnizでGPSモジュールの情報を取得

GPSモジュール

使用するGPSモジュールはGY-GPS6MV2

これはUARTでデータを取得することができる。

筆者はAmazonで購入した。

obnizはUARTを最大2つ使用できるので、このモジュールからNMEAを取得する。

obnizとGPSモジュールのサンプル

obnizとGPSモジュールの接続は次のとおり。

f:id:mickey_happygolucky:20181208103047p:plain
obnizとGPSモジュールの接続

GPSモジュールから取得したNMEAのデータをテキストエリアに出力するサンプルを次に示す。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Obniz Test</title>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://unpkg.com/obniz/obniz.js"></script>
</head>
<body>

<div id="obniz-debug"></div>
<textarea id="text" style="width:100%; height:200px; resize:none;"></textarea>

<script>
    const obniz = new Obniz('OBNIZ_ID_HERE'); //ここに8桁のIDを設定。
    obniz.onconnect = async function () {
        obniz.io0.output(1); //VCC
        obniz.io3.output(0); //GND

        const uart = obniz.getFreeUart();
        uart.start({tx:1, rx:2, baud:9600});

        uart.onreceive = function (data, text) {
            const textarea = $('#text');
            textarea.text(textarea.text() + text);
            textarea.scrollTop(textarea[0].scrollHeight);
        };
    }
</script>

</body>
</html>

OBNIZ_ID_HEREのところに、8桁のobniz IDを設定すると、そのobnizを操作することができる。 OBNIZ_ID_HEREのままにしておくと、実行時にobniz IDを入力するようにダイアログが出る。

これをブラウザで実行するとこのようになる。

f:id:mickey_happygolucky:20181208103212p:plain
実行画面

ラズベリーパイ3 Yoctoでdebパッケージを使用する

Yoctoでは、デフォルトでrpmを使用してレシピごとのパッケージを作成し、 OSイメージを作成する段階で、それらをインストールすることでソフトをインストールしている。

core-image-minimalやcore-image-base、rpi-basic-imageの最小構成のイメージでは、 パッケージのインストール情報がルートファイルシステムに保存されないため、 コマンドとしてrpmやdpkgをインストールしてあっても、パッケージを正しくあつかえない。

パッケージのインストール情報をルートファイルに保存し、 パッケージシステムをdebに変更して、 yoctoで作成したOSでRaspbian向けのパッケージを使用かどうかを調査したのでメモ。

結論を先に書くとdpkgは使用できるが、Raspbian向けのパッケージは実質使用できない。

armhfの野良debは使用できるかもしれない。

パッケージのインストール情報

パッケージのインストール情報をルートファイルシステムに保存するには、 (EXTRA_)IMAGE_FEATURES変数にpackage-managementを追加する。

この値を追加することで、パッケージのインストール情報と、パッケージシステムで使用するコマンド(rpmとかdpkg)も一緒にインストールされる。

local.confで設定する場合は次のようにする。

EXTRA_IMAGE_FEATURES_append = " package-management"

パッケージシステムをdebに変更

パッケージシステムをデフォルトのrpmからdebに変更するには、 PACKAGE_CLASSES変数の値をpackage_debに設定する。

local.confに次の行を追加する。

PACKAGE_CLASSES = "package_deb"

ルートファイルシステムの容量を拡張

デフォルトのままでは、ルートファイルシステムのイメージは、 必要最低限のサイズでパーティショニングされるため、少し余裕を持たせる。

local.confに次の内容を追加する。

IMAGE_ROOTFS_EXTRA_SPACE = "2097152"

これで、2GiBの容量でイメージが作成される。

core-image-baseで動作確認

いつもの手順でcore-image-baseをイメージを作成し、ラズベリーパイ3を起動する。

次のコマンドを実行しdebパッケージのインストール情報が保存されているかを確認する。

# dpkg -l
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name           Version      Architecture Description
+++-==============-============-============-=================================
ii  alsa-conf      1.1.6-r0     armhf        ALSA sound library
ii  alsa-state     0.2.0-r5     armhf        Alsa scenario files to enable als
ii  alsa-states    0.2.0-r5     armhf        Alsa scenario files to enable als
ii  alsa-utils-als 1.1.6-r0     armhf        Saves/restores ALSA-settings in /
ii  alsa-utils-als 1.1.6-r0     armhf        ncurses-based control for ALSA mi
ii  apm            3.2.2-15-r0  armhf        Utilities for Advanced Power Mana
...(snip)...

これでビルド済みのarmhf向けdebパッケージを使用することができる。

Raspbianのパッケージを使用できるか?

sources.listを追加

/etc/apt/sources.list.d/stretch.listを次の内容で作成する。

deb http://ftp.jaist.ac.jp/raspbian/ stretch main contrib non-free rpi

# deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://archive.raspbian.org/raspbian/ stretch main contrib non-free rpi

aptでパッケージを更新

パッケージを更新する。

# apt-get update
W: GPG error: http://ftp.jaist.ac.jp/raspbian stretch InRelease: Unknown error executing apt-key
W: The repository 'http://ftp.jaist.ac.jp/raspbian stretch InRelease' is not signed.
N: Data from such a repository can't be authenticated and is therefore potentially dangerous to use.
N: See apt-secure(8) manpage for repository creation and user configuration details.

GPGのキー関連で警告が出ている。

# apt-get upgrade
...(snip)...
Do you want to continue? [Y/n]
...(snip)...
Install these packages without verification? [y/N] y
...(snip)...
Unpacking busybox (1:1.22.0-19) over (1.29.2-r0) ...
dpkg (subprocess): unable to execute rm command for cleanup (rm): No such file or directory
dpkg: error while cleaning up:
 subprocess rm command for cleanup returned error exit status 2
dpkg (subprocess): unable to execute rm command for cleanup (rm): No such file or directory
dpkg: error processing archive /var/cache/apt/archives/libusb-1.0-0_2%3a1.0.21-1_armhf.deb (--unpack):
 subprocess rm command for cleanup returned error exit status 2
Errors were encountered while processing:
 /var/cache/apt/archives/libusb-1.0-0_2%3a1.0.21-1_armhf.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

アップグレードを実行するとエラー。

結論

パッケージに収録されているファイルの構成などが異なっているため、 あると思って削除しに行ったら見つからないなどのエラーが発生する。

また、perl関連のパッケージなどで、yoctoとRaspbianでパッケージ名が一致しないものなどがあり、 ファイルがコンフリクトしたりするケースもある。

これらの結果から、Raspbian向けのパッケージを使用することは実質できないと思ったほうが良い。

armhf向けの野良パッケージをインストールするのに使えるかなぁという感じ。

ラズベリーパイ3でthudを試す

yoctoの最新版がやっとリリースされた。バージョンは2.6で、コードネームは「Thud」.

早速、ラズベリーパイ3で動かして見る。

環境構築

いつものやつ。

INITはsystemdで。

ソース取得

$ mkdir -p rpi-thud/layers
$ cd rpi-thud/layers
$ git clone git://git.yoctoproject.org/poky.git -b thud
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b thud
$ git clone git://git.openembedded.org/meta-openembedded -b thud
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi/

local.confの修正

MACHINEの行を修正する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

ビルド

$ bitbake core-image-base

最小限のイメージを作成する場合、 これまではrpi-basic-imageを使用していたが、今後はcore-image-baseを使用する。

出来上がったイメージをddなどでSDカードに書き込む。

sudo dd if=tmp/deploy/images/raspberrypi3/core-image-base-raspberrypi3.rpi-sdimg of=/dev/sdX bs=100M

sdXは環境によりsdbやsdcなどになる。

動作確認

作成したSDカードをラズベリーパイ3に挿入し電源を入れる。

ログイン後次のコマンドを実行し、OSを確認する。

# cat /etc/os-release
ID="poky"
NAME="Poky (Yocto Project Reference Distro)"
VERSION="2.6 (thud)"
VERSION_ID="2.6"
PRETTY_NAME="Poky (Yocto Project Reference Distro) 2.6 (thud)"

thudが動作していることが確認できる。

yoctoでセルフコンパイル環境を作成する

yoctoではクロスコンパイル用のSDKを簡単に作成することができる。

実はセルフコンパイル環境もさほど難しくなく作成できるが、こちらはあまり知られていない気がする。

ロスコンパイルできる環境があれば、PCのほうがリソースはリッチなのでコンパイル時間も短くて住むので、セルフコンパイル環境を構築する必要性を感じないかもしれない。

しかし、Makefileなどがセルフコンパイルにしか対応していない場合などで、 クロスコンパイル対応が難しい場合など、手っ取り早くセルフコンパイルしたい場合がある。

(EXTRA_)IMAGE_FEATURESを設定することで、セルフコンパイル環境が作成できることを調査したのでメモ。

環境構築

ラズベリーパイ3向けの環境。いつものやつ。

ソース取得

$ mkdir -p rpi-sumo/layers
$ cd rpi-sumo/layers
$ git clone git://git.yoctoproject.org/poky.git -b sumo
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b sumo
$ git clone git://git.openembedded.org/meta-openembedded -b sumo
$ cd ../

環境変数設定

$ source layers/poky/oe-init-build-env build

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi/

local.confの修正

MACHINEの行を修正する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

セルフコンパイル環境の調査

セルフコンパイル環境を含んでいると見られるイメージには次の2つがある。

  • core-image-lsb-sdk
  • core-image-sato-sdk

これらにはそれぞれ「-sdk」がついていないイメージがある。

  • core-image-lsb
  • core-image-sato

「bitbake -e」で「IMAGE_FEATURES」変数を見てみる。

$ bitbake core-image-lsb -e | grep '^IMAGE_FEATURES='
IMAGE_FEATURES="debug-tweaks hwcodecs package-management splash ssh-server-openssh"

$ bitbake core-image-lsb-sdk -e | grep '^IMAGE_FEATURES='
IMAGE_FEATURES="debug-tweaks dev-pkgs eclipse-debug hwcodecs package-management splash ssh-server-openssh tools-debug tools-profile tools-sdk tools-testapps"

$ bitbake core-image-sato -e | grep '^IMAGE_FEATURES='
IMAGE_FEATURES="debug-tweaks hwcodecs package-management splash ssh-server-dropbear x11-base x11-sato"

$ bitbake core-image-sato-sdk -e | grep '^IMAGE_FEATURES='
IMAGE_FEATURES="debug-tweaks dev-pkgs eclipse-debug hwcodecs package-management splash ssh-server-openssh tools-debug tools-profile tools-sdk tools-testapps x11-base x11-sato"

それぞれのイメージで「-sdk」によって追加されるは次のようになっている。

  • dev-pkgs
  • eclipse-debug
  • tools-debug
  • tools-profile
  • tools-sdk
  • tools-testapp

「core-image-base-sdk」のようなイメージは標準では存在しないので、「core-image-base」に対して「sdk」相当の機能を追加するには、これら6つの機能を「IMAGE_FEATURES」に追加してやればよい。 ただしlocal.confからは「IMAGE_FEATURES」変数を操作することは推奨されていない1ので「EXTRA_IMAGE_FEATURES」変数に追加する。

local.confに次の内容を追加する。

EXTRA_IMAGE_FEATURES_append = " dev-pkgs eclipse-debug tools-debug tools-profile tools-sdk tools-testapp"

エラー

ERROR: Nothing PROVIDES 'core-image-base'
core-image-base was skipped: 'tools-testapp' in IMAGE_FEATURES (added via EXTRA_IMAGE_FEATURES) is not a valid image feature. Valid features: allow-empty-password dbg-pkgs debug-tweaks dev-pkgs doc doc-pkgs eclipse-debug empty-root-password hwcodecs nfs-client nfs-server package-management post-install-logging ptest-pkgs read-only-rootfs splash ssh-server-dropbear ssh-server-openssh staticdev-pkgs tools-debug tools-profile tools-sdk tools-testapps x11 x11-base x11-sato

tools-testappが原因でエラーが発生したので削除する。

EXTRA_IMAGE_FEATURES_append = " dev-pkgs eclipse-debug tools-debug tools-profile tools-sdk"

その他のツール

cmakeやgitなどはインストールされないので、必要に応じて追加する。

IMAGE_INSTALL_append = " git cmake"

  1. best practices dictate using IMAGE_FEATURES from within a recipe and using EXTRA_IMAGE_FEATURES from within your local.conf file, which is found in the Build Directory.

Termux環境にpycurlをインストールする

そんなに需要はない情報だとおもう。

Pydroid環境にpycurlがインストールできなかったので、Termux環境にpycurlを入れようとしたらハマった。

pythonのインストール

まずは、pythonをインストール。pipだとビルドが走ったりするので、clangも一緒に。

$ pkg install python
$ pkg install clang python-dev

pycurlのインストール(失敗)

とりあえず、インストールしてみる。

$ pip install pycurl

crypt.hがないとか、SSLがないとかいろいろエラーがでる。

エラー回避

必要そうなパッケージを追加して、ビルド時だけ必要な環境変数を設定する。

$ pkg install libcurl-dev
$ pkg install libcrypt-dev
$ pkg install openssl-dev
$ export PYCURL_SSL_LIBRARY=openssl

pycurlのインストール

お願い入ってくれ。

$ pip install pycurl

どうやらうまく行ったらしい。