読者です 読者をやめる 読者になる 読者になる

アルペンスキータイミングシステム開発中

アルペンスキー用のタイム測定器を開発しようとしています。

node wiring-piでsuperagentが動かない

早速nodeでRESTでデータを送信しようと思ったらwiring-piの割り込みからsuperagentでの送信がうまくいかない。

仕方がないので他を探してみると結構ある。

  • onoff
  • gpio
  • ri-gpio
  • rpi-gpio
  • rpio

onoff

延滞も問題なし。superagentの動作も問題ないのでこれで決まり。

2016-05-27 07:54:02.0010 (739)
2016-05-27 07:54:03.0000 (934)
2016-05-27 07:54:04.0000 (996)
2016-05-27 07:54:05.0000 (1000)
2016-05-27 07:54:06.0000 (1000)
2016-05-27 07:54:07.0000 (999)
2016-05-27 07:54:08.0000 (999)
2016-05-27 07:54:09.0000 (999)
2016-05-27 07:54:10.0000 (999)

gpio

www.npmjs.com

EventEmitterを使ってることからあんまり期待しなかったが延滞が500ms位ある。 root無しでも動作する。

2016-05-26 22:22:25.0380 (1140)
2016-05-26 22:22:26.0420 (944)
2016-05-26 22:22:27.0510 (1001)
2016-05-26 22:22:28.0520 (1000)
2016-05-26 22:22:29.0610 (1008)
2016-05-26 22:22:30.0670 (1005)

pi-gpio

www.npmjs.com

割り込みに対応していないので評価無し

rpi-gpio

延滞が10msぐらい。

2016-05-26 23:06:31.0060 (692)
2016-05-26 23:06:32.0010 (997)
2016-05-26 23:06:33.0010 (1000)
2016-05-26 23:06:34.0010 (1000)
2016-05-26 23:06:35.0010 (1000)
2016-05-26 23:06:36.0010 (1000)
2016-05-26 23:06:37.0010 (1000)
2016-05-26 23:06:38.0010 (1000)

rpio

延滞は少ないが致命的な問題がある。

2016-05-26 23:10:37.0000 (294)
2016-05-26 23:10:38.0000 (943)
2016-05-26 23:10:39.0010 (997)
2016-05-26 23:10:40.0000 (999)
2016-05-26 23:10:41.0010 (1001)
2016-05-26 23:10:42.0000 (999)
2016-05-26 23:10:43.0000 (999)
2016-05-26 23:10:44.0010 (1000)
2016-05-26 23:10:45.0000 (998)
2016-05-26 23:10:46.0000 (999)

しかしこれを起動するとログに下のようなのが出てそのあとスタックダンプが表示される。その後chronyの時間がおかしくなるのでgpioの割り込みのirqが重複しているのか?

kernel: [29796.056340] irq 79: nobody cared (try booting with the "irqpoll" option)
kernel: [29796.056357] CPU: 0 PID: 5020 Comm: node Not tainted 4.4.10-v7+ #2
kernel: [29796.056362] Hardware name: BCM2709
.....
kernel: [29796.056581] [<8033f5f8>] bcm2835_gpio_irq_handler
kernel: [29796.056587] Disabling IRQ #79
chronyd[878]: Selected source GPS
chronyd[878]: Selected source PPS1
chronyd[878]: Selected source GPS
chronyd[878]: Selected source PPS1
chronyd[878]: Selected source GPS

wiringPiで割り込み比較

gpioで入力を受けるにはポーリングでは遅すぎるので割り込みを使いたい。

WiringPiを使うのが一般的なようで割り込みも使える。 ライブラリもC,python,nodeがあるのでそれぞれ試してみる。

gpsのppsの立ち上がりを検出してみる。

C

ソース

#include <wiringPi.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

static volatile int int_count = 0;
static struct timespec  now, prev;

static void int_test(void)
{
    int_count++;
    timespec_get(&now, TIME_UTC);

    struct tm tm = *localtime(&now.tv_sec);

    long nsec = now.tv_nsec - prev.tv_nsec + 1000000000 * (now.tv_sec - prev.tv_sec);

    printf("%d-%d-%d %02d:%02d:%02d.%09ld (%ld)\n",
        tm.tm_year + 1900,
        tm.tm_mon + 1,
        tm.tm_mday,
        tm.tm_hour,
        tm.tm_min,
        tm.tm_sec,
        now.tv_nsec,
        nsec);

    prev = now;

    //fflush(stdout);
}

