0

node.jsとarduinoでロータリーエンコーダー

ロータリーエンコーダー

2相出力のふつうの秋月で200円で売ってるやつを使った


マウスのホイールなどの中にも入っているセンサーで、上のツマミの部分を回すと両端の2つのピンからon-on・on-off・off-on・off-offの4状態を送ってくる。
中央のピンに5V入れて、両端の出力ピンは10kΩの抵抗でGNDにプルダウンしてArduinoのデジタルIOピンに入れた。

以前にも使ったことある。


プログラム

nodeとarduino-firmata npmでarduinoを制御した


こういうRotaryEncoderクラスを作った。回転させると”rotate”イベントを発行してくれる。

RotaryEncoder.coffee
events = require 'eventemitter2'
ArduinoFirmata = require 'arduino-firmata'

module.exports = class RotaryEncoder extends events.EventEmitter2
constructor: (@arduino, @pinA, @pinB) ->
console.log "set rotary encoder pin : #{@pinA} and #{@pinB}"
@state = 0

if @arduino.isOpen()
@initPins()

@arduino.on 'connect', =>
@initPins()

@arduino.on 'digitalChange', (e) =>
@pastState = @state
@state = @getState()
if (@state+3+1)%3 is @pastState
@emit 'rotate', 'right'
else if (@state+3-1)%3 is @pastState
@emit 'rotate', 'left'

initPins: ->
@arduino.pinMode @pinA, ArduinoFirmata.INPUT
@arduino.pinMode @pinB, ArduinoFirmata.INPUT

getState: ->
if @arduino.digitalRead @pinA
if @arduino.digitalRead @pinB
return 2
else
return 0
else
if @arduino.digitalRead @pinB
return 1
else
return 3


使う
path = require 'path'
ArduinoFirmata = require 'arduino-firmata'
RotaryEncoder = require path.resolve 'RotaryEncoder'

arduino = new ArduinoFirmata().connect()
rotenc = new RotaryEncoder arduino, 4, 3 ## digital pin 4,3を使う

rotenc.on 'rotate', (direction) ->
console.log "#{direction}に回った"

arduino-firmata npmはデジタルIOピンの状態が変化した時に発行してくれる、on “digitalChange”イベントがあるのでこういうハードウェアも簡単にイベント駆動で扱える。

1

Arduino – ロータリーエンコーダ改

Arduino – ロータリーエンコーダを回すのコードがアレだったのでシンプルに書き直した。回路はそのままで。

char rot_state; // 状態 0~3
char rot_pinA = 8; // PinA of RotaryEncoder
char rot_pinB = 9; // PinB
void setup()
{
  Serial.begin(9600);
  pinMode(rot_pinA, INPUT);
  pinMode(rot_pinB, INPUT);
  rot_state = rotary_getState(rot_pinA, rot_pinB); // 現在の状態を保存しておく
}
void loop()
{
  // ロータリーエンコーダの状態を監視して、PCにシリアル通信で送る
  char rot_past = rot_state;
  rot_state = rotary_getState(rot_pinA, rot_pinB); // 今の状態
  char rot_dir = rotary_getDir(rot_state, rot_past); // 回転方向
  switch(rot_dir){
    case 2:
      Serial.println("R"); // 右回転
      break;
    case 1:
      Serial.println("L"); // 左回転
      break;
  }
  delayMicroseconds(10);
}
/* ロータリーエンコーダの状態番号0~3を取得する */
char rotary_getState(char pinA, char pinB){
  // 2つのピンからの入力状態をチェック
  if(digitalRead(pinA)){
    if(digitalRead(pinB)) return 2;
    else return 0;
  }
  else{
    if(digitalRead(pinB)) return 1;
    else return 3;
  }
}
/* 1つ前の状態と比較して、回転方向を取得する */
char rotary_getDir(int state, int past_state){
  if((state+3+1)%3 == past_state) return 1; // 左回り
  else if((state+3-1)%3 == past_state) return 2; // 右回り
  return 0; // 変化無し
}

1

Arduino – ロータリーエンコーダを回す

Arduinoでロータリーエンコーダの回転を検出した。

ロータリーエンコーダは可変抵抗と異なり、アナログ値を出すのではなく回転方向を2byteでデジタル出力するので、無限に回転させる事ができる。マウスのホイールもロータリーエンコーダで出来ている。

Source Code (Arduino 0009 Alpha / ATmega8 16MHz)

基本的に回路もプログラムもATMega8でやったのと全く同じ方法でできた。

s.h.log: AVR – mega8でロータリーエンコーダを回す

ロータリーエンコーダーの真ん中の足をGNDに接続し、左右の足をそれぞれマイコンのピンに接続。また、左右の足とマイコンのピンの間で、10kΩの抵抗を使ってプルアップする(VCCに接続する)

まあ写真の通り。

