Перейти к публикации
iT4iT.CLUB
Kitsum

Замок с радиочастотной идентификацией

Рекомендованные сообщения

 

5 минут назад, Small_d сказал:

melfis, как подключен эзернет шилд? Через роутер или напрямую к компу?
Через роутер, проверяйте IP сервера в скетче
Если на прямую, то надо сперва DHCP сервер поднять, чтоб комп раздал IP на эзернет шилд. 
Без DHCP патч корд другой нужен, кроссовер. Вроде)) Поправьте, если ошибаюсь.

В скетче указать IP сервера, т.е. компа
Пользователь по умолчанию root, но можно в базе и своего создать, только права ему дать нужные, ну и пароль, если есть.

У меня работало либо с роутером, который раздавал IP, либо с поднятым DHCP сервером на компе, чтоб исключить лишнее звено (роутер). Без них так же выдавало 0.0.0.0 и соответственно ничего не работало.

подключено напрямую к пк. как я вас понял брать ip адрес из свойств подключения? либо назначить принудительный адрес. а dhcp вроде включено. 

lml.JPG

Изменено пользователем melfis

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@melfis открой не настройки, а сведения подключения и посмотри какой IP сейчас у твоего компа.

Изменено пользователем Small_d

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Только что, Small_d сказал:

@melfis открой не настройки, а свойства подключения и посмотри какой IP сейчас у твоего компа.

ну так я про это говорю. из свойств подключения брать айпи для ардуинки?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

не для ардуинки, а для скетча, ардуино получит другой адрес, а в скетче укажи адрес который на компе в данный момент.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@melfis

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

После этого Вам необходимо выполнить SQL запрос, описанный в оригинальном посте, для создание таблицы. По-хорошему, доступ к базе "test" должен осуществляться с использованием отдельной учетной записи имеющий права чтения и записи только в приделах этой базы данных. Но для теста Вы можете использовать учетную запись администратора, но только для теста. Потом обязательно позаботьтесь о безопасности.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Работает и без роутера, только пришлось отдельно установить на комп DHCP сервер. Разницы ведь нет, кто будет раздавать? 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@melfis Если подключено напрямую, то выставите вручную адрес для компьютера, например, 192.168.0.2 маска 255.255.255.0 и адрес для микроконтроллера 192.168.0.3 с аналогичной маской. Проверить связь сможете обычной утилитой ping.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
59 минут назад, Kitsum сказал:

@melfis Если подключено напрямую, то выставите вручную адрес для компьютера, например, 192.168.0.2 маска 255.255.255.0 и адрес для микроконтроллера 192.168.0.3 с аналогичной маской. Проверить связь сможете обычной утилитой ping.

вот подключил к роутеру. вписал в скетч айпи компьютера. выводится в мониторинге начал айпи который роутер присвоил ардуинке. но при прикладывании ключа появляется :r:r:r:r:r и летит до бесконечности

lml.JPG

Снимок.JPG

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@melfis Используете оригинальную программу с указанного выше поста или Вы вносили изменения, если вносили, то какие? Убедитесь в корректности данных учетной записи пользователя базы данной и разрешен ли её доступ с внешних адресов.

arduino_ethernet_rc522_mysql.png

К сожалению, Вам самостоятельно придется пройти путь настройки MySQL сервера, но уверяю Вас, если все сделаете правильно, то результат Вам понравится. По возможности, постарайтесь проверить доступность базы с другого компьютера. Будьте очень внимательны, и все перепроверяйте!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
3 часа назад, Kitsum сказал:

@melfis Используете оригинальную программу с указанного выше поста или Вы вносили изменения, если вносили, то какие? Убедитесь в корректности данных учетной записи пользователя базы данной и разрешен ли её доступ с внешних адресов.

arduino_ethernet_rc522_mysql.png

К сожалению, Вам самостоятельно придется пройти путь настройки MySQL сервера, но уверяю Вас, если все сделаете правильно, то результат Вам понравится. По возможности, постарайтесь проверить доступность базы с другого компьютера. Будьте очень внимательны, и все перепроверяйте!

я работал с базами. все создано. и внешнее подключение для пользователя. я и порты открыл в ручную. но с вашим скетчем у меня ничего не получилось. а сейчас вот попробовал скетч который предложил svchekalin а вы его подправили. и у меня все работает идеально. не знаю где я косячу с вашим скетчем или это особенности моего шилда бракованного капельку. спасибо вам за помощь:$

