Recently spent the past few days tinkering with NodeMCU and Arduino. I have a project where I have a WifiEnabled NodeMCU send data to an Arduino to further process downstream. I was able to POC that my NodeMCU and Arduino Uno were able to communicate when I use a basic Master-Slave configuration.
However, the big issue i am having is get the NodeMCU to send data through wire.write() outside the loop() function in the Master code.
This is an example of working code communication:
#include <Wire.h>
void setup() {
Serial.begin(9600); /* begin serial for debug */
Wire.begin(D1, D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}
void loop() {
Wire.beginTransmission(8); /* begin with device address 8 */
Wire.write("Hello Arduino"); /* sends hello string */
Wire.endTransmission(); /* stop transmitting */
Serial.println();
delay(1000);
}
It works great and the Slave Uno can see the message "Hello Arduino" hundreds of times. However, with my project, there are various different writes depending on the input the Master(NodeMCU) gets. So instead I would like the loop() to call another function that will decide what to do. For simplicity, I will just call another function that will initiate the write. This is roughly what i wanted to see work:
#include <Wire.h>
void setup() {
Serial.begin(9600); /* begin serial for debug */
Wire.begin(D1, D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}
void loop() {
Wire.beginTransmission(8); /* begin with device address 8 */
Wire.write("Hello Arduino"); /* sends hello string */
Wire.endTransmission(); /* stop transmitting */
Serial.println();
open_lock();
delay(1000);
}
void open_lock(){
Serial.println("unsecure");
Wire.beginTransmission(8); /* begin with device address 8 */
Wire.write("this is unsecure page"); /* delete after */
Wire.write("{\"gpio\":3,\"state\":1}"); /* sends hello string */
Wire.write("{\"gpio\":2,\"state\":0}"); /* sends hello string */
Wire.endTransmission(); /* stop transmitting */
}
However, with this code, the Slave seems to only get one message of "Hello Arduino" and nothing else. The process seems like it hangs and there is nothing that gets outputted after the first "Hello Arduino".
Any body have any idea why this is happening? I am not sure if the library for Wire only works inside the loop function. I spent a lot of hours yesterday trying different things.
Thanks for the reply. In this case i put a lot of text for troubleshooting purposes. When i put the additional text from the open_lock() method into the loop() method, it works fine. It seems to be an issue with the Wire.write being also called within another method rather than the amount of text.
Even when i tested with just 1 line in open_lock() method, it didnt work either. Even If the open_lock() is causing too much data to get sent, wouldnt we still see a lot of "Hello Arduino" messages but only a partial text from the open_lock () method?
What code is running on the Arduino? Could the problem be that you are sending the two I2C communications so close together that the Arduino does not have time to properly process the data?
@david_2018 This is an interesting idea. Maybe ill add a delay on the master to test. But this is the code on the Uno Slave:
#include <Wire.h>
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(115200); /* start serial for debug */
Serial.print("Test");
}
void loop() {
delay(100);
}
// 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 */
Serial.print(c); /* print the character */
}
Serial.println(); /* to newline */
}
@david_2018 Are you saying receiveEvent should not be used with Serials? I was using it before and it worked when it was in the loop(). I was following other tutorials and they used receiveEvents to communicate data over the I2C bus through Serial
Originally the Arduino would lock up if the serial transmit buffer filled up, since the interrupt that pulls characters from the buffer cannot occur when interrupts are disabled, but that was changed so that when interrupts are disabled the Serial library itself checks the hardware UART to see when it can accept another character. The problem then becomes that you will stay in the ISR until enough characters have been sent to free up sufficient space in the buffer.
Not sure if this would cause the problem you are seeing - I would think exceeding the 32 byte I2C buffer length would be a greater problem.
Just to verify, are you allowing for the voltage difference between the Arduino and NodeMCU, or are you using a 3.3 volt Arduino board?
So i actually thing i stumbled on something that works but not sure why it works. So after @runaway_pancake suggestion, i noticed that it worked. I was curious if it was the one-liner and after doing several test, i found a version that works.
Mater (NodeMCU) code:
#include <Wire.h>
void setup() {
Serial.begin(115200); /* begin serial for debug */
Wire.begin(D1, D2); /* join i2c bus with SDA=D1 and SCL=D2 of NodeMCU */
}
void loop() {
Serial.println();
openlock();
delay(1000);
openlocktwo();
delay(1000);
}
void openlock(){
Wire.beginTransmission(8);
Wire.write("{\"gpio\":3,\"state\":1}");
Wire.endTransmission();
}
void openlocktwo() {
Wire.beginTransmission(8);
Wire.write("{\"gpio\":2,\"state\":0}");
Wire.endTransmission();
}
So here, i put each write in a separate function that is called between writes. This outputed each of the results. So i wonder if this is what @david_2018 was talking about the string coming too fast for the uno to decipher it? Is there a better way to do this? Ultimately, i want the UNO to get 2 strings for GPIO state.