Arduino и 1-Wire — эмуляция ведомого устройства с помощью библиотеки OneWireSlave


Arduino и 1-Wire - эмуляция ведомого устройства

Библиотека OneWireSlave позволяет использовать ардуину на линии 1-Wire в качестве ведомого устройства. Подробнее о OneWire можно прочитать здесь. Теоретически на линии может быть до 2^48 или 281 триллионов ведомых устройств.
С помощью этой библиотеки на одну линию совместно с датчиками температуры и прочими устройствами, использующими протокол 1-Wire, можно подключить несколько ардуин.

OneWireSlave(uint8_t pin);

— конструктор, параметром указывается пин ардуины для OneWire линии.

void setRom(char rom[8]);

— устанавливает тип и адрес устройства. Если библиотека скомпилирована с поддержкой расчета контрольной суммы crc8, то она подсчитывается и заносится в последний байт rom.

bool waitForRequest(bool ignore_errors);

— функция блокирует выполнение программы и ожидает обращения к текущему устройству. Функция возвращает управление программе, если ведущее устройство отправило команду MATCH ROM с адресом текущего устройства или команду SKIP ROM. Во время ожидания функция принимает участие в поисковых запросах ведущего устройства и отвечает на команду READ ROM. Таким образом обеспечивается поддержка базового протокола. Если параметр ignore_errors установлен, то функция будет игнорировать ошибки чтения/записи, иначе вернет FALSE.

bool waitReset(uint16_t timeout_ms);

— функция ждет RESET импульса на линии, после чего возвращает управление программе. Параметр timeout_ms устанавливает время в миллисекундах, по истечение которого функция принудительно вернет управление программе. Если timeout_ms равен 0, то программа будет вечно ожидать RESET импульс, пока не дождется. Стандартное значение timeout_ms — 1000. Возвращает TRUE, если принят сигнал RESET, в случае слишком короткого или слишком длинного сигнала или по истечение времени ожидания вернет FALSE.

bool presence(uint8_t delta);

— функция посылает сигнал PRESENCE согласно протоколу. Если в конце передачи сигнала на линии по какой-либо причине будет низкий уровень сигнала, функция вернет FALSE.

bool search();

— функция участвует в процедуре поиска устройств, описанной в документации протокола OneWire. Возвращает TRUE, если при поиске было найдено текущее устройство, иначе FALSE.

uint8_t sendData(char buf[], uint8_t len);

— отсылает на линию указанный объем данных. Возвращает количество отосланных байт данных.

uint8_t recvData(char buf[], uint8_t len);

— считывает с линии указанный объем данных и заносит их в указанный буфер. Возвращает количество успешно считанных байт данных.

void send(uint8_t v);

— отсылает на линию один байт данных.

uint8_t recv(void);

— считывает с линии один байт данных.

void sendBit(uint8_t v);

— отсылает на линию один бит данных.

uint8_t recvBit(void);

— считывает с линии один бит данных.

static uint8_t crc8(char addr[], uint8_t len);

— функция подсчитывает контрольную сумму по алгоритму crc8 указанного объема данных.

uint8_t errno;

Если выполнение функции закончилось неудачей, то она занесет номер ошибки в эту переменную.

Ошибки:
ONEWIRE_NO_ERROR при выполнении функции не возникло никаких исключений.
ONEWIRE_READ_TIMESLOT_TIMEOUT истекло время ожидания таймслота от ведомого устройства при чтении с линии. Устанавливается функциями recv*.
ONEWIRE_WRITE_TIMESLOT_TIMEOUT истекло время ожидания таймслота от ведомого устройства при записи на линию. Устанавливается функциями send*.
ONEWIRE_WAIT_RESET_TIMEOUT истекло время ожидания импульса RESET на линии. Устанавливается функцией waitReset.
ONEWIRE_VERY_LONG_RESET импульс RESET принят, но оказался слишком длинным (возможно это не RESET, а что-то другое). Устанавливается функцией waitReset.
ONEWIRE_VERY_SHORT_RESET импульс RESET оказался слишком коротким. Такая ошибка может появиться, если при ожидании RESET сигнала другое устройство начало передачу данных.
ONEWIRE_PRESENCE_LOW_ON_LINE по окончании передачи PRESENCE сигнала на линии оказывается низкий уровень сигнала. Согласно протоколу все устройства в момент считывания сигнала с линии должны были прекратить передачу импульса PRESENCE.

Пример скетча, эмулирующего работу ключа ibutton с микросхемой DS1990A