int main(void)
{
    printf("start...\n");

    if (wiringPiSetupGpio())
        return 1;

    pinMode(6, INPUT);
    wiringPiISR(6, INT_EDGE_RISING, int_test);

    while (1)
    {
        sleep(1);
    }


    printf("exit\n");

    return 0;
}

結果

start...
2016-5-26 09:55:43.000089185 (-1424870815)
2016-5-26 09:55:43.000530170 (440985)
2016-5-26 09:55:44.000102998 (999572828)
2016-5-26 09:55:45.000085196 (999982198)
2016-5-26 09:55:46.000109009 (1000023813)
2016-5-26 09:55:47.000088967 (999979958)
2016-5-26 09:55:48.000084343 (999995376)
2016-5-26 09:55:49.000106124 (1000021781)
2016-5-26 09:55:50.000088388 (999982264)
2016-5-26 09:55:51.000086608 (999998220)
2016-5-26 09:55:52.000078772 (999992164)
2016-5-26 09:55:53.000079524 (1000000752)
2016-5-26 09:55:54.000100432 (1000020908)
2016-5-26 09:55:55.000085508 (999985076)
2016-5-26 09:55:56.000083448 (999997940)
2016-5-26 09:55:57.000100346 (1000016898)
2016-5-26 09:55:58.000082192 (999981846)
2016-5-26 09:55:59.000082945 (1000000753)
2016-5-26 09:56:00.000106041 (1000023096)

誤差40usぐらいには入ってそう。延滞は100usぐらい。 思ったよりかなりいい。

Python

ソースs

#!/usr/bin/python

import RPi.GPIO as GPIO
import time
import datetime

cnt = 0
led = 26
#pin_in = 17
pin_in = 6
prev = datetime.datetime.now();

def btn_rised(v):
        global cnt, prev
        GPIO.output(led, 1);
        time.sleep(0.01);
        GPIO.output(led, 0);
        now = datetime.datetime.now();
        diff = now - prev;

        print(str(now) + ' (' + str(diff) + ')')
        cnt += 1
        prev = now


GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(led, GPIO.OUT)
GPIO.output(led, 0)

GPIO.setup(pin_in, GPIO.IN, GPIO.PUD_UP)
GPIO.add_event_detect(pin_in, GPIO.RISING, btn_rised)

while True:
        time.sleep(1)


GPIO.output(led, 0)
GPIO.remove_event_detect(pin_in)

GPIO.cleanup()

結果

2016-05-26 10:03:21.010254 (0:00:00.053590)
2016-05-26 10:03:22.010158 (0:00:00.999904)
2016-05-26 10:03:23.010156 (0:00:00.999998)
2016-05-26 10:03:24.010252 (0:00:01.000096)
2016-05-26 10:03:25.010223 (0:00:00.999971)
2016-05-26 10:03:26.010223 (0:00:01)
2016-05-26 10:03:27.010220 (0:00:00.999997)
2016-05-26 10:03:28.010220 (0:00:01)
2016-05-26 10:03:29.010223 (0:00:01.000003)
2016-05-26 10:03:30.010230 (0:00:01.000007)
2016-05-26 10:03:31.010224 (0:00:00.999994)
2016-05-26 10:03:32.010226 (0:00:01.000002)
2016-05-26 10:03:33.010231 (0:00:01.000005)
2016-05-26 10:03:34.010222 (0:00:00.999991)
2016-05-26 10:03:35.010234 (0:00:01.000012)

誤差こちらも誤差は40usくらいだが延滞は100msもある。

Node

ソース

var wpi = require('wiring-pi');
var moment = require('moment');

wpi.setup('gpio');
wpi.pinMode(6, wpi.INPUT);
wpi.pullUpDnControl(6, wpi.PUD_UP);
wpi.wiringPiISR(6, wpi.INT_EDGE_RISING, function(delta) {
  console.log(moment().format("YYYY-MM-DD HH:mm:ss.SSSS") + ' (' + delta + ')');
});

結果

