MultiSpeed I2C Scanner - 50,100,200,400 KHz.

I have pull ups of 3.2K (measured value), so my signal might be more "square"

I have added a link to your MultiSpeed I2C Scanner in Arduino Playground - I2cScanner
See the "Interesting Links" section.
But I don't like that link to reply 4.

robtillaart, could you give your Multispeed I2C Scanner it's own place and adjust that link in the i2c_scanner ?
You could add it to the i2c_scanner page, but perhaps that is confusing for new users, that page is very simple now, and that is better for new users.
Another option to edit your first post, and make that the main article and for your scanner.

good proposal,
will put the link in the first post.

@krodal

  • moved last code to first post
  • updated link in the 12csanner page
  • added a small header in i2c scanner page (you may remove it)

Short testrun on a MEGA r3 today => worked smooth.

Hi Rob,

I used your MultispeedScanner 0.1.03 for Test an I2C Circuit. It works fine with a Duemilanova, but if I use an Arduino Due (with levelshifter from 3.3V to 5V) I got no Answer from any I2C chip. The change for the Due I did is to comment out the "TWBR-Statement". I used IDE 1.5.6-R2.
In this Postings you can find als some scope and small pieces from the a logic analyzer.
http://forum.arduino.cc/index.php?topic=234999.msg1692208#msg1692208

Regards, Detlef

AFAIK, the DUE has problems with I2C but I never dived into the details.

Can you get meaningful results from other I2C scanners on the DUE?

Two modifications were necessary for MultiSpeed I2C Scanner to work with Due.

  1. TWBR must be emulated by adding the following code after the #include statements (cf. ARM support · Issue #48 · jrowberg/i2cdevlib · GitHub):
#if defined(__arm__)
class TWBRemulation
{
public:
    inline TWBRemulation & operator = (int val) __attribute__((always_inline)) {
    if (val == 12 || val == ((F_CPU / 400000) - 16) / 2) {
        #if F_BUS == 48000000
        I2C0_F = 0x1A; // 400 kHz
        #elif F_BUS == 24000000
        I2C0_F = 0x45; // 400 kHz
        #endif
    }
    return *this;
}
};
TWBRemulation TWBR;
#endif
  1. Due's pull-up resistors are unsuitable for most I2C devices (cf. Due I2C not working - Arduino Due - Arduino Forum). Thus I removed the onboard network resistor, and used external 4.7k pull-ups.

(Maybe using the Wire1 library instead of Wire and connecting an externally-pulluped I2C bus to SCL1/SDA1 pins will work, too, but I just checked the above mentioned workaround works.)

