Howto

Armadillo-400シリーズでModbusの通信をする

Armadillo-400シリーズのRS-232Cインターフェースにセンサーを接続し、Modbus Protocolでデータ取得を行う方法についてご紹介します。
※センサーは実体ではなくアプリケーションで擬似的に構成しています。

1. Modobusとは
2. 開発環境にModbusのクロスライブラリを組み込む
3. ソースコードの作成
4. Atmark Distへの組み込み
5. 擬似センサーの準備
6. 動作確認
7. まとめ

本解説は動作確認にArmadillo-420を使用する事を前提として記載しています。
解説内容はArmadillo-400シリーズ全てに適用可能ですので、他のArmadilloをご使用の場合には、Armadillo-420固有の記載となっている部分を適宜読み替えてください。

本解説では以下の開発環境を使用しています。

ATDEatde3-20120709
Atmark Distatmark-dist-20131122
Linuxカーネルlinux-2.6.26-at18

以下のイメージファイルは手順の2.~4.を適用し作成したものです。
すぐに動作を確認したい場合は、本イメージファイルを使用し5.~6.の手順を実行してください。

カーネルイメージlinux.bin.gz
ユーザーランドイメージromfs.img.gz

1. Modbusとは

ModbusはModbus Protocolを実装したネットワークです。
プロトコル仕様が一般公開されており、また非常にシンプルな構成であるため、FA分野で広く使用されています。
物理レイヤとしてRS-232CやRS-485を採用し、通信プロトコルとしてModbusを採用するセンサーも市場に存在しています。
また、ModbusにはEthernetを使用したmodbus tcpも存在しています。

Modbusの詳細な情報やプロトコル仕様については以下のサイトをご参照ください。
http://modbus.org/

2. 開発環境にModbusのクロスライブラリを組み込む

Modbusを使用するためのライブラリ(libmodbus)はATDE3には標準で組み込まれていません。
従い、以下のサイトよりlibmodbusライブラリをダウンロードし、クロスライブラリの組み込みを行う必要があります。
http://libmodbus.org/

入手したアーカイブを以下のコマンドでクロスコンパイルし組み込みを行います。

atmark@atde3:~$ tar zxvf libmodbus-3.0.5.tar.gz
atmark@atde3:~$ mkdir cross-build
atmark@atde3:~$ cd cross-build
atmark@atde3:~/cross-build$ ../libmodbus-3.0.5/configure --prefix=/usr/arm-linux-gnueabi --host=arm-linux-gnueabi
atmark@atde3:~/cross-build$ make
atmark@atde3:~/cross-build$ sudo make install

3. ソースコードの作成

以下にArmadilloがModbusのマスターとなり、スレーブと通信するためのサンプルソースコード(modbus_master.c)を示します。

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <modbus.h>
#include <time.h>

#define SLAVE_ID 17
#define REGISTER_ADDRESS 8
#define SERIAL_SPEED 115200
#define SERIAL_PARITY 'N'
#define SERIAL_DATABIT 8
#define SERIAL_STOPBIT 1

int main(int argc, char *argv[])
{
    time_t timer;    
    modbus_t *ctx;
    int rc;
    int ret = EXIT_SUCCESS;
    uint16_t  tab_rp_registers;

    if (argc < 2) {
        printf("Usage: %s <device>\n\n", argv[0]);
        return EXIT_SUCCESS;
    }

    /* libmodbusコンテキストを生成 */
    ctx = modbus_new_rtu(argv[1], SERIAL_SPEED, SERIAL_PARITY,
                 SERIAL_DATABIT, SERIAL_STOPBIT);

    if (ctx == NULL) {
        fprintf(stderr, "Unable to allocate libmodbus context\n"
                "modbus error\n");
        return EXIT_FAILURE;
    }

    /* デバッグモードの設定 */
    modbus_set_debug(ctx, FALSE);

    /* エラーリカバリーモードの設定 */
    modbus_set_error_recovery(ctx,
                  MODBUS_ERROR_RECOVERY_LINK |
                  MODBUS_ERROR_RECOVERY_PROTOCOL );

    /* コンテキストのスレーブ番号設定 */
    if (modbus_set_slave(ctx, SLAVE_ID) == -1) {
        fprintf(stderr, "set slave failed: %s\n"
                "modbus error\n",
                modbus_strerror(errno));
        modbus_free(ctx);
        return EXIT_FAILURE;
    }

    /* Modbus接続の確立 */
    if (modbus_connect(ctx) == -1) {
        fprintf(stderr, "Connection failed: %s\n"
                "modbus error\n",
                    modbus_strerror(errno));

        modbus_free(ctx);
        return EXIT_FAILURE;
    }

    while (1) {

        sleep(1);
        time(&timer);

        /* input registerの読み出し */
        rc = modbus_read_input_registers(ctx, REGISTER_ADDRESS,
                             1, &tab_rp_registers);

        if ( rc < 0 ) {
            fprintf(stderr, "faild: %s\n"
                    "modbus error\n",
                    modbus_strerror(errno));
            ret = EXIT_FAILURE;
            break;
        }

        printf("%s Temperature:%d\n\n", ctime(&timer), tab_rp_registers);
    }


    /* Modbus接続のクローズ */
    modbus_close(ctx);

    /* libmodbusコンテキストを開放 */
    modbus_free(ctx);

    return ret;
}

