I2C мобильный сканер

ТехнарьКто

★★★✩✩✩✩
13 Янв 2020
140
182
Очень простой мобильный прибор "поисковик" для быстрого определения адреса модуля с интерфейсом Inter-Integrated Circuit.
I2C поисковик:
/*   I2C scanner на arduino и LED1602 без кирилицы
[LIST]
[*]управление одной кнопкой и выводом на дисплей русскими словами
[*]сделано ТехнарьКто для себя в 2016 году
[*]работает на arduino uno, arduino nano, arduino pro mini
[*]Схема - ардуина с кнопкой
[*]кнопка  - контакт замыкается при нажатии
[*]от D3 к кнопке со второго контакта кнопки на GND
[*]Подключение I2C к Arduino
[*](A4) aрдуины к устройству с I2C на SDA
[*](A5) ардуины к устройству с I2C на SCL
[*]Не забудь подключить питание к проверяемому устройству I2C так,
[*]чтобы GND был общим, проверяемых устройств с ардуиной
[*]LCD RS pin to digital pin 8
[*]LCD Enable pin to digital pin 9
[*]LCD D4 pin to digital pin 4
[*]LCD D5 pin to digital pin 5
[*]LCD D6 pin to digital pin 6
[*]LCD D7 pin to digital pin 7
[*]LCD R/W pin to ground
[*]LCD VSS pin to ground
[*]LCD VDD pin to 5V
[*]10K variable resistor: 5V - LCD pin V0 - ground
[*]LCD pin A to 5V
[*]LCD pin K to 1K resistor to ground
[*]ТехнарьКто -> Модификация от  декабрь 2020 для сайта AlexGyver
[*]скетч исправлен для компиляции в среде Arduino IDE V 1.8.0
[*]https://www.arduino.cc/en/Main/OldSoftwareReleases#previous
[*]для возможности использования как под Windows XP, так и для более новых операционных систем.
[*]Использована новая на данный момент библиотека русификации дешевых LCD1602
[*]LCD_1602_RUS_ALL V 1.2.3 https://github.com/ssilver2007/LCD_1602_RUS_ALL
[*]Компиляция скетча с библиотекой в среде Arduino IDE V 1.8.0 проверена в WindowsXP и Windows7
[/LIST]
*/
#include <Wire.h>
#define _LCD_TYPE 2
#include <LCD_1602_RUS_ALL.h>
LCD_1602_RUS <LiquidCrystal> lcd(8, 9, 4, 5, 6, 7 ); // LCD(RS,E,D4,D5,D6,D7)
String atAddress = "at: ";
// кнопка
int button = 3;
const int BUTTON_NONE   = 0;
const int BUTTON_SELECT = 1;
// Состояние программы
int state;
const int DEFAULT_STATE = 0;
const int SCAN_STATE = 1;
const int FIND_STATE = 2;
const int ERROR_STATE = 3;
const int COMPLETE_STATE = 4;
boolean completeShow = false;// признак необходимости отображения окончания сканирования
boolean findUpdate = false;// признак необходимости обновления экрана при обнаружении устройства
int nDevices = 0;// количество найденных устройств
byte error, address;
byte min_address = 0;// минимальный адрес для сканирования
byte max_address = 127;// максимальный адрес для сканирования (с зарезервированными адресами, пусть будет)
byte foundAddress = 0;// адрес найденного устройства
int adr[127];
void setup()
{
  pinMode(button,INPUT_PULLUP); //подтяжка кнопки внутренним резистором к VCC
  Wire.begin();
  lcd.begin(16, 2);            
  lcd.setCursor(0, 0);// Установить 1 символ 1 строки
  lcd.print ("Поиск адреса I2C");
  delay(100);
  lcd.setCursor(1, 1);// Установить 2 символ 1 строки
  lcd.print("Moд. TexнapьKTO");
  delay(900);
  lcd.clear();
  lcd.setCursor(2, 0);// Установить 3 символ 1 строки
  lcd.print("Пуск поиска");
  lcd.setCursor(0, 1);// Установить 1 символ 2 строки
  lcd.print ("нажми кнопку...");
}
void loop()
  {
  if(getPressedButton() == BUTTON_SELECT)//запуск и продолжение сканирования только по нажатию кнопки
  {
    state = SCAN_STATE;// переходим в состояние сканирования
    findUpdate = true;// разрешаем обновление экрана при обнаружении устройства
    lcd.clear();
    lcd.setCursor(0, 0);// Установить 1 символ 1 строки
    lcd.print("Поиск адресов...");  
  }
  if(state == SCAN_STATE)// продолжаем сканирование
  {
    if(address < max_address)// еще имеются адреса для сканирования
    {
      scan();// выполним сканирование текущего адреса
    }
    else//сканирование завершено
    {
      state = COMPLETE_STATE;
      completeShow = true;
    }  
  }
  if(state == COMPLETE_STATE)// сканирование завершено
  {
    if(completeShow)// отображаем найденое
    {
      lcd.clear();
      lcd.setCursor(0, 0);// Установить 1 символ 1 строки
      lcd.print("Найдено: ");
      String nd = String(nDevices,DEC);
      // String s = wasFound + nd;
      lcd.print(nd);
      //lcd.print(s);
      lcd.print(" устр.");
      if(nDevices > 0)
     {
      label:
int i;
for (i = 0; i < nDevices; i = i + 1)
  {
  lcd.setCursor(0, 1); // установить 1 символ 2 строки
  lcd.print("                ");
  lcd.setCursor(0, 1); // установить 1 символ 2 строки
  lcd.print((i+1),DEC);
  lcd.print("-е: ");
  lcd.print("0x");
  lcd.print(adr[i],HEX);
  lcd.print("  ");
  lcd.print(adr[i],DEC);
  delay(5000);                  // 5 сек задержка при показе найденных адресов
  }
  goto label; // переходим к метке label
      }
      if(nDevices == 0)
      {
      lcd.setCursor(0, 1); // установить 1 символ 2 строки
      lcd.print("устройств нет");
      }
    }
    completeShow = false;
  }
  if(state == FIND_STATE)
  {  
    if(findUpdate)// сюда заходим только однократно при обнаружении устройства
    {    
adr[nDevices]=foundAddress;
      nDevices = nDevices + 1;// увеличиваем счетчик найденных устройств

      lcd.clear();
      lcd.setCursor(0, 1);// Установить 1 символ 2 строки
      lcd.print("нажми  кнопку...");
      String a = String(foundAddress, HEX);
      String ad = String(foundAddress,DEC);

      //String s = atAddress + ad + " 0x" + a;
      String s = ad + " 0x" + a;
      lcd.setCursor(0, 0);// установить 1 символ 1 строки
      lcd.print("Найден: ");
      lcd.print(s);
      findUpdate = false;// запрещаем обновление экрана при обнаружении устройства
    }    
  }
  if(state == ERROR_STATE)//
  {
    // неопределенные устройства плюсуем к найденным
    nDevices = nDevices + 1;// увеличиваем счетчик найденных устройств
    lcd.clear();
   // lcd.print(unknownDevice);
    lcd.setCursor(0, 1); // установить 1 символ 2 строки
    String a = String(foundAddress, HEX);
    String ad = String(foundAddress,DEC);
    String s = atAddress + ad + " 0x" + a;
    lcd.print(s);
    findUpdate = false;// запрещаем обновление экрана при обнаружении устройства  
  }
}
// метод сканирования текущего адреса
void scan()
{
  if(address < max_address)
  {
    error = 1;// уберем ошибку 0 и 4 если была
    // Сканирование использует возвращаемое значение функции
    // Write.endTransmisstion для обнаружения устройства на текущем адресе сканирования  
     lcd.setCursor(5, 1); // Установить 5 символ 2 строки
     lcd.print(address, DEC);
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
     lcd.setCursor(12, 1); // Установить 12 символ 2 строки
     String hex = toHEX(address);
     lcd.print("0x");
     lcd.print(hex);
    if(error == 0)//найдено устройство
    {
      state = FIND_STATE;
      foundAddress = address;
          }
    if(error == 4)//найдено устройство
    {
      state = ERROR_STATE;
      foundAddress = address;
    }
    address = address + 1;// увеличим текущий сканируемый адрес
    delay(200);  
  }
}
// определение нажатой кнопки
int getPressedButton()
{
  int buttonValue = digitalRead(button); // считываем значения с аналогового входа(A0)
  if (buttonValue == LOW)
  {
    return BUTTON_SELECT;
  }
  return BUTTON_NONE;
}
// метод преобразования числа типа byte в виде строки в HEX представлении
String toHEX(byte number)
{
  String ret;
  if(number < 16)
  {
    ret = "0" + String(number,HEX);
  }
  else
  {
    ret = String(number,HEX);
  }        
  return ret;
}
Сборочный рисунок
ris-scan-I2C.jpg
После сборки включив кнопку питания и соответственно подав напряжение питания на ардуино и дисплей, надо покрутить подстроечный резистор на 10 килоОм до получения на экране текста. После этого прибор готов к эксплуатации.


