I2C comm between Nano & 8266

Hello,

pulling all my (grey) hairs out ....can't get this to work (and running out of grey hairs as well... :grin: )

OK....what's up:

I want to send some data from a Wemos D1 mini 8266 to a Nano by I2C

At first I used a code that just sends and receives a byte (a counter from 1 to 100) and that gets received by the Nano so that tells me hardware is ok and they can communicate.

But now I want to send a structure.

I use this example https://forum.arduino.cc/t/use-i2c-for-communication-between-arduinos/653958/2

Whatever I do....can't get it to work.

If you have a look at my sending code for the 8266 in the send_data routine I print out valA.

That's working so it's telling me it enters this routine and there is a structure assigned and valA is present and is having the right value (for this test I did not change this value, first wanted it to work before doing that.

Now...

the receiver code.

You can see that in the loop there is a "Hello": that is working so the loop is processed however it seems that no data at all is being received because it never prints out "new data received" so it never enters this loop..even worse: also the text "we were here" never gets printed.

So....

It looks like the receiver never receives anything so I suspect nothing is being transmitted....

But again: when I just change the sketches for sending a byte in a loop: that's working fine.

So hardware, addresses etc is ok, I guess it's something with the structure not being send but I really have no clue.

What I also tried is making txData volatile (send sketch: volatile I2cTxStruct txData = {"xxx", 77, 0};)
and also made it volatile in the receive sketch but no luck as well.

What I also tried (after searching these forums) is the setClockstretch command however, even without this command the byte send/receive did already work.

Anyone a suggestion? Thanks!

Sending sketch for the 8266 master

/*
  
  Nano: A4=SDA  A5=SCL
  Wemos: D2=SDA D1=SCL
  
 
 Structure Sender program Wemos
*/

#define Slave_Addr 9          //I2C address receiver (nano)
#define loop_delay 1000

#include <Wire.h>

struct I2cTxStruct {
    char textA[16];         // 16 bytes
    int valA;               //  2
    unsigned long valB;     //  4
    byte padding[10];       // 10 to fill up to 32 bytes
                            //------
                            // 32
}; //end struct

I2cTxStruct txData = {"xxx", 77, 0};  //also tried volatile


void setup() {
  Serial.begin (115200);
//  Wire.setClockStretchLimit(40000);    // in µs, no change
  Wire.begin();
  Serial.println (__FILE__);

} //end setup


void loop() {

 send_data();
 delay (loop_delay);
} //end loop

void send_data() {
  Wire.beginTransmission(Slave_Addr);
  Wire.write((byte*) &txData, sizeof(txData));
  Wire.endTransmission();

  Serial.println ((String)"valA = " +  txData.valA);
} //end send_data


Nano receive as slave:

/*
 

  Nano: A4=SDA  A5=SCL
  Wemos: D2=SDA D1=SCL

  Receive structure Nano
*/

# define loop_delay 1000
# define Slave_Addr 9          //I2C adres van de Wemos

#include <Wire.h>

struct I2cRxStruct {
    char textB[16];         // 16 bytes
    int valC;               //  2
    unsigned long valD;     //  4
    byte padding[10];       // 10
                            //------
                            // 32
};

I2cRxStruct rxData;

bool newRxData = false;

void setup() {
  Serial.begin(115200);

  Wire.begin(Slave_Addr);
  Wire.onReceive(receiveEvent);
  
  
} //end setup

void loop() {
  //Serial.println("Hello");
  if (newRxData == true) {
     Serial.println("New data received");
     showNewData();
     newRxData = false;
    } //end check nw data
} //end loop

void showNewData() {

    Serial.print("This just in    ");
    Serial.print(rxData.textB);
    Serial.print(' ');
    Serial.print(rxData.valC);
    Serial.print(' ');
    Serial.println(rxData.valD);
}

void receiveEvent(int numBytesReceived) {
 
 if (newRxData == false) {
            // copy the data to rxData
        Wire.readBytes( (byte*) &rxData, numBytesReceived);
        Serial.println ("we were here");
        newRxData = true;
    }
    else {
            // dump the data
        while(Wire.available() > 0) {
            byte c = Wire.read();
        }
    }
} //end receiveEvent



Are the sizes of the variables such as int the same size on both boards ?

How many bytes is an int on the Nano ?
How many bytes is an int on the ESP8266 ?

Mmm...thank you.

Never knew that there were differences in declared sizes between boards where the datatypes are the same....

Will have a look into that and see how I can manage that.....

Thanks

A sketch:

struct I2cRxStruct {
    char textB[16];
    int valC;
    unsigned long valD;
    byte padding[10];
};

void setup() {
   Serial.begin(115200);
   delay(2000);
   Serial.print("sizeof(struct I2cRxStruct) = ");
   Serial.println(sizeof(struct I2cRxStruct));
   Serial.print("sizeof(int) = ");
   Serial.println(sizeof(int));
}

void loop() {
}

Results of running on a Nano:

sizeof(struct I2cRxStruct) = 32
sizeof(int) = 2

Results of running on an ESP8266:

sizeof(struct I2cRxStruct) = 36
sizeof(int) = 4

Conclusions:

  1. An int is a different size on a Nano than it is on an ESP8266.
  2. The default packing of structures also seems to be different.

Edit: slight modifications to pack the structure members and explicitly ask for a 16 bit integer:

struct __attribute__((__packed__)) I2cRxStruct {
    char textB[16];
    int16_t valC;
    uint32_t valD;
    byte padding[10];
};

void setup() {
   Serial.begin(115200);
   delay(2000);
   Serial.print("sizeof(struct I2cRxStruct) = ");
   Serial.println(sizeof(struct I2cRxStruct));
   Serial.print("sizeof(int16_t) = ");
   Serial.println(sizeof(int16_t));
}

void loop() {
}

produces the same output on both:

sizeof(struct I2cRxStruct) = 32
sizeof(int16_t) = 2

Sending structures between different processors can be a whole can of worms though. Imagine if one of them was big endian and the other little endian...

1 Like

Yes, not surprised about your hairs. Why, why are you doing this? A recipe for hair loss. Using 2 or more microcontrollers in the same project is almost never easy, especially for beginners. Even experienced engineers avoid it.

If you have run out of pins on the ESP, analog or digital, there are better solutions than connecting it to a Nano, which doesn't even run at the same voltage (assuming you mean a classic Nano 3 (ATMEGA328)).

Tell us more about your project and we can suggest how to do it without the Nano.

1 Like

wow...learning a lot now (I've been trying to get my sketch working for dayyyyys (and nights as well).....

So...good to know, thank you for your help, will investigate how to get these structures on different boards "in sync"....

@PaulRB:

ok....this is why I want to connect these 2:

In my living room I have an Uno and on is is mounted a "Multi-Function-Shield with some 7segment displays, some buttons etc.

Attached to this Uno I have 2 18DS temp sensors for measuring my fridge and freezer (so thats 1-wire technology)
I have a hall effect sensor (with magnet) for measuring the distance of the door of my fridge (when it's open for too long a buzzer on the shield will buzz) I have a HDC3022 connected (I2S) for measuring the temp in my living room ....ooh...and I have a relais // to the motor of my fridge so I can see if the fridge is turned on by thermostat or not.

Now...this all works great but it's very much packed leaving no data lines open for anything else.

Then: about I week ago I had my first attempt hooking up a 8266 to the Arduino Cloud here and as I connected a HDC3022 to this board and my freezer/fridge sensors as well I was (finally) able to see these values in the cloud which was a great experience!

So now I want to combine these 2.

And as I do not have pins left on the Uno but as I do have I2C running (to communicate with the HDC) I thought: let's connect the uno with the 8266 and let me send the temp from fridge and freezer on request (as the Uno must be in slave mode) to the 8266, the 8266 can read out the HDC as well so I then have the 3 temps I can send to the cloud.

That was the idea.

But...I'm nowhere an experienced user.

So....would it be better to cancel it this way??

Thanks!

If you want to ensure that the data sizes are the same between boards, which can be important when interfacing them, then use the standard type declarations such as int16_t for a 16 bit signed integer, etc on both sides

1 Like

YESSSS!!!!!!!!

Got it working!!

Used the code from 'van _der_decken',(great help, thank you) applied it on the sending and receiver side and now the serial monitor on the receiver is showing exactly what the sending side did send!!

Great!!! (you do not want to know how many hours I spend trying to make this work....)

So! A very important step, now I can go on. (step-by-step).

My thoughts are: setting up the 8266 as a I2C master with the Uno and the HDC3022 as slave.

The 8266 will request the HDC temp and also send this temp to the Uno so the Uno MFS can display it (like it does now)

Then the 8266 asks the Uno the other temps from fridge and freezer and when those 3 are complete it will send it to the cloud (so I can monitor it on my phone or so).

Well...important step made, thank you all for your help, lesson (or 2 or x...learned)

BTW: yes....8266 and Uno/Nano are not the same voltage but the 8266 is 5V tolerant and the Nano accepts 3.3V as a logic 1.

Could also use a level shifter if needed later on....

1 or 2 pins

1 pin. Analog or digital?

I assume that's a mistake and you meant i2c

1 pin. Input or output?

4 pins plus the SDA & SCL pins.

What occupies the Uno's or Nano's other pins? You mentioned some buttons and 7-seg displays. Please give more detail about those.

well...

the problem is that this MultiFunctionalShield which is piggybagged on top of the Uno uses a lot of data lines.

The info of this board is here : MFS-board Uno

(It's Dutch language but you can see the schematics and see it's nearly full.)

Available pins are on J3 (that block of pins in the lower right corner)

D3 : in my case connected to a red led to see if the fridge door is open
D6: in my case connected to my 2 DS18B12 (1-wire protocol)
D9: in my case used for the relais-motor on switch (switches to ground)
A5: left open as it is in use for I2C

I soldered wires to A4 and A5 (and + and GND) to get I2C available.

All on-board leds used for indicating what temp_sensor is showing the data (fridge, freezer, living room) and (4) whether the motor of the fridge is on or not.

So....everything is used, either by me or the shield and I use nearly all (if not all) functions of the shield and it's a very practical thing to have so I want to stick with that and therefor thought it would be a good idea to communicate via I2C to my 8266.....

The hall effect is an analogue one so I needed an analogue input so I used A3.
A3 is also used for button3 but as I do not use button 3 (I do use button 1&2) I can safely use this one.

I had a recent thread about this, and received lots of grief from the professionals about connecting Nano output GPIO lines directly to ESP8266 GPIO input pins. I intended to switch the Nano outputs between INPUT mode and OUTPUT LOW, with the ESP8266 inputs being INPUT_PULLUP. This was felt to be risky because errors in my code, or unknown actions by a library or bootloader, might change the Nano outputs to OUTPUT HIGH, thus risking the release of smoke.

As a result, I decided to insert reverse-biased diodes into the lines, so the Nano could bring the lines low, but could not raise them to 5v.

I had also heard about the 8266 GPIOs being 5V tolerant, but I haven't seen any official support for the idea. However, a test one might do is to apply a 3.3V source to an ESP8266 GPIO in INPUT mode, through an ammeter, and then start raising the 3.3V source. You would want to find the voltage at which current just begins to flow. If it's above 5V, then I think the anecdotal wisdom about 5V tolerance is probably right. In any case, from what I've read, the ESP8266 protection is not just the conventional diode to Vcc. It's more zener-like.

1 Like

From Google seach:

1 Like

That's useful information.

I will either add the diodes (I love the simplicity of this) or I will use a level shifter module to prevent the module breaking over time.

Thanks!

Probably wasting my time now, but you could do everything you want with just the ESP8266. No Uno, no shield. It would have been simpler and easier than the effort you have/will put into making this Uno-ESP Frankenstein's monster circuit. Don't let the limited number of pins on the ESP worry you, there are ways to add as many as you need.

Hopefully you have/will have learned that using 2 microcontrollers in the same project makes it significantly more difficult and less likely to be successful. There are better, easier ways that need only one MCU.

Also that shields are often not designed for use in real projects, they are designed with education/play in mind, so it doesn't matter if they use every pin the Arduino has. That's why I have always avoided them. And if you don't use shields, why buy shield-compatible Arduino like Uno? Other Arduino designs/form-factors are much more practical and flexible.

I basically agree with this. In my case, I did all the logic coding (about 1000 lines of code) for a Nano when it was supposed make a call on a land line phone. Then when it switched to doing a push notification, I just didn't want to take the time to port it to an ESP32. Also, I have three Lolin D1 Minis in stock, with nothing else to do with them, and I had already done the Pushover code for that. So it just seemed easier to put the two together. And the D1 Mini really doesn't have enough pins, not to mention that you can't use some of the pins it does have. And finally, I have two I2C devices, and it turns out that the ESP8266 doesn't really have a hardware I2C peripheral, so apparently the whole I2C thing is bit banged. But if I had it to do over again, I would probably just use an ESP32 with enough pins.

The one reservation I have about the ESP parts is that I'm not sure there is documentation to allow register-level manipulations like you can do with the Atmega328P. I know it's inadequate on the ESP8266. Hopefully it's better with the ESP32. Oh, and the watchdog timers. I was never able to completely turn them off on the ESP8266.

Not to resurrect the sad old horse that’s been dead and decomposing for years, and then beat it some more, but the test you propose has been done:

Is ESP8266 5 V Tolerant? This Curve Tracer Says Yes! | Hackaday

There’s no need to pile on here: the comments on that article point out all the different reasons why those test results shouldn’t be trusted. (I had a Nano and ESP8266 talking together for about 18 months, without level shifters, with no issues; eventually migrated the project to a single ESP32. Of course, that data point shouldn’t be trusted, either.)

1 Like

Actually, the more interesting question to me is why in that graph there is any current flow at all at 3.3V and below if the GPIO is in INPUT mode.

This is well above my pay grade, but the ESP8266 reportedly has a ā€œsnap-backā€ circuit for protection; maybe the current is leakage through that circuit.

Also, the graph here has ā€œleakageā€ in uA, not the mA in the Hackaday article.

Is ESP8266 I/O really 5V tolerant? - Digital Me