自作７セグLEDドライバをRaspberry piで動かすROSのシンプルなSubscriberをC++でサクッと書いてみた

##はじめに
上田隆一氏の著書「RaspberryPiで学ぶROSロボット入門」で学習中、同装置はGPIO経由でLEDやモータを操作している訳だし、何か安価な方法でROS学習をする方法がないかと模索していた時に、米田聡氏の氏の著書「RaspberryPiで学ぶARMデバイスドライバープログラミング」の１桁７セグLEDの回路に触発されて、Ubuntuのデバイスドライバーの自作から、ROSノードでの操作を実験しています。

本日は、７セグLEDの表示を操作する`ROS`の`Subscriber`制作学習の記録を記事にしてみました。

- Raspberry Pi のGPIOテスト用の１桁７セグLED回路基板の制作は、[こちらの過去記事](https://qiita.com/ivvakanni/items/e29378dc666d8204fa58 "ubuntu 18.04 にraspi-configをインストールしてGPIOの初期設定を変更した話")をご参照ください。
- １桁７セグLED回路基板の自作デバイスドライバの制作について、[こちらの記事](https://qiita.com/ivvakanni/items/ef872eb1120ea0a10355 "Raspberry Pi 3B+ で「初めてのカーネルモジュール～７セグLEDドライバー」の代替えサンプルがサクッとできた話。")をご参照ください。

##やりたいこと

`Raspberry Pi`の`GPIO`につないだ1桁7セグLED駆動用ICの`TC4511BP`を自作ドライバーを使って直接操作して、`ROS`で受信した信号に従ってLED表示の数字を変化させる。

###制作環境

- Raspberry Pi 3B+[^0]
- ubuntu 18.04 LTS[^1]
- ROS Melodic[^2]
- Microsoft Visual Studio Code (Windows 10)

[^0]: 実機テストはしていませんが、ubuntu18.04が動けばRaspberry pi 2や3Bでも多分動くはずです。
[^1]: ubuntuでのROS Melodicのインストールは、[こちらのROS Wiki](http://wiki.ros.org/melodic/Installation/Ubuntu)の記事を参照にしてください。
[^2]: Armrf用の32ビット版を使用しています。[こちらのUbuntu本家のサイト](https://ubuntu.com/download/raspberry-pi)からダウンロードできます。

###オリジナルのソースコード

Ros.orgの「[シンプルな配信者(Publisher)と購読者(Subscriber)を書く(C++)](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29)」をベースに使用しました。
また、Publisher 側は変更せずにそのまま使用しました。

###修正後のPublisherのソースコード

```c:listener.cpp
/*
 * Copyright (C) 2008, Morgan Quigley and Willow Garage, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "ros/ros.h"
#include "std_msgs/String.h"

/** Added Code starts from here. */
#include <fstream>
#include <iostream>
#include <string>

void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
//  ROS_INFO("I heard: [%s]", msg->data.c_str());
    std::ofstream ofs("/dev/odssledc"); 
    ofs << msg->data.c_str() << std::endl;
}
/** Added code ends here. **/

int main(int argc, char **argv)
{

  ros::init(argc, argv, "listener");
  ros::NodeHandle n;
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
  ros::spin();

    return 0;
}
```

実際にオリジナルのソースコードに修正をしている部分は、実は以下の数行だけです。

```c:
#include <fstream>
#include <iostream>
#include <string>

void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
//  ROS_INFO("I heard: [%s]", msg->data.c_str());
    std::ofstream ofs("/dev/odssledc"); 
    ofs << msg->data.c_str() << std::endl;
}
```

このサブスクライバをつかって、オリジナルのパブリッシャからの数字を受け取って７セグLEDに数字を表示させます。目まぐるしく数字が変わっていくので、その様子を眺めているだけでも結構楽しいです。

もう一つ、Pythonで最初に作ったサブスクライバも短いコードなので参考として掲載します。

```py:odssled3.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import UInt16

def write_mydevice(num):
    mydevice = "/dev/odssledc"
    try:
        with open(mydevice,"w") as f:
            f.write(str(num))
    except IOError:
        rospy.logger("error: write to " + mydevice)

def receive_num(data):
    write_mydevice(data.data)

if __name__ == '__main__':
    rospy.init_node('odssled')
    rospy.Subscriber("odssled",UInt16, receive_num)
    rospy.spin()
```

こちらのコードは、「RaspberryPiで学ぶROSロボット入門」の125ページに掲載されている buzzer3.py を修正作成したものです。修正といっても実は、デバイスドライバーの指定部分`mydevice = "/dev/odssledc"`以外は何も変更していません。

数字を表示させるときは、次のように入力して簡単に操作できます。`3`の表示には、以下のようにします。

```
$ rostopic pub -1 '/odssled' std_msgs/UInt16 3
```

##おわりに

ROSからRaspberry piのGPIOの出力についての操作の仕方はこれでおおよそ理解できました。`python`での動作はどうしてもモッサリしているのですが、`c++`のコードなら、サクサク動いてくれるので何しろ気持ちがいいです。

次の課題は、まずは、`GPIO2`と`GPIO3`につないでいるタクトスイッチの値を読むためのドライバの修正です。2つのタクトスイッチで、数字をカウントアップ、ダウンさせる操作ができるようにROSのアクションも作ってみます。