#include "WProgram.h"
#include "OneWireSlave.h"

char rom[8] = {0x01, 0xAD, 0xDA, 0xCE, 0x0F, 0x00, 0x00, 0x00};
OneWireSlave ds(8);

void setup() {
    ds.setRom(rom);
}

void loop() {
    ds.waitForRequest(false);
}

В данном случае функция waitForRequest вернет FLASE с ошибкой ONEWIRE_READ_TIMESLOT_TIMEOUT, т.к. домофон (или его эмулятор) не выбирает устройство, а только считывает его адрес.

В случае обрыва линии, на выводе может возникнуть неопределенное состояние, поэтому рекомендуется вывод ардуины подключить к земле через резистор 220к.

Заметка: с реальным домофоном без дополнительной обвязки ардуина работать не будет, т.к. в современные домофоны устанавливаются защиты от копированных ключей. Защита срабатывает на основе некоторых электрических и возможно временных параметров.

Скачать библиотеку: OneWireSlave.zip
OneWireSlave2.zip (с доработкой от Алексея Мочалова(lex512))

Автор статьи: Александр Гордеев — победитель конкурса «Напиши эмулятор iButton-а для Arduino и получи ProtoShield в подарок!»

По теме
Протокол 1-Wire
Практическое программирование Arduino/CraftDuino — протокол 1-Wire и iButton
Arduino/CraftDuino и эмулятор iButton