Снимок.JPG

Изменено пользователем melfis

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@melfis шилд явно в исправном состоянии. В программе от @svchekalin всего лишь добавлены два дополнительных запроса в базу на ведение логов ну и естественно модифицированы запросы под другое имя базы. Все остальное касается непосредственно только самой базы данных - название, имена таблиц, "явки пароли". И т.к у меня и у @svchekalin оригинальный скетч работал, то могу сделать вывод, что Ваша причина где-то на поверхности и очень проста, но пока не была явно замечена.

Могу предложить следующий вариант.

  1. Все удалите и начните с белого листа
  2. Используйте оригинальный скет для микроконтроллера
  3. Создайте базу, добавьте таблицу и один случайный ключ
  4. Подключитесь к базе и запросите этот ключ с самого сервера или другого компьютера
  5. Если проблема повторилась, то скиньте мне в приват бэкап всей базы с MySQL сервера, копию лога ошибок MySQL сервера и копию скетча (копируйте прямо из IDE со всеми изменениями которые вы вносили).

Будем разбираться основательно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Всем привет. 
По ходу написания своего диплома, написал небольшую форму на С# для администрирования базы данных. 
В форме выводится таблица, возможно изменять данные ключей, добавлять и удалять ключи. Для работы нужна вторая ардуинка с RFID модулем, чисто как читалка для ключа. Все работает, но есть один нюанс. COM порт, у меня указан конкретно под мое устройство в самом проекте в свойствах SerialPort1. 
Собственно нужна помощь, в доработке. 
На форму можно добавить combobox с выводом доступных ком портов. При выборе которого, считывание происходило бы именно с него.
На данный момент сделана просто защита от дурака с помощью Button.Enable, т.е. при нажатии кнопки сканировать ключ, все кнопки затухают до тех пор пока не поднесешь ключ к сканеру, поднес, ключ записался в поле ключ, кнопки стали доступны, далее добавляешь, удаляешь или изменяешь данные. На данный момент, после нажатия кнопки сканировать ключ, программа будет ждать ключа и больше ни на что не реагировать. Поднесли ключ, можно дальше работать.

Хотелось бы, что бы при запуске, можно было выбрать ком порт. Выбранный порт записался бы в SerislPort1 NamePort. При нажатии кнопки Сканировать ключ, если ком порт не верный выводилось предупреждение, типа выберите правильный ком порт.
Если верный, то появляется надпись поднесите ключ к сканеру.

Проект могу скинуть в личку, писал в MS Visual Studio 2015. Сдача диплома с 15 июня, можно конечно и так, но хочется продумать мелочи. Выложить готовый проект для всеобщего пользования буду готов после доделки и после сдачи)) Дабы антиплагиат мне все не испортил ;)

Загрузка компортов при запуске формы(cbPorts - это combobox)

private void Form1_FormLoad(object sender, EventArgs e)
{
 
string[] ports = SerialPort.GetPortNames();
      cbPorts.Items.Clear();
      cbPorts.Items.AddRange(ports);
 
}

Кнопка Сканировать ключ

//Кнопка сканирования RFID ключа
        private void btnScan_Click(object sender, EventArgs e)
        {
            scaner();
        }

        //Сканирование UID ключа
        private void scaner()
        {
            serialPort1.Open();
            string uidkey = serialPort1.ReadLine();
            serialPort1.Close();
            textBox1.Text = uidkey;
        }

Как сделать, что бы сканирование ключа происходило исходя из выбранного выше компорта в комбобоксе?



 

scud_admin_bb.png

scud_client.jpg

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

С выбором ком порта разобрался, надо было добавить всего лишь одну строку.
После:

//Сканирование UID ключа
        private void scaner()
        {
Добавить
serialPort1.PortName = cbPorts.Text;

Далее дописываем

	serialPort1.Open();
            string uidkey = serialPort1.ReadLine();
            serialPort1.Close();
            textBox1.Text = uidkey;
        }

Теперь другая проблема.

При выборе неверного компорта и нажатия кнопки Сканировать ключ, прога ждет ключа, но мы отсканировать не можем, в итоге висяк. На кнопки н реагирует и т.д. 
Т.е. нужно сделать таймер секунд на 10, после чего закрывать компорт. Ну и вывести сообщение, выберите другой компорт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@Small_d Думаю это будет полезным.

  1. Описание класса SerialPort
  2. Метод SerialPort.Open (обратите внимание на раздел Исключения)
  3. Свойство SerialPort.IsOpen
  4. Свойство SerialPort.ReadTimeout
  5. Свойство SerialPort.WriteTimeout
  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Здравствуйте. перерыл пловину гугла. пка нашел ету тему.

супер разработка!!

подскажите пожалуйста в иснии есть тип карты 0,1,2. доступ разрешен или нет и мастер.

рабтает ли ета функция? пока только изучаю ардуино.

и не нашел в счкетче гда проверяет что за тип ключа.

 

и еще вопрос. как можно доработать систему чтобы доступ работал по времени?

к примеру у нас на работе режим работы с 9 до 18.

но есть те у кого режим с 7 до 18.

 

и нужно ограничить дооступ. одни могут утромы открыть дврь а другие нет.

и к прмеру директор может круглосуточно заходить и выходить.

а все остальные только в указанное время.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@slava_573 доброе время суток.

15 часов назад, slava_573 сказал:

подскажите пожалуйста в иснии есть тип карты 0,1,2. доступ разрешен или нет и мастер.

рабтает ли ета функция?

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

    if(typeKey == F("1") or typeKey == F("2")) {
      if(!mode) {
        Serial.println(F("access allow"));
        // Доступ разрешен
        if(modeLock) {
          openTimer = millis()/1000;
          digitalWrite(PIN_RELAY, LOW); 
        }
        else digitalWrite(PIN_RELAY, !digitalRead(PIN_RELAY));
        squeaker(2, 2200, 200, 200);
      }
      else {
        if(typeKey != F("1")) {
          Serial.println(F("error: key elrady exists in eeprom"));
          squeaker(2, 500, 300);
        }
      }
    }

По поводу Ваших потребностей с доработкой замка. Думаю, что это возможно и сходу вижу два варианта

  1. Кардинально переработать SQL запрос и проводить все необходимые проверки на уровне сервера базы данных.
  2. Использовать между контроллером и сервером базы данных посредника, например, PHP скрипт, который будет проводить все необходимые проверки и принимать соответствующие решения. На контроллере останется только задача управления замком, а вся логика переляжет на плечи PHP и даст возможность модифицировать систему под будущие потребности.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
20 часов назад, Kitsum сказал:

@slava_573 доброе время суток.

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

Скрытый текст


    if(typeKey == F("1") or typeKey == F("2")) {
      if(!mode) {
        Serial.println(F("access allow"));
        // Доступ разрешен
        if(modeLock) {
          openTimer = millis()/1000;
          digitalWrite(PIN_RELAY, LOW); 
        }
        else digitalWrite(PIN_RELAY, !digitalRead(PIN_RELAY));
        squeaker(2, 2200, 200, 200);
      }
      else {
        if(typeKey != F("1")) {
          Serial.println(F("error: key elrady exists in eeprom"));
          squeaker(2, 500, 300);
        }
      }
    }

 

По поводу Ваших потребностей с доработкой замка. Думаю, что это возможно и сходу вижу два варианта

  1. Кардинально переработать SQL запрос и проводить все необходимые проверки на уровне сервера базы данных.
  2. Использовать между контроллером и сервером базы данных посредника, например, PHP скрипт, который будет проводить все необходимые проверки и принимать соответствующие решения. На контроллере останется только задача управления замком, а вся логика переляжет на плечи PHP и даст возможность модифицировать систему под будущие потребности.

а если в базу данных добавить 2 колонки "от" и "до" 

+

добавить часы реального временни и 2 переменные "от" и "до"

далее делаем проверку считаного ключа. если такой есть. скачиваем данные "тип ключа" и скачиваем цифры "от" и "до" и записываем их в ардуино.

ну и дальше сверка времени. если реальное времмя равно большо "от" и меньше "до" - открыть. иначе ошибка.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@slava_573 На мой взгляд, часы в замке это излишне. Кроме того, что конструкция усложняется, мы получаем дополнительное слабое звено.

Добавьте в таблицу два поля time_start и time_end с типом int, в них мы будет хранить часы (в формате 24) начала и конца рабочего времени. Желательно выставить этим полям значения по умолчанию, скажет 7 и 18 соответственно. Далее в программе микроконтроллера необходимо реализовать дополнительный запрос.

const char QUERY_ST[] = "SELECT type FROM test.rc522 WHERE uid = %s and time_start <= (select HOUR(NOW())) and time_end > (select HOUR(NOW()));";

Теперь потребуется добавить выбор нужного запроса в зависимости от того, в каком режиме находится замок - дежурный или программирование.

Находим

sprintf(query, QUERY_S, toQuery);

Меняем на

sprintf(query, (mode? QUERY_S : QUERY_ST), toQuery);

После этого необходимо убедиться в точности часов Вашего MySQL сервера. В целом этого будет достаточно для организации доступа по времени для каждого ключа.

PS: на практике не проверял, но Вам должно хватить этой информации для дальнейшей реализации.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Прочитал все внимательно от "А" до "Я" - это супер! А есть ли такая возможность приобрести уже готовый девай, с выходом под четыре электромагнита? С уважением

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@Shaman59rus Пока такую задачу никто не ставил, но всегда можно договориться. Напишите в приват, что конкретно вы хотите получить, поподробнее про 4 электромагнита и в какую сумму необходимо уложиться. Попробуем реализовать Вашу задумку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
В 26.06.2017 в 12:02, Kitsum сказал:

@slava_573 На мой взгляд, часы в замке это излишне. Кроме того, что конструкция усложняется, мы получаем дополнительное слабое звено.

Добавьте в таблицу два поля time_start и time_end с типом int, в них мы будет хранить часы (в формате 24) начала и конца рабочего времени. Желательно выставить этим полям значения по умолчанию, скажет 7 и 18 соответственно. Далее в программе микроконтроллера необходимо реализовать дополнительный запрос.


const char QUERY_ST[] = "SELECT type FROM test.rc522 WHERE uid = %s and time_start <= (select HOUR(NOW())) and time_end > (select HOUR(NOW()));";

Теперь потребуется добавить выбор нужного запроса в зависимости от того, в каком режиме находится замок - дежурный или программирование.

Находим


sprintf(query, QUERY_S, toQuery);

Меняем на


sprintf(query, (mode? QUERY_S : QUERY_ST), toQuery);

После этого необходимо убедиться в точности часов Вашего MySQL сервера. В целом этого будет достаточно для организации доступа по времени для каждого ключа.

PS: на практике не проверял, но Вам должно хватить этой информации для дальнейшей реализации.

Супер разработка. Но подскажите пожалуйста такую вещ. когда пропадает интернет. что тогда делать?

перемычка в режиме: открыл картой, прошло время двери закрылись сами

но как только пропадает интернет - не возможно зайти по карте и не реагирует кнопка в нутри.

получается "Замуровали")

 

Может можно добавить список ключей которыми можно откыть двери без интернета + чтобы кнопка работала без интернета (а то и выйти нельзя)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1 минуту назад, svchekalin сказал:

На выход кнопка работает и потом зачем интернет ?

интернет для связи с базой данных. ну или даже если локально - неважно. например сгорел роутер, упала сеть.

и получается тогда даже кнопка на выход не работает.

 

когда подключен ethernet кабель - все ок работает. Когда отключаю - не работает даже кнопка на выход

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

14 минуты назад, svchekalin сказал:

На выход кнопка работает и потом зачем интернет ?

попробуйте отключить ethernet  и перегрузить ардуино (выключить и включить)

пока ардуино не получит айпи адреса - ничего не будет

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Пожалуйста, войдите для комментирования

Вы сможете оставить комментарий после входа



