Greetings, i have been attempting to open communication between my NodeMCU and a Arduino Nano.
The NodeMCU is wired up to shift registers with 16bit, which then controls 16 Relays.
However, i also have a Arduino Nano in a mesh with other nanos Hooked to each own NRF24L01, and actuators like Relays.. sensors and LCD's for showing information.
This whole system communicates together, so information about remote actuators/sensors can flow into the NodeMCU's website, aswell as being controlled by the NodeMCU.
I am however scratching my head trying to figure out what i did wrong here, my temperature value gets recieved and printed as NaN. while all the other values are recieved just fine.
however, the absolute first loop @ startup of the NodeMCU, recieves a temperature value, then stops recieving anything.
Here's my Code:
I2C SLAVE(NANO)
bool forced = false;
float outside_temperature;
float outside_humidity;
float outside_pressure;
void setup() {
Wire.begin(8); /* join i2c bus with address 8 */
Wire.onReceive(receiveEvent); /* register receive event */
Wire.onRequest(requestEvent); /* register request event */
String request = "";
// function that executes whenever data is received from master
void receiveEvent(int howMany) {
request = "";
while (0 <Wire.available()) {
char c = Wire.read(); /* receive byte as a character */
request += c;
Serial.print(c); /* print the character */
}
Serial.println(); /* to newline */
Serial.println(request);
}
union floatToBytes {
char buffer[4];
float val;
} Data;
// function that executes whenever data is requested from master
void requestEvent() {
if (request == "forced"){
if (forced){
Data.val = 1.00;
}else{Data.val = 0.00;}
}
if (request == "temperature"){
Data.val = outside_temperature;
}
if (request == "humidity"){
Data.val = outside_humidity;
}
if (request == "pressure"){
Data.val = outside_pressure;
}
Wire.write(Data.buffer, sizeof(Data.buffer));
}
I2C MASTER (NodeMCU), it is in the Void loop():
struct Enviroment_variables{
float temperature;
float humidity;
float pressure;
};
Enviroment_variables outside;
bool requested = false;
int request_count = 0;
union floatToBytes {
char buffer[4];
float val;
} Data;
void loop() {
unsigned long now = millis();
if (now - last_sent >= interval) { // If it's time to send or request data, do it!
if (node_2_forced){
Serial.println("Node2_Forced");
}
website_values_update_node();
last_sent = now;
Serial.print("\n");
Serial.print("Temperature: ");
Serial.print(outside.temperature);
Serial.print("\n");
Serial.print("Humidity: ");
Serial.print(outside.humidity);
Serial.print("\n");
Serial.print("Pressure: ");
Serial.print(outside.pressure);
}
unsigned long now_recieve = millis();
if (now_recieve - last_recieved >= i2c_interval) {
Wire.beginTransmission(8); /* begin with device address 8 */
if (request_count == 0){
Wire.write("temperature");
}
if (request_count == 1){
Wire.write("humidity");
}
if (request_count == 2){
Wire.write("pressure");
}
if (request_count == 3){
Wire.write("forced");
}
Wire.endTransmission(); /* stop transmitting */
//Send request after telling the slave what to send.
Wire.requestFrom(8, 4);
requested = true;
last_recieved = now_recieve;
}
uint8_t index = 0;
while (Wire.available()){
Data.buffer[index] = Wire.read(); /* receive byte as a character */
index ++;
}
if (requested){
if (request_count == 0){
outside.temperature = Data.val;
}
if (request_count == 1){
outside.humidity = Data.val;
}
if (request_count == 2){
outside.pressure = Data.val;
}
if (request_count == 3){
if (Data.val == 1.0){
node_2_forced = true;
}else{node_2_forced = false;}
Serial.print(Data.val);
}
request_count ++;
if (request_count > 3){
request_count = 0;
}
requested = false;
}
}
Try posting the whole sketches rather than just a small section. It is difficult to tell what datatypes are in use from the section you have posted or how values get set.
I assume outside is a struct or class - can you post it together with your other declarations/definitions
I looks like you may be getting an array overflow
The ESP8266 runs at 3.3V and the Arduino Nano with ATmega328P runs at 5V. That means you have a voltage mismatch on the I2C bus which makes is less reliable.
The ESP8266 is (more or less) 5V tolerant, so it will not get damaged. However, I prefer to use a I2C level shifter.
Please upgrade your sketch with:
Remove all Serial functions from the onReceive and onRequest interrupt functions.
Never use a String object in a interrupt function.
We don't send commands as text, we use binary data.
Use a 'struct' to combine all your data. That also avoids splitting a float into bytes.
Yes i have considered using a level shifter, however i noticed all other data being sent and recieved was spot on and stable, therefore i have not yet gone that way.
The serial functions i have put there in order to find out if i have made any mistake, they are put there after the fault came to be.
I did not know about the string Object inside a interrupt function, how may you have sent a specific request then? using numeric codes in binary? how may you do this? i must admit i have thought about using something as just a byte 1, or 2 ... etc as a specific command, may this be better? anyhow some may say sending readable commands over a protocol might be easier and better for later readability. well,unless one heavily comments the code with every thought in mind.
i did use a struct at first, But it did not end well, so therefore i use union to transfer bytes then reasemble it at reciever. i would enjoy if you would come up with a straight forward way to send a struct succesfully over I2C using 4 Floats in the same struct. and also, using example: 4 floats and some other data type in the same struct. I also attempted to send a union containing a struct, but that either did not end good.
the following code in which a I2C master transmits a struct to an I2C slave was adapted from
I2C Master writer
// Wire Master Writer - Demonstrates use of the Wire library
// adapted from https://www.arduino.cc/en/Tutorial/MasterWrite
// to transmit/receive a structure over I2C
#include <Wire.h>
// struct to hold data transmitted to slave
struct {
uint8_t seq; // sequence number
char ch; // character
uint16_t i; // 16bit unsigned int
float x; // real data
uint8_t spare; // pack to a 16bit boundary
uint8_t crc; // CRC or checksum
}
data ={0,' ', 1, 3.14159};
void setup()
{
Serial.begin(115200);
Serial.println("I2C master");
Wire.begin(); // join i2c bus (address optional for master)
}
void loop()
{
Serial.print("I2C transmitting seq "); Serial.println(++data.seq);
if(data.ch++> 127)data.ch=' '; // setup data
data.i+=5;
data.x*=1.01;
Wire.beginTransmission(4); // transmit to device #4
Wire.write((uint8_t *)&data, sizeof(data));
Wire.endTransmission(); // stop transmitting
delay(500);
}
I2C slave receiver
// Wire Slave Receiver - Demonstrates use of the Wire library
// adapted from https://www.arduino.cc/en/Tutorial/MasterWrite
// to transmit/receive a structure over I2C
#include <Wire.h>
volatile int howMany=0; // indicates how many bytes received by I2C slave
volatile struct { // struct to hold data transmitted to slave
uint8_t seq; // sequence number
char ch; // character
uint16_t i; // 16bit unsigned int
float x; // real data
uint8_t spare; // pack to a 16bit boundary
uint8_t crc; // CRC or checksum
}
data;
void setup()
{
Serial.begin(115200); // start serial for output
Serial.println("I2C slave");
Wire.begin(4); // join i2c bus with address #4
Wire.onReceive(receiveEvent); // register event
}
void loop() // checking for I2C data received
{
if(howMany) { // if bytes received display
Serial.print("\nreceived howmany= ");Serial.println(howMany);
Serial.print("seq= "), Serial.println(data.seq);
Serial.print("i= "), Serial.println(data.i);
Serial.print("x= "), Serial.println(data.x);
howMany=0; // clear ready for next frame
}
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
uint8_t *ptr=(uint8_t *) &data; // pointer to struct for received data
::howMany=howMany;
while(Wire.available()) // loop reading data
*ptr++= Wire.read(); // read the byte
}
the structure contains
sequence number so receiver can check for lost or duplicate frames
data types char, int and float
a crc check (checksum or CRC8) not implemented
extra code code be added in the receiver
to acknowledge receipt of a valid frame
to check for lost frames or corrupt frames (using crc) - a retransmission request could then be sent to the transmitter requesting retransmission, etc.