Arduino と ATtinyを使って I2C Slaveデバイスを作ってみる (2) 作ってみる編
めりーくりすまーす!
世の中IoTとかAIとか騒がしいですね!
ArduinoとATtinyでI2Cデバイスとか作れば、たぶんIoTで使えるよね?使おうね?(強引)という記事の2回目です。
前回は準備編として環境構築からLチカまで実施しました。
今回は、前回作ったLチカからI2Cへの対応部分を書こうと思います!
今回の内容
- 今回のゴール
- ところでI2Cとは?
- 用意するもの
- Step.1 : ATtiny85へスケッチを書き込む
- Step.2 : マスター側のArduinoへスケッチを書き込む
- Step.3 : マスター側ArduinoとSlave側ATtiny85を接続する回路をつくる
- Step.4 : 電源ON!
- おまけ : CHIRIMEN for Raspberry Pi 3 でも動かしてみる
今回のゴール
今回は、ATtinyを使ったI2C Slaveデバイスの例として、I2C経由でコマンドを受け取ったら音を鳴らすデバイスを作ってみたいと思います。
I2Cコマンドを送る側のデバイスは何でも良いのですが、手っ取り早くArduino UNOを利用することで話しを進めます。
Arduino UNOからも直接音が出せるので、この構成だけだとあまり意味がないように思えるかもしれませんが、こうしてI2C Slave Deviceとしてモジュール化しておくことで例えばRaspberry Piなど他のボードからも利用可能な部品が自作できるようになります。
I2Cとは?
I2Cとは2線式の同期式シリアル通信インタフェースです。「アイ・スクエア・シー」とか「アイ・ ツー・シー」などと読みます。
SDA(シリアルデータ)と SCL(シリアルクロック)の2本の線で通信を行います。
上図のように、i2cのSDA、SCLは複数のモジュール間で共有します。(「I2Cバス」と言います。)
I2Cではマスターとスレーブの間で通信が行われます。常にマスター側からスレーブ側に要求が行われます。スレーブ側からマスター側へ要求を行うことはできません。
マスターは、スレーブが持つ「SlaveAddress」を用いて、特定のスレーブとの通信を行います。
このため、同じI2Cバス上に同じSlaveAddressのスレーブを繋ぐことはできません。
通信するモジュール同士が同一基板上にない場合には、SDA、SCLの2本の通信線に加え電源やGNDの線を加えて4本のケーブルを用いて接続するのが一般的です。
詳細は下記をご参照ください。
- I2C - Wikipedia
- I2Cバス仕様書 最新版(日本語、English)
- I2Cの使い方(後閑哲也氏サイト)
用意するもの
- 前回 使ったものすべて。
- I2Cマスター用のArduino (書き込み用のArduino UNOとは別のものを用意) ※私は手元にたまたま転がってた Sparkfun Pro Microの互換機を使いましたが、Arduino UNOがもう1台あるという前提で本稿は進めます。もちろん他のArduinoでもOK。
- なんか雑にブレッドボードに刺さるように加工済みのスピーカー
- ブレッドボードもう1つ。と、ジャンパー線少々
- 4.7kΩ 抵抗 x 2
加工済みスピーカー
Step.1 : ATtiny85へスケッチを書き込む
それでは早速、ATtiny85にスケッチを書き込んでみましょう。
書き込み方は、前回 同様 Arduino UNO ISP 経由となります。
今回用意したのは、下記のような仕様のスケッチです。
- I2C Slave Address 0x40
- writeコマンドに対応
- writeコマントを受け取ったら、そのデータ(ノートナンバー) に応じたビープ音を鳴らす
- ノートナンバーは 24〜108 の間のみ対応。その範囲外のデータを渡すと音を止める
なお、ATtiny85は 8Mhzに設定しておいてください。(前回のLチカと同様)
#include <Wire.h>
#include <avr/pgmspace.h>
// http://www.technoblogy.com/show?KVO
#define Note_C 239
#define Note_CS 225
#define Note_D 213
#define Note_DS 201
#define Note_E 190
#define Note_F 179
#define Note_FS 169
#define Note_G 159
#define Note_GS 150
#define Note_A 142
#define Note_AS 134
#define Note_B 127
uint8_t notes[] = {
Note_C,Note_CS,Note_D,Note_DS,Note_E,Note_F,Note_FS,Note_G,Note_GS,Note_A,Note_AS,Note_B
};
const int SPEAKER = 1; // Speaker PIN
uint8_t note = 0xFF; // Sound Off
uint8_t trig = false;
void sTone(uint8_t note, long duration)
{
uint8_t divisor = notes[note % 12];
uint8_t octave = 0;
TCCR1 = 0x90;
if((note >= 24)&&(note <= 108)){
octave = note / 12;
}
if(octave != 0){
TCCR1 = 0x90 | (11-octave); // for 8MHz clock
OCR1C = divisor-1;
if(duration > 0){
delay(duration);
TCCR1 = 0x90;
}
}
}
void sNoTone(){
TCCR1 = 0x90;
}
void setup() {
Wire.begin(0x40);
Wire.onReceive(receiveEvent);
pinMode(SPEAKER, OUTPUT);
}
void loop() {
delay(5);
if(trig == true){
if(note < 0x80){
sTone(note,-1);
}else{
sNoTone();
}
trig = false;
}
}
void receiveEvent(int howMany) {
if(Wire.available()){
note = Wire.read();
trig = true;
}
while (Wire.available()){
Wire.read();
}
}
スケッチの解説
-
<Wire.h>
で、I2Cに関するクラスWire
を有効にしています。 - 音の出力に
tone()
を使おうと思ったのですが、どうもATTinyCore
のtone()だと音痴なようなので、参考サイト の方式で音を鳴らしています。 - 音の出力には、
1
番PINを使います。ここにスピーカーの+側を直結します。スピーカーの-側はGNDへ。 -
receiveEvent()
でI2Cマスターからのwrite
コマンドを受信しています。
さて、このままだとまだテストできません。
続けてマスター側を作りましょう。
一旦ここで、ISP書き込み用のArduinoのUSBケーブルはPCから抜いておきます。
Step 2 : マスター側のArduinoへスケッチを書き込む
I2Cマスター用にArduino をもう一台用意しましょう。本当は何でも良いのですが、説明の都合上、UNOがもう一台あることにします。
私は、Sparkfun Pro Micro という Arduinoの互換機のさらに互換機(中華製で安くよく出回ってる ATmega32u4搭載ボード) を利用しました。
ATtiny85へのISP書き込み用のArduino UNOをマスター用にも使おうとすると、いちいち ArduinoISPを書き直ししなければならないので、もう1台用意した方が良いです。
#include <Wire.h>
void setup()
{
Wire.begin();
}
typedef struct stSONG {
uint8_t note;
uint8_t duration;
}SONG;
// きよしこのよる
SONG song[] = {
{55,3},
{57,1},
{55,2},
{52,5},
{200,1},
{55,3},
{57,1},
{55,2},
{52,5},
{200,1},
{62,3},
{200,1},
{62,2},
{59,5},
{200,1},
{60,3},
{200,1},
{60,2},
{55,5},
{200,1},
{57,3},
{200,1},
{57,2},
{60,3},
{59,1},
{57,2},
{55,3},
{57,1},
{55,2},
{52,5},
{200,1},
{57,3},
{200,1},
{57,2},
{60,3},
{59,1},
{57,2},
{55,3},
{57,1},
{55,2},
{52,5},
{200,1},
{62,3},
{200,1},
{62,2},
{65,3},
{62,1},
{59,2},
{60,6},
{64,6},
{60,2},
{55,2},
{52,2},
{55,3},
{53,1},
{50,2},
{48,10},
{200,14}
};
void loop()
{
int cnt;
int dataSize = sizeof(song) / sizeof(struct stSONG);
for(cnt = 0;cnt < dataSize; cnt++){
playNote(song[cnt].note);
delay(300*song[cnt].duration);
}
}
void playNote(byte note){
Wire.beginTransmission(0x40);
Wire.write(note);
Wire.endTransmission();
}
こちらは先ほどATtiny85で作ったI2C Slaveデバイスに対して「きよしこのよる」の曲データを流し込むスケッチになっています。
マスター側のArduinoへ書き込みましょう。
スケッチの解説
-
I2Cマスター
にするために、<Wire.h>
をインクルードしてWire
を有効にしています。Wire.begin()
に対して引数を与えないとマスターになります。 -
Wire.beginTransmission(0x40)
で、スレーブアドレス0x40
番のスレーブデバイスと通信を開始しています。 -
Wire.write()
でデータを1byte書き込んでいます。今回はメロディーの音程データを書き込んでいます。 -
Wire.endTransmission()
スレーブデバオスへのデータ書き込み終了の合図です。
Step.3 : マスター側ArduinoとSlave側ATtiny85を接続する回路をつくる
スレーブ側、マスター側両方のスケッチ書き込みが終わりましたので、ここからは回路を作りましょう。
まずは、マスター側Arduino と PCの間のUSBケーブルを抜いてArduinoの電源を落とします。
とりあえず、下図のように配線してみてください。
ISP書き込み用のブレッドボードから、ATtiny85だけ抜いて別のブレッドボード上で配線します。
そのようにしておかないと、バグがあった時にISP書き込み用の回路をまた作らねばならず、超面倒です。
- マスター側Arduino SDA → ATtiny85 5番PIN
- マスター側Arduino SCL → ATtiny85 7番PIN
- マスター側Arduino 5V → ATtiny85 8番PIN
- マスター側Arduino GND → ATtiny85 4番PIN
- ATtiny85 6番PIN → スピーカーの + 側
- ATtiny85 GND → スピーカーの - 側
Step.4 : 電源ON!
配線が終わったら、マスター側のArduinoのUSBケーブルをPCに接続!
いきなりメロディーが流れてくるはずです。
まずは、お疲れ様でした。
まとめ
- WireライブラリでI2Cデバイスも簡単につくれる!
1日遅れなのにやっつけ風味ですいません...
ちょっと図とか足りないので書き足す予定...
おまけ : CHIRIMEN for Raspberry Pi 3 でも動かしてみる
せっかくI2C Slave Deviceができた (?) ので、CHIRIMEN for Raspberry Pi 3でも動かしてみましょう。
CHIRIMEN for Raspberry Pi 3は、Raspberry Pi 3のブラウザからGPIOやI2C経由でデバイス制御ができるプロトタイピング環境です。
準備
CHIRIMEN for Raspberry Pi 3
に必要なモノや使い方は下記を参照してください。
繋ぎ方
今回作った ATtiny85 + スピーカー なデバイスを図のようにRaspberry Pi 3と繋ぎます。
接続はRaspberry Piの電源を切った状態でお願いします。
コード
上記図のように接続が終わったら、Raspberry Pi 3の電源を入れましょう。
起動が終わったら、ブラウザを立ち上げ、下記URLにアクセスします。
▷ Run
をクリックすると、メロディーが流れるはずです!
みなさま、それではよいクリスマスを!