13 комментариев на «“Arduino и 1-Wire — эмуляция ведомого устройства с помощью библиотеки OneWireSlave”»

  1. Не совсем понял практический смысл как использовать ардуину на линии 1-Wire в качестве ведомого устройства, но чувствуется работа проделана большая. Great work!

    • Вернее не так. «Для каких целей спользовать ардуину на линии 1-Wire в качестве ведомого устройства» 🙂

  2. А для каких целей используют микроконтроллер?
    Вот для этих целей и Ардуино, 1-Wire это просто один из вариантов доступа в дополнение к UART или Ethernet(нужен доп шилд)

    • Кстати вдогонку, есть интересная реализация взаимодействий 2х и более Ардуино по I2C, один из которых Мастер.

  3. здоровым.
    Я не работаю… Я должен Arduino IButton как сканеры, но когда я подключить эмулятор для читателя, я через последовательный монитор Arduino пишет неполную ID — 1 8B C8 FF FF FF FF. и так домофона на эмуляторе не работает.

  4. Светлых мыслей.
    Благодарность авторам и выложившим сюда описание людям.
    А можно написать минимальные требование для библиотеки?
    В attiny13 влезет?
    Хорошо бы порт нв stm8s003f3p6 по 17р. за проц 🙂

  5. Если залить это на Arduino то можно открыть этим домофон?

    Не могу понять что это за ошибка?

    CC:\Users\MLG\Documents\Arduino\sketch_jan13b\sketch_jan13b.ino:4:62: warning: narrowing conversion of ‘173’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]

    char rom[8] = {0x01, 0xAD, 0xDA, 0xCE, 0x0F, 0x00, 0x00, 0x00};

    ^

    C:\Users\MLG\Documents\Arduino\sketch_jan13b\sketch_jan13b.ino:4:62: warning: narrowing conversion of ‘218’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]

    C:\Users\MLG\Documents\Arduino\sketch_jan13b\sketch_jan13b.ino:4:62: warning: narrowing conversion of ‘206’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]

    C:\Users\MLG\Documents\Arduino\sketch_jan13b\sketch_jan13b.ino: In function ‘void setup()’:

    C:\Users\MLG\Documents\Arduino\sketch_jan13b\sketch_jan13b.ino:8:18: warning: invalid conversion from ‘char*’ to ‘unsigned char*’ [-fpermissive]

    ds.setRom(rom);

    ^

    In file included from C:\Users\MLG\Documents\Arduino\sketch_jan13b\sketch_jan13b.ino:2:0:

    C:\Users\MLG\Documents\Arduino\libraries\OneWireSlave/OneWireSlave.h:44:10: note: initializing argument 1 of ‘void OneWireSlave::setRom(unsigned char*)’

    void setRom(unsigned char rom[8]);

    ^

  6. народ кто может помочь добавить данную функцию в этот скетч.

    #include <OneWire.h>
    #include "pitches.h"
    
    
    const byte greenLed = 10; //зеленая лампочка
    const byte redLed = 11;  //красная лампочка
    const byte gerkonPin = 8; //gerkon
    const byte iButtonPin = 12; //пин для ibutton
    const byte tonePin = 7; // пин для пищалки
    const byte waterPin = 9; // пин датчика воды
    
    const byte alarmTime = 3; // время, чтобы поднести ключ при разблокировке
    const byte startTime = 5; // время на закрытие двери при постановке на сигнализацию
    
    OneWire  ds(iButtonPin); //ibutton
    byte code1[8] = {0x01, 0x9F, 0xDC, 0x02, 0x00, 0x00, 0x96, 0x1C};  //первый ключ
    byte code2[8] = {0x01, 0xF0, 0x30, 0xB5, 0x00, 0x00, 0x00, 0xDB}; //второй ключ (он же и для
    
    boolean waterAlarmed = 0; //флаг сработавшего оповещения о воде
    boolean alarmState = 0; //флаг работающей сигнализации
    boolean gerkonState = 0; //первично закрытая дверь (1 - закрыто, 0 - открыто), пока на кнопке.
    boolean Hacked = 0;
    // стандартные коды: 1- включение, 2- выключение, 3- тревога
    void setup()
    {
        Serial.begin(115200);
        delay(2000); //задержка включения
        pinMode(greenLed, OUTPUT); //задаем режим работы пинов
        pinMode(redLed, OUTPUT);
        pinMode(waterPin, INPUT);
        pinMode(gerkonPin, INPUT);
        Notify(98); // оповещение о включении ардуино (напр. после перезагрузки)
    }
    void loop()
    {
        byte returnedValue = 0;
        if(alarmState==0) { //Сигнализация выключена, ждем пока поднесут ключ для ВКЛЮЧЕНИЯ
            returnedValue = findKey();
            if (returnedValue == 1)  { // нашли известный ключ
                guardON(); //вкл статус сигнализации
            }
        }
        if(alarmState==1) { // если включена сигнализация, то
            gerkonState = digitalRead(gerkonPin); //смотрим геркон
           //  Serial.println(gerkonState, HEX);
            if (gerkonState == 1 && Hacked != 1) { //открылась дверь и квартира не взломана ;)
                Notify(1); // сообщаем компьютеру об открытии двери
                alarmON(); //включаем сигналку
            }
            returnedValue = findKey(); //нужно выключить изнутри квартиры
            if (returnedValue == 1)  { // нашли известный ключ и
                alarmState=0;
                guardOFF();
            }
        }
        returnedValue = 0; //обнуляем для обработки датчиков воды
        //returnedValue = checkWater();
        //if (returnedValue==1) { // запускаем оповещение о срабатывании датчика воды
        //  Notify(10); //сообщаем о срабатывании датчика воды
        //}
        delay (500);
    }
    int checkWater() //функция проверки датчика воды
    {
        int WsensorState = analogRead(waterPin);
        Serial.println(WsensorState);
        if (WsensorState == 1 && waterAlarmed == 0){
            waterAlarmed =1;
            return 1;
        }
        return 0;
    }
    int findKey() // функция поиска ключа
    
    
    
    {
        byte i;
        byte addr[8];
        byte checkOK = 0; //счетчик совпадений массива с эталоном
        //Serial.println("searching key...");
        if (!ds.search(addr)) {  // тут надо сделать нормально сделатЬ!!!
        //Serial.println("No key connected..."); // сообщаем об этом
    ds.reset_search();
    delay(200);
    return; // и прерываем программу
    
            for(int i = 0; i<8; i++)
    Serial.print(addr[i],HEX);
    Serial.print(" ");
    }
    Serial.println("\n");
    
    
    for (int i=0; i<8; i++)
    
                if (addr[i]==code1[i]) {
                    checkOK++;
                }
    
                if (addr[i]==code2[i]) {
                    //сюда надо добавить начало отправки и через 2 секунды остановить
                }
    
    
            if (checkOK == 8 ){
            return 1;
        }
    return 0;
    }
    
    
    
    void guardON() // выполнение действие при на сигнализацию
    {
        int j;
        alarmState=1; //включаем статус сигнализации
        guardSound(1); //бибикаем
        for (j=0; j < startTime; j++) {
            guardBlink(1); //моргаем лампочками и ждем время для закрытия двери
        }
        Notify(0);
    }
    void guardOFF() // выполнение действие при снятии с сигнализации
    {
        alarmState=0; // выключаем статус сигнализации
        Hacked = 0; // отменям статус "взломано"
        digitalWrite(greenLed, LOW);
        guardSound(2); //бибикаем
        guardBlink(2); //моргаем лампочками
        Notify(3);
    }
    int alarmON() // выполнение действий при срабатывании сигнализации
    {
        int checkcount=0;
        while (checkcount < alarmTime) { // даем alarmTime секунд для поднесения ключа
            int alarmCancel = findKey(); //ищем ключик
            if (alarmCancel == 1) { // нашли ключ
                guardOFF(); //отключаем сигналку,
                break; // выходим из цикла
            }
            checkcount++;
            guardBlink(3); //пищим о том, что ищем ключ
            guardSound(3); //светим о том, что ищем ключ
        }
        if (checkcount==alarmTime) { // если не нашли, то включаем оповещение
            Notify(2);
            guardBlink(4); //пищим о том, что УСЁ ПРОПАЛО
            guardSound(4); //светим о том, что УСЁ ПРОПАЛО
            Hacked = 1;
        }
    }
    int guardBlink (int x) //функция светового оповещения
    {
        if (x==1) { // первое ожидание для закрытия двери.
            digitalWrite(redLed, HIGH);
            digitalWrite(greenLed, LOW);
            delay(500);
            digitalWrite(redLed, LOW);
            digitalWrite(greenLed, HIGH);
            delay(500);
        }
        if (x==2) { // выключаем сигнализацию и диоды
            digitalWrite(redLed, LOW);
            digitalWrite(greenLed, LOW);
        }
        if (x==3) { // во время ожидания ключа при открытой двери
            digitalWrite(redLed, HIGH);
            delay (250);
            digitalWrite(redLed, LOW);
            delay (250);
        }
        if (x==4) { // просрочено, ахтунг
            digitalWrite(redLed, HIGH);
        }
    }
    int guardSound (int x)  //функция звукового оповещения
    {
        if (x==1) {
            int melody[] = { // музыка при включении сигнализацию
                NOTE_G3, NOTE_G3, NOTE_G3
            };
            int noteDurations[] = {
                4,4,4
            };
            for (int thisNote = 0; thisNote < 3; thisNote++) {
                int noteDuration = 1000/noteDurations[thisNote];
                tone(tonePin, melody[thisNote],noteDuration);
                int pauseBetweenNotes = noteDuration * 1.30;
                delay(pauseBetweenNotes);
                noTone(tonePin);
            }
        }
        if (x==2) { // выключаем сигнализацию
            int melody[] = {
                NOTE_C2, NOTE_G1,NOTE_G2, NOTE_A1, NOTE_G1,0, NOTE_B2, NOTE_C3
            };
            int noteDurations[] = {
                4, 8, 8, 4,4,4,4,4
            };
            for (int thisNote = 0; thisNote < 8; thisNote++) {
                int noteDuration = 1000/noteDurations[thisNote];
                tone(tonePin, melody[thisNote],noteDuration);
                int pauseBetweenNotes = noteDuration * 1.30;
                delay(pauseBetweenNotes);
                noTone(tonePin);
            }
        }
        if (x==3) { //тон ожидания ключа
            tone(tonePin,NOTE_C4, 100);
        }
        if (x==4) { //тон просроченного ключа
            tone(tonePin, 500); //включаем на 500 Гц
            delay(100); //ждем 100 Мс
            tone(tonePin, 1000); //включаем на 1000 Гц
            delay(100); //ждем 100 Мс
        }
    }
    int Notify(int x) //функция оповещения в последователньый порт
    {
        if (x==0) { // оповещение о постановке на сигнализацию
            Serial.println("Сигнализация включена");
        }
        if (x==1) { // оповещение о открытии двери
            Serial.println("Дверь открыта");
        }
        if (x==2) { // оповещение о взломе квартиры!
            Serial.println("Тревога, проникновение");
        }
        if (x==3) { // оповещение о выключении сигнализации
            Serial.println("Сигнализация отключена");
        }
        if (x==98) { // оповещение о включении ardunio
            Serial.println("Устройство готово к работе");
        }
        if (x==99) { // оповещение о ОШИБКЕ
            Serial.println("Ошибка");
        }
    }

    действия:
    1) если приложен ключ 1 {0x01, 0x9F, 0xDC, 0x02, 0x00, 0x00, 0x96, 0x1C}, выполняется первая команда записанная в скетче.
    2) если приложен ключ 2 {0x01, 0xF0, 0x30, 0xB5, 0x00, 0x00, 0x00, 0xDB}, начинается инициализация передачи кода {0x01, 0xF0, 0x30, 0xB5, 0x00, 0x00, 0x00, 0xDB} на пин 6

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение