Наглядный мониторинг большого числа обьектов (например, каналы IPTV) с помощью Arduino

В данной статье я расскажу как реализовал свою давнюю задумку — наглядное отображение состояния иптв-каналов. Вообще изначально речь шла вообще о мониторинге — например, карта города, на нее расставлены светодиоды (в тех местах где находится оборудование), и глядя на эту карту всегда можно сразу наглядно увидеть — что где сломалось.
Но в моем случае речь идет именно про IPTV каналы.

Каналов этих у нас много, 160 штук почти, это не считая служебных, из которых например мультиплексируются федералка (т.е. берется например канал ТНТ с двух ресиверов расположенных в разных частях города (в идеале хочется конечно вообще из другого города), мультикастом приватными потоками пригоняются в мультиплексор, который уже выдает один публичный мультикаст, при этом при аварии одного из каналов автоматически переключает на резерв.

Естественно мониторинг программными средствами я наладил, т.е. наваял целый комплекс из, например, скриптов, которые пробегают все каналы — подписываются на них, проверяют есть ли ошибки в потоке, не слетела ли кодировка канала, пытается исправить их передергиванием кам-модулей (используется telnet, snmp) просто банально перезагрузка ресивера, если количество сбойнувших каналов идущих с него больше половины, естественно смс-ка мне обо всех значимых событиях. Еще к этому еще и собирается статистика по всей сети с стб-шек, со свичей, какие прошивки у народа, какие приставки, какие каналы и какие конкретно передачи больше всего смотрят люди (очень кстати любопытные данные).

Для наглядности есть программа которая в виде мозаики пробегается по каналам, или держит определенную
часть каналов на экране (все 160 каналов сами понимаете на один экран не умещаются, но 4х6=24 SD канала на компе с хорошим процом/видюхой/сетевухой — вполне реально).

В общем, программно то все это уже есть, но вот чтоб без компьютера и в виде светодиодиков — не было…
И тут я познакомился с Arduinо.
Сами наверно понимаете, к чему это привело.

Поскольку с паяльником я не дружил (да и сейчас не силен — но определенные подвижки уже надеюсь есть) и совсем не дружил с схемотехникой и т.п. — было мне тяжело поначалу (да и сейчас тоже), но в этом ведь и была цель — изучить новое, познать неведомое ранее. Я не буду подробно останавливаться как в процессе узнавал про такие вещи как сдвиговые регистры, озарения, как можно оказывается управлять целой матрицой светодиодов, используя такое явление как «динамическая индикация». Просто расскажу что получилось в итоге (а у меня получилось!).

кликабельно

Итак, я напаял на готовую прототипную плату матрицу из светодиодов 16х10, подключил их через 4 сдвиговых регистра 74HC595. Используется arduino и ethershield на enc28j60 для нее.
Получилось примерно вот такое (картинки кликабельны)

кликабельно

кликабельно

кликабельно

Код для среды разработки с версией 0.22 и библиотеки ethershield версии 1.1.
Уже на данный момент я точно знаю есть поновее, если вдруг захотите повторить — возможно придется адаптировать

#include "etherShield.h"

static uint8_t mymac[6] = {0x00,0x80,0x48,0x2d,0xf7,0x25};  // задаем mac-адрес девайса
static uint8_t myip[4] = {10,20,30,40};                   // ip-адрес 10.20.30.40

#define MYPORT 5555
#define BUFFER_SIZE 500
static uint8_t buf[BUFFER_SIZE+1];
static char number[7];


const byte clockPin = 7; //
const byte latchPin = 8; //  номера выходов ардуины для записи чисел в регистры
const byte dataPin =  9; //
 
const byte NumRegs = 4; // количество сдвиговых регистров
const byte NumCols=10;
const byte NumRows=16;

// паяльщик из меня тот еще - поэтому я старался паять чтоб было удобно, 
// а все неудобства переложил на программирование
// Вобщем я запаял 4 регистра, и к выходам регистров выводил либо строку, 
// либо колонку
// все выходы регистров последовательно (логически) пронумеровал от 1 до 32
// Поскольку мне понадобилось только 26 выходов (10+16) - припаял я ессно не все


byte Col_bits[NumCols] = {3,5,7,11,13,15,19,21,23,27};   
        // вот в этом массиве я указал соотношение колонок
        // к номеру выхода регистров, т.е. 1-я колонка 
        // светодиодов припаяна к 3 выходу регистров и т.д.

byte Row_bits[NumRows] = {32,30,28,26,24,22,20,18,16,14,12,10,8,6,4,2}; 
        // в этом массиве перечислены номера
        // выходов регистров отвечающих за строки
byte Regs[NumRegs];
int reg_n, bit_n, in_r, in_c;   //вспомогательные переменные, массивы. 
                                //Главное чтоб размер данных не был больше 2кбайт! ;)

                                // массив-матрица состояний сетодиодов
byte matrix[NumRows][NumCols]={{1,0,1,0,0,0,1,1,1,1}, 
                               {1,0,1,0,0,0,0,0,0,1},
                               {1,0,1,0,0,0,0,0,0,1},
                               {1,0,1,0,0,0,0,0,1,0},
                               {1,0,1,0,0,0,0,0,1,0},
                               {1,0,1,1,0,0,0,0,1,0},
                               {1,0,1,0,1,0,0,1,0,0},
                               {1,0,1,0,1,0,0,1,0,0},
                               {1,0,1,0,1,0,0,1,0,0},
                               {1,0,1,0,1,0,1,0,0,0},
                               {1,0,1,0,1,0,1,0,0,0},
                               {1,0,1,1,0,0,1,1,1,1},
                               {0,0,0,0,0,0,0,0,0,0},
                               {0,0,0,0,0,0,0,0,0,0},
                               {0,0,0,0,0,0,0,0,0,0},
                               {0,0,0,0,0,0,0,0,0,0}};
 


EtherShield es=EtherShield();


void setup()
{

  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);

  es.ES_enc28j60Init(mymac);                          // поднимаем сетевуху
  es.ES_init_ip_arp_udp_tcp(mymac,myip, MYPORT);      // открываем сокет
}

