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

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

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

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

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


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

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

явно кз !  Согласись короткое замыкание диагностировать по фото дело не благодарное я бы начал с отключения по очереди проводочков на макетке с кнопами. кстати а почему там две кнопки ?

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

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


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

кстати а почему там две кнопки ?

Одна открывает дверь, а другая затирает EEPROM.

@svchekalin Тяжело разобраться, но я могу точно сказать, что с кнопками уже косяки. Резисторы у кнопок в таком варианте абсолютно бесполезны т.к закорочены самой макетной платой. Я попытаюсь дальше понять по фото что к чему, но это не простая работа т.к из-за платы расширения уже возникает ряд вопросов. А какое напряжение она выдает после преобразователя? какое напряжение между пинами V и G этой платы?

Еще момент с током потребления при нажатии на кнопку.

В 08.02.2017 в 22:41, BOTASAN сказал:

Кз полюбому так как ампер метр показывает при нажатии большое употребление ампер 1.7 а почти.

На сколько мне известно, с вывода микроконтроллера нельзя снять ток выше 50мА, а ток в 1.7А скорее всего равен максимальному, который способен выдать Ваш источник питания. Это явный КЗ, и похоже в обход стабилизатора самой Arduino! Я так думаю, потому что Вы используете аналог NANO и на обратной стороне платы, скорее всего, расположен стабилизатор AMS1117. Он способен выдать не более 1А, а это намного меньше 1.7А при КЗ.

  1. Разберите всю схему
  2. Не используйте плату расширения на которой установлен Ваш микроконтроллер
  3. Соберите все внимательно, с нуля и без спешки
  4. Перепроверьте монтаж после сборки
  5. Питание возьмите от штатного USB разъема

По зуммеру ничего добавить не могу, разве, что - возьмите другую, заведомо рабочую плату, и проверьте сам зуммер через функцию tone

PS: это тот минимум который я могу Вам посоветовать в данной ситуации

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


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

Спасибо большое. Пока нет времени работа. Постараюсь перелопатить всё заново. Узнать бы причину где накосячил так как в дальнейшем буду делать ещё замок. Кинул видео там конечно жесть но думаю вас улыбну замком )))

https://cloud.mail.ru/public/FPig/AWfE2Wka5

https://cloud.mail.ru/public/8AVN/7C448Sbgx

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


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

Думаю для старта заменю макетку с кнопками. Думаю основной косяк в ней так как проблема только с кнопками так всё работает.

  • Like 1

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


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

Всё верно замыкало на кнопках нужно было сменить дорожки и всё заработало ))) Всем спасибо за помощь. Думаю в ближайшее время всё буду мудрить в стационар на входную дверь.

  • Like 1

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


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

Здравствуйте. У меня тут в связи с ремонтом возникли идеи. Хотелось бы сделать, что бы при входе в цех стоял электронный считыватель(RC522) с экраном(HD44780) . Отрывающий дверь (замок сделан из стеклоподъёмника) и включающий свет в цехе(4 реле). С возможностью выйти с помощью кнопки. А уходя после рабочего дня я своей картой выключаю электричество и закрываю дверь.

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


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

Доброе время суток @SergejMElnikov

  1. Для каких целей необходим дисплей, что Вы хотите на него выводить?
  2. Расскажите подробнее про начало и окончание рабочего дня. Как Вы себе его представляете? Пока не совсем понятен механизм, учитывая, что выходит человек, используя внутреннюю кнопку, а входит по ключу. Допустим, свет включается, если он еще не включен, при идентификации любого из известных ключей, то, как распознать, что свет необходимо выключить... Если, кто-то вышел по той или иной причине, как реагировать на повторный вход? Или только один ключ включает и выключает свет параллельно с открытием двери, а все остальные только могут открывать дверь в рабочее время? Если так, то может ли человек с таким ключом уйти посреди рабочего дня?

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


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

Доброй ночи. В моих представлениях это работает так. Мой ключ главный (открывает дверь, включает свет, станки, открывает склад). И несколько ключей работников (открывающих дверь, включающих свет) при этом станки не включаются, и не открывается склад. Экран возможно лишный, но он есть хотелось бы ввести его в систему)))

Скриншот 06-03-2017 235332.png

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


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

@SergejMElnikov Значит при появлении основного ключа, кроме открытия входной двери и включения света, происходит разблокирование склада и появляется питание на оборудовании. Все остальные ключи могут только открыть дверь и включить свет. Остается под вопросом, в какой момент необходимо закрыть склад, и когда выключать свет. Точнее будет вопрос, при каких обстоятельствах или действиях необходимо все выключить и заблокировать? Человек ведь выходит по кнопке, значит, ему нужно произвести некие манипуляции уже с ключом снаружи.

И еще один момент. Допустим, обладатель основного ключа уходит и хочет заблокировать склад и снять питания с оборудования, при этом в помещении остаются люди, а значит, свет для них тоже потухнет.

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

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

Пусть замок входной двери работает отдельно. Принцип работы стандартный - знаем ключ, пускаем, через 5 секунд блокируем дверь. Если освещение в помещении при этом было выключено, то включаем его. И т.к люди выходят по кнопке, да и вообще учет количества людей в помещении не ведется (это отдельная тема), то установить дополнительную не фиксирующуюся клавишу для подачи сигнала микроконтроллеру, что люди помещение покидают и контроллер сразу или через какое-то время выключает свет. Естественно, что после выхода по кнопке дверь будет автоматически заблокирована через 5 секунд, как и в случае с входом. Или возложить обязанности дополнительной клавиши на событие длительного удержание кнопки открытия двери.

Склад имеет свой отдельный замок, но работает он  в другом режиме - у него отсутствует автоматическая блокировка двери. Поднесли ключ, замок открылся, поднесли еще раз, закрылся. Если дверь на склад открыта, то на оборудование в помещении подается напряжение. Закрыли склад, оборудование обесточивается. Хотя питания оборудования можно связать со светом, а не складом. Можно уйти, просто закрыв склад. Если люди остались, то они могут продолжить чем-то заниматься.

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

 

  • Like 1

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


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

часы... по времени ариентировать карты. а я бы морду свою на дисплее хотел видеть )) потом процесс соеденения и бмена данными с базойй на экране вывести мжно тоже симпотно

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

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


Ссылка на сообщение
Поделиться на других сайтах
В 01.10.2016 в 14:09, svchekalin сказал:

Добавлю свою маленькую лепту в проэкт. Немного поправил скетч и теперь замок пишет в две дополнительные таблицы события разрешенных и запрещенных действий т.е. ключ в базе и при доступе записывается его айди и время дата доступа в табличку allow также если ключа нет в базе он записывается аналогично в табличку deny. Накрапал коекакой php код который отображает данные таблиц и может дабавить ключ в базу.

скетч

  Показать содержимое

// Необходимые библиотеки
#include <avr/wdt.h>          
#include <SPI.h>
#include <MFRC522.h>
#include <Bounce2.h>
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>

// Необходимые пины
#define PIN_SS         9  // RFID
#define PIN_RST        8  // RFID

#define PIN_RELAY      7  // RELAY
#define PIN_MODE       6  // MODE
#define PIN_TONE       3  // TONE
#define PIN_OPEN       2  // OPEN

IPAddress ip2(192, 168, 2, 2);

MFRC522 mfrc522(PIN_SS, PIN_RST);

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress server(192, 168, 2, 1);  // IP адрес MySQL сервера
char user[] = "ваш юзер нэйм";               // MySQL username
char password[] = "ваш пароль к базе";        // MySQL password

const char QUERY_S[] = "SELECT type FROM adatum.rc522 WHERE uid = %s;";
const char QUERY_I[] = "INSERT INTO adatum.rc522 (uid) VALUES ('%s');";
const char QUERY_X[] = "INSERT INTO adatum.allow (uid) VALUES ('%s');";//allow
const char QUERY_Y[] = "INSERT INTO adatum.deny (uid) VALUES ('%s');";//deny
char query[128];

