Содержание

Умный лабиринт для крыс на Iskra JS

Что это?

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

Для наших питомцев мы придумали и сделали интерактивный лабиринт — чтобы крысам было где побегать и порезвиться, а мы могли следить за их самочувствием, контролировать количество съеденной пищи и играть с ними даже не находясь дома.

Множество дверей, подсветка и даже кормушка управляются через веб-интерфейс, а логика работы контроллера написана на JavaScript.

Что нам понадобится?

  1. Блок питания 5 Вольт 5 Ампер
  2. Блок питания 12 Вольт 2 Ампера
  3. Большая плоская коробка
  4. Прозрачная крышка

Как собрать?

  1. Возьмите три пластины «мега». Две из них разрежьте так, чтобы получились четыре пластины 13×7 шипов. Они будут стенами лабиринта. Вставьте стены в третью пластину «мега» #структора.
  2. С помощью двух панелей «крепление микросервопривода 3×4 шипа» и трех панелей («пластина 2×3 шипа») сделайте «домик» для микросервопривода. Полностью домик собирать не обязательно.
  3. Возьмите пластину «мега» и разрежьте её на три части 15×5 шипов. Это будут двери лабиринта. Возьмите качалку для микросервопривода овальной формы. Вставьте её в панель #структора «крепление качалки 2×4 шипа» и прикрутите к валу сервопривода. Прикрепите дверь к качалке с помощью панелей #структора 2×3 шипа.
  4. Готовый механизм двери разместите внутри стен лабиринта так, чтобы дверь в закрытом состоянии перекрывала проход для крысы. Для этого сделайте вырез в стене шириной чуть больше ширины двери. Сделайте в середине блока отверстие для вывода проводов. Проденьте провода в это отверстие.
  5. Сделайте 11 блоков стен и 15 дверей. Разместите их так, чтобы крысе было интересно бегать между ними. Для удобства поставьте перемычки между блоками из оставшихся деталей #структора длиной 5–6 шипов.
  6. Соберите карусельку с помощью #структора из набора «крепления моторов и датчиков линии». Установите на вал мотора втулку, а затем пластину «мега».
  7. Управление каруселькой сделайте с помощью инфракрасного датчика движения и силового ключа. Схема подключения приведена на рисунке.
  8. Разместите датчик и силовой ключ в пустом блоке лабиринта.
  9. В другом свободном блоке соберите кормушку. Дверцу кормушки сделайте по аналогии с дверями лабиринта.
  10. Разместите лабиринт внутри большой плоской коробки. В дне коробки сделайте отверстие для проводов и мотора карусельки. В крышке коробки сделайте прорези для дверей.
  11. Возьмите Iskra JS и установите на нее Ethernet Shield 2. Выше установите Troyka Shield.
  12. С помощью 3-проводного шлейфа «папа-мама» удлините кабели микросервоприводов и подведите их к Iskra JS.
  13. Iskra JS имеет 18 выводов ШИМ, к которым можно подключить сервоприводы. Используйте Troyka Shield для подключения 3-проводных шлейфов. Аналогично используйте пины SCL и SDA. Пин P3 оставьте свободным для подключения к нему светодиодной ленты. Возьмите гнездо питания 2,1 мм и с помощью проводов «папа-папа» и соедините пин 5V Iskra с плюсом клеммника, а пин GND Iskra с минусом клеммника. В результате должна получиться такая схема:

    Не стоит пробовать питать лабиринт от USB компьютера. Подключайте USB только при включенном источнике питания!

  14. По периметру коробки проложите светодиодную ленту. Подключите ее к источнику питания 12 Вольт. Управляющий провод ленты подключите к пину P3 на плате Iskra JS. Соедините «минус» источника питания с «минусом» Iskra JS.
  15. Подключите один конец патч-корда витой пары в разъем Ethernet своего устройства. Второй конец подключите к своему Wi-Fi роутеру.
  16. Подключите кабель microUSB к Iskra JS и своему компьютеру.
  17. Теперь осталось только подключить камеру и настроить трансляцию on-line.

Алгоритм

Исходный код

// подключаем библиотеку '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);
}
// Настраиваем соединение с 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');
  });
});

Исходный код проекта целиком доступен на github’е.

Демонстрация работы устройства

Что дальше?

В данном проекте можно использовать любую другую плату с выходом в интернет. Если у вас уже есть Iskra JS, можно добавить к ней Wi-Fi модуль ESP8266 или даже GPRS Shield, немного изменив скрипт. Фантазия не ограничивается дверями и каруселькой. Можно добавить любые сенсоры в форм-факторе Troyka Module.