POV-спидометр для велосипеда

  • Платформы: Espruino Pico, Iskra JS
  • Языки программирования: JavaScript
  • Тэги: велосипед, подсветка, Espruino Pico, персистенция, рисуем светодиодами, обман зрения

Что это?

Хотите выделиться в толпе велосипедистов? Используя знания из прошлых проектов, соберём простой велокомпьютер и покажем скорость прямо на колесе байка.

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

  1. Синяя изолента
  2. Пластиковые стяжки
  3. Монтажный провод
  4. Аккумулятор

Как собрать?

  1. Изучите проект POV-бегущей строки. Бегущая строка для велосипеда основана на этом проекте. Вместо акселерометра используйте цифровой датчик линии. Он замеряет скорость велосипеда и служит началом координат для отображения строки на колесе.
  2. Один пиксель строки состоит из светодиода и резистора, подключенных к Espruino Pico.
  3. Возьмите макетную плату Breadboard PCB на 400 точек и соберите устройство из восьми таких пикселей. Припаяйте светодиоды к пинам Espruino Pico A8, A6, A7, B1, B10, B13, B14 и B15 через резисторы 220 Ом. Правильно подобрать сопротивление резисторов поможет статья в конспекте-Arduino. Припаяйте Espruino Pico и провода питания датчика линии.
  4. Разместите плату, датчик линии и аккумулятор на колесе. Закрепите с помощью пластиковых стяжек.

Алгоритм

  • Каждый раз при прохождении датчика линии мимо рамы велосипеда запускается обработчик прерывания.
  • Функция обработки прерывания вычисляет скорость движения велосипеда. Зная интервал времени между текущим и предыдущим оборотами, скорость можно рассчитать по формуле:
  • Угол сектора колеса, внутри которого будет отображаться скорость, определяет время свечения светодиодов. Чем меньше угол сектора, тем меньше время свечения.
  • Угловая частота вращения колеса вычисляется по формуле: Чем больше скорость колеса, тем меньше время свечения светодиодов.

Исходный код

veloPOV.js
// массив символов
var image = {
  '0': [0x7C, 0x8A, 0x92, 0xA2, 0x7C],
  '1': [0x00, 0x42, 0xFE, 0x02, 0x00],
  '2': [0x42, 0x86, 0x8A, 0x92, 0x62],
  '3': [0x44, 0x82, 0x92, 0x92, 0x6C],
  '4': [0x10, 0x30, 0x50, 0xFE, 0x10],
  '5': [0xE4, 0xA2, 0xA2, 0xA2, 0x9C],
  '6': [0x3C, 0x52, 0x92, 0x92, 0x0C],
  '7': [0x80, 0x86, 0x98, 0xE0, 0x80],
  '8': [0x6C, 0x92, 0x92, 0x92, 0x6C],
  '9': [0x60, 0x92, 0x92, 0x94, 0x78],
  'k': [0xFE, 0x08, 0x08, 0x14, 0x22],
  'm': [0x3E, 0x20, 0x1C, 0x20, 0x1E],
  '/': [0x02, 0x0C, 0x10, 0x60, 0x80],
  'h': [0xFE, 0x10, 0x10, 0x10, 0x0E]
};
 
// массив пинов для подключения светодиодов
var led = [A8, A6, A7, B1, B10, B13, B14, B15];
 
// радиус колеса в метрах
var wheelRadius = 0.165;
// количество пустых столбцов между буквами
var spaceColomns = 1;
// текст для отображения
var toShow = '';
// угол отображения текста в радианах
var tetta = 0.1;
// счетчик обновления текста
var update = 0;
// длина окружности колеса
var circle = 2 * 3.14159 * wheelRadius;
 
// ставим триггер на срабатывание датчика линии, подключенного к пину B3
setWatch(function(e) {
  // период вращения колеса в секундах
  var dT = e.time - e.lastTime;
  // линейная скорость велосипеда в метрах в секунду
  var speed = circle / dT;
  // обновляем текст для отображения каждые 5 оборотов
  if (update++ % 5 === 0) {
    // переводим из метров в секунду в километры в час
    toShow = Math.round(speed * 3.6) + 'km/h';
  }
  // угловая частота колеса
  var omega = speed / wheelRadius;
  // временной период отображения текста
  var wordPeriod = tetta / omega;
  // временной период отображения одной колонки символа
  var colomnPeriod = wordPeriod / (toShow.length * (spaceColomns + 5) - spaceColomns);
  // выводим текст на POV дисплей
  show(toShow, colomnPeriod);
}, B3, {
  edge: 'rising',
  repeat: true
});
 
// функция отображения текста на дисплее
function show(word, colomnPeriod) {
  var w = 0;
  var i = 0;
  var pause = 0;
 
  // показываем каждый столбец "пикселей" в символах с периодом colomnPeriod
  var id = setInterval(function () {
    // если требуется сделать паузу между символами
    if (pause-- > 0) {
      // гасим все пиксели
      colomn(0x00);
    } else {
      // иначе показываем столбцы пикселей
      colomn(image[word[w]][i++]);
    }
    // когда показали все столбцы в символе
    if (i === 5) {
      i = 0;
      // задаем паузу после символа
      pause = spaceColomns;
      // когда показали все символы в строке, гасим пиксели и отключаем таймер
      if (++w === word.length) {
        colomn(0x00);
        clearInterval(id);
      }
    }
  }, colomnPeriod);
 
  // функция включения нужных пикселей в столбце
  function colomn(col) {
    for (var j = 0; j < 8; ++j, col >>= 1) {
      digitalWrite(led[j], !(col & 1));
    }
  }
}

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

Что дальше?

Добавьте в проект Bluetooth-модуль HC-06 и меняйте текст строки со смартфона прямо на ходу.

Не хотите зависеть от внешнего света при работе с инфракрасным датчиком? Замените его на датчик Холла, а на раму велосипеда прикрепите магнит.

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