Arduino Pro over I2C with Eagle Tree v3

Hi,

I’m using the Eagle Tree v3 data logger to record various parameters of a high-power (2-3kW) brushless DC motor i.e. current, voltage, rpm, temperature, throttle. It is really quite a nice piece of hardware and very robust/capable/useful for a wide range of motor sizes, both brushed and brushless. The data logger communicates with its native Windows software over USB, but it also has I2C out to an LCD display. I’m trying to interface with the Arduino Pro 5V/16MHz, so keying off the LCD comms channel seemed the way to go.

I’ve looked at the comms traffic over a Saleae logic analyzer with I2C decoding capability. The eagle tree is master writing to slave address 0x3b (the LCD display). The LCD sends an ACK for every byte sent. For now, I can leave the details of the comms protocol aside, though I understand it completely from the LCD module datasheet i.e. what hex bytes are doing what, and what each byte means. For each byte of data, it takes this form: [address byte] [control byte] [data byte]

I have the logger set up to transmit 10 readings a second, where a reading contains all my parameters. These come in the form of 4 pulses of 50-200 bytes at approximately 175kbit/s set by SCL, spaced such that one full reading takes 25ms.

When I execute the following code only, the arduino keeps up with each byte sent and transmits an ACK just fine:

#include <Wire.h>

void setup()
{
Wire.begin(0x3b);
//Wire.onReceive(receiveEvent); // register event
}

void loop() {}

That is to say, when all I do is join the bus, the Arduino does a perfect job of emulating everything the LCD does, so the Eagle tree transmits all the data it should.

However, when I execute this code i.e. trying to record the sent bytes, I get problems:

#include <Wire.h>

void setup()
{
Wire.begin(0x3b);
Wire.onReceive(receiveEvent); // register event
}

void loop() {}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
while(Wire.available())
byte b = Wire.receive();
}

The arduino starts skipping some of the ACKs for address bytes. Every skipped ACK results in a skipped data character. For example, instead of 39.54V, I might get something like 95. The skipped ACKs are consistent with time for all sets of readings, but they don’t follow a readily apparent pattern like one on one off (some sets are one on one off, some get a string of 10 or more bytes right). Also, because of the skipping, the Eagle Tree interprets this as the LCD panel intermittently leaving, and retransmits its LCD startup routine, with different timing. The effect is that I get two garbled readings per second.

I should say that I’m using Arduino 012 (though I’ve tried 013 as well), and that I altered twi.h to have a buffer size of 128 (some of the strings of bytes transmitted are about 100 bytes long i.e. between control bytes), uncommented ATMEGA8 to enable pull up resistors on analog 4 and 5, and changed TWI_FREQ to 175000 (from 100000). I also changed wire.h to have a buffer size of 128, though I don’t think this matters.

Does anyone have any insight into what’s going on? Is this interrupt routine receiveEvent slow enough that it’s disrupting whatever code is responsible for sending the ACKs? Any help or possible solutions would be appreciated.

Thanks!

Hi again,

So I’ve made some additional progress in determining what my problem is, but I’d really appreciate input from someone more knowledgeable in the inner workings of the Wire library than myself. I’ve looked around a bit and this has helped, but I haven’t made the final leap.

I should also say that I think this datalogger, which weighs only 25g and is very robust, would be excellent in a wide range of motor control applications i.e. for telemetry or real time current/rpm feedback.

I’ve narrowed my problem to timing. I’ve gotten the following code working without dropping any bytes except one important one on startup, which still causes this recurring half second delay startup routine to trigger after every reading. The effect is that I get about two ungarbled parameter readings a second (instead of 10).

#include <Wire.h>

void setup()
{
Wire.begin(0x3b);
Wire.onReceive(receiveEvent);
}

void loop() {}

void receiveEvent(int howMany) {

while(Wire.available()) {
byte b = Wire.receive(); //receive the byte
//packet[packet_size++] = Wire.receive();

}
}

I believe the receiveEvent interrupt call can’t do much beside read in the byte. If I start doing ANYTHING else, like even one println statement or a write to an array or even if I declare byte b as a global variable, I start getting skipped characters. Also, it doesn’t appear to be consistent in which characters are being skipped, but it is always an address byte i.e. I can get 80 consecutive characters read in if they fall between an address byte and a stop signal, but every couple/few address bytes are liable to be dropped, if I do anything in my receiveEvent interrupt besides read the byte. And when I say they are dropped, I mean that they are sent by the logger, but they are not acknowledged by the arduino (no ACK), so the logger doesn’t send the subsequent control and data bytes, which results in a dropped character.

I am sure of this timing interference in receiveEvent, because if I set up serial comms at different baud rates and do a println statement after byte b = Wire.receive(), I see corresponding lags on my logic analyzer between when a character can be read in.

I find this strange, because it seems other people are able to execute a number of commands in their receiveEvent interrupt, including in the Wire slave receive example, in which two println statements are executed. And the SCL frequency for the logger appears to be pretty close to 175kHz, which is not so far off from the standard 100kHz that I should be getting such wildly different behavior.

