Well, it takes more time to figure this out and check. Also made a video about him, see the link at the bottom of this answer. All credits go to @ user0042, which points me in the right direction. By default, for a wired library, there is no need to use stability, reliability, so you need to "replace" it with the following:
I2C Master Library - http://dsscircuits.com/articles/arduino-i2c-master-library
There are more advantages to using this library, it is smaller in compilation size, read the article above for more information.
I changed my software, the "key" to detect the device on the bus, could be simplified:
bool TEnjoyPad::isPnpDeviceAvailable( uint8_t iAddress ) { return ( I2c.write( (int)iAddress, (int)0x00 ) == 0x00 ); }
Note. Type casting (int) is required to avoid a compiler warning, but it works fine without it.
I am sending **0x00 command** that do nothing, however the device seems to be responding. The function I made returns true when connected and false if not.
I doesn't test it with other i2c devices yet, however, will try later and update this question. For now it seems to working fine. NOTICE: SEE UPDATE BELOW:
PNP Approach
Step 1
In the first version, I did not use resistors (laziness), but it would be nice to stabilize the tire readings. Add two resistors (4.7K) to the + 5V output to the data lines. It is very important to do this in order to avoid false positives and to avoid the fact that the Arduino can still freeze because of this.
Step # 2
You need to track device changes / status for each I2C device. I use three states:
- Connected
- Connected (it's connected)
- Disconnected (or never connected)
Step # 3
If you use the class to "speak" on the device, it must be dynamically created when the device is available. In my example, it is something like this:
TOLEDdisplay* display;
Step # 4
Before each update, you need to check the availability and initialization status (the three states mentioned in step 3). This is very important to avoid unnecessary delays / code execution (stress).
- If it has not been connected before, you need to create a class
- If it was connected before (reconnecting), you need to reinitialize (class and therefore device)
Step # 5
You need to check for changes in a loop or interrupt. Better do it in a loop instead of interrupting.
Step # 6
Perform updates when changes are detected. Use a short delay of about 200 ms before a real update.
Sample code example
You cannot use this code, however it can give you an idea of ββhow to create the code. I use a lot of macros to simplify my actual code, so it is easier to read:
void TEnjoyPad::showAbout() // only showed at initialization { __tepClearDisplay(); __tepSetDisplayText( "ENJOYPAD v1.0" , TOD_TEXT_ALIGN_CENTER, TEP_DISPLAY_LINE1 ); __tepSetDisplayText( "(c) 2017 codebeat" , TOD_TEXT_ALIGN_CENTER, TEP_DISPLAY_LINE2 ); __tepRefreshDisplay(); setDelay( 2000 ); updateDisplay(); } void TEnjoyPad::updateDisplay() { if( !__tepDisplayIsInit() ) { return; } __tepDrawDisplayBitmap( TEP_DISPLAY, // bitmap 0, TEP_DISPLAY_LINE0, // x,y TEP_DISPLAY_WIDTH, TEP_DISPLAY_HEIGHT ); uint8_t i = TEP_MIN_MODE - 1; __tepDrawDisplayClearRect( 0, 10, 128, 35 ); while( ++i <= TEP_MAX_MODE ) { if ( emuMode != i ) { // erase area, delete what NOT selected __tepDrawDisplayClearRect( TEP_DISPLAY_MODE_ICON_X + ((i - 1) * (TEP_DISPLAY_MODE_ICON_WIDTH + TEP_DISPLAY_MODE_ICON_SPACING)), TEP_DISPLAY_MODE_ICON_Y, TEP_DISPLAY_MODE_ICON_WIDTH, TEP_DISPLAY_MODE_ICON_HEIGHT ); } else { __tepSetDisplayText( TEP_MODE_GET_NAME(i), TOD_TEXT_ALIGN_CENTER, TEP_DISPLAY_LINE1 ); } } __tepRefreshDisplay(); } void TEnjoyPad::beginDisplay( bool bIsFound = false ) { static bool bWasConnected = false; bIsFound = bIsFound?true:isPnpDeviceAvailable( TEP_PNP_ADDR_DISPLAY ); if( bIsFound ) { if( !bWasConnected ) { if( pnpStates[ TEP_PNP_IDX_DISPLAY ] ) { // Reset setDelay( 200 ); // Reset display bIsFound = isPnpDeviceAvailable( TEP_PNP_ADDR_DISPLAY ); if( bIsFound ) { __tepDisplay->begin(); updateDisplay(); } } else { // (re-)connected" ); __tepCreateDisplay(); // This macro checks also if class is created __tepInitDisplay(); showAbout(); // Set class is created pnpStates[ TEP_PNP_IDX_DISPLAY ] = TEP_PNP_ADDR_DISPLAY; } } bWasConnected = bIsFound; } else { // Disconnected bWasConnected = false; } } // In a loop I call this function: uint8_t TEnjoyPad::i2CPnpScan() { uint8_t iAddress = 0x7F; // 127 bool bFound = false; uint8_t iFound = 0; //Serial.println( "Scanning PNP devices...." ); while ( --iAddress ) { //Serial.print( "Scanning address: 0x" ); //Serial.println( iAddress, HEX ); if( iAddress == TEP_PNP_ADDR_DISPLAY ) { beginDisplay( bFound = isPnpDeviceAvailable( iAddress ) ); iFound+=bFound; } } return iFound; }
Demo video
I am also creating a demo video, a proof of concept, to show that this method works fine. You can watch the video on YouTube: https://www.youtube.com/watch?v=ODWqPQJk8Xo
Thank you all for your help and hopefully this information can help others as well.
UPDATE:
My method works fine with multiple I2C devices. I wrote this updated I2CScanner:
I2CScanner code you can use: