Известно, что слабый микроконтроллер ардуино не способен пропустить через себя видеопоток.
А если подойти к этой задаче с другого бока?

… и растянуть процесс съёмки во времени.

Хе, многие уже и сами догадались, эта техника съёмки называется timelapse (замедленная покадровая съёмка). То есть это никакая не видеосъёмка, а фотосъёмка, в результате которой создаётся видео.

Признаюсь, изначально я не планировал делать замедленную съёмку. Просто хотел сделать для своей ардуины регистрацию событий, в том числе и с фотографиями. Ну а потом пошло-поехало. Если мой читатель не занимается разработкой, то может просто посмотреть полученный результат (зайти из под Chrome).

Из чего состоит моя система:
  • плата Arduino Mega;
  • модуль JPEG камеры;
  • служебная программа и база данных MongoDB;
  • WEB сервер для размещения HTML файлов;


Когда ардуина посылает свои данные на сервер, то к каждому параметру прикрепляется своя метка времени.
Отправили параметр — добавилась запись в хранилище, ещё два раза отправили — сохранились ещё две записи.

Вся работа с хранилищем ведётся через служебную программу (далее посредник), которая запускается на стационарном компьютере. При этом, сам WEB сервер отдаёт только статичный контент. То есть, все клиенты ведут информационный обмен через служебную программу посредника, аналогично популярному протоколу MQTT. Основное отличие от MQTT будет в том, что этот посредник напрямую работает с хранилищем данных, обеспечивая работу с историческими данными. Этот факт упрощает схему взаимодействия, и не требует дополнительного сетевого трафика для сохранение данных.

Для удобства разработки своих веб приложений, я создал javascript библиотеку с таким API:

Так создаётся клиент для работы с сетевым хранилищем:
var client = new MgtClient("localhost", "login", "password", onDebugLog);

Аргументы функции:
  1. сетевой адрес, на котором запущена программа посредника, можно просто указать IP, например так — «127.0.0.1»;
  2. логин пользователя;
  3. пароль пользователя;
  4. функция обратного вызова, для отладочных строковых сообщений;


Функция обратного вызова для отладочных сообщений может выглядеть так:
function onDebugLog(aStr) {
  // метка времени и сообщение будут выводиться в отладочную консоль браузера
  console.log((new Date()).getTimeString() + ' ' + aStr + '\n'); 
}


Пока не сложно? Дальше будет потрудней.

Структура запроса к хранилищу:
var request = {
  name: "параметр 1", // имя запрашиваемого параметра
  placeId: 1, // идентификатор вашего объекта                      
  beginTime: 1458108472000, // начальное время в миллисекундах от 1 Января 1970 года
  endTime: 1458194872000, // конечное время в миллисекундах от 1 Января 1970 года (не включительно)
  limit: 10000 // максимальное количество записей, не обязательный параметр (по умолчанию без ограничения)
};


Ещё не запутались?

Тогда вот структура ответа на запрос:
var result = {
  times: [], // массив временных меток полученных записей (время в миллисекундах от 1 Января 1970 года)
  values: [], // массив значений полученных записей
  position: 20, // порядковый номер указывающий на новые данные в массиве (данные могут поступать порциями)
  status: "progress", // состояние запущенного процесса ("progress", "abort", "done", "fail")
  progress: 91 // индикатор выполнения (в процентах)   
};


Ага, уже сложней?

Состояние поля «status»:
  • «done» — получили всё что запросили (либо получены данные за весь временной диапазон, либо сработало ограничение на количество записей);
  • «progress» — указывает, что это не последняя порция данных, процесс скачивания ещё не окончен;
  • «abort» — прервалось скачивания данных (сработало ограничение на общий объём выкачиваемых данных), можете тут же сформировать новый запрос на получение недостающих данных;
  • «fail» — что то пошло не так (может тока нет в розетке?)


Вы думаете это всё? К сожалению, нет.

Запрашиваемые параметры могут быть разного типа.
  • Если параметр числовой, то в массиве значений окажутся числа.
  • Если строковый, то в массиве значений будут строки.
  • Если булев, то в массиве значений будут «true» или «false».
  • Если бинарный (например JPEG картинка), то в массиве значений будут массивы байт.
  • Если это событие, то вернуться массивы, сформированные особым образом.

Пример одной записи события:
var event = [
  "сработал датчик движения", // заголовок произошедшего события
  "термометр", // имя параметра 1
  27.5, // значение параметра 1
  "светодиод", // имя параметра 2
  true, // значение параметра 2
  ...
  "фото", // имя последнего параметра
  1458108472000 // сюрприз!!! это всего лишь временная метка сохранённого изображения,
                // нужно сформировать дополнительный запрос для выкачивания этого изображения.
];

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

Уфф, самое сложное уже позади.

А так выглядит сама отправка запроса:
// aRequest - структура запроса
// onReadArchive - обратная функция для получения ответа/ответов
client.readArchive(aRequest, onReadArchive);                                                           


Обратная функция для получения ответов:
// aResult - структура ответа
onReadArchive(aResult) {
  // TODO  вставьте свой код обработки пришедших данных

  // если вернём "false", то продолжим принимать ответы (если процесс приёма не завершён)
  // если вернём "true", то обрываем получение остальных ответов (если процесс приёма не завершён) 
  return false; 
}


Наконец мы подошли к самому монтажу видео.

Для создания видео я использовал javascript библиотеку Whammy, здесь подробнее.

Функция, которая создаёт видео:

<script src="whammy.js"></script>
<canvas id="canvas" style="display:none"></canvas>
<video id="player" controls autoplay loop></video>

function createVideo() {
  var canvas = document.getElementById("canvas");
  var context = canvas.getContext("2d");
  canvas.width = '640'; // это ширина фоток в пикселях
  canvas.height = '480'; // это высота фоток в пикселях
  var framerate = 10; // устанавливаем количество кадров в секунду
  var quality = 0.8; //  устанавливаем качество видео

  var video = new Whammy.Video(framerate, quality); // объект для создания видео в формате WebM
 
  for (var i = 0; i < images.length; i++) { // пройдёмся по всем изображениям
    var image = images[i];
    context.globalAlpha = 1;
    context.drawImage(image, 0, 0, 640, 480); // сначала размещаем изображение на канву
    video.add(context); // добавляем новый кадр
  }

  var output = video.compile(); // создаём видео из кадров
  var url = URL.createObjectURL(output); // конвертируем в нужный формат

  document.getElementById('player').src = url; // подсовываем результат нашему проигрывателю
}


К сожалению, создать видео можно не во всех браузерах. Например, мой любимый Firefox не умеет преобразовывать изображения в формат WebP, на основе которого и происходит конвертация в видео. И хотя я нашёл javascript библиотеку для такого преобразования, но конвертировала она так медленно (а кадров было так много), что я отказался от её применения. Впрочем, во всех браузерах с «хромовским» движком эта штука работать будет.

Здесь вы можете посмотреть что у меня получилось.

Не имея цельной документации, могу предложить мои предыдущие статьи.
Статья 1
Статья 2

Ну вот и всё, и сказать то мне больше нечего.

Комментарии (18)


  1. sim31r
    18.03.2016 11:17
    +4

    Нет схемы, типа камеры, мало информации по аппаратной части, хорошо бы добавить.


  1. valeraba
    18.03.2016 11:24
    -1

    1. merl1n
      18.03.2016 14:43
      +2

      С такой камерой каждый сможет :)
      А ты возьми и сделай на основе OV7670.
      Я уже молчу про отсутствие кода, схемы…
      А Javascript мы и сами знаем.

      http://privateblog.info/arduino-uno-i-kamera-ov7670-primer-ispolzovaniya/


      1. valeraba
        18.03.2016 15:21

        Мне интересна именно JPEG камера, чтобы можно было протаскивать фотки через узкий канал GPRS и не платить много денег.


        1. merl1n
          18.03.2016 17:21
          +2

          А какой смысл был в Arduino Mega +Camera (17 + 10 = 27$)? не проще ли подключить обычную Webcamer'у и читать сразу с неё?
          Такое прекрасно реализуется на OpenCV
          http://privateblog.info/java-tips-and-tricks/kak-poluchit-kartinku-s-kamery-s-pomoshhyu-opencv/


          1. valeraba
            18.03.2016 17:29

            Что касается веб камер, то у меня в системе даже встроена поддержка WebRTC, можете видео погонять с браузера на браузер. И я согласен, что стоит такая веб камера дёшево, но к сожалению, к ардуино такую не подключить.


            1. merl1n
              18.03.2016 17:45
              +2

              Я про подключение вебкамеры напрямую к серверу без arduino. У вас, кажется, arduino так и подключена.

              Вообще, у вас заголовок статьи про arduino и видео, а в содержании и пример, как пользоваться какой-то js библиотекой.


              1. valeraba
                18.03.2016 18:03

                Вы не совсем поняли архитектуру, есть запущенное серверное приложение (посредник), к нему подцепляются с одной стороны железки, а с другой стороны html страницы (через websocket). Как получить рабочий скетч для ардуино, я писал в своих предыдущих статьях. А здесь я привёл API, с помощью которого можно выкачивать исторические данные с сервера (посредника). И уже потом, на основе выкаченных фоток, монтировать видео. В целом я согласен с вами, статья скорее про работу с регистратором.


    1. sim31r
      18.03.2016 23:31

      Спасибо за уточнение. А не могли бы вы дать ссылку на формат данных из камеры, понятно, что можно «прослушать» обмен данных с утилитой, но возможно, есть подробное описание работы с jpeg камерой? Время передачи изображения, как я понимаю, минимум 500 000/115 200 = 4 секунды?


      1. valeraba
        19.03.2016 03:41

        https://yadom.fr/downloadable/download/sample/sample_id/31/
        На моём сайте найдёте все исходники для ардуино, там же будет лежать и моя библиотечка "PhotoCamera_OV528.h"

        И вы правы, узкое место это UART 115200.


  1. Raegdan
    18.03.2016 11:51

    Если, конечно, цель проекта — не поразбираться для души, а сделать прикладное устройство в практических целях — то почему дуинка, а не микрокомпьютер?


  1. valeraba
    18.03.2016 12:03

    Это же не серийное изделие, ардуина для быстрых прототипов вполне годится.
    На счёт принципиальной схемы:
    1) провода питания
    2) провод Ethernet
    3) камеру цепляем на UART (у меня две камеры, поэтому использовал два UART)
    4) светодиоды на любые порты


  1. ub9obe
    18.03.2016 14:41
    +1

    «Микроконтроллера ардуино» не существует, существует продукт ардуино на микроконтроллерах atmel.
    Ардуино due на основе AT91SAM3X8E с тактовой 80мгц способно на многое.


    1. merl1n
      18.03.2016 14:44

      Тоже хочу ov7670 перенести на неё. там скорости даже должно хватать для передачи по bluetooth.


    1. valeraba
      18.03.2016 14:45

      то да, ваша формулировка будет точней


  1. RuslanMIT16
    18.03.2016 17:43

    Причиной использования именно Меги является ее мощь (SRAM, Flash, Frequency, и т д?).
    Я просто сейчас работаю над проектом где хотелось бы внедрить видео стримчик.
    Но только ограничение по характеристике:

    The MCU module includes the following features:
    • 9S12C32 MCU in 48-pin LQFP
    • 8 Mhz crystal
    • PLL circuit
    • local 5 Volt low-dropout 100 mA regulator
    • RS232 transceiver circuit
    • standard 6-pin BDM connector
    • on-chip Serial Monitor
    • BOOT/RUN switch to support Serial Monitor
    • 32K Bytes Flash
    • 2K Bytes Ram
    • user access to 16 digital port lines, including Port M, Port T, and PORTE
    • user access to eight 10-bit analog input or digital I/O port lines (Port AD)
    • five PWM timer channels (PT0 — PT4)
    • eight Input Capture/Output Compare (Port T)
    • serial peripheral interface (SPI)
    • serial communications interface (SCI)
    • controller area network (CAN 2.0)
    • key wake-up inputs
    • user access to MCU reset signal
    • background debug mode (BDM) connector
    • pinout is super-set of competing 24-pin modules on the market


    1. valeraba
      18.03.2016 17:49

      Возьмите Raspberry Pi, там и рабочие примеры есть для видео.


      1. RuslanMIT16
        19.03.2016 23:34

        Я бы с радостью, но это ограничение курса, мы должны использовать именно данный МК.
        Спасибо за ответ.