airpocketのアイコン画像
airpocket 2022年08月23日作成 © MIT
セットアップや使用方法 セットアップや使用方法 閲覧数 429
airpocket 2022年08月23日作成 © MIT セットアップや使用方法 セットアップや使用方法 閲覧数 429

Wi-SUN Add-onボード SPRESENSE-WiSUN-EVK-701 を使ってみる

Wi-SUN Add-onボード SPRESENSE-WiSUN-EVK-701 を使ってみる

はじめに

ただいま、2022年 SPRESENSE™ 活用コンテストに向けて絶賛開発中なのですが、RohmさんのWi-SUN Add-onボード SPRESENSE-WiSUN-EVK-701のモニター品をいただいたので使い方の覚書です。
このボードを選定した理由は、省電力で動きそう(イメージ)、Wi-SUNって聞いたことがあるけど触ったことない、メインボードだけで動かせる、といったあたりです。
ただしちょっと想定外だったこともあり、使用例やドキュメント類が少ない、受信用に使用するUSBドングルが高価と言った点は、個人でやるには少し手を出しにくい部分です。

参考にさせていただいた情報もあり、デバイスプラスさんのこの記事をベースにセットアップを進めましたがちょっと詰まったところもあったので蛇足かとも思いましたが覚書程度に。

とりあえず動いたよ、程度なので基本は元資料にあたってください。

参考

デバイスプラスさんの記事

https://deviceplus.jp/mc-general/weather-station-with-wisun-01/
https://deviceplus.jp/mc-general/weather-station-with-wisun-02/
https://deviceplus.jp/mc-general/weather-station-with-wisun-03/

WiSUN-EVK-701の使い方は第3回、第4回に詳しく解説されてる(予定)ですが、第4回はまだ公開されてません。お待ちしてますmm

GitHub

https://github.com/RohmSemiconductor/Arduino/tree/master/SPRESENSE-WISUN-EVK-701

RohmさんのGitHubに、サンプルコードとライブラリとドキュメントがあります。

ボードの回路図

https://www.rohm.co.jp/documents/11401/6353165/SPRESENSE-WiSUN-EVK-701-schematic.pdf

セットアップには直接関係ありませんが、ボードの回路図が公開されています。
@spresense さん、情報共有ありがとうございます。

使用する部品

Spresense メインボード

メインボードだけで動かします。
https://www.switch-science.com/catalog/3900/

Wi-SUN Add-onボード SPRESENSE-WiSUN-EVK-701

今回はモニター品としていただきました。core staff onlineさん、chip 1 stopさんで在庫確認できました。
https://www.zaikostore.com/zaikostore/stockDetail?productIdOfHitotsukara=pr14922577&productName_forFind=SPRESENSE-WISUN-EVK-701&typeStock_forFind=all

Wi-SUN USBドングル BP35C2

core staff onlineさんで購入しました。私が調べた限りでは最安値、でも15800円(税抜き)・・・。
https://www.zaikostore.com/zaikostore/stockDetail?productIdOfHitotsukara=pr11257823&productName_forFind=BP35C2&typeStock_forFind=all

USBドングルの設定

USBドングルは、PCに接続して初期設定をすると通信ができる様になります。
この部分はデバイスプラスさんの3回目記事の通りです。
RPiで使用するため、USBドングルの初期設定用pythonスクリプトにちょっと追加して、ボード側の設定に必要な情報も確認しています。

# /usr/bin/env python3
# -*- coding: utf-8 -*-
 
import serial
from time import sleep
 
con=serial.Serial('/dev/ttyUSB0',115200)
print(con.portstr)
 
def sendcmd(cmd_str):
    con.write(cmd_str.encode())
    while True:
        str_bf=con.readline()
        if str_bf !="":
            print(str_bf)
            if str_bf == b'OK\r\n':
                break
    sleep(0.5)
 
sendcmd('SKSREG SF0 1\r\n')
sendcmd('SKSREG S2 23\r\n')
sendcmd('SKSREG S3 5678\r\n')
sendcmd('SKSREG SA9 1\r\n')
sendcmd('SKSREG SA2 1\r\n')
sendcmd('SKSTART\r\n')
sendcmd('SKSETHPWD 001D12910003584C 1111222233334444\r\n')    #通信先のadd-on ボードのmacアドレスとパスワード設定
sendcmd('SKINFO\r\n')    #add-on ボードの設定に必要なUSBドングルの情報を確認
con.close()

sendcmd()で、USBドングルにコマンドを送っています。送信したコマンドの詳細は公式ドキュメントをご確認ください。下2行がポイントで、SKSETHPWD以降に通信先のadd-onボードのmacアドレスと新たに設定したパスワードを指定しています。add-onボードのマックアドレスは、ボードのシールに記載されています。パスワードは任意の文字列でOKですが、add-onボードの設定時に使用します。
私の場合は「001D12910003584C」がmacアドレスで、「1111222233334444」がパスワードです。

SKINFOは、USBドングルの情報を出力させるコマンドです。このスクリプトを実行すると次のような出力が出るはずです。最後から2行目の「b'EINFO FE80:0000:0000:0000:021D:1291:0002:2E8B 001D129100022E8B 23 5678 1 \r\n'」の部分はadd-onボードの設定時に通信先の情報として使用します。

>>> %Run wisun_init.py
/dev/ttyUSB0
b'SKSREG SF0 1\r\n'
b'OK\r\n'
b'SKSREG S2 23\r\n'
b'OK\r\n'
b'SKSREG S3 5678\r\n'
b'OK\r\n'
b'SKSREG SA9 1\r\n'
b'OK\r\n'
b'SKSREG SA2 1\r\n'
b'OK\r\n'
b'SKSTART\r\n'
b'OK\r\n'
b'SKSETHPWD 001D12910003584C 1111222233334444\r\n'
b'OK\r\n'
b'SKINFO\r\n'
b'EINFO FE80:0000:0000:0000:021D:1291:0002:2E8B 001D129100022E8B 23 5678 1 \r\n'
b'OK\r\n'

Spresenseのプログラム

Spresense側にはadd-onボードを使用するためのプログラムを書き込みます。
動作テストには、Rohmさんのサンプルプログラムを一部変更して使用します。
合せて、RohmさんのGitHubからbp35c0-j11.hとbp35c-j.cppをダウンロードし、メインプログラムと同じフォルダに保存してください。

メインプログラム

まずはメインプログラムからですが、任意の文字列を送信できる様に一部修正しています。