本サンプルの動作を以下に説明します。
modbusによる通信を行う場合は、まずmodbus_new_rtu関数によりコンテキストを生成します。
本サンプルでは、プログラムの第一引数で指定したデバイスを使用し、modbus通信を行います。

modbus_set_slave関数では通信を行うスレーブ番号を指定します。
modbusはシングルマスター/マルチスレーブのプロトコルなので、送信先のデバイスをスレーブ番号で区別します。

通信設定が完了したところで、modbus_connect関数を使用し、modbus接続を確立します。
スレーブからのデータは、modbus_read_input_registers関数で取得する事が可能です。

通信が完了したら、modbus_connect関数で接続したmodbus通信を、modbus_close関数で閉じ、modbus_new_rtu関数で生成したコンテキストは、modbus_free関数で解放します。

なお、本サンプルは、modbusがエラーとなるまで無限ループする仕様となっているので、停止させる場合はCtrl + Cなどで強制終了させる必要があります。

4. Atmark Distへの組み込み

ATDE3のホームディレクトリ直下に、Atmark Dist及びLinuxカーネルのディレクトリが存在する事を前提とします。
Atmark Dist内からLinuxカーネルのディレクトリへ、linux-2.6.xの名前でシンボリックリンクを張ります。

atmark@atde3:~$ cd atmark-dis/
atmark@atde3:~/atmark-dist$ ln -s ../linux-2.6.26-at18 ./linux-2.6.x

Armadillo実践開発ガイド 第一部 独自プロダクトの追加」に記載の手順を参考にproductディレクトリを作成します。

atmark@atde3:~/atmark-dist$ cd vendors/AtmarkTechno
atmark@atde3:~/atmark-dist/vendors/AtmarkTechno$ cp -r Armadillo-420 my-product

作成したproductディレクトリにmodbusディレクトリを作成し、modbus_master.cと以下のMakefileを格納してください。

EXEC = modbus_master
PKGCONFIG_LIBDIR:= PKG_CONFIG_LIBDIR=$(CROSS_USR_DIR)/lib/pkgconfig
PKGCONFIG_CFLAGS        = `$(PKGCONFIG_LIBDIR) pkg-config --cflags libmodbus`
PKGCONFIG_LIBS          =  `$(PKGCONFIG_LIBDIR) pkg-config --libs libmodbus`

all: $(EXEC)

$(EXEC): $(EXEC).o
                $(CC) $(EXEC).o -o $@ $(PKGCONFIG_CFLAGS) $(PKGCONFIG_LIBS)

clean: 
                rm -f $(EXEC) *.o *~

romfs:
                $(ROMFSINST) /bin/$(EXEC)

%.o: %.c
                $(CC) -c $(PKGCONFIG_CFLAGS) -o $@ $<

作成したmodbusディレクトリにファイルが格納されている事をlsコマンドで確認します。

atmark@atde3:~/atmark-dist/vendors/AtmarkTechno$ ls my-product/modbus
Makefile  modbus_master.c

その後、~/atmark-dist/vendors/AtmarkTechno/my-product/Makefileの以下の記載を

SUBDIR_y =
SUBDIR_$(CONFIG_VENDOR_GPIOCTRL_GPIOCTRL)       += gpioctrl/
(省略)
SUBDIR_$(CONFIG_VENDOR_AWL13_AWL13)             += awl13/

次のように変更します。

SUBDIR_y =
SUBDIR_$(CONFIG_VENDOR_GPIOCTRL_GPIOCTRL)       += gpioctrl/
(省略)
SUBDIR_$(CONFIG_VENDOR_AWL13_AWL13)             += awl13/
SUBDIR_y                                        += modbus/

上記Makefileの変更後、~/atmark-dist/へ移動しビルドします。
~/atmark-dist/images内にイメージファイルが生成されるので、これを使用する事でmodbus_masterが組み込まれたイメージを動作させる事ができます。

5. 擬似センサーの準備

modbusスレーブとして動作する温度センサーを、アプリケーションで擬似的に実現します。
この擬似センサーアプリをATDE3で実行させる手順を紹介します。

擬似センサーアプリは以下からダウンロードする事が可能です。
擬似センサーアプリケーション

まずATDE3でmodbusのライブラリを使用するため、以下のコマンドを実行しlibmodbusのセルフコンパイルを行います。

なお、本手順においてlibmodbusは、開発環境にModbusのクロスライブラリを組み込むで取得し、ホームディレクトリに展開されている事を想定しています。

atmark@atde3:~$ mkdir host-build
atmark@atde3:~$ cd host-build
atmark@atde3:~/host-build$ ../libmodbus-3.0.5/configure
atmark@atde3:~/host-build$ make
atmark@atde3:~/host-build$ sudo make install
atmark@atde3:~/host-build$ sudo ldconfig

サンプルの擬似センサーアプリを、以下のコマンドでコンパイルすると準備完了です。

atmark@atde3:~$ tar zxf modbus_sensor.tar.gz
atmark@atde3:~$ cd modbus_sensor
atmark@atde3:~$ make

6. 動作確認

動作確認に使用するArmadillo-420のCON9から出力されるシリアルインターフェース2を、Modbus通信に使用します。
シリアルインターフェース2はCMOSレベルの信号出力となっていますので、RS-232Cレベル変換アダプタを接続してください。
RS-232Cレベル変換アダプタの接続方法については、「Armadillo-400シリーズハードウェアマニュアル 付録F RS-232C レベル変換アダプタ(OP-SCLVL-10)」を参照してください。

まず、ATDE3上の擬似センサーアプリを起動させる必要があります。
以下のコマンドによりアプリを起動させてください。
擬似センサーアプリの引数にはシリアルポートのデバイスファイルを指定します。
なお、「Armadillo実践開発ガイド 第一部 シリアルポートの設定」に従い、VMwareでシリアルポートを使用できるよう、あらかじめ設定してください。

※シリアルポートのデバイスファイル名(/dev/ttyS1)はご使用の環境に合わせ、適宜読み替えてください。

atmark@atde3:~$ cd modbus_sensor
atmark@atde3:~/modbus_sensor$ ./modbus_sensor /dev/ttyS1

次にArmadillo上のmodbus_masterを起動します。
modbus_masterの第一引数については、シリアル通信を行うポートのデバイスファイルを指定します。

[armadillo ~]# modbus_master /dev/ttymxc2

上記のコマンドを実行すると、Armadilloには以下のように、時間と擬似センサーからの取得データが表示されます。

Fri Dec 20 11:25:34 2013
 Temperature:3

また、擬似センサーアプリからは、以下のような表示が出力されます。

Waiting for a indication...
<11><04><00><08><00><01><B2><98>
[11][04][02][00][03][38][F2]
temperature = 3

<>で囲まれたデータはmodbusプロトコルを使用し受信したデータの生データです。
また、[]で囲まれたデータはmodbusプロトコルを使用し送信するデータの生データです。
擬似センサーアプリ上では、<>で囲まれたデータはArmadilloが送信したデータとなります。

なお、このmodbus通信の生データはmodbus_set_debug関数の第二引数をTRUEにする事で出力する事ができます。

7. まとめ

  • libmodbusを使用する事によりArmadilloでModbus通信を行う事ができる
  • Modbusを使用する場合は開発環境にクロスライブラリを組み込んでおく必要がある