void loop()
{
  uint16_t plen, dat_p, ptr;

  while(1) 
  {
    burn_matrix(); 
        // вызываем функцию которая физически зажигает светодиоды из состояния в 
        // массиве она пробегает и зажигает их колонками (потом гасит). За счет того 
        // что это делается достаточно быстро и часто, человеческий глаз не успевает
        //  заметить что светодиоды гаснут - кажется что они горят постоянно

    // Здесь стандартно - read packet, handle ping and wait for a tcp packet:
    dat_p=es.ES_packetloop_icmp_tcp(buf,es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf));

  
    /* dat_p will be unequal to zero if there is a valid request */
    if(dat_p == 0)
    { // no request
      continue;
    }

    // на всякий случаем делаем типа защиту от случайных данных пришедших по сети :)
    // реагируем только на сообщение начинающиеся на строку "IbZ "
    if (strncmp("IbZ ",(char *)&(buf[dat_p]),4)==0)
    {
      // начиная с 4-байта идут номера светодиодов которые надо зажечь
      ptr = dat_p+4;

      for(in_r=0; in_r < NumRows; in_r++)
      {
        for(in_c=0; in_c < NumCols; in_c++)
        {
          matrix[in_r][in_c] = 0; // сначала все гасим (не физически, а в массиве ;) )
        }
      }

      // начинаем парсить сообщение - каждый байт - номер светодиода 
      // который надо зажечь, конец сообщения - 0
      while(buf[ptr] != 0)
      {
        in_r = (buf[ptr]-1) % 16;
        in_c = (buf[ptr]-1) / 16;
        matrix[in_r][in_c] = 1;  // зажигаем светодиод в матрице
        ptr++;
      }

    }

  }
}



void burn_matrix()      
{    // функция для поджигания светодиодов - зажигает 1-ю колонку, гасит ее, 
     // поджигает 2-ю колонку, гасит ее и так далее до 10-й колонки

  for(int c = 0; c < NumCols; c++)
  {
    for(int i = 0; i < NumRegs; i++)
    {
      Regs[i]=255;
    }
    reg_n = (Col_bits[c]-1) / 8;
    bit_n = (Col_bits[c]-1) % 8;
    bitWrite(Regs[reg_n], bit_n, 0);

    for(int r = 0; r < NumRows; r++)
    {
        reg_n = (Row_bits[r]-1) / 8;
        bit_n = (Row_bits[r]-1) % 8;
        bitWrite(Regs[reg_n], bit_n, matrix[r][c]);
    }  
    registerWrite();
    delay(1);
  }
}

//это функция собственно записи в сдвиговые регистры-тот момент когда зажигаются светодиоды
void registerWrite() 
{
  digitalWrite(latchPin, LOW);
  for(int cur_reg = NumRegs-1; cur_reg >= 0; cur_reg-- )
  {
    shiftOut(dataPin, clockPin, MSBFIRST, Regs[cur_reg]);
  }
  digitalWrite(latchPin, HIGH);
}



