Skip to content

ESP8266 I2C Master issue #4396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Eddiiie opened this issue Feb 20, 2018 · 24 comments
Closed

ESP8266 I2C Master issue #4396

Eddiiie opened this issue Feb 20, 2018 · 24 comments
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.

Comments

@Eddiiie
Copy link

Eddiiie commented Feb 20, 2018

Basic Infos

I am working with a multimaster setup. Have PoC code that works just fine on Uno and Mega, but when I try to run it on ESP8266 Arduino Lib it hangs on waiting for a variable to change from 0 to 1. - a Callback I guess you'd say.

It is awesome that I can specify pin numbers to use, wish that would work on Mega and Uno...

Code:

#include <Wire.h>

#define I2C_BUFFER_SIZE          64

/* Packet definitions */
#define PACKET_SENDER_OFFSET         0
#define PACKET_DATA_LENGTH_OFFSET    1
#define PACKET_COMMAND_OFFSET        2
#define PACKET_DATA_LENGTH_MASK      0x7F

/* Commands */
#define COMMAND_ACK                  6

/* All I2C addresses are 7 bit */
#define CONTROLLER_I2C_ADDRESS       0x28
#define DISPLAY_I2C_ADDRESS          0x27

static unsigned char i2c_rx_buffer[I2C_BUFFER_SIZE];
static unsigned char i2c_tx_buffer[I2C_BUFFER_SIZE];
  
volatile static boolean send_ack_signal = false;
volatile static boolean recv_ack_signal = false;

void i2c_receive_isr(int byte_count)
{
  unsigned char current_position = 0;
  unsigned char buffer_length = 0;
  unsigned char expected_stream_length = 0;
  unsigned char checksum = CONTROLLER_I2C_ADDRESS << 1;

  Serial.write("i2c_receive_isr entered...\n");
  Serial.flush();

  while (Wire.available() > 0)
  {
    /* Check for buffer overflow */
    if (buffer_length >= I2C_BUFFER_SIZE)
    {
      Serial.write("ERROR: I2C buffer overflow occured!\n");
      return;
    }


    /* Store data */
    i2c_rx_buffer[buffer_length++] = Wire.read();
  }

  
  /* Make sure entire packet was received */
  expected_stream_length = (i2c_rx_buffer[PACKET_DATA_LENGTH_OFFSET] &
                            PACKET_DATA_LENGTH_MASK) +
                            3;
                            
  if (expected_stream_length != buffer_length)
  {
    Serial.write("ERROR: Expected ");
    Serial.write(expected_stream_length);
    Serial.write(" bytes, received ");
    Serial.write(buffer_length);
    Serial.write("\n");
    return;   
  }


  /* Calculate checksum and validate packet integrity */
  for (current_position = 0;
       current_position < buffer_length - 1;
       current_position++)
  {
    checksum ^= i2c_rx_buffer[current_position]; 
  }

  if (checksum != i2c_rx_buffer[buffer_length - 1])
  {
    Serial.write("ERROR: Checksum failure!\n");
    Serial.flush();
    return;
  }

  
  /* Call dispatch routine based on command received */
  switch (i2c_rx_buffer[PACKET_COMMAND_OFFSET])
  {
    case COMMAND_ACK:
      recv_ack_signal = true;
      break;

    default:
      /* Any command needs an ACK sent back */
      send_ack_signal = true;
      break;
  }
}


void setup() {
  int display_online = -1;

  Serial.begin(9600);

  Serial.write("Initializing I2C\n");
  Wire.begin(CONTROLLER_I2C_ADDRESS);
  Wire.begin(12, 14);  // NodeMCU
  Wire.onReceive(i2c_receive_isr);
}


void loop() {
  Serial.write("Waiting to send ack...");
  Serial.flush();
  while (!send_ack_signal)
  {
   delay(500);
  }
  send_ack_signal = false;
  Serial.write("Ack sent...");
}

