2016年7月24日日曜日

I2C接続のDCモータドライバ

3年ぐらい前にRaspberry Piを自走式にして遊んでいた時のシャーシにつけてたモータドライバが煙を吹いてお亡くなりになられたのでI2C接続のモータドライバでも作ってみようかと。
モータドライバを作ると言ってもL298Nを使ったモジュールはeBayでお安く買ったのが在庫有ったのでこれこいつをPWMで制御できる程度のものでいいかなぁと
多分こいつも3年ぐらい前に買ったやつで、壊れたモータドライバのがだいぶ熱を持つし、パワーが無かったので買ってたやつかも。

Attiny85がたくさんあったのでとりあえずI2Cスレーブのライブラリと格闘してPWMが2ch分動かせたんだけど、結局L298で2ch分動かすにはロジックICを使うしかな差そうなんだけど残りの2chのデジタル出力がうまく行かなくて断念…
Arduino Pro microも在庫有ったんだけどi2cの関係で調べていたらオープンソースなモータドライバを見つけてしまったのでパクらせてもらった…

Grove I2Cモータドライバとして普通に売られてるものなんだけど、回路図もプログラムも公開されてるっぽい。
Atmega8はESC修理用のやつが有ったのでそいつを使用してみた。
ただしTQFPなのだ…

まずはおもむろにユニバーサル基板の上にカプトンテープを貼ってとりあえずプログラムを書き込んだ。
最初に書き込んだのはこの後配線が大変なことになりそうだから…
DIPスイッチはI2Cのアドレス変更に使うみたい。

端子を付けて完成。
スズメッキ線で結構裏面を使ってしまった。

3年前に作ってホコリをかぶっていたシャーシに再びモータドライバが!
このモータって3Vじゃなかったっけと言いつつこのモータドライバだと4.8V以上だったはずなのでエネループ4本で駆動してみた。
ちなみにL298Nモジュールにレギュレータ載ってるけど、今回のように低い電圧で動かす場合はジャンパで外電源に切り替えて別なところから供給しないとうまく動かなかった。
多分レギュレータが悪い…(本来このモジュールは12V駆動を想定してるらしい)

モータドライバがI2Cになったことにより上に何載せても良さそう。
Raspberry Piでもいいし、ESP-WROOM-02でもいいし。


2016年7月17日日曜日

USBでDigisparkと通信してみた。

Arduinoは基本的にシリアルポートを持っているけどDigisparkはV-USBでUSB対応担っているのでハードウェアなシリアルポートは持っていない。
一応DigiCDCを使えばUSBでの仮想シリアルポートも使えるようだけど安定性の問題からDigiUSBに取って代わられてるみたい。

DigiUSBはlibusbからDigisparkを通信できるようにしてるようなので、PC側は専用にツールを作らないといけなかったり。
一応PythonベースでArduinoのシリアルモニタのように使えるツールも公開されてるけど…

DigiUSBからDigisparkを制御する方法はC++やVC#での例も有ったので今回はVC#でDigisparkと通信してみた。

とりあえずGithubからサンプルをゲット。
こちらのセットの関数をフォームからゴニョゴニョしただけど簡単にGUIアプリからDigisparkを制御できそう。
ただしシリアルと違って受信イベントがないので定期的に読みに行くかしないと行けないかなぁ
とりあえずLEDのオンオフぐらい。

DigisparkもクローンがeBayでだいぶ安かったのでAttiny85取り用に何個か買って基板だけ放置されてたんだけど、最近Attiny85をお安くそこそこ手にれたので元に戻してみた。
クローンだとReset端子を入出力に使えないので今回の復活でちゃんとResetも使えるようにしたのでUSB接続でもIOが4ポート使える。

I2CもSPIもUSBのピンと被ってないし、センサのデータとか読み出せたりするかなぁ

2016年7月10日日曜日

Digisparkで測定器データをテンキーの代わりに入力

昨日に引き続き、最近ちょくちょくいじっているDigisparkで、ミツトヨのデジマチック出力をUSBキーボードとして認識させて入力するプログラムを作ってみた。
数値を入力したあとにエンターを押してくれるので、エクセルとかに測った数値を連続で入れるときは便利かも?

DigisparkはLEDがP1に接続されているのでこのピンはINPUT_PULLUPが使えないということでOUTPUTなREQを1番に割り当てた。
その他の入力ピンは外部プルアップを使わなくていいように、INPUT_PULLUPが可能な0,2,5を使用した。3と4はUSBで使用するのですべてのピンを使い切ったことに。
互換品のDigisparkとかでResetピンの5番ピンが使用できないやつがあるので、その場合はヒューズビット書き換えで5番ピンを使えるようにしておかないといけない。

