ESP-12E as SPI Master (top) and SPI Slave (bottom) |
PCBs like this relay boards are easy to interface to; you use the same method as the blinking LED sketch in my last post.
This 5V Arduino relay module costs as little as RM5 |
But other PCBs like this MAX7219 LED 7-segment Display costs a mere RM9.60 and uses the SPI or Serial Peripheral Interface:
MAX7219 LED 7-segment display module |
PCF8563T Realtime Clock |
/*
SPI Safe Master Demo Sketch
Connect the SPI Master device to the following pins on the esp8266:
GPIO NodeMCU Name | Uno
===================================
15 D8 SS | D10
13 D7 MOSI | D11
12 D6 MISO | D12
14 D5 SCK | D13
Note: If the ESP is booting at a moment when the SPI Master has the Select line HIGH (deselected)
the ESP8266 WILL FAIL to boot!
This sketch tries to go around this issue by only pulsing the Slave Select line to reset the command
and keeping the line LOW all other time.
*/
#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();
}
uint32_t readStatus()
{
_pulseSS();
SPI.transfer(0x04);
uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24));
_pulseSS();
return status;
}
void writeStatus(uint32_t status)
{
_pulseSS();
SPI.transfer(0x01);
SPI.transfer(status & 0xFF);
SPI.transfer((status >> 8) & 0xFF);
SPI.transfer((status >> 16) & 0xFF);
SPI.transfer((status >> 24) & 0xFF);
_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();
}
void writeData(uint8_t * data, size_t len)
{
uint8_t i=0;
_pulseSS();
SPI.transfer(0x02);
SPI.transfer(0x00);
while(len-- && i < 32) {
SPI.transfer(data[i++]);
}
while(i++ < 32) {
SPI.transfer(0);
}
_pulseSS();
}
String readData()
{
char data[33];
data[32] = 0;
readData((uint8_t *)data);
return String(data);
}
void writeData(const char * data)
{
writeData((uint8_t *)data, strlen(data));
}
};
ESPSafeMaster esp(SS);
void send(const char * message)
{
Serial.print("Master: ");
Serial.println(message);
esp.writeData(message);
delay(10);
Serial.print("Slave: ");
Serial.println(esp.readData());
Serial.println();
}
void setup()
{
Serial.begin(115200);
SPI.begin();
esp.begin();
delay(1000);
send("Hello Slave!");
}
void loop()
{
delay(1000);
send("Are you alive?");
}
Unfortunately the "Examples->SPISlave->SPISlave_Test" did not work for me. The Slave could read the master but its reply seems corrupted. Luckily I found a working version in the ESP8266 Community Forum:
/*
SPI Slave Demo Sketch
Connect the SPI Master device to the following pins on the esp8266:
GPIO NodeMCU Name | Uno
===================================
15 D8 SS | D10
13 D7 MOSI | D11
12 D6 MISO | D12
14 D5 SCK | D13
Note: If the ESP is booting at a moment when the SPI Master has the Select li
ne HIGH (deselected)
the ESP8266 WILL FAIL to boot!
See SPISlave_SafeMaster example for possible workaround
*/
#include "SPISlave.h"
void setup()
{
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
// data has been received from the master. Beware that len is always 32
// and the buffer is autofilled with zeroes if data is less than 32 bytes long
// It's up to the user to implement protocol for handling data length
SPISlave.onData([](uint8_t * data, size_t len) {
String message = String((char *)data);
char answer[33];
if(message.equals("Hello Slave!")) {
strcpy(answer, "Hello Master!");
} else if(message.equals("Are you alive?")) {
sprintf(answer, "Alive for %u seconds!", millis() / 1000);
} else {
strcpy(answer, "Say what?");
}
SPISlave.setData(answer);
Serial.printf("Question: %s\Answer: %s\n", (char *)data, answer);
});
// The master has read out outgoing data buffer
// that buffer can be set with SPISlave.setData
SPISlave.onDataSent([]() {
Serial.println("Answer Sent");
});
// status has been received from the master.
// The status register is a special register that bot the slave and the master can write to and read from.
// Can be used to exchange small data or status information
SPISlave.onStatus([](uint32_t data) {
Serial.printf("Status: %u\n", data);
SPISlave.setStatus(millis()); //set next status
});
// The master has read the status register
SPISlave.onStatusSent([]() {
Serial.println("Status Sent");
});
// Setup SPI Slave registers and pins
SPISlave.begin();
// Additional setting to have MISO change on falling edge
SPI1C2 |= 1 << SPIC2MISODM_S;
// Set the status register (if the master reads it, it will read this value)
SPISlave.setStatus(millis());
// Sets the data registers. Limited to 32 bytes at a time.
// SPISlave.setData(uint8_t * data, size_t len); is also available with the same limitation
SPISlave.setData("Ask me a question!");
}
void loop() {}
The two ESP-12E, master and slave are wired thus:
As per the comments in the two sketches, the ESP-12E pin assignments are:
Master Slave
D8 SS - SS D8
D7 MOSI - MOSI D7
D6 MISO - MISO D6
D5 SCK - SCK D5
I plugged both into the same laptop, and the slave came up as /dev/ttyUSB0 and the master /dev/ttyUSB1. By clicking on "Tools->Port" I could switch between the two serial ports and program the master and slave in turn.
When working correctly you should get the following output from the Master's Serial Monitor:
Master: Are you alive?
Slave: Alive for 44 seconds!
Master: Are you alive?
Slave: Alive for 45 seconds!
Master: Are you alive?
Slave: Alive for 46 seconds!
Master: Are you alive?
Slave: Alive for 47 seconds!
Master: Are you alive?
Slave: Alive for 48 seconds!
Now if you flipped over to /dev/ttyUSB0 and relaunched the Serial Monitor, you should get:
Question: Are you alive?Answer: Alive for 44 seconds!
Answer Sent
Question: Are you alive?Answer: Alive for 45 seconds!
Answer Sent
Question: Are you alive?Answer: Alive for 46 seconds!
Answer Sent
Question: Are you alive?Answer: Alive for 47 seconds!
Answer Sent
There you have it, SPI communication using the ESP8266 and Arduino IDE.
Happy Trails.
hello,
ReplyDeletefrom where did you install library .
https://github.com/esp8266/Arduino/tree/master/libraries/SPISlave/examples
DeleteHello, Can u guide me how to have multiple slaves from this code?
ReplyDeleteUnlike I2C, SPI requires you to add 1 GPIO line for every slave's SS line (ie Slave Select). You then address each slave by setting its SS low, ie digitalWrite(_ss1_pin, LOW) for slave 1, digitalWrite(_ss_pin2, LOW) for slave 2, etc. Hope this helps.
DeleteBuenas tardes.
ReplyDeleteMe ha solucionado muy bien lo que estaba buscando, muchas gracias.
¿alguien conoce algo parecido para una comunicaciĆ³n I2C para los Esp8266?
saludos y muchas gracias de nuevo
https://diyi0t.com/i2c-tutorial-for-arduino-and-esp8266/
DeleteHi,
ReplyDeleteI have one esp8266 configured as slave, which should provide some data when asked for. Unfortunately I have the problem, that no data is sent over MISO.
The logic trace shows the data provided by the master. The Esp8266 receives the data, which can be seen by a notification printed to the serial monitor. The Esp8266 code then calls SPISlave.setData(..) but SPISlave.onDataSent is never executed. The MISO pin stays always high.
The line "SPI1C2 |= 1 << SPIC2MISODM_S;" is also part of the setup function.
I have also another strange behaviour. The receive data looks not ordered correctly. When creating a long out of uint8_t* data, I have to code this in order to get the data which can be seen in the logic trace:
long RxData = (long)data[3] | (long)data[0] | (long)data[1] | (long)data[2];
Does anybody know where this comes from???
Inside the function hspi_slave_setData() the register SPI1W is written from element 8 to 15.
DeleteWhen does the esp8266 send this data? I would expect it will send it in the next SPI job (SS active and SCLK "active"). Is this correct?
I read sometimes that the esp8266 has some specific SPI protocol. Means sending a 0x3 to it tells him to receive and 0x2 means transmit. Is this true? I did not find this handling in the hspi_slave.* files. Such a SPI protocol would kill my idea of receiving from another master, as this master does not know this protocol :(
OK. Now I understood. :(
DeleteMy SPI master sends the data 0x0A000000. The esp8266 receives it and find a 0x2 in 0xA (0b1010). So it is willing to receive data. But the first 0xA is lost for me as "user data". Luckily my master resends the information and i will find the 0xA again at position 3 of my user data :)
My master will never send something else without response. My esp8266 will never response without receiving a inital 0x3 :)
As i cannot change code of my master, my slave will never be able to transmit.
I hate the esp8266 SPI module :)
Thanks! I was dealing with a problem due to the MISO pin delay, but I did not know how to modify SPI1C2 correctly!
ReplyDelete