На видео старый поисковик 2016 года. На фотографиях далее по тексту уже новый поисковик собранный для проверки на макете с подправленным скетчем 2020 года.

Теория.

Все описания крайне упрощены для лучшего понимания базовых основ. Знающих прошу воздержаться от комментирования в стиле описания зарезервированных адресов, десяти битной адресации, работы нескольких ведущих и прочих отвлекающих моментов. Остальным, добро пожаловать в мир подключения кучи (хоть штук сто) устройств к ардуино по трем проводам (SDA, SCL, GND).

Inter-Integrated Circuit сокращенно IIC. Еще бывают названия (ай-квадрат-си) и (ай.ту-си). Суть от этого не меняется — это последовательная асимметричная шина для связи между интегральными схемами внутри электронных приборов. В тексте буду писать I2C. Модулем буду называть устройство которое имеет интерфейс I2C для подключения к ардуино с питанием 5V.

Предупреждение. ВАЖНО! Для подключения к модулям с питанием 3,3V надо использовать преобразователь логических уровней, либо хотя бы токоограничивающие резисторы, а запитывать модуль на 3,3V соответственно можно только от напряжения 3,3V. На видео и фото, подключены модули с питанием 5V.

I2C использует две линии связи SDA и SCL на которые через резисторы в несколько килоОм подано положительное напряжение питания электронной схемы. Резисторы обычно уже установлены в модулях продаваемых китайцами на алиэкспресс. Работает шина относительно линии питания GND. Все SDA соединяют вместе и все SCL соединяют вместе, такое соединение и называют шиной. То что питание плюс и минус надо подавать на все устройства наверное и так должно быть понятно. При внешнем питании модулей, с поисковиком надо обязательно соединить и GND. Для одного модуля с малым потреблением, положительный вывод питания можно использовать от поисковика, чтобы не городить схему с внешним питанием модуля. Каждое устройство, подключённое к шине, может быть программно адресовано по уникальному адресу. Семь бит создают уникальный адрес до 127 устройств на шине. Несколько адресов зарезервировано под расширение функционала поэтому реально на шине можно подключить менее 127 устройств. Восьмой бит означает, устройство будет принимать или передавать данные на следующем байте. Все очень просто. Главное правильно записать адрес устройства в скетч и схема заработает, а куча модулей (хоть сто штук) будет занимать на ардуино всего два пина, без учета питания.

Портативный сканер был собран еще в 2016 году. Недавно мне прислали OLED экранчики с I2C интерфейсом и для подключения в схему мне понадобилось узнать адреса этих дисплеев. Снял с полки старый гаджет и подумал, что можно поделится схемой и скетчем. Для этого пришлось исправить скетч для новой библиотеки и для более свежей ардуино иде.

Одно время меня активно убеждали, что устройство должно быть как кастрюля с кнопкой. Простым и удобным в использовании. Поэтому кроме одной кнопки управления там ничего нет. Подключаете питание к шине с модулями адреса которых хотите проверить. Подключаете SDA, SCL и GND к поисковику. На дисплее написано, что делать. При загрузке сначала показывает служебную информацию и сразу переходит в ожидание запуска.
1.jpg
Нужно для визуального контроля отсутствия косяков. Смотрите, на дисплее надпись есть, значит прибор работает. На модуле при наличии индикатора питания, индикатор горит, значит модуль подключен. Нажимаете кнопку. При переборе адресов, встретив устройство на шине, происходит остановка.
2.jpg
Можете записать найденный адрес.
3.jpg
И так будет останавливаться на каждом найденном устройстве, пока не будет проверено все пространство адресов. В конце напишет сколько устройств найдено
4.jpg

и будет с интервалом в пять секунд показывать номера модулей.
5.jpg
При показе последнего найденного, перейдет к первому и будет показывать с начала. И так пока не выключите. Этот режим нужен для проверки собранного устройства с множеством подключенных модулей.

В простейшем случае, просто втыкаете модуль прямо в поисковик с питанием от поисковика и выключаете после того как записали найденный адрес на первой остановке поиска.

Всем успехов.
 

Вложения

Изменено:

PiratFox

★★★✩✩✩✩
13 Фев 2020
884
190
Автору зачот! ;) А как насчёт нескольких девайсов I2C на одной шине? Я в том смысле, что на дисплее места мало адреса все показывать...
 
Изменено:

PiratFox

★★★✩✩✩✩
13 Фев 2020
884
190
@ТехнарьКто, грамотно, больше вопросов нет. Ещё один зачот! ;) Вы инженер настоящий. Конкретно и по делу пишете. Не против будете, если я Вашу прошивку буду юзать? А то самому писать некогда, да и лень, если честно. :ROFLMAO:
 
Изменено:

ТехнарьКто

★★★✩✩✩✩
13 Янв 2020
140
182
Не против будете, если я Вашу прошивку буду юзать? А то самому писать некогда, да и лень, если честно. :ROFLMAO:
Извиняюсь, что сразу не обратил внимания на это сообщение. Все, что здесь выкладывается, для того и выкладывается, что бы пользовались. Если есть желание изменить скетч, то пожалуйста. Если есть желание включить в свой проект, тоже никто не запрещает. Пользуйтесь.
 
  • Красота! +2
Реакции: PiratFox