2016-05-26 10:06:05.0470 (44)
2016-05-26 10:06:06.0000 (999925)
2016-05-26 10:06:07.0000 (999997)
2016-05-26 10:06:08.0000 (1000003)
2016-05-26 10:06:09.0000 (1000033)
2016-05-26 10:06:10.0000 (1000003)
2016-05-26 10:06:11.0000 (999991)
2016-05-26 10:06:12.0000 (999997)
2016-05-26 10:06:13.0000 (999998)
2016-05-26 10:06:14.0000 (1000015)
2016-05-26 10:06:15.0000 (1000002)
2016-05-26 10:06:16.0000 (1000005)
2016-05-26 10:06:17.0000 (999996)
2016-05-26 10:06:18.0000 (999997)
2016-05-26 10:06:19.0000 (999995)

これだけ見ると誤差20us位に収まってる。延滞はこれだけ見ると0だけどたぶんCと同じ。 スクリプトも簡単だしすごいな。

次はstressで負荷をかけてみる。

$ stress --cpu 4 --vm 4 --io 4 --timeout 10s

やはりスクリプトは簡単に止まるけどCはなかなか粘る。 けど止まるときは止まる。

いろいろ調べると優先度をあげたりするといいようなのでやってみる。 似たようなことをされている人がいるので次のサイトを参考にさせていただいた。

www.homu.net

#include <wiringPi.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sched.h>
#include <string.h>
#include <sys/mman.h>

static volatile int int_count = 0;
static struct timespec  now, prev;
static int state = 0;

static void int_test(void)
{
        int_count++;
        timespec_get(&now, TIME_UTC);
        
        struct tm tm = *localtime(&now.tv_sec);

        state = !state;
        digitalWrite(26, 1);
        delay(100);
        digitalWrite(26, 0);

        long nsec = now.tv_nsec - prev.tv_nsec + 1000000000 * (now.tv_sec - prev.tv_sec);

        printf("%d-%d-%d %02d:%02d:%02d.%09ld (%ld)\n", 
                tm.tm_year + 1900,
                tm.tm_mon + 1,
                tm.tm_mday,
                tm.tm_hour,
                tm.tm_min,
                tm.tm_sec,
                now.tv_nsec,
                nsec);

        prev = now;

        //fflush(stdout);
}

int main(void)
{
        printf("start...\n");

        struct sched_param param;
        param.sched_priority = 99;
        if(sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
                return 1;
        }

        if(mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
                return 2;
        }


        if (wiringPiSetupGpio())
                return 1;

        pinMode(6, INPUT);
        wiringPiISR(6, INT_EDGE_RISING, int_test);

        while (1)
        {
                sleep(1);
        }
        
        printf("exit\n");

        return 0;
}

優先度を変えてもあまり変わらない。それよりmlockallでスワップアウトしなくすると割り込みを取りこぼすことがほとんどない。

Raspberry Pi 3 にDS3234SでRTCを

gps & ntpで時刻を取得できたので手元にrtcがあったので接続してみた。

www.maximintegrated.com

けど結構面倒だった。手順は次のページの通りだが、オーバーレイの設定ふぁるの名前が違っていてそのままだとオーバーレイがうまくいかない。

Raspberry PI and your SPI Real Time Clock (RTC) | ScienceGizmo

ds3234-rpi-overlay.dtsを作れとあるがds3234-rpi.dtsにする。

Makefileを次のようにする

DTC=dtc

all: ds3234-rpi.dtb

ds3234-rpi.dtb: ds3234-rpi.dts
        $(DTC) -@ -I dts -O dtb -o ds3234-rpi.dtbo ds3234-rpi.dts

install-ds3234-rpi.dtbo: ds3234-rpi.dtbo
        cp ds3234-rpi.dtbo /boot/overlays/

install: install-ds3234-rpi.dtbo

clean:
        rm *.dtbo

あとログも大丈夫ですべてOKなように見えても書き込みが出来ない状態に最初なった。 syslogを見るとControl RegとCtrl/Stat Regの値が0x1c、0x88以外になったいる場合は初期化がうまくいっていない時。いくらhwclockとやっても2000年1月1日からの日付しか来ない。

[    4.440687] ds3234 spi0.0: Control Reg: 0x1c
[    4.440768] ds3234 spi0.0: Ctrl/Stat Reg: 0x88

今回はDS3234のCSラインにプルアップを付けることで解消できた。多分ハイインピーダンスの時に信号が振れてゴミがたまったようだ。