КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Урок 45. Кодовый замок из акселерометра

В этом уроке мы создадим кодовый замок, который открывается определенной последовательностью поворотов. Благодаря новому датчику Trema IMU 9 DOF этот замок сможет запоминать свое положение в пространстве. Подбирать комбинацию, не зная нашего ключа, горе - взломщику будет непросто!

Нам понадобится:

Для нашей идеи нам также нужно будет установить библиотеку для модуля положения:

О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.

Схема подключения:

Модули Trema Set Shield
Trema Зуммер 1 площадка (выводы 7-G-V-6-10)
Trema Силовой ключ 3 площадка (выводы 8-G-V-3-9)
Trema IMU 9 DOF 5 площадка (выводы G-V-DA-CL)

Код программы:

/* Кодовый замок из акселерометра */
#define BMX055_DISABLE_BMG            // Не использовать гироскоп
#define BMX055_DISABLE_BMM            // Не использовать магнитометр
#define MAX_VALUE   8.00              // Максимальное значение по оси измерений для 
                                      // положительного результата
#define MIN_VALUE  -8.00              // Минимальное значение по оси измерений для 
                                      // положительного результата
#define PWR_PIN     3                 // Вывод для подключения силового ключа                    
#define BUZ_PIN     6                 // Вывод для подключения зуммера                     
#include <Wire.h>                     // Подключаем библиотеку для работы с аппаратной шиной I2C, до подключения библиотеки iarduino_Position_BMX055.
#include <iarduino_Position_BMX055.h> // Подключаем библиотеку iarduino_Position_BMX055
                                      // для работы с Trema-модулем IMU 9 DOF
enum pos                              // Перечисление для различных положений
{                                     // модуля в пространстве: NONE - не определено, 
  NONE, UP, FRONT, RIGHT,             // UP -  вверх, FRONT - вперед, 
  BACK, LEFT, DOWN                    // RIGHT - вправо, BACK - назад, 
};                                    // LEFT - влево, DOWN - вниз
iarduino_Position_BMX055 sensor(BMA); // Создаем объект sensor, указывая что ему
                                      // требуется работать только с акселерометром
pos current[3] = {NONE, NONE, NONE};  // Массив для записи текущих значений положения 
                                      // модуля в пространстве. Может быть одним из
                                      // значений перечисления pos. None по умолчанию
pos target[3] = {RIGHT, FRONT, LEFT}; // Массив требуемых значений положения модуля
                                      // в пространстве для сравнения с текущими
                                      // Может быть одним из значений перечисления pos
void setup()
{
  Serial.begin(9600);                 // Инициируем передачу данных в монитор 
                                      // последовательного порта на скорости 9600 бит/сек
                                      // для отладки
  pinMode(PWR_PIN, OUTPUT);           // Настраиваем вывод для силового ключа как выходной
  digitalWrite(PWR_PIN, LOW);         // Низкий уровень на выходе
  pinMode(BUZ_PIN, OUTPUT);           // Настраиваем вывод для зуммера как выходной
  digitalWrite(BUZ_PIN, LOW);         // Низкий уровень на выходе
  sensor.begin();                     // Инициируем работу с акселерометром, так как 
                                      // именно для работы с ним создан объект sensor
  sensor.setScale(BMA_4G);            // Указываем акселерометру производить измерения 
                                      // в новом диапазоне ±4g, где g=9.81 м/с²
}
void loop() 
{
  /* Ждем пока есть движение */
  while (isMoving())
  {
    delay(1500);
  }
  /* Обновляем массив текущих положений новыми данными */
  current[0] = current[1];
  current[1] = current[2];
  current[2] = checkPosition();
  /* Сообщаем звуковым сигналом о записи нового положения */
  digitalWrite(BUZ_PIN, HIGH);
  delay(200);
  digitalWrite(BUZ_PIN, LOW);
  /* Если значения в массиве текущих положений совпадают с требуемыми */
  if (current[0] == target[0] && current[1] == target[1] &&
      current[2] == target[2])
  {
    /* Выполняем полезное действие */
    /* Активируем силовой ключ */
    digitalWrite(PWR_PIN, HIGH);
  }
  /* Иначе */
  else
  {
    /* Отменяем выполнение полезного действия */
    /* Деактивируем силовой ключ */
    digitalWrite(PWR_PIN, LOW);
  }
}
/* Функция для определения положения модуля */
pos checkPosition()
{
  pos result = NONE;                  // Возвращаемое значение положения модуля
  sensor.read();                      // Читаем данные акселерометра
  if (sensor.axisZ >= MAX_VALUE)      // Проверяем данные по оси Z
    result = UP;
  else if (sensor.axisZ <= MIN_VALUE)
    result = DOWN;
  if (sensor.axisY >= MAX_VALUE)      // Проверяем данные по оси Y
    result = BACK;
  else if (sensor.axisY <= MIN_VALUE)
    result = FRONT;
  if (sensor.axisX >= MAX_VALUE)      // Проверяем данные по оси X
    result = LEFT;
  else if (sensor.axisX <= MIN_VALUE)
    result = RIGHT;
  return result;                      // Возвращаем результат
}
/* Функция для определения движения модуля */
bool isMoving()
{
  static pos old_pos = NONE;          // Старое положение модуля
  pos new_pos = checkPosition();      // Новое положение модуля
  /* Если новое положение не равно старому */
  if(old_pos != new_pos && new_pos != NONE)
  {
    /* Обновляем старое положение */
    old_pos = new_pos;
    return false;
  }
  return true;
}