インクリメンタル型ロータリーエンコーダーは2本の線で4種類の回転状態を出力する。1クリック回す(カチッと言う)と、4種類の状態が一回りする。

とりあえず状態に0,1,2,3と名前をつけて、10ms毎ぐらいに状態を見に行くと、状態が1増えていたら右に回転、減っていたら左に回転みたいな感じでわかる。

ただし、Arduinoにはタイマーが無いので、メインループ loop() の中で delayMicroseconds(10); を呼んで、10ミリ秒を作っている。

なので、ロータリーエンコーダと同時にタイミングが難しい処理を行うとおかしくなるかもしれない。

例えば赤外線の受信とか。

Read more

0

AVR – mega8でロータリーエンコーダを回す

2週間ぐらい前の事なので曖昧だが、ATmega8のタイマ/カウンタ1で秋月で100円のインクリメンタル型ロータリーエンコーダーを使ってみた。

DSC00968.jpg

写真下のマイコンは関係無い。

既にTiny26Lでやっている人がいたので参考にしながらさくっと書いた。

ソースコード(AVR-GCC)

んで数日後に書き直した。

ソースコード(AVR-GCC)動画

なんで書き直したかは記憶が定かではないのだが、とりあえず大した違いは無いしどちらも動く。昨日も古い方でデモしてきたばかりだし。

動作設定としてはこんな感じで

/** 動作設定 **/
#define FOSC 8000000 // 8MHz
#define BAUD 9600 // 9600bps
#define MYUBRR FOSC/16/BAUD-1 // UART分周率
volatile char uart_recv_data; // UART受信データ
/** ロータリーエンコーダ設定 **/
volatile char rot_state;
#define ROT_TCNT1 65536-32+1 // 8MHz 1024分周で1ms(1000Hz)

タイマ/カウンタ1を初期化する。main()から1回だけ呼び出す必要がある。

/* タイマ/カウンタ1オーバーフロー割り込み設定 */
void overflow1_init(void){
TCCR1B = (1<<CS12)|(0<<CS11)|(0<<CS10); // 256分周
sbi(TIMSK,TOIE1); // タイマ/カウンタ1オーバーフロー割り込み許可
TCNT1 = ROT_TCNT1; // 1ms毎に割り込み設定(ロータリーエンコーダー用)
}

ロータリーエンコーダー監視スレッド。割り込みなので書いておけば一定時間毎に実行される。

/* タイマ/カウンタ1オーバーフロー割り込み
ロータリーエンコーダー監視用 */
SIGNAL(SIG_OVERFLOW1){
TCNT1 = ROT_TCNT1; // タイマ/カウンタ初期化 1ms毎に割り込み設定
char past_state; // 1回前の状態
past_state = rot_state;
if(bit_is_set(PIND,PD3)){
if(bit_is_set(PIND,PD4)) // HH
rot_state = 1;
else // HL
rot_state = 0;
}
else{
if(bit_is_set(PIND,PD4)) // LH
rot_state = 2;
else // LL
rot_state = 3;
}
if((rot_state+3+1)%3 == past_state){ // 右回り
uart_send_str(&qute;R&qute;);
}
else if((rot_state+3-1)%3 == past_state){ // 左回り
uart_send_str(&qute;L&qute;);
}
}

書いてて10ms毎じゃない気がしてきたが、速い分には問題ないし、他にもタイマを回しているのだったら色々タイミングもずれてくると思うので適宜対処して下さい。

ロータリーエンコーダーの真ん中の足をGNDに接続し、左右の足をそれぞれマイコンのピンに接続。また、左右の足とマイコンのピンの間で、10kΩの抵抗を使ってプルアップする(VCCに接続する)

まあ写真の通り。

インクリメンタル型ロータリーエンコーダーは2本の線で4種類の回転状態を出力する。1クリック回す(カチッと言う)と、4種類の状態が一回りする。

とりあえず状態に0,1,2,3と名前をつけて、10ms毎ぐらいに状態を見に行くと、状態が1増えていたら右に回転、減っていたら左に回転みたいな感じでわかる。

■参考サイト

113.ロータリーエンコーダを使う

2343e-conv

AVR試用記-ロータリーエンコーダ

ELM – 接点入力と処理のコツ – ロータリーエンコーダの使い方

ところでタイマ/カウンタ、以前書いた時はイマイチわかってなかったのだが、タイマ割り込みが発生するのはTCCNxが8bitなら256、16bitなら65536になった時起こるとの事。つまりタイマ/カウンタオーバーフロー割り込みとはそういう事。

TCCNxが増えるタイミングは分周率(TCCRx)で決まる。64分周だったら、64クロック毎に1増える。分周なし、8、64、256、1024分周まで5段階あるのでそれでタイミングを調節できる。

また、TCCNxに直接書き込み、カウンタの初期値を設定する事もできる。これは割り込みが起こった瞬間に次の割り込みに備えて代入しなおす必要がある。

この2種類の方法で割り込みタイミングを操作する。