Войти сейчас

  • Похожие публикации

    • Автор: Kitsum
      Привет друзья.
      В данной теме пойдет речь о конфигурации микроконтроллера через UART (Universal Asynchronous Receiver-Transmitter) интерфейс. А рассмотрим мы это на примере MQTT логгера. В данном случае, это будет логгер температуры. Мне это устройство потребовалось на работе, даже не мне, а моим коллегам, и оно действительно работает и приносит огромную пользу т.к контроль температуры производится совместно с отличной, на мой взгляд, системой мониторинга Zabbix с оперативными оповещениями, построением графиков, блэк-джеком и... Подробнее о дружбе Arduino и Zabbix можно почитать тут
      Но как всегда, есть нюансы. А заключаются они в том, что в будущем, обслуживать армию мелких контроллеров придется людям, которые заняты своими задачами и им попросту некогда изучать Arduino, не говоря уже о серьезных альтернативах, разбираться в том, как прописать нужные значения переменных в программу и загрузить её в микроконтроллер. Все настройки необходимо производить быстро, с явным указанием изменяемого параметра и его значения. Ровно также, как это делается с любым промышленным оборудованием.
      И тут на помощь приходит UART
      Микросхема UART to USB имеется в большинстве плат семейства Arduino, а там, где её нет, обычно выведены соответствующие "пины". И все это очень облегчает жизнь т.к позволяет общаться с контроллером, просто подключив его к компьютеру напрямую или через переходник, благо их везде навалом, да и стоят они как пачка семечек. Остается только запустить любой терминал, который умеет доставлять в конец строки символ "перевод строки", что известен в народе как "\n", а в ASCII таблице имеет номер 0A.
      Кстати, в Serial мониторе Arduino IDE выставить символ конца строки можно так

      Ну а дальше только, что и остается, как общаться с устройством на той стороне. И тут мы переходим к основному алгоритму программы. Но перед этим хочу отметить, и это ВАЖНО, что за любое упрощение жизни, всякие красивости и прочее, приходиться платить, и цена довольно высока! В данном случае, это ОЗУ микроконтроллера. Поэтому не раскатываем губы, а если очень хочется, то берем следующий по характеристикам микроконтроллер. А начинать мы будем с ATmega328P, что известен в народе как Arduino UNO, Arduino Nano, IBoard v1.1 и т.д по списку. Заканчивать Вы можете чем угодно, хоть ATmega2560, ESP8266 или ESP32. В противном случае, производим оптимизацию кода, отказываемся от громоздких библиотек, или вообще, от Arduino IDE.
      Что мы хотим получить
      Вся конфигурация микроконтроллера должна храниться в энергонезависимой памяти (ПЗУ) известной нам как EEPROMM. Если в ПЗУ конфигурация отсутствует, необходимо иметь резервный план. И им станет сброс конфигурации на настройки по умолчанию. Это поведение знакомо всем, особенно по домашним дешевым маршрутизаторам, а значит, интуитивно понятно. Выводить справку при начале общения пользователя и устройства, на мой взгляд, как манеры высшего общества. Контроллер должен представляться и сообщать всю необходимую информацию о себе и о том, как с ним вести диалог. Все команды должны быть просты и иметь не двусмысленное значение. И конечно, мы должны иметь возможность просмотра текущего состояния датчиков или процессов, которыми занимается устройство в свободное от общения с нами время. Как сохранять конфигурацию в EEPROM
      Пожалуй, стоит начать с того, как сохранить конфигурацию микроконтроллера в энергонезависимую память. Для этих целей, в стандартный набор инструментов Arduino IDE входит библиотека для работы с EEPROM.
      #include <EEPROM.h> На данный момент нас интересуют две функции, это чтение и запись
      EEPROM.get(address, variable); EEPROM.put(address, variable); Обе принимают два параметра:
      Адрес, начиная с которого будет произведено чтение или запись данных в память Переменная чье содержимое надо сохранить или в которую нужно из памяти прочитать Особенность работы этих функция заключается в том, что в зависимости от типа переданной им переменной во втором параметре, будет произведено чтение или запись ровно того количества данных которое соответствует размеру типа этой самой переменной. На простом языке это означает, что если переменная variable будет иметь типа byte, то и работать мы будем с объемом памяти в 1 байт. И тоже самое произойдет с абсолютно любым типом данных пока мы не упремся в размеры самого EEPROM или ОЗУ микроконтролера. Из этого всего следует, что мы можем создать свой собственный тип данных, разместить в нем необходимую нам информацию и всего лишь двумя функциями помещать его в память и извлекать обратно.
      И в этом нам поможет пользовательский составной тип - структура (struct). Данный тип позволяет объединить в себе различные типы данных, упорядочить их и присвоить им понятные имена.
      Это общий пример для большего понимания, как объединить несколько типов данных в одной структуре, получить к ним доступ, записать и прочитать их из EEPROM.
      Наша структура будет немного сложнее, но суть остается той же самой.
      // Дополнительная структура описывающая IPv4 адреса struct addres { byte a; byte b; byte c; byte d; }; // Структура объекта конфига для хранения в EEPROM struct configObj { addres ip; addres subnet; addres gateway; addres dns; byte mac[6]; byte hex; char server[40]; char topic[40]; } config; Данная структура хранит сетевые настройки для работы с Ethernet модулем (w5100 и выше) Arduino, базовые настройки для связи с MQTT брокером. Сразу при описании структуры мы объявили новую переменную с именем config с типом нашей структуры.
      ВАЖНО: кроме наших данных в структуре имеется дополнительная переменная с именем hex. Её задача, это контроль наличия наших данных в EEPROM. Она всегда должна содержать одно и тоже значение. Представьте ситуацию, что вы взяли контроллер в EEPROM которого находится какая-либо информация (может там чисто, но мы этого не знаем наверняка) и мы прочитаем данные и поместим их в нашу переменную. В итоге мы получим данные которым нельзя доверять, а что еще хуже, это если эти самые данные нарушат работу внешнего оборудования.
      Более правильным, на мой взгляд, будет проверка значений по конкретно определенным адресам. Например, мы знаем, что в 16 байте должно быть значение 0xAA и если оно действительно там, то мы убеждаемся, что это наша информация. Естественно, что контрольных точек может быть несколько и разумеется с разными значениями, это увеличит гарантию того, что данные являются нашими, но 100% гарантии не даст. Для более серьезных проектов есть более серьезные методы, например, подсчет контрольной суммы всего набора данных.
      Также структура может иметь вложенные структуры, у нас ими являются: ip, subnet, gateway, dns. Вы можете отказаться от такого варианта и записывать данные просто в массив байт, как это было сделано с MAC адресом. Естественно, что обращаться к этим полям нужно по-разному.
      Запись данных в поле subnet
      config.subnet = {255, 255, 255, 0}; Запись данных в поле mac
      byte mac[] = {0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02}; memcpy(config.mac, mac, 6); С записью данных в поле server все еще проще
      config.server = "mqtt.it4it.club"; Функция, которая возвращает нашу структуру данных с полностью заполненными полями.
      // Начальный конфиг configObj defaultConfig() { configObj config = { {192, 168, 0, 200}, {255, 255, 255, 0}, {192, 168, 0, 1}, {192, 168, 0, 1}, {0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02}, 0xAA, // Не трогать! Используется для проверки конфигурации в EEPROM F("mqtt.it4it.club"), F("arduino/serial/config") }; return config; } К примеру, два последних значения записывать не обязательно, тогда эти поля останутся пусты если использовать данную функцию для возврата к "заводским" настройкам.
      Вот пример того, как используя описанную нами структуру, мы проверяем целостность настроек в EEPROM и в случае не совпадения hex значений, загружаем настройки по умолчанию.
      const byte startingAddress = 9; bool configured = false; void loadConfig() { EEPROM.get(startingAddress, config); if (config.hex == 170) configured = true; else config = defaultConfig(); configEthernet(); // Функция производящая настройку сети } Как контроллеру начать понимать, что от него хотят
      В Arduino имеется функция, вызываемая каждый раз, когда в передаваемый буфер данных попадает знакомый нам символ перевода строки.
      void serialEvent() { // Вызывается каждый раз, когда что-то прилетает по UART // Данные передаются посимвольно. Если в строке 100 символов, то функция будет вызвана 100 раз } И в контексте обсуждаемой нами программы, мы можем представить ее в следующем виде
      void serialEvent() {   serialEventTime = millis();   if (console.available()) {     char c = (char)console.read();     if (inputCommands.length() < inputCommandsLength) {       if (c != '\n') inputCommands += c;       else if (inputCommands.length()) inputCommandsComplete = true;     }   } } Её задача, символ за символом, собрать в кучу все переданные нами данные и при получении заветного символа перевода строки (именно он даст нам понять, что передача сообщения завершена) сообщить, что команда получена и передать накопленный буфер данных своей напарнице по цеху.
      Но перед тем как это сделать стоит рассмотреть альтернативную ветку развития событий, а именно тот факт, что нам попросту могут прислать огромную, кучу шлака без волшебного символа, а раз могут, значит рано или поздно пришлют. И мы бесполезно потратим ценные ресурсы микроконтроллера, что может привести к непредсказуемым результатам в дальнейшем. Поэтому, в логику функции, мы добавим дополнительное ограничение на количество переданных символов, если оно достигнуто, то попросту перестаем воспринимать последующие данные.
      Останется только избавиться от них, и самым удобным моментом будет, когда этот поток шлака прекратиться. Чтобы об этом узнать мы будем запоминать время, когда пришел каждый из символов переданной строки перезаписывая соответствующую временную переменную данными о следующем символе и т.д пока поток не иссякнет. И как только расхождение текущего времени CPU и времени, когда поступил последний символ превысит некоторое значение, пусть это будет 1 секунда, мы очистим нашу память. Этот простой механизм напоминающий амнезию позволит избавить нас от лишних проблем.
      Переменная отвечающая за размер принимаемого буфера
      const byte inputCommandsLength = 60; Теперь можно переходить к напарнице предыдущей героини и, по совместительству, основной рабочей лошадки - функции обрабатывающей адекватно полученные данные.
      void serialEventHandler() { // вызывается в loop и проверяет взведена ли переменная inputCommandsComplete // в полученных данных пытается распознать команды } По началу я хотел описать данную функцию в упрощенном варианте, но в процессе понял, что ничего хорошего из этого не выйдет, и я решил описать только ключевые моменты, но их будет достаточно, на мой взгляд.
      Разбор serialEventHandler
      Полученные данные будут переданы нам в переменной inputCommands с типом String
      В первую очередь стоит почистить ее от лишних пробельных символов. Они часто встречаются в начале и в конце строки если пользователь копирует текст, а не набирает его самостоятельно. Это распространенная ситуация, приводящая к отказу принятия команды и бороться с ней очень просто.
      inputCommands.trim(); Далее стоит отсеять команды, не несущие никакой динамической информации, например, help, restart, reset и т.п это предписывающие команды которые заставляют контроллер выполнять строго описанные функции без вмешательства в их работу.
      if (inputCommands == F("help")) { consoleHelp(); } else if (inputCommands == F("restart")) { resetFunc(); } else { // Все сложные команды обрабатываются в этом блоке } Как Вы видите, все очень просто и скучно. Но не в том случае если команда динамическая, то есть содержит не только саму команду (заголовок) но и полезную нагрузку (параметр) которая может меняться раз от раза. Простой пример это команда изменения ip адреса и её варианты:
      ip 37.140.198.90 ip 192.168.0.244 ip 10.10.10.88 В данном случае, нам стоит понять, относится ли данная команда именно к ip адресу. Для этого в наборе String имеется отличный метод, позволяющий производить сравнение переданного ему параметра с началом строки.
      if (inputCommands.startsWith(F("ip"))) { // Строка inputCommands начинается с пары символов "ip" } Если все идет так, как мы задумали, то нам стоит отделить динамическую часть - наш параметр, от заголовка и получить полезную нагрузку. В этом нам поможет, опять же из набора String, метод substring позволяющий получать часть строки с указанием начального и конечного символа подстроки. Последний параметр указывать не обязательно и в таком случае мы получим всю строку начиная с указанного символа.
      inputCommands.substring(4) В данном случае начиная с 4-его и заканчивая последним. И как Вы успели заметить, отсчет мы начинаем не с третьего символа, что соответствует нашей строке без вступительного "ip", а на один больше т.к между заголовком и параметром имеется разделяющий символ в виде пробела.
      Далее, полученную строку мы передадим в функцию, занимающуюся разбором на компоненты и принимающую следующие параметры:
      Указатель на переменную с типом char, для этого нам потребуется преобразовать наш тип String Символ разделителя, что для IPv4 является точка "." Указатель на массив типа byte, которому будет присвоен результат разбора Количество искомых элементов в строке И система счисления, подразумеваемая в качестве исходной для записи элементов подстроки /* Парсинг https://stackoverflow.com/questions/35227449/convert-ip-or-mac-address-from-string-to-byte-array-arduino-or-c */ void parseBytes(const char* str, char sep, byte* bytes, int maxBytes, int base) { for (int i = 0; i < maxBytes; i++) { bytes[i] = strtoul(str, NULL, base); str = strchr(str, sep); if (str == NULL || *str == '\0') break; str++; } } В нашем случае выглядеть это будет следующим образом
      byte ip[4]; parseBytes(inputCommands.substring(4).c_str(), '.', ip, 4, 10); А дале все становится еще проще, попросту проверить попадает ли наш ip адрес, в список правильных адресов. И самой простой проверкой послужит проверка первого байта адреса на несоответствие не угодным нам сетям (0, 127, 255)
      if (ip[0] != 127 and ip[0] != 255 and ip[0] != 0) { // Производим необходимые нам действия с ip адресом, например, запись в конфиг config.ip = {ip[0], ip[1], ip[2], ip[3]}; } Вы в праве реализовать собственные проверки, какие только душе угодны.
      Также хотелось бы отметить, что обрабатывать некоторые параметры проще и быстрее через их короткие записи. К таким можно отнести маску подсети устройства. Например, привычный дня нас адрес 192.168.0.1 с маской подсети 255.255.255.0 можно записать в виде 192.168.0.1/24, где цифра 24 указывает нашу подсеть в краткой форме. А, следовательно, мы можем записать несколько кратких форм масок подсети в следующем виде:
      subnet 255.255.255.0 или subnet 24 subnet 255.255.0.0 или subnet 16 subnet 255.0.0.0 или subnet 8 Это основные маски, и я не описывал все существующие т.к в этом нет нужды, но если Вам интересно, то почитать про них можно в wikipedia.
      if (inputCommands.startsWith(F("subnet"))) {     String input = inputCommands.substring(8);     if (input == F("24"))      config.subnet = {255, 255, 255,   0};     else if (input == F("16")) config.subnet = {255, 255,   0,   0};     else if (input == F("8"))  config.subnet = {255,   0,   0,   0};     else { // Все остальные маски попадают в этот блок         byte subnet[4];         parseBytes(input.c_str(), '.', subnet, 4, 10); config.subnet = {subnet[0], subnet[1], subnet[2], subnet[3]};     } } MAC адрес хранится у нас в виде массива байт. Его перезапись другим массивом производится с помощью функции memcpy
      if (inputCommands.startsWith(F("mac"))) { byte mac[6]; parseBytes(inputCommands.substring(4).c_str(), ':', mac, 6, 16); memcpy(config.mac, mac, 6); } Изменение адреса MQTT сервера
      if (inputCommands.startsWith(F("server"))) { String server = inputCommands.substring(8); server.trim(); if (server.length() < 40) server.toCharArray(config.server, 40); } В принципе теперь понятно, как производить получение, разбор и сохранение конфигурации в EEPROM микроконтроллера.
      Как это выглядит на практике
      Заливаем программу в микроконтроллер и подключаемся к Arduino по usb или через переходник. Открываем терминал и нас приветствуют краткой справкой с описанием доступных команд.
      - --------------------------------------------------------------------------------------- # Sensor with data sending to mqtt server (c) it4it.club # Use the "config" command to view the current configuration # To change the configuration, specify the parameter name and its new value with a space, # for example "ip 192.168.0.200", "subnet 255.255.255.0" or "mac AA:BB:CC:DD:EE:FF" # You can also specify a subnet using the mask 24, 16 or 8 # Additional commands: # sensors - view current data from sensors # config - view current configuration # save - saves the current configuration # reset - resets all settings # restart - restarts the device # eeprom clear - removes all contents of eeprom # help - view this help - --------------------------------------------------------------------------------------- Т.к. в EEPROM микроконтроллера не была обнаружена конфигурация (волшебный hex байт нам подсказал), то были задействованы стандартные настройки. Просмотреть текущую конфигурацию можно командой config
      config # ip: 192.168.0.200 # subnet: 255.255.255.0 # gateway: 192.168.0.1 # dns: 192.168.0.1 # mac: 00:AA:BB:CC:DE:02 # server: mqtt.it4it.club # topic: arduino/serial/config чтобы изменить значение любого из этих параметров необходимо передать имя параметра и его новое значение, разделенные между собой пробелом.
      ip 10.10.10.99 # ok gateway 10.10.10.1 # ok dns 10.10.10.1 # ok После окончания конфигурации необходимо сохранить настройки и если были затронуты критические параметры, например, сеть, то перезапустить Arduino соответствующей командой.
      save # ok restart # ok # restarting device... Если параметр был успешно принят, то контроллер ответит нам "ok", а в противном случае ругнется.
      ip 127.0.0.1 # bad ip Также мы получим негативный ответ если команда не была распознана.
      qwerqwer1243 # bad command С остальными командами Вы разберетесь самостоятельно.


      Исходник: MQTT_CLIENT_328_SERIAL_CONFIG.zip
      PS: в общем то это статья родилась только для того, чтобы в соседнем форуме с системой мониторинга Zabbix появилась ссылка на устройство, но я надеюсь, что она также станет полезна любителям домашней автоматизации и не только.
  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу.

×
×
  • Создать...