/*
  SPRESENSE-WISUN-EVK-701
 Copyright (c) 2019 ROHM Co.,Ltd.
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include "bp35c0-j11.h"

unsigned char state = 0 ;

BP35C0J11 bp35c0j11;

void setup() {
  boolean rc = FALSE ;
  bp35c0j11.j11_init();
  rc = bp35c0j11.wait_msg();
  if(rc == TRUE){
    state = 1 ;           //  hardware reset end 
  }else{
    state = 0 ;
  }
 

}

void loop() {

  unsigned char msg_length = 0 ;
  boolean rc = 0 ;

#ifdef DEBUG  
  Serial.print("State = ");
  Serial.println(state, DEC);
#endif

  delay(500);
  switch (state) {
    case(0):   // need hardware reset 
      rc = bp35c0j11.cmd_send(CMD_RESET);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        state = 1 ;
      }
    break;
    case(1):  //  init state 
      rc = bp35c0j11.cmd_send(CMD_INI);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        state = 2;
      }
      break;
    case(2): // HAN PANA setting
      rc = bp35c0j11.cmd_send(CMD_PANA_SET);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        state = 4;
      }
      break;
    case(3): // active scan
      rc = bp35c0j11.cmd_send(CMD_SCAN);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        rc = bp35c0j11.wait_msg();
        if(rc == TRUE){
          state = 4;
        }
      }
      break;
    case(4): //  HAN act
      rc = bp35c0j11.cmd_send(CMD_HAN);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        state = 5;
      }
      break;
    case(5): // HAN PANA act
      rc = bp35c0j11.cmd_send(CMD_PANA);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        rc = bp35c0j11.wait_msg();
        if(rc == TRUE){
            state = 7;
        }
      }
      break;
    case(6): // rcv mode change
      rc = bp35c0j11.cmd_send(CMD_CON_SET);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        state = 7;
      }
      break;
    case(7): // my_port open
      rc = bp35c0j11.cmd_send(CMD_PORTOPEN);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        state = 8;
      }
      break;
    case(8): // UDP send
      bp35c0j11.radiodata = "send data";    //<====この一行を追加するだけ。""内に送信する文字列を記入
      rc = bp35c0j11.cmd_send(CMD_UDPSEND);
      rc = bp35c0j11.wait_msg();
      if(rc == TRUE){
        delay(5000);
      }
      break;
    default:  // error 
        state = 0 ;
      break;

  }
}

変更点は、case(8)内に、
bp35c0j11.radiodata = "send data";
の一行を追加しているのみです。'' ''内に、送信する文字列を入力します。

bp35c0-j11.h

合せて、bp35c0-j11.hを変更します。

/*
  bp35c0-j11.h
 Copyright (c) 2019 ROHM Co.,Ltd.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/
#ifndef _BP35C0J11_H_
#define _BP35C0J11_H_

#define DEBUG


#define CMD_HDR_LEN  ((unsigned char)8)
#define UNI_CODE_LEN ((unsigned char)4)

#define CMD_RESET    (0x00D9)
#define CMD_INI      (0x005F)
#define CMD_HAN      (0x000A)
#define CMD_PANA     (0x003A)
#define CMD_PANA_SET (0x002C)
#define CMD_CON_SET  (0x0025)
#define CMD_UDPSEND  (0x0008)
#define CMD_SCAN     (0x0051)
#define CMD_PORTOPEN (0x0005)

#define NORT_WAKE    (0x6019)
#define RES_INI      (0x205F)
#define RES_HAN      (0x200A)
#define RES_PANA     (0x203A)
#define RES_PANA_SET (0x202C)
#define RES_CON_SET  (0x2025)
#define RES_UDPSEND  (0x2008)
#define RES_SCAN     (0x2051)
#define NORT_SCAN    (0x4051)
#define RES_PORTOPEN (0x2005)
#define NORT_PANA    (0x6028)

#define TIMEOUT      ((unsigned short)10000)

#define PIN_ENABLE  (PIN_D20)    // level shifter enable pin
#define PIN_RESET   (PIN_D21)    // wisun module reset pin

#define TRUE 1
#define FALSE 0

typedef struct {
  unsigned char uni_code[4];
  unsigned char cmd_code[2];
  unsigned char msg_len[2];
  unsigned char hdr_chksum[2];
  unsigned char dat_chksum[2];
  unsigned char data[128];
}CMD_FORMAT;

class BP35C0J11
{
	public:
		char* radiodata = "test";    <===この行を追加する。
		BP35C0J11(void);
		void j11_init(void);
		boolean wait_msg(void);
		boolean cmd_send(unsigned short cmd);
		void static msg_create(unsigned short cmd , unsigned short msg_length ,unsigned short hdr_chksum , unsigned short dat_chksum, unsigned char *pdata , unsigned char *psend_data );
	private:
		void static debugmsg(unsigned short datalength , unsigned char *psend_data);
};
#endif    //_BP35C0J11_H_

class BP35C0J11のpublic直下に
char* radiodata = "test";
の一行を追加しています。メンバ変数をpublicにするのはお行儀が悪いことのようですが許してつかーさい。

bp35c0-j11.cpp

bp35c0-j11.cpp には、通信先となるUSBドングルの情報を記入し、任意のデータを送信するためのコード変更を行います。
変更する部分は、30,31,35,325行目の合計4行です。長いプログラムですので、arduinoIDEなどに貼りつけて行数を表示させて確認してください。

/********************************************************************************
  bp35c0-j11.cpp
  Copyright (c) 2019 ROHM Co.,Ltd.

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*********************************************************************************/
#include <Arduino.h>
#include "bp35c0-j11.h"

unsigned const char uni_req[4] = {0xD0 , 0xEA , 0x83 , 0xFC};
unsigned const char uni_res[4] = {0xD0 , 0xF9 , 0xEE , 0x5D};

unsigned const char ini_data[4] = {0x03 , 0x00 , 0x05 , 0x00};       // エンドデバイス/Sleep 非対応/922.9MHz/20mW出力
unsigned const char pair_id[8] = {0x00 , 0x1D , 0x12 , 0x91 , 0x00 , 0x02 , 0x2E , 0x8B};   // 接続先MACアドレス<==USBドングルから読み取ったパラメータ
unsigned const char mac_adr[16] = {0xFE , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 , 0x1D , 0x12 , 0x91 , 0x00 , 0x02 , 0x2E , 0x8B}; // 接続先IPv6アドレス<USBドングルから読み取ったパラメータ
unsigned const char my_port[2] = { 0x01 , 0x23 };     // オープンするUDPポート
unsigned const char dist_port[2] = { 0x0E , 0x1A };   // 送信先UDPポート
unsigned const char password[16] = { '1' , '1' , '1' , '1' , '2', '2' , '2' , '2' , '3' , '3' , '3' , '3' , '4' , '4' , '4' , '4' };    // PANA認証時のパスワード
//unsigned const char radiodata[4] = { 'T' , 'E' , 'S' , 'T'};         // <=== bp35c0-j11.hでpublic変数として宣言したためコメントアウト

CMD_FORMAT cmd_format;

BP35C0J11::BP35C0J11(void)
{
	
}

/********************************************************************************
*   Name     : j11_init
*   Function : initial setting bp35c0-j11
*   input    : -
*   return   : -
*********************************************************************************/
void BP35C0J11::j11_init(void) {

  // configure output D20/D21
  pinMode(PIN_ENABLE, OUTPUT);      
  pinMode(PIN_RESET, OUTPUT);
  digitalWrite(PIN_ENABLE, HIGH);


  delay(1000);

  // Serial port initial 
  Serial2.begin(115200);
  Serial.begin(115200);
  Serial.write("RESET");
  Serial.println("");

  digitalWrite(PIN_RESET, LOW);     // reset
  delay(500);
  digitalWrite(PIN_RESET, HIGH);

}

/********************************************************************************
*   Name     : wait_msg
*   Function : wait for response from bp35c0-j11
*   input    : -
*   return   : TRUE/FALSE
*********************************************************************************/
boolean BP35C0J11::wait_msg(void)
{
  unsigned long start_time;
  unsigned long current_time;
  unsigned char rcvdata[128] = {0} ;
  unsigned char cnt = 0 ;
  start_time = millis();
  while (Serial2.available() == 0)
  {
    current_time = millis();
    if ((current_time - start_time) > TIMEOUT) {
      Serial.println("receive timeout");
      return FALSE;
    }

  }
  while (Serial2.available() > 0 ) {
    delay(5);
    rcvdata[cnt] = Serial2.read();
#ifdef DEBUG
    Serial.print(rcvdata[cnt] , HEX);
#endif
    cnt++;
    if (cnt >= 128) {
      Serial.println("receive data over flow");
      return FALSE;
    }
  }
  if (rcvdata[0] == uni_res[0] && rcvdata[1] == uni_res[1] &&
      rcvdata[2] == uni_res[2] && rcvdata[3] == uni_res[3]) {     // RESPONSE/NORTIFICATION
    switch (rcvdata[4] << 8 | rcvdata[5]) {
      case (NORT_WAKE):

        break;
      case (RES_INI):
        if (rcvdata[12] == 0x01) {
          Serial.println("Init Success");
        } else {
          Serial.println("Init Error");
          return FALSE;
        }
        break;
      case (RES_PANA_SET):
        if (rcvdata[12] == 0x01) {
          Serial.println("PANA Password set Success");
        } else {
          Serial.println("PANA Password set Error");
          return FALSE;
        }
        break;
      case (RES_SCAN):

        break;
      case (NORT_SCAN):
        break;
      case (RES_HAN):
        if (rcvdata[12] == 0x01) {
          Serial.println("HAN Act Success");
        } else {
          Serial.println("HAN Act Error");
          return FALSE;
        }
        break;
      case (RES_PANA):
        if (rcvdata[12] == 0x01) {
          Serial.println("PANA Act Success");
        } else {
          Serial.println("PANA Act Error");
          return FALSE;
        }
        break;
      case (NORT_PANA):
        if (rcvdata[12] == 0x01) {
          Serial.println("PANA Connect Success");
        } else {
          Serial.println("PANA Connecgt Error");
          return FALSE;
        }
        break;
      case (RES_CON_SET):
        if (rcvdata[12] == 0x01) {
          Serial.println("Normal connect mode");
        } else {
          Serial.println("connect mode change error");
          return FALSE;
        }
        break;
      case (RES_PORTOPEN):
        if (rcvdata[12] == 0x01) {
          Serial.println("UDP port open Success");
        } else {
          Serial.println("UDP port open Error");
          return FALSE;
        }
        break;
      case (RES_UDPSEND):
        if (rcvdata[12] == 0x01) {
          Serial.println("UDP send Success");
        } else {
          Serial.println("UDP send Error");
          return FALSE;
        }
        break;
      case (0x2FFF):
        Serial.println("checksum error");
        return FALSE;
        break;
      default:
        Serial.println("uni code error");
        return FALSE;
        break;
    }

  } else {
    Serial.println("recv data error");
    return FALSE;
  }

  return TRUE;
}



