====== Умный лабиринт для крыс на Iskra JS ====== {{ :projects:maze:160316_lin_7127.jpg?nolink&700 |}} * Платформа: Iskra JS * Язык программирования: JavaScript * Тэги: Iskra JS, лабиринт, крыса, конструктор ПВХ. ===== Что это? ===== Простота содержания хомячков, мышей и крыс обманчива — зверькам нужен простор для игр, а в квартире найти его непросто. Для наших питомцев мы придумали и сделали интерактивный лабиринт — чтобы крысам было где побегать и порезвиться, а мы могли следить за их самочувствием, контролировать количество съеденной пищи и играть с ними даже не находясь дома. Множество дверей, подсветка и даже кормушка управляются через веб-интерфейс, а логика работы контроллера написана на JavaScript. ===== Что нам понадобится? ===== {{ :projects:maze:160316_lin_7141.jpg?nolink&700 |}} - [[amp>product/iskra-js?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Iskra JS]] - [[amp>product/arduino-ethernet-shield?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Ethernet Shield 2]] - [[amp>product/arduino-troyka-shield?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Troyka Shield]] - [[amp>product/servo-fs90?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Микросервопривод FS90]] 17 шт. - [[amp>product/structor-mega?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Пластина мега (#Структор)]] 42 шт. - [[amp>product/structor-servo?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Крепления микросерво (#Структор)]] 9 шт. - [[amp>product/structor-motor-line?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Крепления моторов и датчиков линии (#Структор)]] - [[amp>product/3-wire-cable-extension?utm_source=proj&utm_campaign=maze&utm_medium=wiki | 3-проводной шлейф «папа-мама»]] 30 шт. - [[amp>collection/dc-motors?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Двигатель 12 мм]] - [[amp>product/shaft-coupler-3mm?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Втулки на вал мотора]] - [[amp>product/troyka-mosfet-n-channel-v3?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Силовой ключ (Troyka-модуль)]] - [[amp>product/breadboard-mini?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Breadboard Mini]] - [[amp>product/infrared-motion-sensor?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Инфракрасный датчик движения]] - [[amp>product/ws2811-led-strip-sealed?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Цветная адресуемая светодиодная лента WS2811]] - [[amp>product/patch-cord?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Патч-корд витой пары]] - Блок питания 5 Вольт 5 Ампер - Блок питания 12 Вольт 2 Ампера - Большая плоская коробка - Прозрачная крышка ===== Как собрать? ===== - Возьмите три пластины «мега». Две из них разрежьте так, чтобы получились четыре пластины 13×7 шипов. Они будут стенами лабиринта. Вставьте стены в третью пластину «мега» #структора.{{ :projects:maze:160316_lin_7130.jpg?nolink&700 |}} - С помощью двух панелей «крепление микросервопривода 3×4 шипа» и трех панелей («пластина 2×3 шипа») сделайте «домик» для микросервопривода. Полностью домик собирать не обязательно.{{ :projects:musicrobot:musicrobot_build4.jpg?nolink&700 |}} - Возьмите пластину «мега» и разрежьте её на три части 15×5 шипов. Это будут двери лабиринта. Возьмите качалку для микросервопривода овальной формы. Вставьте её в панель #структора «крепление качалки 2×4 шипа» и прикрутите к валу сервопривода. Прикрепите дверь к качалке с помощью панелей #структора 2×3 шипа.{{ :projects:maze:160316_lin_7154.jpg?nolink&700 |}} - Готовый механизм двери разместите внутри стен лабиринта так, чтобы дверь в закрытом состоянии перекрывала проход для крысы. Для этого сделайте вырез в стене шириной чуть больше ширины двери. Сделайте в середине блока отверстие для вывода проводов. Проденьте провода в это отверстие.{{ :projects:maze:160316_lin_7134.jpg?nolink&700 |}} - Сделайте 11 блоков стен и 15 дверей. Разместите их так, чтобы крысе было интересно бегать между ними. Для удобства поставьте перемычки между блоками из оставшихся деталей #структора длиной 5–6 шипов. - Соберите карусельку с помощью #структора из набора «крепления моторов и датчиков линии». Установите на вал мотора втулку, а затем пластину «мега».{{ :projects:maze:160316_lin_7159.jpg?nolink&700 |}} - Управление каруселькой сделайте с помощью инфракрасного датчика движения и силового ключа. Схема подключения приведена на рисунке.{{:projects:maze:pir_dc_motor.png?nolink|}} - Разместите датчик и силовой ключ в пустом блоке лабиринта.{{ :projects:maze:160316_lin_7135.jpg?nolink&700 |}} - В другом свободном блоке соберите кормушку. Дверцу кормушки сделайте по аналогии с дверями лабиринта. - Разместите лабиринт внутри большой плоской коробки. В дне коробки сделайте отверстие для проводов и мотора карусельки. В крышке коробки сделайте прорези для дверей. - Возьмите Iskra JS и установите на нее Ethernet Shield 2. Выше установите Troyka Shield. - С помощью 3-проводного шлейфа «папа-мама» удлините кабели микросервоприводов и подведите их к Iskra JS.{{ :projects:maze:160316_lin_7136.jpg?nolink&700 |}} - Iskra JS имеет 18 выводов ШИМ, к которым можно подключить сервоприводы. Используйте Troyka Shield для подключения 3-проводных шлейфов. Аналогично используйте пины SCL и SDA. Пин P3 оставьте свободным для подключения к нему светодиодной ленты. Возьмите гнездо питания 2,1 мм и с помощью проводов «папа-папа» и соедините пин 5V Iskra с плюсом клеммника, а пин GND Iskra с минусом клеммника. В результате должна получиться такая схема:{{ :projects:maze:toomanyservos.png?nolink&700 |}}Не стоит пробовать питать лабиринт от USB компьютера. Подключайте USB только при включенном источнике питания! - По периметру коробки проложите светодиодную ленту. Подключите ее к источнику питания 12 Вольт. Управляющий провод ленты подключите к пину P3 на плате Iskra JS. Соедините «минус» источника питания с «минусом» Iskra JS.{{ :projects:maze:rgbstrip.png?nolink&700 |}} - Подключите один конец патч-корда витой пары в разъем Ethernet своего устройства. Второй конец подключите к своему Wi-Fi роутеру. - Подключите кабель microUSB к Iskra JS и своему компьютеру. - Теперь осталось только подключить камеру и настроить трансляцию on-line. ===== Алгоритм ===== * Устанавливаем socket-соединение c сервером по протоколу TCP. * Обновляем данные с помощью TCP-запроса к серверу. * Открываем или закрываем двери лабиринта по команде сервера. * Включаем подсветку диодной лентой, если приняли команду «напугать». * Открываем кормушку, если приняли команду «покормить». * Повторяем TCP-запрос и его обработку каждые 3 секунды. ===== Исходный код ===== * Создайте блок для сервоприводов. Каждые 100 миллисекунд необходимо обновлять углы открытия дверей. Такая частота необходима для плавной работы дверей. Минимальные и максимальные углы сервоприводов подберите экспериментально. // подключаем библиотеку 'servo' для работы с сервоприводами var servo = require('@amperka/servo'); // создаем объект для хранения информации о 15 сервоприводах. // servo - объект для работы с сервоприводом. // state - текущее состояние двери ('o' - открыто, 'c' - закрыто) // angle - угол сервопривода в данный момент // min - минимальный угол сервопривода. Соответствует полностью закрытой двери // max - максимальный угол сервопривода. Соответствует полностью открытой двери var doors = { d1: {servo: servo.connect(A0), state: 'c', angle: 45, min: 6, max: 50}, d2: {servo: servo.connect(A1), state: 'c', angle: 45, min: 19, max: 60}, d3: {servo: servo.connect(A2), state: 'c', angle: 45, min: 5, max: 50}, d4: {servo: servo.connect(A3), state: 'c', angle: 45, min: 17, max: 50}, d5: {servo: servo.connect(P0), state: 'c', angle: 45, min: 20, max: 60}, d7: {servo: servo.connect(P2), state: 'c', angle: 45, min: 0, max: 50}, d9: {servo: servo.connect(P5), state: 'c', angle: 45, min: 19, max: 70}, d10: {servo: servo.connect(P6), state: 'c', angle: 45, min: 10, max: 50}, d11: {servo: servo.connect(P8), state: 'c', angle: 45, min: 1, max: 50}, d12: {servo: servo.connect(P9), state: 'c', angle: 45, min: 0, max: 50}, d13: {servo: servo.connect(P11), state: 'c', angle: 45, min: 5, max: 60}, d14: {servo: servo.connect(P12), state: 'c', angle: 45, min: 5, max: 50}, d15: {servo: servo.connect(P13), state: 'c', angle: 45, min: 20, max: 90}, d16: {servo: servo.connect(SDA), state: 'c', angle: 45, min: 14, max: 75}, d17: {servo: servo.connect(SCL), state: 'c', angle: 45, min: 4, max: 50} }; // каждые 100 миллисекунд обновляем состояние сервоприводов setInterval(function() { for (var door in doors) { // если для сервопривода установлено состояние 'c' ("закрыто") if (doors[door].state === 'c') { // если дверь закрыта не полностью, уменьшаем угол сервопривода if (doors[door].angle > doors[door].min) { doors[door].angle -= 2; } } // если для сервопривода установлено состояние 'o' ("открыто") if (doors[door].state === 'o') { // если дверь открыта не полностью, увеличиваем угол сервопривода if (doors[door].angle < doors[door].max) { doors[door].angle += 5; } } // изменяем управляющий сигнал на сервопривод doors[door].servo.write(doors[door].angle); } }, 100); * Создайте блок управления диодной лентой. //Настраиваем управляемую диодную ленту SPI1.setup({baud:3200000, mosi:P3, sck:A5, miso:P2}); //Резервируем память для управления 32 диодами var arr = new Uint8ClampedArray(32*3); // будем включать пугатель на scareCooldown миллисекунд var scareCooldown = 3000; // функция активации пугателя function activateScare() { var c = 0; // каждые 100 миллисекунд обновляем цвет ленты var timerID = setInterval(function () { c++; // пиксели окрашиваем в красный цвет в помощью функции синуса for (var p = 0; p < arr.length; p += 3) { arr[p ] = (1 + Math.sin(c*2) ) * 126; arr[p+1] = 0; arr[p+2] = 0; } // записываем в ленту новые значения цветов SPI1.send4bit(arr, 0b0001, 0b0011); }, 100); // устанавливаем время мерцания ленты scareCooldown миллисекунд setTimeout(function () { // по истечению этого времени останавливаем таймер мерцания clearInterval(timerID); // каждый пиксель зажигаем белым светом for (var i = 0; i < arr.length; i += 3) { arr[i ] = 100; arr[i+1] = 100; arr[i+2] = 100; } SPI1.send4bit(arr, 0b0001, 0b0011); }, scareCooldown); } * Теперь по аналогии с дверями создайте блок управления кормушкой. // аналогично создаем объект для кормушки var feed = {servo: servo.connect(P1), angle: 0, min: 9, max: 57, cd: 0}; // будем включать кормушку на feedCooldown миллисекунд var feedCooldown = 10000; // активируем кормушку function activateFeed() { // будем открывать кормушку только на feedCooldown миллисекунд feed.cd = feedCooldown; // а обновлять состояние кормушки каждые updatePeriod миллисекунд var updatePeriod = 100; // по аналогии с дверями лабиринта сделаем плавное открытие var timerID = setInterval(function() { // плавно открываем кормушку if (feed.cd > 0) { if (feed.angle > feed.min) { feed.angle -= 5; } } else { // если время истекло, плавно закрываем кормушку if (feed.angle < feed.max) { feed.angle += 2; } else { clearInterval(timerID); } } feed.servo.write(feed.angle); // с каждой итерацией feed.cd уменшается на updatePeriod feed.cd -= updatePeriod; }, updatePeriod); } * Создайте блок сокет-соединения. Каждые 3 секунды на сервер отправляется запрос, ответ на который содержит данные о состоянии всех дверей в лабиринте, а также кормушки и светодиодной ленты. // Настраиваем соединение с Ethernet Shield 2 SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13}); var eth = require('WIZnet').connect(SPI2, P10); // подключаем библиотеку 'net' для работы с сетью Интернет var net = require('net'); // подключаем Ethernet Shield 2 к интерфейсу SPI2 eth.setIP(); // создаем сокет-соединение на указанный IP адрес сервера и порт net.connect({host: '192.168.10.178', port: 1337}, function(socket) { // каждые 3000 миллисекунд посылаем запрос на обновление состояний дверей setInterval(function() { socket.write('Get'); }, 3000); // обрабатываем получение данных от сервера socket.on('data', function(recieved) { // разворачиваем принятые данные в javascript объект var data = JSON.parse(recieved); if (data === undefined) { return; } // для каждой двери обновляем текущее состояние for (var door in data.doors) { doors[door].state = data.doors[door].st; } // если с сервера пришла команда открыть кормушку, активируем ее if (data.feed === 'o') { activateFeed(); } // то же самое для накаливания обстановки if (data.scare === 'o') { activateScare(); } }); // обрабатываем отключение от сервера socket.on('close', function() { print('WARNING: connection closed'); }); }); * Теперь объедините все блоки кода в один файл и вы получите готовый скрипт для работы лабиринта. Не забудьте перенести функции ''require()'' в начало своего скрипта. Исходный код проекта целиком доступен на [[https://github.com/amperka/rat-maze|github]]’е. ===== Демонстрация работы устройства ====== {{youtube>1ducWHmhccQ?large}} ===== Что дальше? ===== В данном проекте можно использовать любую другую плату с выходом в интернет. Если у вас уже есть [[amp>product/iskra-js?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Iskra JS]], можно добавить к ней [[amp>product/troyka-wi-fi?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Wi-Fi модуль ESP8266]] или даже [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=maze&utm_medium=wiki | GPRS Shield]], немного изменив скрипт. Фантазия не ограничивается дверями и каруселькой. Можно добавить любые сенсоры в форм-факторе [[amp>collection/troyka?utm_source=proj&utm_campaign=maze&utm_medium=wiki | Troyka Module]].