#include "i2cscan.h" #ifdef ESP8266 uint8_t portArray[] = {16, 5, 4, 2, 14, 12, 13}; String portMap[] = {"D0", "D1", "D2", "D4", "D5", "D6", "D7"}; #elif defined(ESP32) uint8_t portArray[] = {4, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33}; String portMap[] = {"4", "13", "14", "15", "16", "17", "18", "19", "21", "22", "23", "25", "26", "27", "32", "33"}; #endif namespace I2CSCAN { uint8_t pickDevice(uint8_t addr1, uint8_t addr2, bool scanIfNotFound) { if(I2CSCAN::isI2CExist(addr1)) return addr1; if(!I2CSCAN::isI2CExist(addr2)) { if(scanIfNotFound) { Serial.println("[ERR] I2C: Can't find I2C device on provided addresses, scanning for all I2C devices and returning"); I2CSCAN::scani2cports(); } else { Serial.println("[ERR] I2C: Can't find I2C device on provided addresses"); } return 0; } return addr2; } void scani2cports() { bool found = false; for (uint8_t i = 0; i < sizeof(portArray); i++) { for (uint8_t j = 0; j < sizeof(portArray); j++) { if (i != j) { if(checkI2C(i, j)) found = true; } } } if(!found) { Serial.println("[ERR] I2C: No I2C devices found"); } } bool checkI2C(uint8_t i, uint8_t j) { bool found = false; Wire.begin(portArray[i], portArray[j]); byte error, address; int nDevices; nDevices = 0; for (address = 1; address < 127; address++) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("[DBG] I2C (@ " + portMap[i] + " : " + portMap[j] + "): "); Serial.print("I2C device found at address 0x"); if (address < 16) Serial.print("0"); Serial.print(address, HEX); Serial.println(" !"); nDevices++; found = true; } else if (error == 4) { Serial.print("[ERR] I2C (@ " + portMap[i] + " : " + portMap[j] + "): "); Serial.print("Unknow error at address 0x"); if (address < 16) Serial.print("0"); Serial.println(address, HEX); } } return found; } bool isI2CExist(uint8_t addr) { Wire.beginTransmission(addr); byte error = Wire.endTransmission(); if(error == 0) return true; return false; } /** * This routine turns off the I2C bus and clears it * on return SCA and SCL pins are tri-state inputs. * You need to call Wire.begin() after this to re-enable I2C * This routine does NOT use the Wire library at all. * * returns 0 if bus cleared * 1 if SCL held low. * 2 if SDA held low by slave clock stretch for > 2sec * 3 if SDA held low after 20 clocks. * From: http://www.forward.com.au/pfod/ArduinoProgramming/I2C_ClearBus/index.html * (c)2014 Forward Computing and Control Pty. Ltd. * NSW Australia, www.forward.com.au * This code may be freely used for both private and commerical use */ int clearBus(uint8_t SDA, uint8_t SCL) { #if defined(TWCR) && defined(TWEN) TWCR &= ~(_BV(TWEN)); //Disable the Atmel 2-Wire interface so we can control the SDA and SCL pins directly #endif pinMode(SDA, INPUT_PULLUP); // Make SDA (data) and SCL (clock) pins Inputs with pullup. pinMode(SCL, INPUT_PULLUP); boolean SCL_LOW = (digitalRead(SCL) == LOW); // Check is SCL is Low. if (SCL_LOW) { //If it is held low Arduno cannot become the I2C master. return 1; //I2C bus error. Could not clear SCL clock line held low } boolean SDA_LOW = (digitalRead(SDA) == LOW); // vi. Check SDA input. int clockCount = 20; // > 2x9 clock while (SDA_LOW && (clockCount > 0)) { // vii. If SDA is Low, clockCount--; // Note: I2C bus is open collector so do NOT drive SCL or SDA high. pinMode(SCL, INPUT); // release SCL pullup so that when made output it will be LOW pinMode(SCL, OUTPUT); // then clock SCL Low delayMicroseconds(10); // for >5uS pinMode(SCL, INPUT); // release SCL LOW pinMode(SCL, INPUT_PULLUP); // turn on pullup resistors again // do not force high as slave may be holding it low for clock stretching. delayMicroseconds(10); // for >5uS // The >5uS is so that even the slowest I2C devices are handled. SCL_LOW = (digitalRead(SCL) == LOW); // Check if SCL is Low. int counter = 20; while (SCL_LOW && (counter > 0)) { // loop waiting for SCL to become High only wait 2sec. counter--; delay(100); SCL_LOW = (digitalRead(SCL) == LOW); } if (SCL_LOW) { // still low after 2 sec error return 2; // I2C bus error. Could not clear. SCL clock line held low by slave clock stretch for >2sec } SDA_LOW = (digitalRead(SDA) == LOW); // and check SDA input again and loop } if (SDA_LOW) { // still low return 3; // I2C bus error. Could not clear. SDA data line held low } // else pull SDA line low for Start or Repeated Start pinMode(SDA, INPUT); // remove pullup. pinMode(SDA, OUTPUT); // and then make it LOW i.e. send an I2C Start or Repeated start control. // When there is only one I2C master a Start or Repeat Start has the same function as a Stop and clears the bus. /// A Repeat Start is a Start occurring after a Start with no intervening Stop. delayMicroseconds(10); // wait >5uS pinMode(SDA, INPUT); // remove output low pinMode(SDA, INPUT_PULLUP); // and make SDA high i.e. send I2C STOP control. delayMicroseconds(10); // x. wait >5uS pinMode(SDA, INPUT); // and reset pins as tri-state inputs which is the default state on reset pinMode(SCL, INPUT); return 0; // all ok } }