EthernetClient client;
MySQL_Connection conn((Client *)&client);
MySQL_Cursor cur = MySQL_Cursor(&conn);

//
String key, lastKey;
String typeKey;
char toQuery[12];
unsigned long lastKeyTimer  = 0;

// Переменные необходимые для режима программирования
byte modeProgTime           = 5;     // Количество секунд удержания мастер ключа для входа\выхода в\из режим\а программирования
bool mode                   = false; // НЕ МЕНЯТЬ!
bool modeLock               = false; // НЕ МЕНЯТЬ!
byte modeClean              = 0;     // НЕ МЕНЯТЬ!
unsigned long modeTimer     = 0;     // НЕ МЕНЯТЬ!

// Управление замком
unsigned long openTimer = 0;

// Защита кнопок от дребезга
Bounce key_open  = Bounce();
/*
  Функция звукового оповещения.
  Принимает параметры: количество звуковых сигналов, частота в герцах, продолжительность звука, пауза в милесекундах (не обязательно)
*/
void squeaker(byte count, unsigned int Hz, unsigned int duration, unsigned int sleep = 0)
{
  for(int i=0; i<count; i++) {
    tone(PIN_TONE, Hz, duration);
    if(sleep > 0) delay(sleep);
  }
}
/*
  Функция читает EEPROM и составляет список активных ключей
  Первый байт в памяти содержит количество ключей
  UID ключа содержит 4 байта
  Общая память 1 + количество ключей * 4
  Максимум можно записать 255 ключей
*/
void setup() {
  // Настраиваем сторожевой таймер
  wdt_disable();
  delay(8000);
  wdt_enable(WDTO_8S);

  // Инициализация используемых пинов
  // Реле
  pinMode(PIN_RELAY, OUTPUT);
  digitalWrite(PIN_RELAY, HIGH);
   
  // Кнопка открытия двери
  pinMode(PIN_OPEN,INPUT_PULLUP);
  digitalWrite(PIN_OPEN, HIGH);
  key_open.attach(PIN_OPEN);
  key_open.interval(5);   

  // Перемычка выбора режима работы (0 - автоматическое закрытие двери, 1 - закрытие двери по ключу)
  pinMode(PIN_MODE, INPUT);
  digitalWrite(PIN_MODE, HIGH);

  // Инициализация консоли
  Serial.begin(115200);
  while (!Serial);

  // Инициализация ридера
  SPI.begin();
  mfrc522.PCD_Init();

  Ethernet.begin(mac, ip2);//--------------ip2 переименовать в ip и удалить айпишник для DHCP
  Serial.print(F("IP: "));
  Serial.println(Ethernet.localIP());
  Serial.println();
 
  // Приглашаем в гости
  Serial.println(F("iT4iT CLUB (C) 2015\nhttps://it4it.club\n"));      
}

void loop() {
  // Сбрасываем сторожевой таймер микроконтроллера
  wdt_reset();

  if(openTimer > millis()+10000) openTimer = 0;
  if(lastKeyTimer > millis()+10000) lastKeyTimer = 0;

  // Изменение режима работы закрытия
 modeLock = digitalRead(PIN_MODE);

  // Открытие двери с кнопки
  key_open.update();
  if(!key_open.read() and openTimer == 0 and !mode) {
    if(modeLock or (!modeLock and digitalRead(PIN_RELAY))) {
      openTimer = millis()/1000;
      digitalWrite(PIN_RELAY, LOW);
      Serial.println(F("The door opened from the inside\n"));
      squeaker(5, 3200, 100, 300);
    }
    delay(2000);
  }
 
  // Автоматическое закрытие двери
  if(openTimer != 0) {
    if(millis()/1000 - openTimer > 5) {
      openTimer = 0;
      if(modeLock) {
        digitalWrite(PIN_RELAY, HIGH);
        Serial.println(F("* closed lock"));
      }
      else {
        if(!digitalRead(PIN_RELAY)) digitalWrite(PIN_RELAY, HIGH);
      }
    }
  }
 
  // Если ключ отсутствует или не читается, не выполняем дальнейший код
  if(!mfrc522.PICC_IsNewCardPresent()) {
    // Очистка таймера входа в режим программирования, в случае если ридер свободен
    if(modeTimer != 0) {
      if(++modeClean > 5) modeTimer = modeClean = 0;
    }
    // Таймер для контроля времени хранения последнего ключа в памяти
    if(lastKeyTimer != 0) {
      if((millis()-lastKeyTimer) > 100) {
        lastKey = "";
        lastKeyTimer = 0;
      }
    }
    return;
  }
  if(!mfrc522.PICC_ReadCardSerial()) return;
  // Останавливаем режим очистки
  modeClean = 0;

  // Читаем ключ
  for(byte i=0; i<4; i++) key += mfrc522.uid.uidByte;
  // Один ключ = Один запрос к MySQL серверу. Не флудим.
  if(key != lastKey) {
    if(!conn.connected()) {
      if(!conn.connect(server, 3306, user, password)) {
        squeaker(5, 1000, 200, 200);
        return;
      }
    }
    
    key.toCharArray(toQuery, key.length()+1);
    sprintf(query, QUERY_S, toQuery);
    cur.execute(query);

    column_names *cols = cur.get_columns();
    row_values *row = NULL;
    Serial.println("KEY: " + key);
    if((row = cur.get_next_row()) != NULL) typeKey = String(row->values[0]); else typeKey = "0";
    if(typeKey == F("1") or typeKey == F("2")) {
      if(!mode) {
        Serial.println(F("access allow"));


sprintf(query, QUERY_X, toQuery);
cur.execute(query);

        
        // Доступ разрешен
        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);
        }
      }
    }
    else {
      if(!mode) {
        Serial.println(F("access dany"));

 


sprintf(query, QUERY_Y, toQuery);
cur.execute(query);

 

        
        squeaker(1, 500, 1000);
      }
      else {
        Serial.println(F("add key in MySQL"));
        sprintf(query, QUERY_I, toQuery);
        
        cur.execute(query);
        squeaker(2, 2200, 200, 200);
      }
    }

    lastKey = key;
    cur.close();
    conn.close();
  }
  else {
    if(typeKey == F("1")) {
      if(modeTimer == 0) modeTimer = millis()/1000;
      else {
        if(millis()/1000 - modeTimer > modeProgTime and modeTimer != 0) {
          modeTimer = openTimer = 0;
          if((mode = !mode) == true) {
            // Вход в режим программирования
            digitalWrite(PIN_RELAY, LOW);
            Serial.println(F("MASTER PROGRAMMING MODE ON"));
            squeaker(4, 1200, 200, 200);
          }
          else {
            // Выход из режима программирования
            digitalWrite(PIN_RELAY, HIGH);
            Serial.println(F("MASTER PROGRAMMING MODE OFF"));
            squeaker(4, 2200, 200, 200);
          }
          delay(2000);
        }
      }
    }
  }
 
  lastKeyTimer = millis();
  key = "";
}

содержание файла mysql

  Показать содержимое

-- phpMyAdmin SQL Dump
-- version 4.0.10deb1
-- http://www.phpmyadmin.net
--
-- Хост: localhost
-- Время создания: Сен 15 2016 г., 23:19
-- Версия сервера: 5.5.50-0ubuntu0.14.04.1
-- Версия PHP: 5.5.9-1ubuntu4.19

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

--
-- База данных: `test`
--

-- --------------------------------------------------------

--
-- Структура таблицы `allow`
--

