DIY

X Shifterのスイッチユニットを自作しましたVer1.1 (使った部材やソースコードなど)

はじめに

X Shifterの通信仕様について(2020/07/11 記事修正しました) はじめに 以前から気になっていた機械式コンポを無線電動化するキットX Shifterを購...
で通信仕様を調べたので、手持ちのArduinoNano33BLEでスイッチユニットを自作してみました。
このページでは使った部材やFWおよび作るうえでのメモ書きなどを残しておこうと思います。

材料

・タカチ防水ケースWC72-21-C[マルツで購入]
マルツエレクトロニクス

・arduino nano33 ble[mouserで購入]


・ユニバーサル基盤[秋月電子で購入](使うときは必要なサイズにカッターなどで加工)
商品ページ

・L字ピンヘッダ[秋月電子で購入]
商品ページ

・L字ピンソケット[秋月電子で購入]
商品ページ

・タクトスイッチ[秋月電子で購入]
商品ページ

・ケーブル[近所のホームセンターで購入。50円/m]

低消費電力化するために

この内容は必須ではないです。電池の消耗を抑えるために以下の2つを行いました。
以下の処理を行うとFWの書き込みなどで不都合が出る可能性がありますのでお気を付けください。現在の消費電流は、接続完了後、定常状態で1.3mA程度。システムOFF時10uA程度。

Arduinoボードの3.3Vパターンカットを行う。

↓の赤で囲った箇所の細い個所をカッターなどでカットします。USBの電源から3.3Vラインへの供給がなくなります。(USBからICへの電源供給ができなくなります)

main.cppのデバッグシリアル通信を無効化

arduinoNano33BLEのライブラリをインストールしているフォルダに行き、
main.cppの40行目と41行目の
PluggableUSBD().begin();
SerialUSB.begin(115200);
をコメントアウトする。
これでSerialドライバがオフになるみたいです。FW書き込みなどでPCと接続する際はリセットボタンを2回押してブートモードにしないとPCから認識しなくなるので注意。

ちなみに自分は↓のフォルダにインストールされて、
C:\Users\{ユーザー名}\AppData\Local\Arduino15\packages\arduino\hardware\mbed\1.1.4
その中の\cores\arduinoにmain.cppがありました。

FW(ソースコード)

使用する場合は、ELINK_MACADDRESSに自分のELinkのMACアドレスに置き換えてください。

FWの動作として

①起動後、MACアドレスでスキャン。

②見つかったら通信確立(③へ)する。20秒間見つからなかったらシステムOFF(④へ)する。

③通信確立後、スイッチを押すと(PIN10or11のLowエッジ検出)と割り込み内でフラグを立て、メインループでElinkへの変速指示を送信。10分間スイッチを押さないとシステムOFF(④へ)する。

④システムOFF後は、スイッチを押す(PIN10or11のLowエッジ検出)と再起動して①へ。

またおまじないコード部は私の環境では動作しましたが、すべてのX Shifterで共通かは検証できていませんのでご了承ください。※動いたor動かなかったの情報いただければうれしいです笑
2020/07/23 コードの整理。不要なコードの削除やコメントの追加

#include <ArduinoBLE.h>

/*バージョン*/
#define FW_VERSION "1.3.2"
/*接続用*/
#define ELINK_MACADDRESS "ef:e3:c8:09:96:eb" /*使用するElinkのMACアドレス*/
#define ELINK_LOCALNAME "Elin" /*現在未使用*/

#define PIN_SHIFT_UP    10
#define PIN_SHIFT_DOWN  11

#define SLEEP_ENTERTIME_SEARCH 20000 /*ms*/
#define SLEEP_ENTERTIME 600000 /*ms*/

/*BLE-UUID*/
#define ELINK_SERVICE_UUID "1523" /*Elink Service UUID*/
#define ELINK_CHARACTERISTIC_UUID "1524"  /*Elink 変速用Characteristic UUID*/
#define ELINK_READCHARACTERISTIC_UUID "1525" /*Elinkからの返信用Characteristic UUID*/

bool IsConnected = false;
byte sGearChangePacket[] = { 0x21, 0x00, 0x00, 0x00};
volatile bool sShiftChange = false;

BLEService sElinkService;
BLECharacteristic sElinkMotorCharacteristic;
BLECharacteristic sElinkReadCharacteristic;
BLEDevice sBLEPeripheral;
unsigned long sleepTimer;