I should say that to get the ungarbled readings, I reduced the TWI_BUFFER_LENGTH from 128 to 80 (which is the minimum needed to read all the characters which are responsible for clearing the screen when the proper I2C LCD is connected, which is part of the startup routine). The original TWI_BUFFER_LENGTH is 32. I’ve experimented with different TWI_FREQ values between 100000L and 400000L, to no avail.

Can anyone shed some light on this issue? Have other people had problems using receiveEvent with arduino as a slave? Can anyone think of other ways of doing this?

Again, any and all input would be appreciated. This has become quite frustrating. Thanks!

Edit: Looking over this, I think perhaps it wasn’t clear that when using the arduino, I have the LCD disconnected i.e. I’m not using the arduino as a sniffer, but as an emulator.

Also, does anyone know where howMany is set or what exactly it’s referring to? (the argument of receiveEvent)

The problem is not the Wire library itself, but rather a shortcoming in the AVR hardware that handles the TWI interface. In particular, it appears that the hardware is unable to detect a new start condition while the TWI interrupt flag is set. As this flag is not cleared until after the onReceive function finishes, this can easily become as much as 100 microseconds before the AVR is ready to receive again. Since the I2C spec only requires ~5 microseconds between starts and stops, this means that your data logger has begun the next transmission before the AVR is prepared to receive.

I have been having similar problems using the TWI interface for inter-AVR communication, but my solution thus far has been to implement a retry system. That, unfortunately, won't solve your problem.

There may be a way to fix the problem, though, that involves disabling and re-enabling the TWI in the ISR. I am going to be examining this over the next week as I polish up my project for release, and if I find anything, I'll send you a copy of my revised Wire library.

The only other solutions available are to either know exactly how many bytes are being transmitted and then perform the processing after the last byte and before the stop. However, this would require a rewrite of the entire Wire library to fit your needs, and would not be readily adaptable to any other TWI device.

There might also be a way of NOT processing after a stop, and instead processing following the next SLA+ACK or after 2 ms, whichever comes first. However, this would require a rewrite of both the Wire library and the arduino core.

The following link has a good discussion of the shortcoming, and proposes a solution for if the AVR is the master, but it does not address solving the problem from the slave's side.

http://www.robotroom.com/Atmel-AVR-TWI-I2C-Multi-Master-Problem.html

Best of luck, -Chris

Chris,

Thanks for this. That was very useful. I'm curious if non-Atmel microcontrollers, such as perhaps something from a pic 16 or 18 family also have this shortcoming i.e. not keeping an eye on the bus while in the I2C stop/start interrupt.

Also, this would seem to be a general problem for all arduino programs using the Arduino as slave. How is it, for example, that anyone could get the receiveEvent example code working, which executes 2 println statements while in the interrupt? I'm not sure how many cycles a println statement of 1 byte takes, but even at 115200 baud, you can't print while in the interrupt and still have time to read in the next byte. I assume the hardware shortcoming is a problem with all Arduino families. I ask because I also have a Sanguino using the 644p. I might test on it just to see if the behavior differs at all.

Please keep me updated if your disable/re-enable workaround is successful. I appreciate it.

Thanks,

Stephen

I can’t say personally about pic chips, but I believe some exhibit the problem and some do not.

In any case, I believe I have found a solution. Replace your Wire library with the one in the following zip file, and you should be good to go. (You WILL need to delete your old copy of Wire, as I didn’t bother to change the names of the files.

Give it a try and let me know if it works.

http://engr.georgefox.edu/wiki/uploads/Chirps/Wire_Revised.zip

-Chris

Hello.

I am new to Arduino, trying to monitor a diesel engine.

You seem to be a guy that have thought about these issues.

My engine has a variable reluctance sensor for rpm. I succeeded sending that signal to drive my car rpm analog gauge. I use a LM1906 IC, that (they say) converts VR signal to a square wave signal, which is ok for my analog tacho gauge. LM1906 is an old IC used for ignition & tacho signals.

Now, I need to input the same rpm signal to my Arduino board, to monitor purposes.

What do you think: should I take the signal from the VR sensor or from the LM1906? First thing to consider.

Second: what kind of input assignment should I use in Arduino for either case? Would you please sketch a very simple programming lines for that kind of input to be used in Arduino?

I tried analogInput to an analog pin, don´t seem to work.

I got some reading though: rpm number that shows is about 1000 rpm at any throttle position. However, if I take out the wire from Arduino, the rpm drops to about 400 rpm.

Non of then is correct, as my engine at idle turns at 750 rpm.

By the way, my engine is a 6 in line diesel, mechanic.

And third thing: the injectors of the Zexel injection pump injects about 30 mm3 at each stroke app., when the injection pump rotates between 900 rpm and 2500 rpm.

I think there are 1.5 strokes for any rotation of the engine. Do you agree?

Looking forward to hear from you,

Old Beaver

I have download the revised Wire Lib. But still unable to solve my NXT to Arduino Connection problem.