And please use a Nightly build version of Arduino IDE. Because the Wire library before 1.5.7 including the current 1.5.6r2 has a serious bug on the return values of TwoWire::endTransmission function. (cf. Fix for Due Wire library by bluesign2k · Pull Request #1994 · arduino/Arduino · GitHub )

Thanks for these additions (have no time to confirm, sorry )

Did you get meaningful results with the I2C scanner now?

Oh....
I'm sorry. This morning I found the addition 1 is meaningless and not working as multi speeds. It only scans at 100kHz.
So if you only need to scan at 100kHz, simply comment out the TWBR code and use the nightly ide 1.5.7.

(cf. MPU_DMP6 with Arduino DUE (TWBR not declared) - MPU-6050 6-axis accelerometer/gyroscope (InvenSense) - I2Cdevlib Forums)

updated to 0.1.04 (see first post)

main change is restricting the # addresses to be scanned as I2C address 0..7 and 120..127 have special meanings.

or download from here - Arduino/sketches/MultiSpeedI2CScanner at master · RobTillaart/Arduino · GitHub -

updated to version 0.1.05 on github

not tested enough imho, so not updated in the first post.

as always comments and remarks are welcome.

Hi,
I have added the Multi Speed I2C to my webserver, running on a Arduino Mega 2560.

But when I let it try 500kHz my webserver crashes.
Is it possible to detect a I2C error without causing a crash ?

In Arduino 1.5.8 the new Wire.setClock() can be used.

table-multispeedtest.png

Peter_n:
Is it possible to detect a I2C error without causing a crash ?

Really depends on the cause of the crash.
Does the I2C scanner work when used standalone (no webserver)?
Does the webserver crash if you just set the I2C clock to 500KHz?

You may post your code ....

I found the problem, but I don't know how to solve it.
Setting the TWBR down to 2 (for 800kHz) is normally no problem.

Only in the situation when I have a Arduino as Slave at 5V and another Arduino as Slave at 3.3V (using a level shifter). In that situation the sketch never returnes from Wire.endTransmission() when the speed is too high. At the moment it happens at 670kHz, when TWBR becomes 3.

Below is my function to make a webpage.
On the 'home' page I have a link to "SCAN". When I click it, the file "SCAN" is requested. But instead loading it from the SD card, I recognize the keyword and call the function "MultiSpeedI2CScanner".
Everything (table, colspan) is only up to 400kHz. To allow the higher speeds, the html code for the table should be adjusted.

// MultiSpeedI2CScanner
// To be used in a webserver, it outputs HTML code with the result
// The new Wire.setClock() is available in Arduino IDE 1.5.8, but that is not used yet.
// It is assumed that Wire.begin() has been called already.
//
// Using:
//
//  FILE: MultiSpeedI2CScanner.ino
//  AUTHOR: Rob Tillaart
//  VERSION: 0.1.04
//  PURPOSE: I2C scanner @different speeds
//  DATE: 2013-11-05
//  URL: http://forum.arduino.cc/index.php?topic=197360
//
// Released to the public domain
//

#include <Wire.h>
#include <Ethernet.h>


// scans devices from 50 to 800KHz I2C speeds.
// lower than 50 is not possible
// DS3231 RTC works on 800 KHz. TWBR = 2; (?)
const long speed[] = {                             // 'const' added
  50, 100, 200, 250, 400 };            // , 500, 800 };       // only up to 400 !
const int speeds = sizeof(speed)/sizeof(speed[0]);

// DELAY BETWEEN TESTS
#define RESTORE_LATENCY  5    // for delay between tests of found devices.
bool delayFlag = false;

// MINIMIZE OUTPUT
bool printAll = false;
bool header = true;


void MultiSpeedI2CScanner(EthernetClient *pClient)
{
  //  Wire.begin();          // already called in the sketch of my webserver

  pClient->println(F("<html>"));
  pClient->println(F("<head>"));
  pClient->println(F("<meta content=\"text/html; charset=UTF-8\" http-equiv=\"content-type\">"));
  pClient->println(F("<title>Multi Speed I2C scanner</title>"));
  
  // Make a nice style for the table.
  pClient->println(F("<style type=\"text/css\">"));
  pClient->println(F("table { color:#303030; border-width: 1px; border-color: #0000A0; border-collapse: collapse; }"));
  pClient->println(F("table th { border-width: 1px; padding: 6px; border-style: solid; border-color: #0000A0; background-color: #C0FFFF; }"));
  pClient->println(F("table td { border-width: 1px; padding: 6px; border-style: solid; border-color: #0000A0; background-color: #D0D0FF; }"));
  pClient->println(F("</style>"));
  
  pClient->println(F("</head>"));
  pClient->println(F("<body>"));
 
  I2Cscan( pClient);

  pClient->println(F("</body>"));
  pClient->println(F("</html>"));
}

void I2Cscan(EthernetClient *pClient)
{
  uint32_t startScan;
  uint32_t stopScan;
  
  startScan = millis();
  uint8_t count = 0;

  // The style is defined in the header.
  pClient->println(F("<table>"));
  pClient->println(F("<tbody>"));

  if (header)
  {
    pClient->print(F("<tr>"));
    pClient->print(F("<th colspan=\"8\">Multi Speed I2C scanner</th>"));
    pClient->println(F("</tr>"));
    
    pClient->print(F("<tr>"));
    pClient->print(F("<th>TIME</th>"));
    pClient->print(F("<th colspan=\"2\">Address</th>"));
    pClient->print(F("<th colspan=\"5\">Speed [kHz]</th>"));
    pClient->println(F("</tr>"));

    pClient->print(F("<tr>"));
    pClient->print(F("<th>&nbsp;</th>"));
    pClient->print(F("<th>DEC</th>"));
    pClient->print(F("<th>HEX</th>"));
    
    for (uint8_t s = 0; s < speeds; s++)
    {
      pClient->print(F("<th>"));
      pClient->print(speed[s]);
      pClient->print(F("</th>"));
    }
    
    pClient->println(F("</tr>"));
  }

  // TEST
  // 0.1.04: tests only address range 8..120
  // --------------------------------------------
  // Address	R/W Bit	Description
  // 0000 000   0	General call address
  // 0000 000   1	START byte
  // 0000 001   X	CBUS address
  // 0000 010   X	reserved - different bus format
  // 0000 011   X	reserved - future purposes
  // 0000 1XX   X	High Speed master code
  // 1111 1XX   X	reserved - future purposes
  // 1111 0XX   X	10-bit slave addressing
  for (uint8_t address = 8; address < 120; address++)
  {
    bool printLine = printAll;
    bool found[speeds];
    bool fnd = false;

    for (uint8_t s = 0; s < speeds ; s++)
    {
      TWBR = (F_CPU/(speed[s]*1000) - 16)/2;

      Wire.beginTransmission (address);
      found[s] = (Wire.endTransmission () == 0);
      fnd |= found[s];
      
      // give device 5 millis
      if (fnd && delayFlag) 
        delay(RESTORE_LATENCY);
    }

    if (fnd) count++;
    printLine |= fnd;

    if (printLine)
    {
      pClient->print(F("<tr>"));

      pClient->print(F("<td>"));
      pClient->print(millis());
      pClient->print(F("</td>"));

      pClient->print(F("<td>"));
      pClient->print(address, DEC);
      pClient->print(F("</td>"));

      pClient->print(F("<td>0x"));
      pClient->print(address, HEX);
      pClient->print(F("</td>"));

      for (uint8_t s = 0; s < speeds ; s++)
      {
        pClient->print(F("<td>"));
        pClient->print(found[s]? F("V"):F("."));
        pClient->print(F("</td>"));
      }
      pClient->println(F("</tr>"));

    }
  }

  pClient->println(F("</table>"));

  stopScan = millis();
  if (header)
  {
    pClient->println(F("
"));
    pClient->print(count);
    pClient->print(F(" devices found in "));
    pClient->print(stopScan - startScan);
    pClient->print(F(" milliseconds."));
    pClient->println(F("
"));
  }
}

The problem might be that the interrupts of printing interfere with the bits of the I2C bus.
You need a logic analyser for that I guess.

I think that my I2C bus is crumbling down at 670kHz. The sketch for the webpage is the resulting sketch with the client.prints. I tested it with your sketch with and without Serial.prints, with and without delay and so on. I even did Serial.flush() everywhere to be sure that the Serial library was not longer busy. The result was still the same.

I was assuming that my I2C could be best at 100kHz, because of wires and Arduino Slaves, but now I feel confident to use 200kHz :grin:

Well, I didn't buy an oscilloscope (yet) : budget USB oscilloscope - General Electronics - Arduino Forum

Hi,

for using Arduino Due you need at least Arduino IDE 1.5.8. The guys have changed some parts including wire. So you can use this tool if you replace this line from the code:

      TWBR = (F_CPU/(speed[s]*1000) - 16)/2;

with this part

#if ARDUINO >= 158
      Wire.setClock(speed[s]*1000);
#else
      TWBR = (F_CPU/(speed[s]*1000) - 16)/2;
#endif

I tried this with the Arduino nightly build of 1.6.0 and it works fine on my Due.

Best regards
Nils

added this to the scanner 0.1.06 version

derniwi:
Hi,

for using Arduino Due you need at least Arduino IDE 1.5.8. The guys have changed some parts including wire. So you can use this tool if you replace this line from the code:

      TWBR = (F_CPU/(speed[s]*1000) - 16)/2;

with this part

#if ARDUINO >= 158

Wire.setClock(speed[s]*1000);
#else
      TWBR = (F_CPU/(speed[s]*1000) - 16)/2;
#endif



I tried this with the Arduino nightly build of 1.6.0 and it works fine on my Due.

Best regards
Nils

I did it but ID.exe crashes while compiling !