I want to use arduino to simulate an I2C device with another microcontroller.
Currently, the other microcontroller is sending a write-read command to an i2c device request an address. This is tested working with an i2c sensor.
Now I want to emulate the sensor using arduino. Below is the code I have so far, built from looking at examples from searching around google.
However, the other microcontroller doesn't seem to be recognizing the arduino. Any thoughts on what the issue may be?
Thanks
#include <Wire.h>
int c;
void setup() {
Wire.begin(0x68); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
}
void loop() {
delay(100);
// Wire.write(0x68);
// Wire.endTransmission();
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
Wire.write(0x68);
Wire.endTransmission(); // stop transmitting
// while (1 < Wire.available()) { // loop through all but the last
// c = Wire.read(); // receive byte as a character
// Serial.print(c); // print the character
// }
// if (c == 0){
// //Wire.beginTransmission(0x01); // transmit to device #8
// Wire.write(0x68); // sends one byte
// Wire.endTransmission(); // stop transmitting
// }
}
It is not 100% impossible, but if you search this forum you can find that many tried it and many failed.
A sensor has I2C implemented in hardware.
An Arduino uses hardware and software. The software is the Wire library and for example the receiveEvent() function. To get more time to run the software, the Arduino stretches the SCL clock. That means the Master has to support clock stretching or else it is impossible.
You might start by studying the Wire library and buy a logic analyzer.
In your sketch you use Wire.onReceive(), that is for receiving data. In the receiveEvent() function you do not read the received data, but you try to send data. I think you need the Wire.onRequest() and a requestEvent().
ahw, since you used the Wire library in the wrong way, I don't know if the changed sketch was correct. You have to show the sketch.
The 0x68 that the Arduino Wire library uses is the 7-bit shifted I2C address. How do you know that is correct ?
I don't know if the clock stretching issue causes the Arduino not to be able to acknowledge to its I2C address. As far as I remember, there is no clock stretching at that moment, but I could be wrong.
Can you get a logic analyzer ? A good logic analyzer shows the data as a number, it shows the start and stop bits and the acknowlegde. The saleae logic analyzers can do that, but they are extremely expensive. The extremly cheap option is PulseView/sigrok with a 10 dollar USB device.
The i2c sensor I'm trying to emulate is the MPU-6050, which has an i2c address of 0x68, so just kept it the same. I am able to communicate with the sensor using the other microcontroller, so I know that setup is working (mcu config,wiring, software, etc).
I also have two arduinos. One of which I used as an i2c scanner, to confirm my intended slave arduino has the correct address set.
I don't have a logic analyzer with me, but I do have an oscilloscope. So I know the correct waveform is being sent to the arduino, but the arduino doesn't seem to be responding at all.
I've also done a simple void loop on the slave arduino, to spam i2c transmit the following:
What is the Master device ? An Arduino ? If it is an Arduino board, then I would like to see the complete Master and the complete Slave sketch. If the Master device is not a Arduino board, what can you tell about it ?
Some microcontrollers have a software implementation for I2C, and those do often not allow clock pulse stretching. There can also be trouble with the voltage levels.
The address 0x68 is correct.
As soon as you do a "Wire.begin(0x68)", then the Arduino acknowledges to its Slave address, even without the requestEvent() and receiveEvent() functions.
The requestEvent() should send data, I don't know why you send 0x68.
To simulate a MPU-6050 is not easy.
The receiveEvent() and requestEvent() functions are called from an interrupt function. They should be as short and as fast as possible. You use them now in the right way, but you should not use Serial functions in those functions. The Serial functions use interrupts themself.
The master device is an EFM32GG STK3700. I can show the code for the master if desired. Anything else about it I can provide to help?
Basically I'm writing some i2c drivers for the EFM32, that I want to test out. Was hoping to use the arduino to simply respond to any commands I've written. The MPU-6050 was just an example; the use case I'm trying to emulate here is the self address command/register. This is where the EFM32 requests the device address from arduino, and the arduino responds with it's address. This is why I'm sending 0x68 back to the master device.
The serial functions were for debugging purposes only.
It seems to have the EFM32GG990F1024 processor. According to the datasheet it has a I2C module that can do everything and more. I suppose it does support clock pulse stretching.
I think it runs at 3.0V, that is not compatible with a 5V Arduino board.
Which Arduino board do you use ? An Arduino Uno is a 5V Arduino board and it requires 3.5V at SDA and SCL to see it as a "high". You can fix that with a I2C level shifter.
Can you read the "Who Am I" code from register 0x75 from the real MPU-6050 ?
If you can, then you can try to simulate the registers with the Arduino.
Use an array of 120 bytes, and use a register address (the index of the array). The Master can read one or more bytes with the next code for the Slave.
byte simulated_registers[120];
byte register_address;
void setup()
{
simulated_registers[0x75] = 0x68; // fill the who_am_i
}
void receiveEvent( int howMany)
{
if( howMany > 0) // for safety, check if some data was received.
{
register_address = Wire.read();
}
}
void requestEvent()
{
Wire.write( (byte *) &simulated_registers[register_address], 32);
}
Using Wire.write() with 32 bytes is filling the buffer of the Wire library. It is not known how many bytes the Master is requesting.
Simulating a I2C device with an Arduino board is a major task. If there are other ways to achieve what you want, you better try something else.
If you can make it work, there is still an issue with speed and interrupts.
I can read the "who am I" register from the real MPU-6050 using both the Arduino and the EFM32.
I have an Arduino Uno that I'd like to use as a slave. The EFM32 i2c lines are at 3.3V. Do you suspect the level shifter would solve the ack issue? I didn't think I would need one from 3.3V to 3.5V. The arduino still doesn't seem to be responding.
A level shifter can connect a 3.3V I2C bus to a 5V I2C bus.
If the level is 3.3V, that is in most cases enough for an Arduino Uno (although the datasheet says it needs 3.5V).
If you can read the "who am I" with an Arduino board from a real MPU-6050, can you show that Master sketch ?
Can you use that same Master sketch to read the "who am I" from the Arduino Slave which simulates the MPU-6050 ? I assume you have more than one Arduino board.
For the low level I2C bus acknowledge signal, a logic analyzer is a must. Perhaps you can capture it with a scope.
The first 8 clock pulses is the address + r/w bit, the nineth clock pulse is for the acknowledge. If the SDA stays idle, it is high, and that is not an acknowledge. Can you see that with the scope ?
Attached is a screendump of the free PulseView/sigrok + 10 dollar logic analyzer.
The Slave is the code as you showed. The Master was my own code which reads the "who am I" register.
As you can see, the aknowledge at the 9-th bit is there without delay.
The clock is longer low before reading the "who am I" byte, that is the clock pulse stretching when the requestEvent() is called.
Your Master code is okay. You can remove the "Wire.beginTransmission(MPU_addr);" from the setup().
Perhaps it is a voltage level problem ? It is worth to investigate that.
Do you have a 3.3V Arduino board ? Can you try to lower the voltage of the Arduino Uno ? Can you buy a I2C level converter ?
Did you always connect the GNDs ? And you use short wires for the SDA and SCL ? Not a cable and certainly not a flat ribbon cable ?
I've taken the hints, are have purchased a logic analyzer
Regarding the 3.3V ardunio, do you mean something like a Pro Mini? Can you lower the arduino voltage in software, or are you talking about a mod here?
For the setup, I've connected the grounds. I'm using cables ~ 1 - 2.5inches, but that doesn't seem to matter for the arduino to i2c sensor.
If the code is working as expected, and there doesn't seem to be a reason why the clock stretching issue you mentioned would be affecting this, then I think it must be some issue with the EFM32 dev board.
I ran into a similar issue with using the just EFM32 to i2c sensor. The scoped clock wave looks like a sawtooth wave (see attached). I was only able to get that connection working with a pullup resistor. The same setup doesn't seem to help the arduino connection at all.
It is possible that the I2C library for the EFM32 has bugs. The I2C bus is not as easy as a serial UART bus. The Arduino Wire libraries also had a few bugs. Did you see the glitch in the SDA in my screendump. That is the Wire library. It is still not perfect.
A 8 MHz Pro Mini can run at 3.3V. Some Arduino Uno compatible boards have a switch to set it at 3.3V.
A dirty trick is to upload a sketch, disconnect the USB cable and power the Arduino Uno with 3.3V to its 5V pin. That makes the microcontroller to run at 3.3V and the I2C bus will be a 3.3V I2C bus.
What is the reason to use the EFM32GG STK3700 ?
There are so many good options. A Arduino Uno for basic things. An Arduino Zero or MKR1000 for more advanced things. The Teensy boards have good support. The ESP8266 or ESP32 are okay. Some Raspberry Pi boards are very good value for little money.
If you want to be ahead of everyone else, go for the Adafruit Metro SAMD51.
If this is a EFM32GG STK3700 problem, you should ask on a forum for that product.
I wonder if they really use the processor hardware for the I2C bus, perhaps they use a software implementation for I2C.
2.2k - 10k seems to work for the i2c sensor, but doesn't do anything to clean up the waveform. I'll have to look to see if there are additional ways to configure the GPIO for the EFM32. Maybe that will also help the arduino connection as well.
I've inadvertently done the 3.3V trick a couple times, and it didn't help.
Regarding the choice of microcontroller/board here, unfortunately I don't have a choice. This is ultimately for a board that uses the EFM32, so I'm doing development on the development kit for that IC.
I've got some threads over on the EFM32 forums. This was just to make sure to exhaust the arduino side of things, in case I was missing something.
I'm getting the sense that this is about as far as we're going to be able to go here on the arduino side. Really appreciate your help with this.
ahw:
I'm getting the sense that this is about as far as we're going to be able to go here on the arduino side.
I think so as well. I'm glad that I could confirm that the Arduino as a Slave aknowledges to its I2C address in hardware.
Meanwhile, my 10 dollar logic analyzer stopped working. Maybe it was too cheap
However, the other microcontroller doesn't seem to be recognizing the arduino. Any thoughts on what the issue may be?
At the Master side, execute the following codes and check that the Serial Monitor shows 0x00. 00 measn that the Slave has recognized its own Slave address and has produced the ACK signal. Non-zero value or hang on in an infinite loop is an indication of non-matching slave address.
These are not correct? The correct instructions are:
void receiveEvent(int howMany)
{
Wire.beginTransmission(0x68); //0x68 is the slave address as shown here Wire.begin(0x68);
Wire.write(0x68); //if 0x68 is data -- fine; else, it should be replaced by data
Wire.endTransmission(); // stop transmitting
for(int i = 0; i< bytes; i++)
{
x = Wire.read();
Serial.print("Received: ");
Serial.print(x, HEX);
Serial.print("\n");
}
You are reading a number of bytes from FIFO buffer; so, the storage area should be an array.
GolamMostafa:
At the Master side, execute the following codes and check that the Serial Monitor shows 0x00. 00 measn that the Slave has recognized its own Slave address and has produced the ACK signal. Non-zero value or hang on in an infinite loop is an indication of non-matching slave address.
These are not correct? The correct instructions are:
void receiveEvent(int howMany)
{
Wire.beginTransmission(0x68); //0x68 is the slave address as shown here Wire.begin(0x68);
Wire.write(0x68); //if 0x68 is data -- fine; else, it should be replaced by data
Wire.endTransmission(); // stop transmitting
You are reading a number of bytes from FIFO buffer; so, the storage area should be an array.
Code was correct in subsequent posts. Looks like an issue with the other board.