В данной статье я расскажу как реализовал свою давнюю задумку — наглядное отображение состояния иптв-каналов. Вообще изначально речь шла вообще о мониторинге — например, карта города, на нее расставлены светодиоды (в тех местах где находится оборудование), и глядя на эту карту всегда можно сразу наглядно увидеть — что где сломалось.
Но в моем случае речь идет именно про 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 |
Автор: IbZ