20190218のLinuxに関する記事は7件です。

Ubuntuで付箋を使いたい

(初めてQiitaに投稿します?)

Ubuntuで付箋使いたい!

今の時代手帳もデジタルになってしまいつつあり(私はもうデジタル派です)
付箋ももうデジタルにしちゃいたいですね。
特にいつも使うノートパソコン(私はUbuntu16.04)のデスクトップに
表示させておくと便利ですね。
(図書館の本の返却期限とかレポートの期限をよく忘れてしまう...)

そして、Ubuntuで使えるなにか良い付箋ないか探してみて、
indicator-stickynotes
というのがありました。

以下のサイトで分かりやすく、インストール方法もまとめてくれていた!

デスクトップでUbuntuを使っています

"""
$ sudo add-apt-repository ppa:umang/indicator-stickynotes
$ sudo apt update
$ sudo apt install indicator-stickynotes
"""

これでUbuntuにインストールする事ができます。
(最新のUbuntu18.04はできるか分かりません。)

あとは、簡単に上のバーから選んで、書くだけです。
本当に簡単です。
皆さんも使ってみて下さい。

Screenshot from 2019-02-18 23-41-33.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インターフェイス社ドライバの再コンパイル

この記事について

インターフェイス社のI/Oボードを使いたかったが,コンパイルしなおす必要があったのでその時のメモ。
RTAIの記事の方法で作った環境で,DAボードやカウンタボードを
使えるようにドライバのソースをあれこれ書き換えて悪戦苦闘した結果,何とか動作しました。
(実は,微妙に動作が怪しいのですが・・・)

環境など

  • PC
    • CPU: Intel pentium 4
    • RAM 1024GB
    • HDD: 500GB
    • Ubuntu 16.04 LTS (i386)
      • RTAI 5.1 適用済み
      • Kernel 4.9.80
  • Interface社のドライバ
    • gpg3100 Ver. 5.60-46 (ADボード用)
    • gpg3300 Ver. 4.70-43 (DAボード用)
    • gpg6204 Ver. 4.40-20 (カウンタボード用)

ソース修正とインストール

こちらのサイトをベースに行っていきます。
ただし,このサイトではKernel 4.4.0を利用しており,Kernel 4.9.80では一部関数が変更されておりそのまま使えなかったです。

dpg0100 (共通用ドライバ)

Makefileについては,参考サイトそのままでOKです。

Makefile.dpg0100
obj-m   := dpg0100.o
KDIR    := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

install:
    $(MAKE) -C $(KDIR) M=$(PWD) modules_install

問題はソースファイルの方で,パッチを当ててもエラーが出ます。
エラー文を読む限り,どうやら,get_user_pages()の引数定義が変更されたことと,
page_cache_release()という関数が削除されたことが原因らしいです。

そこで,Bootlinというサイトから,Linuxカーネルのソースのヘッダファイルを確認することにした。

get_user_pages()

まずget_user_pagesについて定義を見ると,4.9.80では,

long get_user_pages(unsigned long start, unsigned long nr_pages,
unsigned int gup_flags, struct page **pages,
struct vm_area_struct **vmas);

(From https://elixir.bootlin.com/linux/v4.9.80/source/include/linux/mm.h#L1281)

となっている。一方で,元々ドライバが対応している2.6.33では,

int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
unsigned long start, int nr_pages, int write, int force,
struct page **pages, struct vm_area_struct **vmas);