とりあえず純正のスイッチ付きケーブルも使用できるように、P5につながってるスイッチがLOWになったときにデータを取得してUSBキーボードとして認識してるDigisparkがキーストロークするような感じにしてみた。純正ケーブルのスイッチは片方GNDに落ちてるのでINPUT_PULLUPしてLOWになったら反応という具合に。
ケーブルのピンアサインはフォーラムに落ちていたのを参考にさせてもらった。
#include "DigiKeyboard.h"
#define REQ 1
#define DAT 0
#define CLK 2
#define BTN 5//reset pin

void setup() {
  pinMode(REQ, OUTPUT);
  pinMode(DAT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(BTN, INPUT_PULLUP);
  digitalWrite(REQ,LOW);
 }

void loop() {
  DigiKeyboard.update();
  if(digitalRead(BTN) == LOW){
    byte mydata[14];
    digitalWrite(REQ, HIGH);
    for(uint8_t i = 0; i < 13; i++ ) {
      uint8_t k = 0;
      for (uint8_t j = 0; j < 4; j++) {
        while( digitalRead(CLK) == LOW) {
          DigiKeyboard.update();
        }
        while( digitalRead(CLK) == HIGH) {
          DigiKeyboard.update();
        }
        bitWrite(k, j, (digitalRead(DAT) & 0x1));
      }
      mydata[i] = k;
    }
    digitalWrite(REQ,LOW);
  
    uint8_t sign = mydata[4];
    uint8_t decimal = mydata[11];
    //uint8_t units = mydata[12];

    char buf[7];
    for(int lp = 0; lp < 6; lp++){
      buf[lp] = mydata[lp+5] + '0';
    }
    buf[6] = 0;
    float num = (atol(buf));
    switch (decimal){
      case 2:
        num = num / 100;
        break;
      case 3:
        num = num / 1000;
        break;
      case 4:
        num = num / 10000;
        break;
      case 5:
        num = num / 100000;
        break;
    }
    if(mydata[1] == 0xF && mydata[2] == 0xF && mydata[3] == 0xF){
      DigiKeyboard.sendKeyStroke(0);
      if(sign == 8){
        DigiKeyboard.print("-");
      }
      DigiKeyboard.print(num, decimal);
      DigiKeyboard.sendKeyStroke(KEY_ENTER);
      DigiKeyboard.delay(60);
      while(digitalRead(BTN) == LOW){
        DigiKeyboard.update();
      }
    }
  }
}
前回はpowを使って小数点の位置を計算していたんだけどpowを使うとスケッチの容量がオーバーしてしまうのでswitch caseで対応した。さらに負の値のときのための計算も容量食うので"-"を入力するかどうかの判別をifでやるようにしたらだいぶ減ったのでこちらも変更。
前回よりもだいぶローテクになってるような気がしたんだけどこっちのほうがスケッチが小さくなったので盛り込んだ。

DigisparkのUSBキーボードエミュレーションはDigiKeyboard.update()を定期的に呼ばないと不明なデバイス扱いになってしまうので、データ受信部の中でタイムアウトとか設定していないのでとりあえずwhileの中でもDigiKeyboard.update()を読んでデバイスの認識がおかしくならないように対応。DigiKeyboard.update()がどの程度時間を食うのかわからなかったけど動いているので良しとしよう…
測定器がつながっていないと無限ループするけど不明なデバイスにはならない!

もしかしたら一旦数値をfloatにしているところも直せば小さくなるかもしれない。どうせキーを1文字ずつ打てばいい話だし…。でもこっちのほうが小数点の桁数が変わる時とかも楽だったのでそのまま。

一応2つ目から4つ目のデータが0xFかどうか確認するようにしておいた。変なデータが来ても安心?

純正のUSBインプットツールは結構高級品みたいだけどアイソレーションとかいろいろ高級な回路になってるのかなぁ?PCからノイズ乗ったりしたらUSBアイソレータとか使えばいいのかな?


2016年7月9日土曜日

ミツトヨのデジマチック出力をArduinoで受信してみる

電池入れ替えたんだけど動かないというミツトヨのデジタルインジケータを借りてきたんだけど、原因は前の電池が液漏れしていた?ので端子の接触不良だけだったという落ち…
ミツトヨの製品ってノギスもそうだけど電池の入れるところめっちゃ金ピカ。多分メッキがしっかりしているのかな?
なので今回は普通にアルコールつけた綿棒とか爪楊枝で傷をつけないようにゴシゴシしただけでピッカピカに!治ってしまった。電池もSR44っていうピッカピカのやつ使ってる。さすが高級な計測器。

ネタが無くなってしまったので最初電池と間違って開けた蓋に出力が出ていたので調べてみると専用のケーブルとオプションでボタンを押すと測定値をエクセルに入力したりできるらしい。

この出力ポート、デジマチック出力と言うみたいだけど海外だとSPC Outputって呼ばれている?ようでArduinoでデータを取得してる例があったので試してみた。しかもスケッチまで公開してくれている!データ的には4bitずつのnibbleで数値はBCD、符号と小数点の位置が含まれてる感じっぽい。

デジマチック出力は同期式シリアル通信のようでClock、Dataとデータ要求のREQとGNDの4本線が出てる。コネクタが5ピンなのはホールドユニット用かな?REQは電池に100kΩ程度でプルアップされていて、ClockとDataはオープンコレクタらしい。REQをGNDに落とすために2SC1815などのNPNトランジスタを使用し、ClockとDataは内蔵プルアップでも行けそうだったので、Arduinoのピン設定でINPUT_PULLUPにしてみた。

純正ケーブルだと10Pinのピンヘッダ用出力なんだけど、今回はお試しだったので端子形状に合わせたスポンジ10x5mmを突っ込んでそこにArduino用のピンヘッダケーブルを無理やり差し込んでつないだ。(良い子は真似しないように)
ちなみに純正ケーブルでスイッチ付きのやつは10pinのところの4番Pinに出てるみたいで、片方はGNDに落ちてるらしい。つまりこのスイッチを使いたければこいつもINPUT_PULLUPにしておいて、LOWになっていたら押されてる判定ができる。

上記サイトのコードをコピペしてもうまく動かなかったのでちょっと修正。
#define REQ 5
#define DAT 2
#define CLK 3

void setup() {
  Serial.begin(115200);
  pinMode(REQ, OUTPUT);
  pinMode(DAT, INPUT_PULLUP);
  pinMode(CLK, INPUT_PULLUP);
  digitalWrite(REQ, LOW);
}

void loop() {
  byte mydata[14];
  digitalWrite(REQ, HIGH);
  for (uint8_t i = 0; i < 13; i++ ) {
    uint8_t k = 0;
    for (uint8_t j = 0; j < 4; j++) {
      while ( digitalRead(CLK) == LOW) { // CLK HIGHが来るまで待つ
      }
      while ( digitalRead(CLK) == HIGH) { // CLK LOWが来るまで待つ
      }
      bitWrite(k, j, (digitalRead(DAT) & 0x1)); // DATピンを読んで順番に格納(最初に読んだのがMSB)
    }
    mydata[i] = k;
  }
  digitalWrite(REQ, LOW);

  uint8_t sign = mydata[4];//符号
  uint8_t decimal = mydata[11];//小数点の位置
  //uint8_t units = mydata[12];

  //char buf[7];
  //for (int lp = 0; lp < 6; lp++) {
  //  buf[lp] = mydata[lp + 5] + '0';
  //}
  //buf[6] = 0;

  char buf[9];
  if ((mydata[0] == 0xF) && (mydata[1] == 0xF)) {
    for (uint8_t lp = 0; lp < 6; lp++) {
      buf[lp] = mydata[lp + 5] + '0';
    }
    buf[6] = 0;
  } else if ((mydata[0] == 0x8) && (mydata[1] == 0xF)) {
    //デジマチック2フォーマット(8桁対応
    for (uint8_t lp = 0; lp < 2; lp++) {
      buf[lp] = mydata[lp + 2] + '0';
    }
    for (uint8_t lp = 0; lp < 6; lp++) {
      buf[lp + 2] = mydata[lp + 5] + '0';
    }
    buf[8] = 0;
  }
  float num = ((atol(buf) / pow(10, decimal)) * ((sign - 4) / 4));
  Serial.println(num);
}
とりあえずこんな感じでArduino UNOで動いた。割り込みを使った例もあったけどそこらへんは用途による?とりあえず修正ついでに小数点の位置と符号にも対応しておいた。

Arduino LeonardoでUSBキーボードとして認識させれば純正のデバイスみたいにボタンを押したら数値を入力してくれるようなデバイスが作れそう…

追記:
スケッチをデジマチック d2(8桁対応)に一部変更しました。(未検証)