// Данный пример ищет модули на шине и выводит информацию о них.
                                                              //
#include <unistd.h>                                           // Подключаем библиотеку unistd, для получения доступа к POSIX API
#include <fcntl.h>                                            // Подключаем библиотеку доступа к файлам
#include <sys/ioctl.h>                                        // Подключаем библиотеку контроля входов/выходов
#include <linux/i2c-dev.h>                                    // Подключаем библиотеку для работы с шиной i2c
#include <stdint.h>                                           // Подключаем библиотеку с макросами целочисленных типов
#include <iostream>                                           //
                                                              //
int file_i2c;                                                 // Создаём переменную для хранения файлового дескриплора
const char* filename = "/dev/i2c-1";                          // Создаём переменную с адресом файла шины i2c
                                                              //
void print(uint8_t i){                                        // Создаём функцию для вывода uint8_t в шеснадцетиричной системе в stdout
  std::cout << std::hex << int(i);                            //
}                                                             //
void print(const char* msg){                                  // Создаём функцию для вывода строки в stdout
  std::cout << msg;                                           //
}                                                             //
                                                              //
void println(const char* msg){                                // Создаём функцию для вывода строки в stdout с последующим символом новой строки
  std::cout << msg << '\n';                                   //
}                                                             //
                                                              //
int main(){                                                   //
    if((file_i2c = open(filename, O_RDWR))<0) return 1;       // Открываем файл шины i2c. Если модель Raspberry ревизии 2.0 (выпущена после 2012 года) - i2c-1, если ревизия 1.0 - i2c-0. Узнать номер шины можно вызвав ls /dev в консоли.
    uint8_t i,j,k[4],f=1,b[1]{4};                             // Объявляем переменные.
    println("Поиск устройств на шине I2C ... ");              //
//  Проходим по всем доступным адресам на шине I2C:           //
    for(i=7; i<127; i++){                                     // Проходим по всем доступным адресам на шине I2C ...
      ioctl(file_i2c, I2C_SLAVE, i);                          // Инициируем передачу данных по шине I2C к устройству с адресом i и битом RW=0 (запись). При этом сама передача не начнётся.
      j=write(file_i2c, b, 1);                                // Указываем единственный байт который мы хотим передать (это номер регистра).
      if(j==1){                                               // Если передача выполнена успешно (0-передача успешна / 1 - переполнен буфер для передачи / 2 - получен NACK при передаче адреса / 3 - получен NACK при передаче данных / 4 - другая ошибка), то ...
        f=0;                                                  // Сбрасываем флаг f указывающий на отсутствие устройств на шине I2C.
        print("Устройство с адресом 0x");                     //
        print(i);                                             //
        read(file_i2c, k, 4);                                 // Инициируем получение данных по шине I2C в массив k. Третий аргумент функции указывает количество байт которое мы желаем прочитать.
        if( ((k[2]>>1)==i) && (k[3]==0xC3 || k[3]==0x3C) ){   // Если значение второго элемента массива k совпадает с адресом устройства, а в третьем элементе хранится идентификатор 0xC3 (модуль Metro), или 3С (модуль Flash), то ...
                         print(" является ");                 //
          if(k[0]==0x01){print("кнопкой");             }else  // Если в нулевом элементе массива k хранится значение 1, значит это кнопка.
          if(k[0]==0x02){print("светодиодом");         }else  // Если в нулевом элементе массива k хранится значение 2, значит это RGB светодиод.
          if(k[0]==0x03){print("потенциометром");      }else  // Если в нулевом элементе массива k хранится значение 3, значит это потенциометр.
          if(k[0]==0x04){print("звукоизлучателем");    }else  // Если в нулевом элементе массива k хранится значение 4, значит это звукоизлучатель.
          if(k[0]==0x05){print("датчиком DHT");        }else  // Если в нулевом элементе массива k хранится значение 5, значит это датчик владности и температуры.
          if(k[0]==0x06){print("датчиком света");      }else  // Если в нулевом элементе массива k хранится значение 6, значит это датчик света.
          if(k[0]==0x07){print("расширителем выводов");}else  // Если в нулевом элементе массива k хранится значение 7, значит это датчик света.
          if(k[0]==0x08){print("LED матрицей");        }else  // Если в нулевом элементе массива k хранится значение 8, значит это светодиодная матрица.
          if(k[0]==0x0A){print("реле на 2 канала");    }else  // Если в нулевом элементе массива k хранится значение A, значит это электромеханическое реле на 2 канала.
          if(k[0]==0x0B){print("реле на 4 канала");    }else  // Если в нулевом элементе массива k хранится значение B, значит это твердотельное реле на 4 канала.
          if(k[0]==0x0C){print("силовым ключём 4 N");  }else  // Если в нулевом элементе массива k хранится значение C, значит это силовой ключ на 4 N-канала.
          if(k[0]==0x0D){print("силовым ключём 4 P");  }else  // Если в нулевом элементе массива k хранится значение D, значит это силовой ключ на 4 P-канала.
          if(k[0]==0x0E){print("силовым ключём 4 N");  }else  // Если в нулевом элементе массива k хранится значение E, значит это силовой ключ на 4 N-канала, с измерением тока.
          if(k[0]==0x0F){print("силовым ключём 4 P");  }else  // Если в нулевом элементе массива k хранится значение F, значит это силовой ключ на 4 P-канала, с измерением тока.
          if(k[0]==0x10){print("бампер с датчиками линий"  ); }else //   Если в 0 элементе массива k хранится значение 0x10, значит это бампер с датчиками линий.
          if(k[0]==0x11){print("джойстик"                  ); }else //   Если в 0 элементе массива k хранится значение 0x11, значит это джойстик.
          if(k[0]==0x12){print("LCD конвертер в I2C"       ); }else //   Если в 0 элементе массива k хранится значение 0x12, значит это адаптер для LCD дисплеев 1602/2004.
          if(k[0]==0x13){print("клавиатура"                ); }else //   Если в 0 элементе массива k хранится значение 0x13, значит это клавиатура.
          if(k[0]==0x14){print("мотор-редуктор с драйвером"); }else //   Если в 0 элементе массива k хранится значение 0x14, значит это мотор.
          if(k[0]==0x15){print("реле на 1 канал"           ); }else //   Если в 0 элементе массива k хранится значение 0x15, значит это электромеханическое реле на 1 канал.
          if(k[0]==0x16){print("LED индикатор на 4 разряда"); }else //   Если в 0 элементе массива k хранится значение 0x16, значит это 4-разрядный LED индикатор.
                        {print("неизвестным модулем"); }      // Если в нулевом элементе массива k хранится иное значение, значит этот модуль был создан позднее чем данный скетч, или на одном адресе находятся несколько устройств.
                         print(" с версией прошивки ");       //
                         print(k[1]);                         // В первом элемента массива k хранится версия прошивки модуля.
                         println(".");                        //
        }else           {println(" не опознано.");}           //
      }                                                       //
     }                                                        //
    if( f )            {println("Устройства не найдены.");}   //
}                                                             //
