im trying to send a struct from an esp8266 to an arduino nano via i2c. i have slightly modified an example to send a struct as bytes but when the master requests data the slave sends data struct 1 time then freezes.
if i remove Wire.onRequest(requestEvent); then the slave continues to receive data from the master without freezing but i cant send data. and ideas what im doing wrong?
i2c master node mcu,
#include <Wire.h>
struct {
int gpios[8] {3, 4, 5, 6, 7, 8, 9};
bool pins[8];
} pinData;
void setup() {
Serial.begin(9600); /* begin serial for debug */
Wire.begin(D1, D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
pinData.gpios[0]=999;
Serial.print( pinData.gpios[0] );
}
void loop() {
Wire.beginTransmission(8); /* begin with device address 8 */
Wire.write("T1START"); /* sends hello string */
Wire.endTransmission(); /* stop transmitting */
Wire.requestFrom(8, sizeof(pinData)); /* request & read data of size 13 from slave */
while (Wire.available()) {
Wire.readBytes((byte*)&pinData, sizeof(pinData));
Serial.print( pinData.gpios[0] );
}
Serial.println();
delay(1000);
}
i2c slave arduino nano,
#include <Wire.h>
String readString;
unsigned long now = 0;
struct {
int gpios[8] {3, 4, 5, 6, 7, 8, 9};
bool pins[8];
} pinData;
void setup() {
Wire.begin(8); /* join i2c bus with address 8 */
Wire.onReceive(receiveEvent); /* register receive event */
Wire.onRequest(requestEvent); /* register request event */
Serial.begin(9600); /* start serial for debug */
Serial.println("hi");
}
void loop() {
if (millis() - now >= 500) {
for (int i; i <= 8; i++) {
pinData.pins[i] = digitalRead(pinData.gpios[i]);
Serial.println(pinData.pins[i]);
}
now = millis();
}
}
// function that executes whenever data is received from master
void receiveEvent(int howMany) {
while (0 < Wire.available()) {
char c = Wire.read(); /* receive byte as a character */
readString += c;
}
if ( readString, "T1START") {
Serial.println("it worked");
}
readString = "";
}
// function that executes whenever data is requested from master
void requestEvent() {
Serial.println("writing to master");
Wire.write((byte *)&pinData, sizeof(pinData));
}
also the single time i do receive the struct from the slave it is incorrect values. is this an endian issue?
Data type int is 16 bits on a nano, 32 bits on the esp8266.
On the nano, declare the struct as volatile since it is being used in an ISR.
There really is no reason to define gpios as int or uint16_t - use uint8_t instead (unless you somehow expect the nano to suddenly have over 255 i/o pins). That will avoid problems with reading a multi-byte volatile variable.
volatile is needed whenever a variable is used in both an ISR and somewhere else in the code. That lets the compiler know that the value of the variable may change at any time, so that it will not make any optimizations with the expectation that the value remains unchanged because it is not altered in the code itself. On the nano, any time the variable is larger than a single byte, it should not be accessed outside the ISR without temporarily disabling interrupts, otherwise an interrupt may occur between accessing each byte of a multi-byte variable because the processor can only access memory a single byte at a time.
since these requests will be changing the state of the gpios i want to make sure that the request gets through and handles the gpio states. I dont want to say, send a request to set a gpio LOW only for it not to arrive for whatever reason. what are my options here?
im trying to wrap my mind around some sort of ack or checksum that both MCU can agree the request was parsed and is correct
well if i wanted to write gpio 1 high i would send request, wire.write("gpio1HIGH); but how am i to make sure the request is resent if it did not make it to the other side. do i request a response from the slave containing the gpio states and see if the state actually change and if not send the request again? i read i2c is suppose to have error checking "whatever that means" but im not confident that every single request/response will make it through to the slave vice versa
an i2c gpio expander probably wouldn't be much more reliable right? only upside of the gpio expander is its easier to read the states of the gpios but i think that might be arguable.
im just filling big buckets of water and i dont want to spend all day mopping a thousand gallons of water from the floor lol. maybe ill just put the code that handles the pins on the slave and control the time values though i2c. its less risky but i would like to just use the salve as expander
i asked how would you receive an arbitrary message?
if the master sends an arbitrary request, the receiver can send data associated with that request or an acknowledgement. if the master doesn't receive a response to the request it can send it again. 3 failures, reset the system
conventional code practice would separate sending and receiving messages. the code may also need to monitor the serial interface for commands, as well as check for a timeout
loop ()
{
if (Serial.available ()) {
...
}
if (Wire.available ()) {
...
}
if (timingSomething && (msec - msecLst) > TIMEOUT) {
...
}
}
there would be separate sub-functions to send messages. that code can keep track of what it expects in response. since you haven't answered my question, i'll assume the code always knows what to expect (i.e. # bytes)
ahh yes the struct is a known size of bytes. so what happens if i send a message with arbitrary size, would this be okay as long as receiver gets message no more than but maybe less than the amount of byte i specify?
i could add a variable to the struct that is changed by the receiver of a message and sent back to the sender with an expected value. this might solve the size problem without sending multiple messages of different sizes
should i send a response back to the sender with an expected value or should i just see if i got a response back at all. i know either way is not perfect but what would you do. would you check a variable for a specific value to know if it was successful or just see if i got a response at all from the receiver