====== Умный лабиринт для крыс на 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]].