bool Elink_Connect(BLEDevice peripheral) {
  int failCnt = 0;
  /*接続試行*/
  while (!peripheral.connect()) {
    failCnt++;
    if (failCnt > 2) {
      return false;
    }
  };

  /*Discover attributesをしないとサービスが見えてこない。*/
  if (peripheral.discoverAttributes()) {
  } else {
    peripheral.disconnect();
    return false;
  }
  /*サービスの取得*/
  if (peripheral.discoverService(ELINK_SERVICE_UUID)) {
    sElinkService = peripheral.service(ELINK_SERVICE_UUID);
  } else {
    peripheral.disconnect();
    return false;
  }
  /*Characteristic(1524)の取得*/
  if (sElinkService.hasCharacteristic(ELINK_CHARACTERISTIC_UUID)) {
    sElinkMotorCharacteristic = sElinkService.characteristic(ELINK_CHARACTERISTIC_UUID);
  }
  else {
    peripheral.disconnect();
    return false;
  }

  /*Characteristic(1525)の取得*/
  if (sElinkService.hasCharacteristic(ELINK_READCHARACTERISTIC_UUID)) {
    sElinkReadCharacteristic = sElinkService.characteristic(ELINK_READCHARACTERISTIC_UUID);
  }
  else {
    peripheral.disconnect();
    return false;
  }

  /*Elinkにおまじないコードを送る*/
  byte sendBytes[] = { 0xaa, 0xcf,0x00,0x00,0x5d,0x4a,0x81,0x89,0x32,0xf7,0x52,0x12,0x36,0x00,0x00,0x00,0x00,0x00};
  if(sElinkMotorCharacteristic.writeValue(sendBytes, sizeof(sendBytes))){
  }
  return true;
}

/*
*割り込み内でBLEのwrite処理を行うとCPUリセットがかかる
*/
void ISR_ShiftUp() {
  if(IsConnected){
    sGearChangePacket = 0x02U;
    sShiftChange = true;
  }
}
void ISR_ShiftDown() {
  if(IsConnected){
    sGearChangePacket = 0x03U;
    sShiftChange = true;
  }
}

void CpuSleep(){
  NRF_POWER->SYSTEMOFF = 1;
}

void timerReset(){
  sleepTimer = millis();
}

void timerCheck(unsigned long tick){
  unsigned long now = millis();
  unsigned long divTime = now - sleepTimer;
  if(divTime > tick){
    CpuSleep();  
  }
}

void setup() {
  digitalWrite(LED_PWR, LOW);

  pinMode(PIN_SHIFT_UP, INPUT_PULLUP);
  pinMode(PIN_SHIFT_DOWN, INPUT_PULLUP);
 
  attachInterrupt(digitalPinToInterrupt(PIN_SHIFT_UP),ISR_ShiftUp, FALLING);
  attachInterrupt(digitalPinToInterrupt(PIN_SHIFT_DOWN), ISR_ShiftDown, FALLING);

  // begin initialization
  if (!BLE.begin()) {
    while (1);
  }
  BLE.setConnectionInterval(0x0006, 0x0c80);

  // Elinkをスキャン
  BLE.scanForAddress(ELINK_MACADDRESS);

  timerReset();
}

void loop() {

  if (IsConnected == true ) {
    /*接続中*/
    delay(100);
    if(!sBLEPeripheral.connected()){
      /*通信切断されていた場合、再接続処理*/
      ELINK_DisConnect();
      return;
    }
    if(sShiftChange == true){
      timerReset();
      sShiftChange = false;
      while(!sElinkMotorCharacteristic.writeValue(sGearChangePacket, sizeof(sGearChangePacket))){
      }
    }
    timerCheck(SLEEP_ENTERTIME);
  } else {
    /*スキャン中*/
    ELINK_DuringScan();
    timerCheck(SLEEP_ENTERTIME_SEARCH);
  }
}

void ELINK_DuringScan() {
  BLEDevice peripheral = BLE.available();
  if (peripheral) {
    BLE.stopScan();/*スキャンをストップしてからConnectしないとConnectに高確率で失敗する*/
    if (!Elink_Connect(peripheral)) {
      /*接続に失敗したら再スキャン*/
      BLE.scanForAddress(ELINK_MACADDRESS);
    } else {
      /*接続完了*/
      IsConnected = true;
      sBLEPeripheral = peripheral;
      timerReset();
    }
  }
}

void ELINK_DisConnect() {
  IsConnected = false;
  BLE.scanForAddress(ELINK_MACADDRESS);
  timerReset();
}

回路図

回路図というほど大したものではないですが、、、

完成したモジュールの外観図

ご参考までに。。。


参考にさせていただいたサイト

ArduionoBLEライブラリ リファレンス
Arduino NANO 33 BLEをArduinoIDEで使えるようにする方法
Arduino Nano33 BLEの低消費電力化について(英語のArduinoFourum・)