Mbed OS 6上でNUCLEO-F767ZIでUDP通信を実装し、ROSと通信する(前編)


目次

1.はじめに
2.環境
3.セッティング
3.mbedでのプログラム
4.実行結果
5.おわり

はじめに

(本記事は8月にごちゃロボで発表した時の内容を若干改変したものになっています。)

ROSとマイコンの通信って悩みますよね…
標準のシリアルやrosserial、最近ならmrosなんてものもあったりします。
本記事では、ROS-マイコン間通信の1つの選択肢として、Mbed OS 6上でUDP通信を実装し、
ROSと通信するところまでを簡単に書いてみようと思います。

ちなみにこの記事を書こうと思ったのは

  • ネット上ではHALを使って実装している情報が多く、mbedは少ない
  • あったとしてもMbedOSのバージョンが古い
  • Mbed OS公式のexampleがわかりにくい
  • そもそも英語の情報ばっか

で割と実装するのに苦労したからです。

前編ではmbed上でのUDP通信の実装、
後編ではROSとマイコンの通信について書こうと思います。
ここが間違っているなどあれば、コメント欄にて訂正をいただけると助かります。

環境

ソフトウェア

  • Mbed OS v6.15
  • Mbed CLI 2
  • GNU Arm Embedded Toolchain 10.3-2021.07
  • Ubuntu 20.04
  • ROS 1 Noetic

ハードウェア

  • LAN端子がついてるUbuntu搭載PC
  • NUCLEO-F767ZI(多分、イーサネット端子ついてるSTM32マイコンならどれでも大丈夫…なはず)

セッティング

今回はマイコンとPCをイーサネットケーブルで直接つなぐようにします。

1. PCとマイコンを接続

家に転がってるお好きなイーサネットケーブルでPCのLAN端子とマイコンのLAN端子を接続してください。

2. Ubuntuの設定を変更

このままだとマイコンがネットワークに接続できないので、Ubuntuのインターネット共有設定を変更する。
ターミナルで、

nm-connection-editor

と入力すると、

という画面が出てくるので、有線接続1をクリック。
そこから、IPv4設定項目に移り、Method他のコンピューターへ共有に変更。そのまま保存してください。

これでセッティングは完了です。

mbedでのプログラム

プログラム例です。

main.cpp
#include "mbed.h"
#include "EthernetInterface.h"
#include "rtos.h"

void receive(UDPSocket *receiver);

int main()
{
    // 送信先情報
    const char *destinationIP = "10.42.0.1";
    const uint16_t destinationPort = 4000;

    // 自機情報
    const char *myIP = "10.42.0.111";
    const char *myNetMask = "255.255.255.0";
    const uint16_t receivePort = 5000;

    // 送信データ
    const char sendData[100] = "Hello world!";

    // イーサネット経由でインターネットに接続するクラス
    EthernetInterface net;
    // IPアドレスとPortの組み合わせを格納しておくクラス(構造体でいいのでは?)
    SocketAddress destination, source, myData;
    // UDP通信関係のクラス
    UDPSocket udp;
    // 受信用スレッド
    Thread receiveThread;

    /* マイコンのネットワーク設定 */
    // DHCPはオフにする(静的にIPなどを設定するため)
    net.set_dhcp(false);
    // IPなど設定
    net.set_network(myIP, myNetMask, "");

    printf("start\n");

    // マイコンをネットワークに接続
    if (net.connect() != 0)
    {
        printf("network connection Error\n");
        return -1;
    }
    else
    {
        printf("network connection success\n");
    }

    // UDPソケットをオープン
    udp.open(&net);

    // portをバインドする
    udp.bind(receivePort);

    // 送信先の情報を入力
    destination.set_ip_address(destinationIP);
    destination.set_port(destinationPort);
    // 受信用のスレッドをスタート
    receiveThread.start(callback(receive, &udp));

    while (1)
    {
        if (const int result = udp.sendto(destination, sendData, sizeof(sendData)) < 0)
        {
            printf("send Error: %d\n", result);
        }
        ThisThread::sleep_for(1s);
    }
    receiveThread.join();

    udp.close();
    net.disconnect();
    return 0;
}

void receive(UDPSocket *receiver)
{
    SocketAddress source;
    char buffer[100];
    while (1)
    {
        memset(buffer, 0, sizeof(buffer));
        if (const int result = receiver->recvfrom(&source, buffer, sizeof(buffer)) < 0)
        {
            printf("receive Error : %d", result);
        }
        else
        {
            printf("from IP:%s port:%d data received:%s", source.get_ip_address(), source.get_port(), buffer);
        }
    }
}

mainスレッドでデータを送信し、receiveThreadスレッドでデータを受信しています。

注意

Mbed CLI2ではCMakeLists.txtをちょっと書き換えないとコンパイルが通らないので注意です。
mbed-netsockettarget_link_librariesに追加してください。

CMakeLists.txt(一部抜粋)
target_link_libraries(${APP_TARGET} 
    mbed-os
    mbed-netsocket
)

実行結果

(間違えて意味もなくipにモザイクをかけてしまいました。すみません。)

画面左側がマイコンのシリアルモニターです。データの受信元のip,portと受信したデータを表示します。

画面右上ではマイコンが送信したデータを受信して表示しています。
ここではncコマンドを使い、UDPサーバーを立てて受信しています。

nc -lu (受信するIPアドレス) (受信するポート)

画面右下ではマイコンにデータを送信しています。
こちらもncコマンドを使ってclientを立てています。

nc -u (マイコンのIPアドレス) (送信先のポート)

ちなみに、日本語も行けるっぽいです。日本語のデータ送る機会あるかな?

おわり

Mbed OS 6上でUDP通信を実装してみました。
後編ではROSと通信してみようと思います。
後編
https://qiita.com/hbvcg00/items/83c29d91d4b382c511ed