CREATE TABLE IF NOT EXISTS `allow` (
  `n` int(11) NOT NULL AUTO_INCREMENT,
  `uid` varchar(15) NOT NULL DEFAULT '',
  `description` varchar(15) NOT NULL DEFAULT '',
  `periud` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`n`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=62 ;

--
-- Дамп данных таблицы `allow`
--

INSERT INTO `allow` (`n`, `uid`, `description`, `periud`) VALUES
(40, '24318669213', '', '2016-09-11 08:33:06');

-- --------------------------------------------------------

--
-- Структура таблицы `deny`
--

CREATE TABLE IF NOT EXISTS `deny` (
  `n` int(11) NOT NULL AUTO_INCREMENT,
  `uid` varchar(15) NOT NULL DEFAULT '',
  `description` varchar(15) NOT NULL DEFAULT '',
  `periud` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`n`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=36 ;

--
-- Дамп данных таблицы `deny`
--

INSERT INTO `deny` (`n`, `uid`, `description`, `periud`) VALUES
(29, '52147166185', '', '2016-09-11 09:45:02');

-- --------------------------------------------------------

--
-- Структура таблицы `rc522`
--

CREATE TABLE IF NOT EXISTS `rc522` (
  `uid` bigint(12) NOT NULL,
  `type` enum('0','1','2') NOT NULL DEFAULT '2',
  `description` varchar(250) DEFAULT NULL,
  `periud` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Дамп данных таблицы `rc522`
--

INSERT INTO `rc522` (`uid`, `type`, `description`, `periud`) VALUES
(525425, '0', '2547867648', '2016-09-11 08:23:15'),
(1807952, '1', 'брелок', '2016-09-10 11:17:15'),
(24313, '1', 'пластиковая карта', '2016-09-10 11:21:08'),
(5213185, '2', '2497267207', '2016-09-10 12:10:29');

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

php список ключей и форма добавления ключа

  Показать содержимое

<head>
<title>Админ-панель</title>
</head>
<!doctype html>
<code lang="utf8">
<?php
echo "Ключи в базе";
    // настройки
    $db_host = 'localhost';
    $db_name = 'test';
    $db_username = 'adatum';
    $db_password = 'Elmax1975';
    $db_table_to_show = 'rc522';

    // Соеденяемся с базой
    $connect_to_db = mysql_connect($db_host, $db_username, $db_password)
    or die("Could not connect: " . mysql_error());

    mysql_select_db($db_name, $connect_to_db)
    or die("Could not select DB: " . mysql_error());
mysql_query("SET NAMES utf8");
    $qr_result = mysql_query("select * from " . $db_table_to_show)
    or die(mysql_error());

    // заголовки
    echo '<table cellpadding="3" cellspacing="3" border="0" align="center" width="1" rules="rows">';
  echo '<thead>';
  echo '<tr align="center">';
  echo '<th>Тип</th>';
  echo '<th>Ключ</th>';
 echo '<th>Имя пользователя</th>';
  echo '<th>Когда зарегистрирован</th>';

  echo '</tr align="center">';
  echo '</thead>';
  echo '<tbody>';
 
   // таблица MySQL
  while($data = mysql_fetch_array($qr_result)){
    echo '<tr>';
    echo '<td>' . $data['type'] . '</td>';
    echo '<td>' . $data['uid'] . '</td>';
    echo '<td>' . $data['description'] . '</td>';
    echo '<td>' . $data['periud'] . '</td>';
 
    echo '</tr>';
  }
 
    echo '</tbody>';
  echo '</table>';

    // закрываем
    mysql_close($connect_to_db);
?>
<body>
<?php
$host="localhost";
$user="adatum";
$pass="Elmax1975"; //установленный вами пароль
$db_name="test";
$link=mysql_connect($host,$user,$pass);
mysql_select_db($db_name,$link);
mysql_query("SET NAMES utf8");

//Если переменная Name передана
if (isset($_POST["Name"])) {
    //Вставляем данные, подставляя их в запрос
    $sql = mysql_query("INSERT INTO `rc522` (`uid`, `type`, `description`)
                        VALUES ('".$_POST['Name']."','".$_POST['Price']."','".$_POST['description']."')");
    //Если вставка прошла успешно
    if ($sql) {
        echo "<p>Ключ успешно добавлен в базу.</p>";
    } else {
        echo "<p>Произошла ошибка.</p>";
    }
}
?>
Добавить ключ
<table>
<form action="" method="post">
<tr>
        <td>ID ключа:</td>
        <td><input type="text" name="Name"></td>
</tr>
<tr>
        <td>Тип:</td>
        <td><input type="text" name="Price" size="3"> 0-не активный 1-админ 2-стандарт.</td>
 </tr>
 <tr>
        <td>Имя пользователя:</td>
        <td><input type="text" name="description"></td>
 </tr>
 <tr>
        <td colspan="2"><input type="submit" value="OK"></td>
    </tr>
</form>
</table>
</body>

 

думаю странички allow и deny добавлять смысла нет так как это часть кода выше

Добрый день. Понравился данный вариант исполнения системы СКУД. Для меня важна система хранения ключей именно в базе MySQL. База импортировалась отлично, и php тоже. Правда в базе в дескрипшене проблемы с кодировками. На странице добавления ключей, русский текст читаем, а в описании знаки вопроса ????? При смене кодировок, либо вообще ничего не читается, либо читается все кроме поля дескрипшен, или там только латинские?
А вот при попытке залить скетч вываливается ошибка на этой строчке:
 

// Читаем ключ
  for (byte i = 0; i < 4; i++) key += mfrc522.uid.uidByte;
Скрытый текст

C:\Users\Денис\Downloads\диплом\ардуино\скетчи\SQL\SQL.ino: In function 'void loop()':

SQL:168: error: ambiguous overload for 'operator+=' (operand types are 'String' and 'byte [10] {aka unsigned char [10]}')

C:\Users\Денис\Downloads\диплом\ардуино\скетчи\SQL\SQL.ino:168:36: note: candidates are:

In file included from C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:231:0,

                 from sketch\SQL.ino.cpp:1:

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:113:11: note: String& String::operator+=(const String&) <near match>

  String & operator += (const String &rhs) {concat(rhs); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:113:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'const String&'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:114:11: note: String& String::operator+=(const char*) <near match>

  String & operator += (const char *cstr)  {concat(cstr); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:114:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'const char*'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:115:11: note: String& String::operator+=(char) <near match>

  String & operator += (char c)   {concat(c); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:115:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'char'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:116:11: note: String& String::operator+=(unsigned char) <near match>

  String & operator += (unsigned char num)  {concat(num); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:116:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'unsigned char'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:117:11: note: String& String::operator+=(int) <near match>

  String & operator += (int num)   {concat(num); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:117:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'int'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:118:11: note: String& String::operator+=(unsigned int) <near match>

  String & operator += (unsigned int num)  {concat(num); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:118:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'unsigned int'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:119:11: note: String& String::operator+=(long int) <near match>

  String & operator += (long num)   {concat(num); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:119:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'long int'

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:120:11: note: String& String::operator+=(long unsigned int) <near match>

  String & operator += (unsigned long num) {concat(num); return (*this);}

           ^

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/WString.h:120:11: note:   no known conversion for argument 1 from 'byte [10] {aka unsigned char [10]}' to 'long unsigned int'

exit status 1
ambiguous overload for 'operator+=' (operand types are 'String' and 'byte [10] {aka unsigned char [10]}')

Подскажите, что подправить, или опять где то очепятка в скетче при заливке на форум?
Благодарю.

  • Like 1

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


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

@Small_d Приветствую, автор дополнения немного поспешил и допустил ошибку в программе микроконтроллера.

Был не указан порядковый номер адреса ключа. Должно быть так.

// Читаем ключ
for(byte i=0; i<4; i++) key += mfrc522.uid.uidByte[i];

Все изменения от @svchekalin не проверял. Будем разбираться по мере необходимости.

Что касаемо кодировок, попробуйте изменить php файл таким образом, чтобы html разметка была похожа не что-то подобное.

<html lang="ru-RU" dir="ltr">
  <head>
    <title>Заголовок</title>
    <meta charset="utf-8">
  </head>
  <body>
	Тут само тело или PHP сценарий
  </body>
</html>

 

  • Like 2

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


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

@Kitsum, благодарю. Скетч залился, кодировка UTF8 ни в какую не захотела работать, то одно кракозябрами, то другое, а вот при смене кодировки в базе и на страничке в ср1251 все отображается отлично и описание ключей и сам текст полей оформления. 
Другая проблема возникла, собрал схему с одной кнопкой, одним реле, и переключателем режимов, как нарисовано на второй странице темы, где пример с базой выложен.

1. Как понять, считывает ли ключ rfid датчик или нет? 
Дело в том, что в консоль у меня выводится только ip ардуинки и приглашение на сайт, причем не важно приложил я метку или нет. Через каждые 10-15 секунд (точно не засекал) выдает ip с приветствием и срабатывает реле. Т.е. получается, что дверь сама постоянно открывается.
Но на кнопку реагирует как надо, нажимаем, идет тройной зумер и в консоль выводится сообщение, дверь открыта, дверь закрыта, реле открывается соответственно. Через какое то время, снова реле открывается само и ip с приглашением выдается. 

2. При считывании UID метки стандартным скетчем, где выдает в dec и hex формате, dec выглядит так - 12, 345, 67, 89 , т.е. записать мастер ключ в базу, я должен просто слитно, 123456789, убрав пробелы и запятые? Или еще как то сконвертить надо? Опять же вопрос возник потому что в консоль ключи не выводит, как на примере.

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


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

@Small_d Позволю себе предположить, что загрузчик Вашего микроконтроллера не поддерживает работу со сторожевым таймером. Уж очень похожи симптомы.

В самом начале описания функции Setup имеются строки активирующие сторожевой таймер Arduino.

  // Настраиваем сторожевой таймер
  wdt_disable();
  delay(8000);
  wdt_enable(WDTO_8S);

При старте, контроллер ждет 8 секунд и после инициализирует сторожевой таймер на сброс при отсутствии контрольного сигнала в течении 8 секунд. За обновление счетчика отвечает функция, вызываемая при каждом вызове loop

  // Сбрасываем сторожевой таймер микроконтроллера
  wdt_reset();

Итого, если у Вас не поддерживается работа с Watchdog, то каждые 16 секунд будет происходить перезагрузка контроллера.

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

  1. Никогда не заливайте программу с активированным сторожевым таймером в микроконтроллер на базе Arduino без задержки перед функцией инициализации таймера. Если конечно вы не уверены на 100%, что загрузчик поддерживает его работу. В противном случае, это чревато последствиями.
  2. На таком ответственном месте контроллер нельзя оставлять без сторожа, в случае необходимости, он приведет в чувства контроллер, а иначе можно остаться перед запертой дверью.

Теперь, что касаемо ключа.

Мы используем адрес ключа, состоящий из 4 байт, некоторые ключи отдают и 7 байт, но мы используем только 4. В итоге ключ представляет из себя строку, полученную в ходе конкатенации всех четырех байт ключа.

Если все подключено правильно, то все поднесенные к сканеру ключи будут отображаться в Serial мониторе. Эту информацию Вы можете взять за основу для создания мастер ключа.

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

  • Like 2

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


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

здравствуйте. при попытке сборки версии замка с подключением к базе данных столкнулся с трудностями:
1. в мониторе порта ошибка кодировки(порой может появится часть текста а порой только квадратики)
2. реле щелкает раз в 5 сек (и в мониторе порта после каждого щелчка появляется один квадратит)
3. при поднесении к считывателю карты информация о ней не появляется в мониторе порта(хотя светодиод на езернет шилде моргает когда я подношу карту а как убираю начинает гореть).
пробовал предыдущие версии ваших скетчей (где запись идет в память там все работает отлично)

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


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

здравствуйте. при попытке сборки версии замка с подключением к базе данных столкнулся с трудностями:
1. в мониторе порта ошибка кодировки(порой может появится часть текста а порой только квадратики)
2. реле щелкает раз в 5 сек (и в мониторе порта после каждого щелчка появляется один квадратит)
3. при поднесении к считывателю карты информация о ней не появляется в мониторе порта(хотя светодиод на езернет шилде моргает когда я подношу карту а как убираю начинает гореть).
пробовал предыдущие версии ваших скетчей (где запись идет в память там все работает отлично)

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

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


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

здравствуйте. при попытке сборки версии замка с подключением к базе данных столкнулся с трудностями:
1. в мониторе порта ошибка кодировки(порой может появится часть текста а порой только квадратики)
2. реле щелкает раз в 5 сек (и в мониторе порта после каждого щелчка появляется один квадратит)
3. при поднесении к считывателю карты информация о ней не появляется в мониторе порта(хотя светодиод на езернет шилде моргает когда я подношу карту а как убираю начинает гореть).
пробовал предыдущие версии ваших скетчей (где запись идет в память там все работает отлично)

 melfis, ошибки кодировки в консоли, скорее всего скорость не выставил. Поставь 115200 бод, вместо 9600. В скетче скорость указана.

С остальным похоже на мои проблемы, почитай над твоими постами сообщения от Kitsum, особенно про сторожевой таймер. Возможно твоя ардуинка тоже с ним не пашет, тогда удали из скетча соответствующие строчки, см. выше... Мне помогло.

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


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

 melfis, ошибки кодировки в консоли, скорее всего скорость не выставил. Поставь 115200 бод, вместо 9600. В скетче скорость указана.

С остальным похоже на мои проблемы, почитай над твоими постами сообщения от Kitsum, особенно про сторожевой таймер. Возможно твоя ардуинка тоже с ним не пашет, тогда удали из скетча соответствующие строчки, см. выше... Мне помогло.

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

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

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


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

@melfis Если считать, что при сборке не допущены ошибки и учитывая, что первоначальный скетч работает, то я посоветовал бы обратить внимание на качество монтажа Ethernet модуля. За братьями Китайцами уже замечались косяки ранее.

Отрабатывает ли открытие с кнопки?

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


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

@melfis Если считать, что при сборке не допущены ошибки и учитывая, что первоначальный скетч работает, то я посоветовал бы обратить внимание на качество монтажа Ethernet модуля. За братьями Китайцами уже замечались косяки ранее.

Отрабатывает ли открытие с кнопки?

здравствуйте. да езернет косячный я нашел что там запаян резистор 511 вместо 510. на кнопку отрабатывает. но очень долго происходит реагирование на нажатие. допустим нажимаю кнопку проходит сек 5-6 и начинает пищать зумер. думаю тут проблема в питании) на данный момент она питается от юсб порта. завтра попробую подключить 9В. может и другие проблемы пропадут. я просто сегодня чет задумался что возможно то проблема в питании) для ардуино рекомендованное то 9В

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

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


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

@melfis Если считать, что при сборке не допущены ошибки и учитывая, что первоначальный скетч работает, то я посоветовал бы обратить внимание на качество монтажа Ethernet модуля. За братьями Китайцами уже замечались косяки ранее.

Отрабатывает ли открытие с кнопки?

гляньте вот все собрал. на кнопку работает и в мониторе порта пишет. на карточку думает секунд 5 и пищит просто но в мониторе порта пусто. наверное потому что к серверу подключения нету?

llll.JPG

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


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

@melfis Конечно, без связи с сервером базы данных работать ничего не будет.

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


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

@melfis Конечно, без связи с сервером базы данных работать ничего не будет.

а тогда можете помочь по серверу немного? установил xampp. запустил mysql сервер. создал таблицу и тд. получается мне в код вписывать адрес его 127.0.0.1? а пользователя писать root@localhost? или просто root?

lml.JPG

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


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

гляньте вот все собрал. на кнопку работает и в мониторе порта пишет. на карточку думает секунд 5 и пищит просто но в мониторе порта пусто. наверное потому что к серверу подключения нету?

llll.JPG

melfis,

 

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

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

 

 

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

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


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

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

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



Войти сейчас

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

    • Автор: 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 пользователей

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

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