ESPをSPIのスレーブにする事を試みる。ESPをマスターとしてSPIのICと通信するのはなんてことなく簡単でネットで調べれば例題がいやと出てくるが、スレーブ化するのには情報が少ない。
先人の方々の情報収集したところ、ESP8266のArduinoIDEのライブラリーが存在する。制約はあるようだが使わせていただき、ライブラリーをテストしてみる。
https://github.com/esp8266/Arduino/blob/master/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino
【テスト1】
ライブラリーといっても、特に取り込みする訳でなくて、8266のライブラリーを取り込むとすでに含まれている。SPISlave_Testの方をESPにそのままアップロード。SPISlave_SafeMasterの方をArduinoNanoに。Nano側のMasterはloopの中を少し編集はしてやった。
void loop() {
delay(1000);
send("Are you alive?");
delay(1000);
send("Hello Slave!");
}
Hello Slaveを送りつけるように少し編集。Helloが最初のsetupの一回だけなんで、入れてみたというだけ。
デバイス的には、コメント欄にあるとおりに
GPIO NodeMCU Name | Uno
===================================
15 D8 SS | D10
13 D7 MOSI | D11
12 D6 MISO | D12
14 D5 SCK | D13
に接続。Arduino5VとESP3.3Vのレベル変換を間に挟んであげている。
これで動くはずだったのだが・・・・出力が・・・
Say What!、「何いうてんねん!」と言われる有様。
要因の切り分けのために、
ロジアナで見ると、一番上がSSピン。SSピンにSPI通信を開始する時にHIGHになって、LOWでアクティブになる(15番のピンが起動時に使うので、常にHIGHで使う時だけLOWという動作だと起動しない。サンプルのコメント欄に記載がある)が、間で、SSが一瞬、HIGH,LOWの信号が出てる・・・。いろいろやった結果103のコンデンサー(10nF)をSSとGNDの間に入れてあげると解消。103とか104のコンデンサーだとなまりが大きくて103に落ち着いた。ブレッドボードで作っているからSPIによくあるあるで、ワイヤーでノイズをひらってしまっているのが原因と思われる。対処してやると、
はい、こんにちは、マスター!一応、SPIを通じて通信している事が確認できる。
exampleのソースコードを眺めて使い方の推測、あくまで推測。
マスターからSPI.transferで最初の1バイトに送られてくるのが0x01~0x04で動作が変わる。
1stバイト | 動作 |
0x01 | ステータスといって、マスターからもスレーブからも状態等の書き込み、読み込みができるレジスタに対して、書き込みをする。 |
0x02 | 一方的にマスターからスレーブに対して信号を送付する。 |
0x03 | マスターから1バイト目に、03を受信するとスレーブは、あらかじめsetDataのコマンドでセットされていたデータをマスターに向かって送付する。 |
0x04 | ステータス状態の読み込む |
【テスト2】
SPIで通信というと、マスターからICの設定などを送りつける場合(例えば、DACで設定電圧にしてねと命令を送りつける場合など)と、マスターからの要求にこたえて、センサーなどの値を返す場合とが考えられるが、最小動作として、後者の方を試みる。
マスター側(テストでも用いたArduinoNano側)のスケッチ:
#include <SPI.h>
class ESPSafeMaster {
private:
uint8_t _ss_pin;
void _pulseSS() {
digitalWrite(_ss_pin, HIGH);
delayMicroseconds(5);
digitalWrite(_ss_pin, LOW);
}
public:
ESPSafeMaster(uint8_t pin)
: _ss_pin(pin) {}
void begin() {
pinMode(_ss_pin, OUTPUT);
_pulseSS();
}
void readData(uint8_t *data) {
_pulseSS();
SPI.transfer(0x03);
SPI.transfer(0x00);
for (uint8_t i = 0; i < 32; i++) { data[i] = SPI.transfer(0); }
_pulseSS();
}
uint8_t *readData() {
static uint8_t data[32];
readData((uint8_t *)data);
return data;
}
};
ESPSafeMaster esp(SS);
void setup() {
Serial.begin(115200);
SPI.begin();
// SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0));
esp.begin();
delay(1000);
}
void loop() {
uint8_t *received_data=NULL;
received_data=esp.readData();
int i = 0;
while (i < 32) {
Serial.print(received_data[i]);
Serial.print(" ");
i++;
}
Serial.println(" ");
free(received_data);
delay(1000);
}
基本、先に試したexampleの不要な部分をカットした形。
あと、readData()の関数を文字列ではなくて、uint8の配列ポインタでやり取りするように編集している。
今度は、ESPのスレーブ側
#include "SPISlave.h"
volatile uint8_t Counter = 0;
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
SPISlave.onDataSent([]() {
uint8_t data_to_send[2] = {Counter,10};
Serial.print(data_to_send[0]);
Serial.print(", ");
Serial.println(data_to_send[1]);
SPISlave.setData(data_to_send, 2);
Counter++;
});
SPISlave.begin();
}
void loop() {}
とてもシンプル。
Counterの値をマスターから呼び出されるたびに一つ大きくするようにしている。data_to_sendは、一つ目の配列がCounterの値で、2つ目の配列には”10”を静的にテストで入れている。
マスターからの1バイト目に0x01を送るとステータス
ちゃんと動かすと、以下の出力がシリアルモニタに得られる。
ちゃんと動作してくれて、マスターからデータReadの要求がかかると、counterの値をマスターに答えてくれる。むろん配列の2つ目は”10”が入っていて、その他残りはゼロ。
デジアナで見てあげるとこんな感じ。
一番上の白色のラインがSSでHIGH→LOWでスタート。
MOSI(上から2つめ)は0x03に続いて、0x00が送られると、それを受信したスレーブ側のESPが返信をする。
これをキャプチャーしたタイミングでは、0x38、十進数で56を返しているタイミングでの取得。
コメント
コメントを投稿