В этом уроке мы создадим кодовый замок, который открывается определенной последовательностью поворотов. Благодаря новому датчику Trema IMU 9 DOF этот замок сможет запоминать свое положение в пространстве. Подбирать комбинацию, не зная нашего ключа, горе - взломщику будет непросто!
Нам понадобится:
- Arduino Uno x 1 шт.
- Trema Set Shield x 1 шт.
- Trema IMU 9 DOF x 1 шт.
- Trema Зуммер х 1 шт.
- Trema Силовой ключ х 1 шт.
- Замок электромагнитный х 1 шт.
- Источник питания 12В х 1 шт.
- Коннектор Power Jack х 1 шт.
Для нашей идеи нам также нужно будет установить библиотеку для модуля положения:
О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.
Схема подключения:
- Установите Trema Set Shield на Arduino Uno.
- Установите Trema Зуммер на 1 посадочную площадку Trema Set Shield.
- Установите Trema Силовой ключ на 3 посадочную площадку Trema Set Shield.
- Установите Trema IMU 9 DOF на 5 посадочную площадку Trema Set Shield.
- При желании все установленные на Trema-Set Shield модули можно закрепить, используя нейлоновые винты и стойки.
Модули | 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. Изменить длину пароля несколько сложнее, но тоже возможно. Для этого нужно поменять размеры массивов (значения должны совпадать!) и не забыть инициализировать их последовательностью подходящей длины. Кроме того, условие проверки соответствия должно обрабатывать каждую пару значений из массива.
Обсуждение