The 'while' loop never exits on ESP8266.

"send_ack_signal" is the function I defined that gets registered when you call Wire.onReceive().

Hardware

Hardware: ?ESP-12?
Core Version: ?2.1.0-rc2?
I've confirmed this on Heltec Wifi 8 kit, NodeMCU, and WeMOS D1.

Settings in IDE

Module: Heltec Wiki 8 kit, NodeMCU, WeMOS D1
Flash Size: 4MB I believe
CPU Frequency: Tested 80 and 160
Flash Mode: huh?
Flash Frequency: once a day? :)
Upload Using: Arduino ISP ?
Reset Method: Power cycle

@Eddiiie
Copy link
Author

Eddiiie commented Feb 20, 2018

Adding #include <Arduino.h> made no changes.

Meanwhile,the I2C Master and Slave examples work fine. That is, I can put I2C_Slave on Uno and I2C_Master on NodeMCU and get hello hello hello hello. The examples use different functions but I used them to prove my level shifter works from 3.3 to 5v and wiring was ok. It works without the shifter too.

Why doesn't "send_ack_signal" ever come true on ESP8266? Help!

@devyte
Copy link
Collaborator

devyte commented Feb 20, 2018

@Eddiiie please edit your post and put a complete minimal code example (MCVE sketch). Without that, it's unlikely this will be looked at.

@devyte devyte added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label Feb 20, 2018
@Eddiiie
Copy link
Author

Eddiiie commented Feb 20, 2018 via email

@WereCatf
Copy link
Contributor

@Eddiiie Where does this send_ack_signal come from, then? There is no such thing anywhere in the ESP8266 Arduino-env source-code.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 20, 2018

It is clearly magic that the Arduino has that the ESP doesn't. :)
I am working on sample code now to post. It is a bit more involved than my simplified code above.
Standby... And, thank you!

@Eddiiie
Copy link
Author

Eddiiie commented Feb 20, 2018

code moved to top


This code compiles and hangs on the same while loop.  
The code does not manipulate the variable.   Something in the background manipulates the variable.
When the device comes online, that variable changes to 1.  
I'll dig and try to find/recall the source of that.

It works on Arduino and Uno.  I mean, the code will hang on that while loop until the device is connected and online.  It is really doing something.

@WereCatf
Copy link
Contributor

I think you are leaving something out or you have a modified codebase, because there is no reference, whatsoever, to send_ack_signal anywhere in either the Atmel- or the ESP8266-codebase. If nothing uses the variable, then obviously nothing is going to change it from false to true, either.

@devyte
Copy link
Collaborator

devyte commented Feb 20, 2018

I agree with previous comment: a quick search reveals no such variable anywhere in the AVR Wire code base. Or anywhere else, for that matter.
From the posted sketch, I somehow doubt that send_ack_signal is some hidden global (declaration).
I'm closing this as layer 8 for now.

@devyte devyte closed this as completed Feb 20, 2018
@Eddiiie
Copy link
Author

Eddiiie commented Feb 21, 2018

Sorry my bad. It has been a really hard day.

The sample code is missing "Wire.onReceive(i2c_receive_isr);" just after the "Wire.begin(12, 14);" line.

Wire.onReceive "Registers a function to be called when a slave device receives a transmission from a master."

My code Wire.Receive defines a function "i2c_receive_isr" that sets a global boolean variable "send_ack_signal" to TRUE (or FALSE).

SO, it appears the issue in my case is that the function defined in Wire.onReceive() is not being executed on the ESP8266, whereas it is working on the Mega and Uno.

I will now add some serial.print lines in the i2c_receive_isr function to confirm or deny this.

@devyte
Copy link
Collaborator

devyte commented Feb 21, 2018

The isr should be in RAM, and the global should be declared volatile.
Update your posted sketch.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 21, 2018

Code updated.. Note, I added volatile, tested, no change. Also added print lines to the ISR code, it does not show up.

@WereCatf
Copy link
Contributor

I just looked at the actual Wire-library and... well, it doesn't look like I2C-slave is supported at all on the ESP8266 at the moment. The relevant functions don't actually do anything and all the slave-mode code has been commented out. No reason is mentioned in the comments, so I do not know why.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 21, 2018

thank you @WereCatf .
Can we get this reopened? @devyte

@devyte
Copy link
Collaborator

devyte commented Feb 21, 2018

This issue describes an i2c master issue. The previous comment addresses an i2c slave shortcoming, which is a different issue. If you need the latter, try pr #3287 , and report back how it works. If you confirm that pr, it would help to move that feature forward.
But again, that is a different issue, so not here.
OTOH, if your title is wrong, and your issue is really a i2c slave issue, this is likely a duplicate of another issue that is already open, and in any case is addressed by #3287 .
On the third hand (foot?), if your issue really is a i2c master issue, per @WereCatf your posted code doesn't seem to make sense, so this issue would need to be explained in further detail to better understand it, and then it could be reopened. I don't think this is the case, but please correct me if I'm wrong.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

Let's leave it closed. I'll stick with the Uno.

I think the issue can be summed up to be that the function (isr) defined by calling wire.onReceive() never executes. Like you say, this is most likely a known issue. #3287 is pretty close.

Thanks for the time.

@devyte
Copy link
Collaborator

devyte commented Feb 23, 2018

@Eddiiie does #3287 work for you?

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

I am sorry, does 3287 have code that I can test somehow? I've never done that before. Is it a pull request? I will google how to do it. All the notes and links is overwhelming at first.

From what I understand, in proper I2C protocol every time a Master talks to a slave, the slave needs to acknowledge that the conversation has been heard. (or something like that :P) To me, in order for this to happen, the Master needs to change to slave mode for a moment to get that ACK.

Maybe my explanation is not exactly accurate but I believe wire.onReceive() is a slave state type thing...

The reason the I2C_Master example code works is because it just puts the bits on the wire, it does not look for an ACK or proper handshake.

Off to see what to do with a pull request?

@WereCatf
Copy link
Contributor

@Eddiiie

To me, in order for this to happen, the Master needs to change to slave mode for a moment to get that ACK.

Not quite: the I2C-master sends 8 bits on the line (ie. the I2C-address of the slave it wants to communicate with and one bit to indicate whether it wants data from the slave or to send data to it), then the master waits passively for a bit and the slave is supposed to send an ACK by pulling SCL LOW for a bit. No need to change from master to slave.

As for Wire.onReceive() -- yes, it's a slave-thing and, like I mentioned, the function doesn't do anything at the moment.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

@WereCatf thanks once again. It is not a master thing at all.. It is that the code does not exist yet.
So I am assume #3287 does not have the code either, almost got the command line right to get the PR.

Are bounties / bribes accepted?
Seriously, do you guys do this out of the goodness of your hearts?

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

would it be ok to change the title of this to "Please complete Wire.onRequest()" ?
or Dupe of # ??

@WereCatf
Copy link
Contributor

@Eddiiie Why would you assume that? #3287 does have onReceive(), but I haven't tested how well the code works as I haven't had any personal need for using the ESP as a slave-device and I can't test it now, because I'm moving soon and I have packed all of my boards up already.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

I happy to test but will need my hand held through getting the PR code and making sure it is used rather than the master. I am trying to finger that out now.

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

https://github.com/esp8266/Arduino/tree/04f2c0e27e1485bcc9b52b9340c21d6d95ee55c8/libraries/Wire

I'll just rename the existing files in program files (x86)\Arduino\hardware\esp8266com\libraries\Wire

@Eddiiie
Copy link
Author

Eddiiie commented Feb 23, 2018

Hmm it does not appear to be using it from that folder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.
Projects
None yet
Development

No branches or pull requests

3 participants