(From https://elixir.bootlin.com/linux/v2.6.33/source/include/linux/mm.h#L836)

となっていました。
ここで,対応関係が不明なのが,4.9.80のgup_flagsと2.6.33のwriteforceです。
調べてみるとどうやら,ビット演算でFOLL_WRITEFOLL_FORCEのORを取ったものをgup_flagsに格納すればよい模様。

page_cache_release()

次に,page_cache_releaseについて定義を見ると,2.6.33では,

#define page_cache_release(page) put_page(page)
(From https://elixir.bootlin.com/linux/v2.6.33/source/include/linux/pagemap.h#L84)

となっており,ただ別名としてマクロを組んでいるだけのようでした。
put_pages()関数については4.9.80にも存在したので,こちらに置き換えるだけで良さそうです。

パッチファイル

ということで,以上を反映した結果コンパイルが通りました。

dpg0100_v4980.patch
--- dpg0100.c.orig  2013-06-19 18:22:34.000000000 +0900
+++ dpg0100.c   2019-02-18 00:34:38.363152823 +0900
@@ -16,7 +16,7 @@
 #include <linux/pci.h>
 #include <linux/sched.h>
 #include <asm/delay.h>
-#include <asm/system.h>
+// #include <asm/system.h>
 #include <linux/hdreg.h>
 #include <linux/slab.h>

@@ -228,7 +228,8 @@
 }
 int gpg_MINOR_f(void *file)
 {
-   return MINOR(((struct file *)file)->f_dentry->d_inode->i_rdev);
+// return MINOR(((struct file *)file)->f_dentry->d_inode->i_rdev);
+   return iminor(file_inode((struct file *) file));
 }
 void *gpg_get_file_private_data(void *file)
 {
@@ -974,15 +975,26 @@
    }

    down_read(&current->mm->mmap_sem);
+   /*
    ret = get_user_pages(current, current->mm, (Buffer & PAGE_MASK),
        mdl->NumberOfPages, Direction, 1,
        (struct page **)mdl->PageList, NULL);
+   */
+   if(Direction == 1) {
+       ret = get_user_pages((Buffer & PAGE_MASK), mdl->NumberOfPages,
+                        FOLL_FORCE | FOLL_WRITE, (struct page **)mdl->PageList, NULL);
+   } else {
+       ret = get_user_pages((Buffer & PAGE_MASK), mdl->NumberOfPages,
+                        FOLL_FORCE, (struct page **)mdl->PageList, NULL);
+   }
+   
    up_read(&current->mm->mmap_sem);
    if(ret < mdl->NumberOfPages) {
        /* failed to lock pages */
        if(ret > 0) {
            for(i = 0; i < ret; i++) {
-               page_cache_release((struct page *)mdl->PageList[i]);
+               //page_cache_release((struct page *)mdl->PageList[i]);
+               put_page((struct page *)mdl->PageList[i]);
            }
            kfree(mdl->PageList);
            mdl->PageList = NULL;
@@ -1004,7 +1016,8 @@
            if(!PageReserved((struct page *)mdl->PageList[i])) {
                SetPageDirty((struct page *)mdl->PageList[i]);
            }
-           page_cache_release((struct page *)mdl->PageList[i]);
+           //page_cache_release((struct page *)mdl->PageList[i]);
+           put_page((struct page *)mdl->PageList[i]);
        }
        kfree(mdl->PageList);
        mdl->PageList = NULL;

dpg0101 (セットアップ用ドライバ)

ここは,修正なしでもエラーなくコンパイルが終了します。
ただし,file_operations構造体.unlocked_ioctlの型がlongで定義されていますが,ドライバソースではint型ですので警告が表示されます。
一応,以下のように変更すると警告はなくなりました。

dpg0101_v4980.patch
--- dpg0101.c   2019-02-15 17:33:55.076551578 +0900
+++ dpg0101.c.orig  2013-11-13 15:48:12.000000000 +0900
@@ -323,8 +323,8 @@
 /*  ioctl system call : ifdevmgr_ioctl                         */
 /* ******************************************************** */
 #if (LINUX_VERSION_CODE >= VERSION(2,6,36))
-long ifdevmgr_ioctl(struct file *file, unsigned int iocmd, unsigned long ioarg)
-#else
+int ifdevmgr_ioctl(struct file *file, unsigned int iocmd, unsigned long ioarg)
+#else
 int ifdevmgr_ioctl(struct inode *inode, struct file *file, unsigned int iocmd, unsigned long ioarg)
 #endif
 {

gpg3100 (ADボード用ドライバ)

ここでも,gpg0100と同様の変更を追加しました。

gpg3100_v4980.patch
--- ./ad_entry.c.orig   2016-07-12 15:59:36.000000000 +0900
+++ ./ad_entry.c    2019-02-18 01:08:56.902702284 +0900
@@ -592,6 +592,7 @@
    /* Try to fault in all of the necessary pages */
    down_read(&current->mm->mmap_sem);

+   /*
    ret = get_user_pages(
        current, 
        current->mm, 
@@ -601,14 +602,25 @@
        1,
        (struct page **)mdl->PageList, 
        NULL);
+   */
+
+   if(Direction == WRITE) {
+       ret = get_user_pages((Buffer & PAGE_MASK), mdl->NumberOfPages,
+                        FOLL_FORCE | FOLL_WRITE, (struct page **)mdl->PageList, NULL);
+   } else {
+       ret = get_user_pages((Buffer & PAGE_MASK), mdl->NumberOfPages,
+           FOLL_FORCE, (struct page **)mdl->PageList, NULL);
+   }
+
    up_read(&current->mm->mmap_sem);
-   
+
    /* Errors and no page mapped should return here */
    if (ret < mdl->NumberOfPages) {
        /* failed to lock pages */
        if (ret > 0) {
            for (i = 0; i < ret; i++) {
-               page_cache_release((struct page *)mdl->PageList[i]);
+               //page_cache_release((struct page *)mdl->PageList[i]);
+               put_page((struct page *)mdl->PageList[i]);
            }
            kfree(mdl->PageList);
            mdl->PageList = NULL;
@@ -635,7 +647,8 @@
            if (!PageReserved((struct page *)mdl->PageList[i])) {
                SetPageDirty((struct page *)mdl->PageList[i]);
            }
-           page_cache_release((struct page *)mdl->PageList[i]);
+           //page_cache_release((struct page *)mdl->PageList[i]);
+           put_page((struct page *)mdl->PageList[i]);
        }
        kfree(mdl->PageList);
        mdl->PageList = NULL;
@@ -874,6 +887,11 @@
    }
 }

+inline long interruptible_sleep_on_timeout(wait_queue_head_t * q, long timeout)
+{
+   return wait_event_interruptible_timeout(*q, 0, timeout);
+}
+
 /************************************************/
 /* gpg_ad_interruptible_sleep_on_timeout        */
 /************************************************/
@@ -1894,7 +1912,8 @@
  ************************************************/
 int gpg_ad_MINOR_f(void *file)
 {
-   return MINOR(((struct file *)file)->f_dentry->d_inode->i_rdev);
+   //return MINOR(((struct file *)file)->f_dentry->d_inode->i_rdev);
+   return iminor(file_inode((struct file *) file));
 }

 /************************************************

gpg3300 (DAボード用ドライバ)

ここは参考サイトのままで大丈夫でした。

gpg6204 (カウンタボード用ドライバ)

ここは,今までの合わせ技で対応します。

gpg6204_v4980.patch
--- penc_drventry.c.orig    2019-02-18 01:18:42.336979926 +0900
+++ penc_drventry.c 2019-02-09 01:34:38.041634924 +0900
@@ -185,6 +185,10 @@

    return;
 }
+inline long interruptible_sleep_on_timeout(wait_queue_head_t * q, long timeout)
+{
+   return wait_event_interruptible_timeout(*q, 0, timeout);
+}

 /************************************************/
 /* gpg_penc_interruptible_sleep_on_timeout      */
@@ -489,7 +493,8 @@
 /* ioctl system call                            */
 /************************************************/
 #if (LINUX_VERSION_CODE >= VERSION(2,6,36))
-static int penc_ioctl(struct file *file, unsigned int iocmd, unsigned long ioarg)
+//static int penc_ioctl(struct file *file, unsigned int iocmd, unsigned long ioarg)
+static long penc_ioctl(struct file *file, unsigned int iocmd, unsigned long ioarg)
 #else
 static int penc_ioctl(struct inode *inode, struct file *file, unsigned int iocmd, unsigned long ioarg)
 #endif
@@ -540,7 +545,8 @@
 #else
 module_param(cp6204_debuglevel, ulong, 0);
 module_param(penc_major, int, 0);
-module_param(cp6204_version_disp, int, 1);
+// module_param(cp6204_version_disp, int, 1);
+module_param(cp6204_version_disp, int, 0);
 #endif

 #if (LINUX_VERSION_CODE >= VERSION(2,6,0))

また,ヘッダファイルなどのシンボリックリンクが必要です。

ln -s /usr/include/dpg0100.h
ln -s /usr/include/fbipenc.h
ln -s ../ver26/bocp6204.o

この状態でもコンパイルは通りますが,多くの警告が出ます。
ここで,dpg0100のコンパイル時のModule.symversをコピーしておくと,警告が消えてくれます。

動作確認

全てのカーネルモジュールをmakeしてmake installしたら,認識できるかテストします。
まず,参考サイトに従って,depmodとmodprobeでモジュールを挿入します。

depmod -a
modprobe dpg0100
modprobe dpg0101
modprobe cp3100
modprobe cp3300
modprobe cp6204

lsmod

lsmodで,きちんと挿入できていることが確認できます。

エラー発生!!

次に,dpg0101を実行し,デバイスノードを生成させます。
以下のように入力することで,すべてのデバイスのノードを生成できるはずです。

dpg0101
3100
3
3300 #ここでエラー
3
6204
99

が,しかし,以下のような画面となり,
2つ目以降のデバイスが設定できないことが分かります・・・。

**************************************************
 Setup Utility  
--------------------------------------------------
 Version: 1.50-09  
--------------------------------------------------
 Copyright 2003, 2011      Interface Corporation. 
                             All rights reserved. 
**************************************************

Enter the model number of the product: GPG/GPH-3300
 >> Another error
 No Interface PCI/CompactPCI board is found.

しかし,エラー前のcp3100についてはきちんとノードが作成されているようです。
また,dpg0101の実行前後でlsmodやdmesgすると,dpg0101.koがなぜか勝手にunloadされているようです。
(どうやらこれがエラーの原因のようですが,解決策は不明です・・・。)

とりあえずの対症療法

ということで,dpg0101.koを一回ずつロードしてデバイスノードを作成するようにしました。
以下のスクリプトで自動実行できます。

dpg0101_auto.sh
#!/bin/bash

modprobe dpg0101
dpg0101 -s 3100
modprobe dpg0101
dpg0101 -s 3300
modprobe dpg0101
dpg0101 -s 6204

後は,テキトーにサンプルプログラムを組んで,テスタやオシロなどで実際の入出力を確認しましょう。

後書き

なんだか怪しい部分もありますが,とりあえずDAボードの出力は確認できました。
前に使っていたART-Linux(Ubuntu10.04)の時はすんなりとインストール出来て,
dpg0101でこんな苦労することは(多分)なかったので,なかなか対処が分からず困りました・・・。
原因や解決策が分かる方がいらっしゃれば,ぜひ教えてください。
また,間違いなどあれば遠慮なくご指摘ください。

にしても,RTAIにはせっかくComediが対応しているんだから,
誰かComediドライバ書いてくれないかなぁなんて思ったり思わなかったり・・・。

参考文献

Ubuntu 16.04LTS でのインターフェース社ドライバの使用 : 最も参考になるページ(日本語)
Linux source code identifier (v2.6.33) - Bootlin : 2.6.33のカーネルソース
Linux source code identifier (v4.9.80) - Bootlin : 4.9.80のカーネルソース

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Linux2(CentOS7)で何も考えずにストレージ拡張できるようにする

はじめに

私が作業するとき「あれ、これどうだったっけな」ってググるのが面倒だったのと
忘れっぽいのでメモ。

環境

AWS
EC2 t2.micro

CentOS7(Amazon Linux2)での論理拡張手順(xfs編)

1.AWSのコンソールでEBS選択し、GUI上から明示的に増加させる。

2.SSHでEC2ログイン

3.lsblkコマンドを使い論理ストレージの現状を確認。

[ec2-user /]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1 259:0 0 20G 0 disk
-nvme0n1p1 259:1 0 8G 0 part /
-nvme0n1p128 259:2 0 1M 0 part```

4.ディスク容量を確認。

[ec2-user /]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 249M 1.7G 13% /run
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/nvme0n1p1 8.0G 8.0G 4.3M 100% /
tmpfs 389M 0 389M 0% /run/user/1000
tmpfs 389M 0 389M 0% /run/user/1001

100%でひっ迫。

5.追加を実施。指定ファイル数があるなら-dの引数で指定する。指定しないと全部割り当たる。
正し、成功する場合と失敗する場合がある。
No space left on deviceが出力され失敗した場合は5-1に移行。

[ec2-user /]$ sudo growpart /dev/nvme0n1 1
CHANGED: partition=1 start=4096 old: size=16773087 end=16777183 new: size=41938911,end=41943007

-------------------(成功した場合は6まで飛ぶ)-----------------------

5-1.inode残ファイル数を確認。

[ec2-user /]$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 493131 291 492840 1% /dev
tmpfs 497543 1 497542 1% /dev/shm
tmpfs 497543 379 497164 1% /run
tmpfs 497543 16 497527 1% /sys/fs/cgroup
/dev/nvme0n1p1 51368 51273 95 100% /
tmpfs 497543 1 497542 1% /run/user/1000
tmpfs 497543 1 497542 1% /run/user/0

5-2.問答無用で不要なファイルを探す。
find / -xdev -type f | cut -d "/" -f 2 | sort | uniq -c | sort -nr

5-3.不要なファイルを倒す。

5-4.inode残数が問題なさそうなら5.に戻る

-----------------------(ここまで)----------------------------

6.またlsblkコマンドを使い論理ストレージの現状を確認。

[ec2-user /]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1 259:0 0 20G 0 disk
tqnvme0n1p1 259:1 0 20G 0 part /
mqnvme0n1p128 259:2 0 1M 0 part

7.この段階で問題なければストレージを拡張する。
ただし、Cent7ではxfs_growfsを使う。
resize2fsは使えないということを忘れてはならない。
resizeを打つとスパブロされる。

[ec2-user /]$ sudo xfs_growfs /dev/nvme0n1p1
meta-data=/dev/nvme0n1p1 isize=512 agcount=4, agsize=524159 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1 spinodes=0
data = bsize=4096 blocks=2096635, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
data blocks changed from 2096635 to 5242363

最後の一文で出来たことが分かる。

8.確認
[ec2-user /]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 249M 1.7G 13% /run
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/nvme0n1p1 20G 8.0G 12G 41% /
tmpfs 389M 0 389M 0% /run/user/1000
tmpfs 389M 0 389M 0% /run/user/1001

よしOK。

[ec2-user /]$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 493131 291 492840 1% /dev
tmpfs 497543 1 497542 1% /dev/shm
tmpfs 497543 391 497152 1% /run
tmpfs 497543 16 497527 1% /sys/fs/cgroup
/dev/nvme0n1p1 10484720 51263 10433457 1% /
tmpfs 497543 1 497542 1% /run/user/1000
tmpfs 497543 1 497542 1% /run/user/1001

こっちも大丈夫。

終わりに

ただのメモ書きです。本当にありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure のシリアルコンソールを使って Linux OS の ブートメッセージ を見られないときのトラブルシューティング

はじめに

Azure ではシリアルコンソールを利用してシングルユーザモードになれたりする のだが、ブートメッセージが出てこなかったため、原因と対策を考える

事象

過去に作ったCentOS 7.3 を利用して発生。シリアルコンソールを繋ぎながら再起動してもブートメッセージの画面が出てこない。
下記は最新の CentOS 7.5 をマーケットプレイスから作成して同じ操作をして出てくる画面
image.png

原因

/boot/grub2/grub.cfg を比較すると、新しいマシンの方にいくつか古い方に見られない行があった。

# sdiff grub.cfg_cent75 grub.cfg_cent73

(中略)

serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1  | terminal_output console
terminal_input serial console                                 <
terminal_output serial console                                <

(後略)

以下のサイトによると、シリアルデバイスのイニシャライズができていなかったから...?
https://www.gnu.org/software/grub/manual/grub/grub.html#serial

CentOS 7.3 でもシリアルコンソール自体はできるが、起動時にブートメッセージを表示するにはこの行が必要なのかもしれない。

serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1

対策

当該行を grub.cfg に追記したら無事にブートメッセージが表示されるようになった。
image.png

参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OMRON USB型環境センサー 2JCIE-BUをLinux(debian9/OpenBlocks IoT)からUSB接続して使用する

OMRON USB型環境センサー 2JCIE-BUは、USBにより電源供給される、オムロン環境センサーのUSB版となります。
本製品の使い方としては、電源供給をUSBで、通信は従来品と同様にBLE通信を行えば、従来品同様通信が可能ですが、合わせてUSB経由でのデータ取得が可能です。

image.png

https://www.omron.co.jp/ecb/product-detail?partId=73063

OpenBlocks IoTシリーズのFW3.3ではBLEによる接続は標準サポートしていますが、USBでの有線接続には対応していません。
今回は、本センサーからUSB経由でデータを取得するための方法についてNode-REDによる実装を行ってみました。
なお、本記事はOpenBlocks IoT(debian9)上で検証を行っていますが、debian9はもとより、他の汎用Linuxデストリビューションでも利用可能かと思います。

Linuxにおけるデバイスの扱いと設定について

本センサーのUSB接続はUSBシリアル接続で、対応するのはftdi_sioです。
ただし、本チップのvendor_idとproduct_idはftdi_sioには含まれていませんので、ドライバを再構築するか、以下の手順でnew_idを書き込むことによりftdi社のUSBシリアルデバイスとして認識します。

modprobe ftdi_sio
echo "0590 00d4" > /sys/bus/usb-serial/drivers/ftdi_sio/new_id

Linux(debian9)起動時の設定方法の例としては、以下の内容のファイルをrules.dに追加します。
/etc/udev/rules.d/80-rename-2JCIE-BU.rules

ACTION=="add", ATTRS{idVendor}=="0590", ATTRS{idProduct}=="00d4", RUN+="/sbin/modprobe ftdi_sio" RUN+="/bin/sh -c 'echo 0590 00d4 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'", SYMLINK+="2JCIE-BU"

再起動し、/dev/2JCIE-BUが/dev/ttyUSB*(*は0以上の数字)のシンボリックリンクとして作成されていることを確認してください。

この状態で、環境センサはシリアルデバイスとして利用できる状態になっています。
2JCIE-BUのユーザーズマニュアルに従い、各種設定や、データの取り出しを行うことが可能となります。
マニュアルは、上記の製品情報ページから入手してください。

Node-REDによるセンサーデータの取得例

センサーデータの取得や、JSON形式への展開は、Node-REDを利用するとシリアル通信周りのコーディングを簡略化することができます。
image.png

Node-RED によるサンプルフロー

[{"id":"cdd06147.5ca3e","type":"inject","z":"54fcbeea.dded1","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":180,"y":80,"wires":[["367aaed0.bd7b32"]]},{"id":"f7c337bc.3c6d48","type":"serial in","z":"54fcbeea.dded1","name":"OMRON ENV USB SERIAL","serial":"68582010.39d02","x":180,"y":260,"wires":[["d82c2274.95f8c","87a98eaf.0f089"]]},{"id":"122b2e04.b068e2","type":"serial out","z":"54fcbeea.dded1","name":"OMRON ENV USB Serial","serial":"68582010.39d02","x":670,"y":80,"wires":[]},{"id":"367aaed0.bd7b32","type":"function","z":"54fcbeea.dded1","name":"テストコマ","func":"var buf = new Buffer([0x52, 0x42, 5, 0,0x01,0x22,0x50,0xe2,0xbb ]);\nmsg.payload = buf;\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":80,"wires":[["122b2e04.b068e2","e2eaa90b.d38c08"]]},{"id":"e2eaa90b.d38c08","type":"debug","z":"54fcbeea.dded1","name":"","active":true,"console":"false","complete":"payload","x":680,"y":160,"wires":[]},{"id":"d82c2274.95f8c","type":"function","z":"54fcbeea.dded1","name":"BuffertoJSON","func":"var crc = 0xFFFF;\nvar odd;\n\nfor (var i = 0; i < msg.payload.readInt16LE(2)+2; i++) {\n    crc = crc ^ msg.payload[i];\n\n    for (var j = 0; j < 8; j++) {\n        odd = crc & 0x0001;\n        crc = crc >> 1;\n        if (odd) {\n            crc = crc ^ 0xA001;\n        }\n    }\n}\nif ( crc== msg.payload.readInt16LE(msg.payload.readInt16LE(2)+2)){\n    temperature= msg.payload.readInt16LE(8)/100;\n    humidity= msg.payload.readInt16LE(10)/100;\n    light= msg.payload.readInt16LE(12);\n    pressure= msg.payload.readInt32LE(14)/1000;\n    sound= msg.payload.readInt16LE(18)/100;\n    etvoc= msg.payload.readInt16LE(20)/100;\n    eco2= msg.payload.readInt16LE(22)/100;\n    discomfortindex= msg.payload.readInt16LE(24)/100;\n    heatstroke= msg.payload.readInt16LE(26)/100;\n    msg.payload=\n    {\n        temperature: temperature,\n        humidity: humidity,\n        light: light,\n        pressure: pressure,\n        sound: sound,\n        etvoc: etvoc,\n        eco2,\n        discomfortindex: discomfortindex,\n        heatstroke: heatstroke\n    }\n    return msg;\n}","outputs":"1","noerr":0,"x":460,"y":260,"wires":[["649191de.8cf33"]]},{"id":"649191de.8cf33","type":"debug","z":"54fcbeea.dded1","name":"","active":true,"console":"false","complete":"false","x":750,"y":260,"wires":[]},{"id":"87a98eaf.0f089","type":"debug","z":"54fcbeea.dded1","name":"","active":true,"console":"false","complete":"false","x":420,"y":340,"wires":[]},{"id":"68582010.39d02","type":"serial-port","z":"","serialport":"/dev/2JCIE-BU","serialbaud":"115200","databits":"8","parity":"none","stopbits":"1","newline":"100","bin":"bin","out":"time","addchar":false}]

サンプルでは、
USB Original Address 0x5022 (Latest data short)を指定して、以下のデータ取得コマンドを発行し、
受信したレスポンスデータのCRC16チェックを行い、正しい場合のみJSON形式に展開しています。

0x52, 0x42, 0x05, 0x00, 0x01, 0x22, 0x50, 0xe2,0xbb

Node-REDでシリアルデバイスへのデータリクエストは、functionノードで以下の記述を行っています。

var buf = new Buffer([0x52, 0x42, 5, 0,0x01,0x22,0x50,0xe2,0xbb ]);
msg.payload = buf;
return msg;

シリアルデバイスの設定は、

Serial Port: /dev/2JCIE-BU
Setting: 115200bps / 8bit / NonParity / 1stopbit            
Input: Split input  after a timeout 100ms
 and deliver binary buffers

としています。
injectボタンを押すとコマンドがシリアルデバイスに出力され、レスポンスをJSON形式でパースしたものが、debugノードで確認できます。

起動時より自動的に一定時間毎のデータ計測を行いたい場合は、injectノードを編集し、「Node-RED起動時に実行」「繰り返し」等の設定を行ってください。
JSON形式にしていますので、Node-REDで利用できるノードを用いて、サーバやクラウドのIoT Hubへ送信したり、データベースへの書き込みを行うことも容易かと思います。
また、OpenBlocks IoTシリーズの場合、ユーザーデバイス登録を行い、IPC Outへパスを設定することで、パースしたJSONデータをPD repeaterで扱うことにより、各種クラウドやAzure IoT Edgeのコンテナモジュールへの送信について、WebUIで設定/リモート管理出来ます。

最後に

本センサーはUSB経由でログデータの取り出しなども行えますので、いろいろ試してみてください。

なお、本センサーはUSBポートに直結出来ますが、PC本体の発熱により温度センサーの値が上昇しますので、環境センサーとして利用する場合は、延長ケーブルを用意して測定場所を離す必要があります。
また、精度の高い振動検出などを行う場合は、非常に細い線材によりUSB配線を行う必要もあるかと思います。
設置場所や配線材については、設置する場所やモノに合わせて工夫する必要があります。

--
(ご)  Toshiya Goto
    goto@plathome.co.jp

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NVIDIA環境でPython+Qtアプリを起動する(shader作成に失敗する場合)

PythonでQtアプリを起動すると、「QOpenGLShader: could not create shader」と表示されて失敗することがある。

原因

https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 にあるが、状況によりlibGL.so.1ではなくlibGL.soがロードされ、これがDebianパッケージのドライバだと/usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.soにリンクされていることが原因である。
(NVIDIAインストーラならばこういう状況は発生しないが、カーネル更新時に自動でdkmsが働かないためドライバを入れ忘れる可能性が出てしまう、なのでパッケージを使うのが良かろう)

方法

短期的には、sudo ln -sf libGL.so.1 /usr/lib/x86_64-linux-gnu/libGL.soとすればよいが、グラフィックドライバ等の更新でmesa-divertedに戻ってしまう。恒久的に書き換えるには以下を実行すれば良い。

sudo update-alternatives --install /usr/lib/mesa-diverted/libGL.so-master libGL.so-master /usr/lib/x86_64-linux-gnu/nvidia 9999 \
--slave /usr/lib/x86_64-linux-gnu/libEGL.so glx--libEGL.so-x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/nvidia/current/libEGL.so.1 \
--slave /usr/lib/x86_64-linux-gnu/libGL.so glx--libGL.so-x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/nvidia/current/libGL.so.1 \
--slave /usr/lib/x86_64-linux-gnu/libGLESv1_CM.so glx--libGLESv1_CM.so-x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/nvidia/current/libGLESv1_CM_nvidia.so.1 \
--slave /usr/lib/x86_64-linux-gnu/libGLESv2.so glx--libGLESv2.so-x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/nvidia/current/libGLESv2_nvidia.so.2

このスクリプトは /var/lib/dpkg/info/glx-alternative-mesa.postinst を読んだ上で自分で構築したものである。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Raspberry Pi で割り込みを禁止したい (≒DI/EI したい)

背景

Linuxで使うことが前提の Raspberry Pi ではユーザのプログラムは当然のことながら OS のスケジューラのもとに動きます。そのため時間に厳密なプログラムを書くのはとても難しいです。端的に言うと、正確に一定間隔で何かを処理するのが苦手です。こんなときはタイマ割り込みもしくは時計のポーリングなのですが、オーバーヘッドの少なさとかコンテキストの単純さを考えて後者を使いたい場合も多いと思います。その際、各種割り込みが邪魔をします。何か単純な一時的な割り込みの禁止ができたらいいなと思っていろいろやってみたらできたので記事にしました。おっさんにはおなじみのZ80のDI/EIです。

できること

gettimeofday()をポーリングして100 usの周期動作をするプログラム (後述) を書きました。標準の場合と割り込みを禁止した場合の結果 (実測した周期) がこちらです。
rpiint.png

どうですか? 一目瞭然ですね。割り込みを禁止すればタイミングにシビアないろいろな制御ができそうです。例えば 赤外線リモコン とか。(注: もちろん割り込みを禁止にすることはたくさんの副作用を伴います。禁止時間は最小限にとどめましょう。)

やりかたの概要

Raspberry Pi の SoC BCM2835/2837 のマニュアルによれば "interrupt disable register" と "interrupt enable register" というのがあるようなのでこれを直接叩きます。GPIOでおなじみの /dev/memmmap() するあれです。

  1. mmap()して物理アドレスを直接叩けるようにする。
  2. レジスタを読み出して、現在の設定情報を保存しておく。
  3. interrupt disable register にオール1を書き込んですべての割り込みを禁止する。
  4. 時間にシビアな必要な処理を行う。
  5. interrupt enable register に 2. で保存した値を書き込み最初の状態に戻す。

とこれだけです。Z80的に言うと、2〜3がDI, 5がEIです。

コード例

上記のグラフのデータを作成するのに使ったコードです。お使いのRaspberry Pi に合わせて #define PERI_BASE PI?_PERI_BASE の部分を変えてください。

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/time.h>

#define BLOCK_SIZE (4 * 1024)
#define PI2_PERI_BASE 0x3f000000
#define PI1_PERI_BASE 0x20000000
#define PERI_BASE PI1_PERI_BASE // Raspberry 0/1 or 2/3

// 指定時間 (us) ポーリングで待つ
inline static void waitUs(struct timeval tv_zero, int until_us) {
  struct timeval tv;
  int us;

  while (1) {
    gettimeofday(&tv, NULL);
    us = (tv.tv_sec - tv_zero.tv_sec) * 1000000 + (tv.tv_usec - tv_zero.tv_usec);
    if (us >= until_us) break;
  }
}


int main() {
  int mem_fd;
  char *map;
  int i;
  int us_next;
  struct timeval tv0, tv1;
  int dt[10000];
  int irq1, irq2, irq3;   // 割り込み情報保存よう (最後に irqen に書き戻す)

  volatile unsigned int *irqen1;
  volatile unsigned int *irqen2;
  volatile unsigned int *irqen3;
  volatile unsigned int *irqdi1;
  volatile unsigned int *irqdi2;
  volatile unsigned int *irqdi3;

  if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) return -1;
  map = (char*) mmap(NULL,
             BLOCK_SIZE,
             PROT_READ | PROT_WRITE,
             MAP_SHARED,
             mem_fd,
             PERI_BASE + 0xb000
             );
  if (map == MAP_FAILED)  return -1;
  irqen1 = (volatile unsigned int *) (map + 0x210);
  irqen2 = (volatile unsigned int *) (map + 0x214);
  irqen3 = (volatile unsigned int *) (map + 0x218);
  irqdi1 = (volatile unsigned int *) (map + 0x21c);
  irqdi2 = (volatile unsigned int *) (map + 0x220);
  irqdi3 = (volatile unsigned int *) (map + 0x224);

  gettimeofday(&tv0, NULL);
  irq1 = *irqen1;
  irq2 = *irqen2;
  irq3 = *irqen3;
  // 以下3行で全部の割り込みを禁止する
  *irqdi1 = 0xffffffff;
  *irqdi2 = 0xffffffff;
  *irqdi3 = 0xffffffff;

  for (i = 0, us_next = 100; i < 10000; i++, us_next += 100) {
    waitUs(tv0, us_next);
    gettimeofday(&tv1, NULL);
    dt[i] = (tv1.tv_usec - tv0.tv_usec) + (tv1.tv_sec - tv0.tv_sec) * 1000000;
  }
  // 以下3行で割り込み許可フラグをもとに戻す。
  *irqen1 = irq1;
  *irqen2 = irq2;
  *irqen3 = irq3;
  // 結果表示
  for (i = 1; i < 10000; i++) {
    printf("%d\n", dt[i] - dt[i - 1]);
  }
  return 0;
}


  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む