/********************************************************************************
*   Name     : cmd_send
*   Function : REQUEST command to bp35c0-j11
*   input    : cmd  - REQUEST command 
*   return   : TRUE/FALSE
*********************************************************************************/
boolean BP35C0J11::cmd_send(unsigned short cmd) {
  unsigned short hdr_chksum = uni_req[0] + uni_req[1] + uni_req[2] + uni_req[3] ;
  unsigned short dat_chksum = 0 ;
  unsigned short msg_length = 0 ;
  unsigned short dat_length = 0 ;
  unsigned short send_dat_size = 0 ;
  unsigned char data[128] = {0};

  unsigned char send_data[128] = {0} ;
  unsigned char cnt = 0 ;

  switch (cmd) {
    case (CMD_RESET):
      dat_length = 0;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_RESET + msg_length;
      dat_chksum = 0 ;
      msg_create(CMD_RESET , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif

      break;
    case (CMD_INI):
      dat_length = (unsigned short)4;
      msg_length = (unsigned short )( 4 + dat_length);
      hdr_chksum += CMD_INI + msg_length ;
      for (cnt = 0 ; cnt < dat_length ; cnt++ ) {
        data[cnt] = ini_data[cnt] ;
      }
      for (cnt = 0 ; cnt < dat_length ; cnt++) {
        dat_chksum += data[cnt];
      }
      msg_create(CMD_INI , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif
      break;
    case (CMD_PANA_SET):
      dat_length = (unsigned short)16 ;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_PANA_SET + msg_length;
      for (cnt = 0 ; cnt < dat_length ; cnt++ ) {
        data[cnt] = password[cnt] ;
      }
      for (cnt = 0 ; cnt < dat_length ; cnt++) {
        dat_chksum += data[cnt];
      }
      msg_create(CMD_PANA_SET , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif

      break;
    case (CMD_SCAN):
      break;
    case (CMD_HAN):
      dat_length = (unsigned short)8 ;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_HAN + msg_length;
      for (cnt = 0 ; cnt < dat_length ; cnt++ ) {
        data[cnt] = pair_id[cnt] ;
      }
      for (cnt = 0 ; cnt < dat_length ; cnt++) {
        dat_chksum += data[cnt];
      }
      msg_create(CMD_HAN , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, (msg_length + CMD_HDR_LEN));
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif


      break;
    case (CMD_PANA):
      dat_length = 0;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_PANA + msg_length;
      dat_chksum = 0 ;
      msg_create(CMD_PANA , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif
      break;
    case (CMD_CON_SET):
      dat_length = 1;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_CON_SET + msg_length;
      data[0] = 0x02 ;
      dat_chksum = data[0] ;
      msg_create(CMD_CON_SET , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif

      break;
    case (CMD_PORTOPEN):
      dat_length = 2;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_PORTOPEN + msg_length;
      for (cnt = 0 ; cnt < dat_length ; cnt++ ) {
        data[cnt] = my_port[cnt] ;
      }
      for (cnt = 0 ; cnt < dat_length ; cnt++) {
        dat_chksum += data[cnt];
      }
      msg_create(CMD_PORTOPEN , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif
      break;
    case (CMD_UDPSEND):
      send_dat_size = strlen(radiodata);           //<======送信データサイズを固定長の 4 から、変数のサイズに変更する。
      dat_length = 22 + send_dat_size ;
      msg_length = (unsigned short)(4 + dat_length);
      hdr_chksum += CMD_UDPSEND + msg_length;
      for (cnt = 0 ; cnt < 16 ; cnt++ ) {
        data[cnt] = mac_adr[cnt] ;
      }
      data[16] = my_port[0] ;
      data[17] = my_port[1] ;          // 送信元UDPポート :0x0123
      data[18] = dist_port[0] ;
      data[19] = dist_port[1] ;        // 送信先UDPポート:0x0E1A
      data[20] = (unsigned char)(send_dat_size >> 8);
      data[21] = (unsigned char)(send_dat_size & 0xFF); // send data length
      for (cnt = 0 ; cnt < send_dat_size ; cnt++) {
        data[22 + cnt] = radiodata[cnt];              // data
      }
      for (cnt = 0 ; cnt < dat_length ; cnt++) {
        dat_chksum += data[cnt];
      }
      msg_create(CMD_UDPSEND , msg_length , hdr_chksum , dat_chksum, data , send_data );
      Serial2.write(send_data, msg_length + CMD_HDR_LEN);
#ifdef DEBUG
      debugmsg( msg_length + CMD_HDR_LEN , send_data);
#endif
      break;
    default:

      break;
  }

  return TRUE;
}

/********************************************************************************
*   Name     : msg_create
*   Function : create Request command format
*   input    : cmd - Request command
*              msg_length - message data length
*              hdr_chksum - header checksum
               dat_chksum - data checksum
               *pdada     - wireless data
               *psend_data- request command format data
*   return   : -
*********************************************************************************/
void static BP35C0J11::msg_create(unsigned short cmd , unsigned short msg_length , unsigned short hdr_chksum , unsigned short dat_chksum, unsigned char *pdata , unsigned char *psend_data )
{
  unsigned char cnt = 0 ;

  for (cnt = 0 ; cnt < 4 ; cnt++) {
    psend_data[cnt] = uni_req[cnt];
  }
  psend_data[4] = (unsigned char)((cmd & 0xFF00) >> 8);
  psend_data[5] = (unsigned char)(cmd & 0xFF);
  psend_data[6] = (unsigned char)((msg_length & 0xFF00) >> 8);
  psend_data[7] = (unsigned char)(msg_length & 0xFF);
  psend_data[8] = (unsigned char)((hdr_chksum & 0xFF00) >> 8);
  psend_data[9] = (unsigned char)(hdr_chksum & 0xFF);
  psend_data[10] = (unsigned char)((dat_chksum & 0xFF00) >> 8);
  psend_data[11] = (unsigned char)(dat_chksum & 0xFF);

  if (msg_length > 4) {
    for (cnt = 0 ; cnt < msg_length - 4 ; cnt++)
    {
      psend_data[12 + cnt] = pdata[cnt];
    }
  }
}



/********************************************************************************
*   Name     : debugmsg
*   Function : output serial console for debug
*   input    : datalength - output data lengh
               psend_data - output data pointer
*   return   : -
*********************************************************************************/
void BP35C0J11::debugmsg(unsigned short datalength , unsigned char* psend_data) {
  unsigned char cnt = 0 ;

  for ( cnt = 0 ; cnt < datalength ; cnt++) {
    Serial.print(psend_data[cnt] , HEX);
    Serial.print(" ");
  }
  Serial.println("");
}

変更部分だけ抜き出してみます。

30、31行目には接続先のUSBドングルのパラメータを記入します。
実際には29~34行目まではすべて設定すべきパラメータですが、USBドングル側の設定をすでに反映させているため今回は変更の必要はありません。必要に応じて任意の値に変更してください。

unsigned const char pair_id[8] = {0x00 , 0x1D , 0x12 , 0x91 , 0x00 , 0x02 , 0x2E , 0x8B};   // 接続先MACアドレス
unsigned const char mac_adr[16] = {0xFE , 0x80 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 , 0x1D , 0x12 , 0x91 , 0x00 , 0x02 , 0x2E , 0x8B}; // 接続先IPv6アドレス

ここで 設定するpair_id[8]と、mac_adr[16]は、RPi側のプログラムで出力したUSBドングルのパラメータを使用します。
それぞれの値は以下のパラメータに対応しています。

USBドングルから出力されたパラメータ

b'EINFO FE80:0000:0000:0000:021D:1291:0002:2E8B 001D129100022E8B 23 5678 1 \r\n'

このパラメータのうち、「FE80:0000:0000:0000:021D:1291:0002:2E8B」がmac_adr[16]に、「001D129100022E8B」がpair_id[8]に対応しているのが判るかと思います。

35行目は、送信するデータと格納する変数を定義していますが、bp35c0_j11.hで定義しているためコメントアウトします。

//unsigned const char radiodata[4] = { 'T' , 'E' , 'S' , 'T'};         // コメントアウト

325行目は、送信するデータサイズを定義していますが、任意のデータを送信するため、送信するデータに合わせてデータサイズの定義を変更するように書き換えています。。

send_dat_size = strlen(radiodata);

以上の変更により、メインプログラムで指定したradiodataを送信できます。

RPiで受信したデータを読む

RPiでデータを読むためには、USBドングルを初期化プログラムで初期化したうえで、次のプログラムを実行してください。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Wi-SUN Rx program
import serial
import datetime
con=serial.Serial('/dev/ttyUSB0',115200)
print(con.portstr)
 
while True:
    str_bf=con.readline()
    if str_bf !="":
        print(str_bf)

このプログラムは受信したデータをだらだらと表示するだけのプログラムです。
よく探すと、Spresens側で送信したradiodataと同じ文字列が見つかるはずです。
下の画像は、RPi上のThonnyで受信用スクリプトを動かした様子です。多くのデータの最後に「send data」の文字が見えます。

キャプションを入力できます

ややこしいデータのなかから、ラベルをつけたデータを拾い出すプログラムは、デバイスプラスさんの第三回目の記事で紹介されていますので参考にしてください。

まとめ

ちょっと情報量の少ないボードだったため、当初は戸惑いもありましたがデバイスプラスさんの記事のお掛けでWi-SUN Add-onボード SPRESENSE-WiSUN-EVK-701を動かすことができました。ヨカッタ。

airpocketのアイコン画像
電子工作、プログラミング、AI、DIY、XR、IoT M5Stack / Raspberry Pi / Arduino / spresense / K210 / ESP32 / Maix / maicro:bit / oculus / Jetson Nano / minipupper etc
  • airpocket さんが 2022/08/23 に 編集 をしました。 (メッセージ: 初版)
  • Opening
    HakoHiroのアイコン画像 HakoHiro 2022/08/24

    当方もSpresense 活用コンテストに向けて開発中です。Wi-SUN ボードも入手したかったのですが、ご指摘のように対向通信するためのボードが高いので、コンテストの課題としては断念しました。でも、この通信規格の長距離性能は魅力です。出来れば 長距離性能の評価レポート等をゆっくりと後でも良いのでご報告いただければ嬉しいです。

    airpocketのアイコン画像 airpocket 2022/08/24

    実はUSBドングルがこんなに高価だと知らずに申し込みしちゃったんですw
    私も100m以上の通信距離を確保したくて選定していますので結果もレポートします。屋内外を結ぶ通信で、あまり見通しも良くない環境を想定してますので、どれくらいの性能を発揮してくれるか楽しみです。

    1 件の返信が折りたたまれています
ログインしてコメントを投稿する