Алгоритм простой: если канал перестает работать — сразу видно — начинает гореть и не гаснет светодиод.
Когда канал проверяется — светодиод горит, если все хорошо — светодиод тухнет, т.е. видно как бегает скрипт проверяющий каналы. Он запускается на сервере мониторинга раз в полчаса (на каждый канал уходит чуть больше 8 секунд), приватная федералка проверяется отдельно и здесь не отображается — в планах (к сожалению правда наверно уже неосуществимых :( ) сделать матрицу на большее число светодиодов, запустить несколько серверов мониторинга (распараллелить процесс, чтобы все каналы проверялись каждые 5-10 минут).

Здесь коротенькое видео:



Ну и как принято, примерная стоимость основных использованных материалов (в основном все куплено на ebay/китайшопах):
плата Breadboard Prototype PCB Print Circuit Board 18 x 30 ~$4
160 зеленых светодиодов 3мм меньше $5 можно найти
Arduino ~$16
ENC28J60 Ethernet Shield for Arduino Duemilanove/Uno ~$16
4 регистра 74HC595 ~$3
В общей сложности ~$45-50
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 20
  • +1
    Мы тоже хотели сделать нечто подобное, даже некоторые наработки были, потом оказалось, что отдельный монитор с выделенным компьютером (сейчас распбери доедет, вообще хорошо получится ) всяко удобнее — полезной информации больше умещается.
    • +3
      Согласен. И дешевле еще и проще и эффективнее. На компьютере же все это уже есть, но это все слишком обычно. А когда реализуешь свою давнюю задумку своими руками, а она еще и работает как надо — знаете как приятно? :)
    • 0
      Можно было не моргать постоянно диодами, т.к. данный сдвиговый регистр может «защелкиваться» — т.е. надо один раз установить маску битов, и защелкнуть её, после этого будут постоянно гореть нужные диоды.
      • 0
        Если к к каждому выходу регистра подключить по одному светодиоду — то да, можно защелкнуть и забыть. Но тогда нужно 160/8 = 20 регистров, что и распаивать тяжелее, да и дороже получается (вначале я кстати так и хотел сделать).
        Когда подключаешь матрицу как в моем примере, при защелкивании можно добиться что светодиоды будут гореть какими-нибудь полосами/рядами, но если надо рандомно зажечь два светодиода тут, три в другом углу, 10 штук посередке ромбиком — просто так это уже не сделаешь.
        На помощь приходит динамическая индикация.
        Защелкивание регистров разумеется здесь происходит — иначе светодиоды не зажгутся просто. Тут используется тот момент что человеческий глаз он инертен, т.е. если быстро зажигать и гасить светодиод в цикле — человеческий глаз не успеет заметить что светодиод гаснет, для него он будет постоянно горящим.
        И именно поэтому здесь постоянно поджигается нужные светодиоды из одного рядя, потом из второго, третьего и т.д. и все это по кругу.
      • 0
        Статья напомнила:

        На одном предыдущем месте работы, в мои обязанности (были прописаны в должностной инструкции) входила ночная проверка каналов цифрового телевидения, особенно — пакета каналов для взрослых. Причем приходилось проделывать это вручную, на LCD панеле. Особенно я любил проверять Discovery Channel, уж очень мне программка «Выжить любой ценой» приглянулась.
        • 0
          А зачем вручную? Есть же куча программных средств для этих целей.
          • 0
            В этом вся соль :) — особенно то что это было прописано в должностной инструкции.
        • 0
          Прикольно, достойно, интересно, но кустарно. Т.е. в качестве проверки собственных сил и развлекалова — гут.

          Как аппаратное решение широкого спектра действия — не очень. Я в этом вижу базу для продолжения более фундаментальной и серьезной работы. Как и Вы и сами писали — планы. Это хорошо. И лучше сделать. Хотя бы для себя.
          • 0
            Дороговато вам регистры встали. 3бакса за 4 штуки. я на рынке их по 0,5бакса видел.
            • 0
              Это за 1 бакс так сокрушаетесь?
              • 0
                Да. Я же за него сколько «семок» смогу купить…
                • 0
                  О семках не подумал…
            • 0
              Можно парсить MPEG-TS (и PES) прямо на девборде, а не на отдельной машинке. Но сомневаюсь что Arduino для этого будет достаточно, BeagleBone для этого лучше подойдёт.
              • 0
                Arduino даже JPEG то поместить некуда — скудное количество памяти и отсутствие возможности ее расширения, хехе. Вот, ничего, в блоге arduino.cc писали, что после пасхи выйдет Arduino на ARM, тогда и посмотрим :)
                • 0
                  Лишь бы пасха не оказалась китайской.
              • 0
                не совсем понял — а откуда берется информация о том, что на канале есть проблемы?
                • 0
                  Есть скрипт на сервере (php) который выполняет функцию мониторинга — постоянно по кругу проверяет канал один за другим, проверяет есть ли вообще трансляция и если есть — не кодирована ли она. И соответственно при проверке каждого канала делает примерно следующее
                           if($arduino_is_live)
                           {
                             ... заполняется $arduino_msg ...
                             $fp=fsockopen("tcp://10.20.30.40", 5555, $errno, $errstr, 3);
                             if($fp)
                             {
                               fwrite($fp, $arduino_msg);
                               fclose($fp);
                             }
                             else
                             {
                               $arduino_is_live = false;
                             }
                  

                  в переменную $arduino_msg загоняется номер текущего проверяемого канала и все сбойнувшие. В конце цикла — посылается еще раз сообщение где перечислены только сбойнувшие каналы.
                  Скрипт проверки использует VLC (записывается файл и парсятся/анализируются логи). По результатам проверки, если есть много проблем на каком-то ресивере (больше половины каналов не работает) — ребутает его (snmp или телнет в зависимости от модели ресивера) и отправляет смс-ку если список сбойнувщих каналов изменился.
                • 0
                  Изображения не открываются.
                  • 0
                    К сожалению накрылась домашняя файлопомойка на которой был мой сайт с фотками (а на хабрасторадж мне видно пока по рангу не положено выкладывать) — часть я восстановил, а вот картинку со схемой, к сожалению, смогу восстановить только в понедельник.

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

                Самое читаемое