Алгоритм работы:

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

Затем в основном цикле сперва проверяем наличия движения у модуля. Для этого написана функция isMoving(). Она возвращает значение false если движения нет и true если замечено движение. Основной компонент этой функции — статическая переменная old_pos нашего перечисления pos. Именно она запоминает предыдущее положение модуля и хранит его между вызовами функции isMoving(). Чтобы понять, изменилось ли положение модуля мы вызываем другую функцию — checkPosition(). Оставим пока на время детали реализации этой функции и посмотрим, что она возвращает. Результатом выполнения этой функции будет текущее положение модуля в пространстве. Это может быть одно из значений нашего перечисления: NONE (если положение не определено), UP (вверх), FRONT (вперед), RIGHT (вправо), BACK (назад), LEFT (влево), DOWN (вниз).

Если новое положение аналогично старому, значит модуль никуда не двигался и можно продолжать ждать движения. Если новое положение оказалось NONE, значит модуль находится в каком-то промежуточном состоянии и однозначно судить о его положении нельзя. Опять воздержимся от каких-либо действий и подождем. Наконец, если из функции checkPosition() получено новое положение, то логично предположить, что модуль действительно переместился и было движение. В таком случае возвращаемое значение функции isMoving() будет true (но надо не забыть при этом обновить значение переменной old_pos!).

Новое положение нужно добавить в конец нашего массива текущих положений. При этом сначала обновляются первое (меняется на второе) и второе (меняется на третье) места в массиве, а третьим значением будет результат нового вызова функции checkPosition(). Эта функция обращается непосредственно к модулю, чтобы получить новые значения от акселерометра и вернуть в качестве результата одно из возможных положений. Чувствительность настроек можно задать с помощью директив препроцессора #define MAX_VALUE и #define MIN_VALUE (по умолчанию равны 8.00 и -8.00). Чтобы пользователь знал, что новое положение распознано нашим устройством, подадим короткий звуковой сигнал на зуммер.

В заключение следует проверить, соответствуют ли текущие положения из массива current значениям из массива target, которые необходимы для открытия замка. И хотя в данном примере мы управляем силовым ключом, в вашем случае это может быть что угодно: зажечь лампочку, светодиод, повернуть сервопривод, проиграть мелодию или даже все вместе. Затем программа переходит к ожиданию нового положения и все повторяется.

Примечание:

В данном примере паролем служат три последовательных положения: RIGHT > FRONT > LEFT. Пароль очень легко изменить, указав, например, значения UP > LEFT > DOWN. Изменить длину пароля несколько сложнее, но тоже возможно. Для этого нужно поменять размеры массивов (значения должны совпадать!) и не забыть инициализировать их последовательностью подходящей длины. Кроме того, условие проверки соответствия должно обрабатывать каждую пару значений из массива.




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями