Управление камерой, приборами и данные с датчиков на экране телевизора

Устройство на Arduino, используется микросхема max7456.

Это OSD (on screen display) монохромный
256 зарезервированных символов
TSSOP 28 – корпус
Подключается к телевизору по входам «тюльпан».

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

Управляется все пультом с используемого телевизора. Сначала предисловие, почему возникла необходимость данного устройства. На лампу и RGB-ленту постоянно неизвестно где пульт, к камере доступ с компа по WEB-интерфейсу, что не очень удобно, если хочешь посмотреть кто пришел. Если все будет на экране телевизора и управляться пультом с этого же телевизора, конечно же упростит пользование данными приборами. Конечно и пульт от телевизора теряется, но гораздо реже.

Микросхема max7456 имелась лишняя (покупал для платы OSD для радиоуправляемого автомобиля, но пришла бракованная плата GPS и пока проект откладывается)

Вот схема устройства (нет пока датчика движения и микросхемы часов D1307)

И скетч для Arduino (неполный — ещё не пришли датчик движения и микросхема часов DS1307)

Используются библиотеки

MAX7456 —  вывод символов через max7456;

IRremote — получение и отправка ИК;

Servo — для управления сервоприводами поворота камеры;

OneWire — для работы с датчиками температуры DS18B20;

DHT — для работы с датчиком DH11 (измерение влажности воздуха)

BMP085 — для работы с датчиком BMP085 (атмосферное давление)

#define DATAOUT 11//MOSI
#define DATAIN 12//MISO
#define SPICLOCK 13//sck
#define MAX7456SELECT 10//ss
#define VSYNC 0x02// INT0

#define DMM_reg 0x04
#define DMAH_reg 0x05
#define DMAL_reg 0x06
#define DMDI_reg 0x07
#define VM0_reg 0x00
#define VM1_reg 0x01

#define VIDEO_BUFFER_DISABLE 0x01
#define MAX7456_RESET 0x02
#define VERTICAL_SYNC_NEXT_VSYNC 0x04
#define OSD_ENABLE 0x08
#define SYNC_MODE_AUTO 0x00
#define SYNC_MODE_INTERNAL 0x30
#define SYNC_MODE_EXTERNAL 0x20
#define VIDEO_MODE_PAL 0x40
#define VIDEO_MODE_NTSC 0x00

#define BLINK_DUTY_CYCLE_50_50 0x00
#define BLINK_DUTY_CYCLE_33_66 0x01
#define BLINK_DUTY_CYCLE_25_75 0x02
#define BLINK_DUTY_CYCLE_75_25 0x03

#define BLINK_TIME_0 0x00
#define BLINK_TIME_1 0x04
#define BLINK_TIME_2 0x08
#define BLINK_TIME_3 0x0C

#define BACKGROUND_BRIGHTNESS_0 0x00
#define BACKGROUND_BRIGHTNESS_7 0x10
#define BACKGROUND_BRIGHTNESS_14 0x20
#define BACKGROUND_BRIGHTNESS_21 0x30
#define BACKGROUND_BRIGHTNESS_28 0x40
#define BACKGROUND_BRIGHTNESS_35 0x50
#define BACKGROUND_BRIGHTNESS_42 0x60
#define BACKGROUND_BRIGHTNESS_49 0x70

#define BACKGROUND_MODE_GRAY 0x40

#define CLEAR_display 0x04
#define CLEAR_display_vert 0x06
#define END_string 0xff
#define ENABLE_display 0x08
#define ENABLE_display_vert 0x0c
#define MAX7456_reset 0x02
#define DISABLE_display 0x00

#define WHITE_level_80 0x03
#define WHITE_level_90 0x02
#define WHITE_level_100 0x01
#define WHITE_level_120 0x00

#define MAX_screen_size 390
#define MAX_screen_rows 16

byte spi_junk, eeprom_junk;
int x;

//***************************
struct MENU // структура меню
{
char* name1[27]; // наименование
int y[27];
int x[27];
int activ[27];
int pos1; // позиция меню
char bottom2[100];
int offbottom2;
};
MENU MENU1={
{«Камера»,»xD6″,»xD2″,»xD5″,»xD7″,»00″,
«Датчики»,»Выкл»,
«Прибор»,»Ч»,»Б»,»К»,»О»,»С»,»Г»,»З»,»Б»,
«Ч»,»Б»,»К»,»О»,»С»,»Г»,»З»,»Б»,
«Выкл»,»00:00″ },
{2,4,5,4,5,4,10,13,19,19,20,21,22,23,24,25,26,19,20,21,22,23,24,25,26,19,22},
{1,2,2,3,3,4,1, 6, 1, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 7, 13},
{1,0,0,0,0,0,1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
0,» «,0};

struct MYDATA // структура меню
{
int servo_ud; // положение камеры up-down
int servo_lr; // положение камеры left-right
float t1; // значение t1
float t2; // значение t2
float h1; // значение h1 — относительная влажность
float p1; // значение p1 — давление
int dd; // датчик движения Выкл-Вкл
int posl1; // лампа (0-7)
int posl2; // лента (0-7)
int posp1; // прожектор
};
MYDATA MYDATA1={0,0,0.0,0.0,0.0,0.0,0,0,0,0};

#include «IRremote.h»
int RECV_PIN=2;
IRrecv irrecv(RECV_PIN);
IRsend irsend;
//decode_results results;
unsigned long ir_dt, old_ir;
long ir_kod;
unsigned long ir_time1, ir_time2;
#define B_LEFT1 0x11
#define B_LEFT2 0x811
#define B_RIGHT1 0x10
#define B_RIGHT2 0x810
#define B_OK1 0x25
#define B_OK2 0x825
#define B_CLEARMENU1 0x37
#define B_CLEARMENU2 0x837

#include «Servo.h»
Servo camera_hor,camera_vert;

#include «OneWire.h»
OneWire ds(8); // on pin 8
byte my_addr[3][8]={{0x28,0x67,0xE5,0xC7,2,0,0,0xA0},
{0x28,0x94,0xB4,0x65,4,0,0,0xAC},
{0x28,0xF6,0x98,0xBA,2,0,0,0×92}};
#include «Wire.h»
#include «BMP085.h»
BMP085 dps = BMP085();
long Pressure085 = 0, Altitude085 = 0;

#include «DHT.h»

#define DHTTYPE DHT11 // DHT 11
DHT dht(7, DHTTYPE);
float dh1;
//////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(9600);
Serial.flush();
Serial.println(«setup»);

pinMode(MAX7456SELECT,OUTPUT);
digitalWrite(MAX7456SELECT,HIGH);

pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK,OUTPUT);
pinMode(VSYNC, INPUT);

SPCR = (1<0)
{
Serial.println(ir_kod,HEX);
change_pos_menu(ir_kod);
ir_kod=0;
}
delay(100);

}
//////////////////////////////////////////////////////////////
byte spi_transfer(volatile byte data)
{
SPDR = data;
while (!(SPSR & (1<> 8;
char_address_lo = linepos;

settings = B00000001;

if (blink) {
settings |= (1 << 4);
}

if (invert){
settings |= (1 << 3);
}

digitalWrite(MAX7456SELECT,LOW);

spi_transfer(DMM_reg);
spi_transfer(settings);

spi_transfer(DMAH_reg);
spi_transfer(char_address_hi);

spi_transfer(DMAL_reg);
spi_transfer(char_address_lo);

while(s[local_count]!=’�’)
{
screen_char = s[local_count];
if(screen_char != 208 && screen_char != 209)
{
spi_transfer(DMDI_reg);
spi_transfer(screen_char);
}
local_count++;
}

spi_transfer(DMDI_reg);
spi_transfer(END_string);

spi_transfer(DMM_reg);
spi_transfer(B00000000);

digitalWrite(MAX7456SELECT,HIGH);
}
// получить код переданный с ИК пульта
void get_ir_kod()
{

decode_results results;
detachInterrupt(0); // отключить прерывание 0
if (irrecv.decode(&results))
{
if (results.value > 0 && results.value < 0xFFFFFFFF)
{
ir_dt = results.value;
ir_time2=millis();
if (ir_time2-ir_time1>400)
{ir_kod = ir_dt;ir_time1=ir_time2;}
else
ir_kod = 0;
}
irrecv.resume();
}
attachInterrupt(0, get_ir_kod, FALLING);
}
//
void show_menu()
{
for(int i=0;i<27;i++)
{
if(i==MENU1.pos1 && MENU1.activ[i]==1)
OSD_write_to_screen(MENU1.name1[i],MENU1.y[i],MENU1.x[i], 1,1);
else if(MENU1.activ[i]==1)
OSD_write_to_screen(MENU1.name1[i],MENU1.y[i],MENU1.x[i], 0,0);
}
}
// проверка на определенные для действий клавиши
void change_pos_menu(long kod)
{
int newpos=0;int dd=1;
if(kod==B_LEFT1 || kod==B_LEFT2) // уменьшить звуко
{ do {
newpos=max(MENU1.pos1-dd,0);dd++;
} while(MENU1.activ[newpos]<1);
MENU1.pos1=newpos;}
else if(kod==B_RIGHT1 || kod==B_RIGHT2) // увеличить звуко
{ do {
newpos=min(MENU1.pos1+dd,26);dd++;
} while(MENU1.activ[newpos]<1);
MENU1.pos1=newpos;}
else if(kod==B_OK1 || kod==B_OK2) // ok
prg_ok();
else if(kod==B_CLEARMENU1 || kod==B_CLEARMENU2) // ok
clear_menu2();
else ;
Serial.println(MENU1.pos1);

}
// очистка всех подменю
void clear_menu2()
{
MENU1.pos1=0;
for(int i=0;i<27;i++)
{MENU1.activ[i]=0;
for(int j=2;j<8;j++)
OSD_write_to_screen(» «, i, j, 0,0);
}
MENU1.activ[0]=1;MENU1.activ[6]=1;
MENU1.activ[8]=1;MENU1.activ[26]=1;
}
// клавиша ok
void prg_ok()
{char c1[4];char dd[7];
switch(MENU1.pos1)
{
case 0 : // меню камера
if(MENU1.activ[2]==1)
{
OSD_write_to_screen(» «,2,2,0,0);
OSD_write_to_screen(» «,2,3,0,0);
OSD_write_to_screen(» «,2,4,0,0);
MENU1.activ[1]=0;MENU1.activ[2]=0;MENU1.activ[3]=0;
MENU1.activ[4]=0;MENU1.activ[5]=0;
}
else
{
MENU1.activ[1]=1;MENU1.activ[2]=1;MENU1.activ[3]=1;
MENU1.activ[4]=1;MENU1.activ[5]=1;
}
break;
case 1 : // камера вниз
MYDATA1.servo_ud=max(MYDATA1.servo_ud-10,-90);
camera_vert.attach(5);
camera_vert.write(MYDATA1.servo_ud+90);
delay(500);
camera_vert.detach();
Serial.print(«down=»);Serial.println(MYDATA1.servo_ud);
break;
case 2 : // камера вверх
MYDATA1.servo_ud=min(MYDATA1.servo_ud+10,90);
camera_vert.attach(5);
camera_vert.write(MYDATA1.servo_ud+90);
delay(500);
camera_vert.detach();
Serial.print(«up=»);Serial.println(MYDATA1.servo_ud);
break;
case 3 : // камера влево
MYDATA1.servo_lr=max(MYDATA1.servo_lr-10,-90);
camera_hor.attach(4);
camera_hor.write(MYDATA1.servo_lr+90);
delay(500);
camera_hor.detach();
Serial.print(«left=»);Serial.println(MYDATA1.servo_lr);
break;
case 4 : // «камера вправо»
MYDATA1.servo_lr=min(MYDATA1.servo_lr+10,90);
camera_hor.attach(4);
camera_hor.write(MYDATA1.servo_lr+90);
delay(500);
Serial.print(«right=»);Serial.println(MYDATA1.servo_lr);
camera_hor.detach();
break;
case 5 : // «камера в 0″
MYDATA1.servo_ud=0;MYDATA1.servo_lr=0;
camera_hor.attach(4);camera_vert.attach(5);
camera_hor.write(90);camera_vert.write(90);
delay(500);
camera_hor.detach();camera_vert.detach();
break;
case 6 : // меню датчики
if(MENU1.activ[7]==1)
{
OSD_write_to_screen(» «,10,2,0,0);
OSD_write_to_screen(» «,10,3,0,0);
OSD_write_to_screen(» «,10,4,0,0);
OSD_write_to_screen(» «,10,5,0,0);
OSD_write_to_screen(» «,10,6,0,0);
MENU1.activ[7]=0;
}
else
{
MENU1.activ[7]=1;
dtostrf(getMeteoData(2),0,2,dd);
OSD_write_to_screen(dd,10,2,0,0);
//sprintf(dd,»%f»,getMeteoData(1));
dtostrf(getMeteoData(1),0,2,dd);
OSD_write_to_screen(dd,10,3,0,0);
Serial.print(«dht=»);
detachInterrupt(0);
delay(100);
irsend.sendRC5(0xff,12);
delay(1000);
attachInterrupt(0, get_ir_kod, FALLING);
dh1=dht.readHumidity();
Serial.println(dh1);
dtostrf(dht.readHumidity(),0,0,dd);
dd[3]=0x25;dd[4]=’ ‘;dd[2]=’ ‘;
OSD_write_to_screen(dd,10,4,0,0);
dps.getPressure(&Pressure085);
dtostrf(Pressure085/133.3,0,2,dd);
OSD_write_to_screen(dd,10,5,0,0);
OSD_write_to_screen(«ДД «,10,6,0,0);
OSD_write_to_screen(«Вкл «,13,6,0,0);
irrecv.enableIRIn();
}
break;
case 7 : // датчик движения
MYDATA1.dd=1-MYDATA1.dd;
if(MYDATA1.dd==0) MENU1.name1[7]=»Выкл»;
else MENU1.name1[7]=»Вкл «;
send_ir_kod(0x01);
send_ir_kod(0x838);
break;
case 8 : // меню приборы
if(MENU1.activ[9]==1)
{
OSD_write_to_screen(» «,19,2,0,0);
OSD_write_to_screen(» «,19,3,0,0);
MENU1.activ[9]=0;MENU1.activ[10]=0;
MENU1.activ[11]=0;MENU1.activ[12]=0;MENU1.activ[13]=0;
MENU1.activ[14]=0;MENU1.activ[15]=0;MENU1.activ[16]=0;
OSD_write_to_screen(» «,19,4,0,0);
OSD_write_to_screen(» «,19,5,0,0);
MENU1.activ[17]=0;MENU1.activ[18]=0;
MENU1.activ[19]=0;MENU1.activ[20]=0;MENU1.activ[21]=0;
MENU1.activ[22]=0;MENU1.activ[23]=0;MENU1.activ[24]=0;
OSD_write_to_screen(» «,19,6,0,0);
MENU1.activ[25]=0;
OSD_write_to_screen(» «,19,7,0,0);
}
else
{
OSD_write_to_screen(«Лампа»,19,2,0,0);
MENU1.activ[9]=1;MENU1.activ[10]=1;
MENU1.activ[11]=1;MENU1.activ[12]=1;MENU1.activ[13]=1;
MENU1.activ[14]=1;MENU1.activ[15]=1;MENU1.activ[16]=1;
OSD_write_to_screen(«Лента»,19,4,0,0);
MENU1.activ[17]=1;MENU1.activ[18]=1;
MENU1.activ[19]=1;MENU1.activ[20]=1;MENU1.activ[21]=1;
MENU1.activ[22]=1;MENU1.activ[23]=1;MENU1.activ[24]=1;
OSD_write_to_screen(«Прож»,19,6,0,0);
MENU1.activ[25]=1;
OSD_write_to_screen(» «,19,7,0,0);
}
break;
// лампа1
case 9 : // черный
MYDATA1.posl1=0;send_ir_kod(0x1);break;
case 10 : // белый
MYDATA1.posl1=1;send_ir_kod(0x2);break;
case 11 : //
MYDATA1.posl1=2;send_ir_kod(0x2);break;
case 12 : //
MYDATA1.posl1=3;send_ir_kod(0x2);break;
case 13 : //
MYDATA1.posl1=4;send_ir_kod(0x2);break;
case 14 : //
MYDATA1.posl1=5;send_ir_kod(0x2);break;
case 15 : //
MYDATA1.posl1=6;send_ir_kod(0x2);break;
case 16 : //
MYDATA1.posl1=7;send_ir_kod(0x2);break;
// лента светодиодная
//0xDF,0x9F,0x5F,0xEF,0xAF,0x6F,0x1F,0xBF
case 17 : // черный
MYDATA1.posl2=0;send_ir_kod(0xBF);break;
case 18 : // белый
MYDATA1.posl2=1;send_ir_kod(0x1F);break;
case 19 : // R
MYDATA1.posl2=2;send_ir_kod(0xDF);break;
case 20 : // RB
MYDATA1.posl2=3;send_ir_kod(0x2);break;
case 21 : // B
MYDATA1.posl2=4;send_ir_kod(0x5F);break;
case 22 : // BG
MYDATA1.posl2=5;send_ir_kod(0x2);break;
case 23 : // G
MYDATA1.posl2=6;send_ir_kod(0x9F);break;
case 24 : // RG
MYDATA1.posl2=7;send_ir_kod(0x2);break;
// прожектор
case 25 :
MYDATA1.posp1=1-MYDATA1.posp1;
if(MYDATA1.posp1==0) MENU1.name1[25]=»Выкл»;
else MENU1.name1[25]=»Вкл «;
break;
default:
break;
}
sprintf(c1,»%d»,abs(MYDATA1.servo_ud));
OSD_write_to_screen(» «,2,2,0,0);OSD_write_to_screen(» «,6,2,0,0);
if(MYDATA1.servo_ud<0 && MENU1.activ[2]==1)
{OSD_write_to_screen(c1,2,2,0,0);}
else if(MYDATA1.servo_ud>0 && MENU1.activ[2]==1)
{OSD_write_to_screen(c1,6,2,0,0);}
else ;
sprintf(c1,»%d»,abs(MYDATA1.servo_lr));
OSD_write_to_screen(» «,2,3,0,0);OSD_write_to_screen(» «,6,3,0,0);
if(MYDATA1.servo_lr<0 && MENU1.activ[2]==1)
{OSD_write_to_screen(c1,2,3,0,0);}
else if(MYDATA1.servo_lr>0 && MENU1.activ[2]==1)
{OSD_write_to_screen(c1,6,3,0,0);}
else ;
}
// Отправка ИК-кода
void send_ir_kod(long sendkod)
{
detachInterrupt(0);
delay(100);
irsend.sendRC5(sendkod,12);
delay(1000);
attachInterrupt(0, get_ir_kod, FALLING);
irrecv.enableIRIn();
Serial.print(«sendkod=»);Serial.println(sendkod,HEX);
}
// получение температуры датчика
float getMeteoData(int nn)
{
byte i;
byte present = 0;
byte data[12];
byte addr[8];
int Temp;
float fTemp;
char m0[5];
String str1=»»;

ds.reset();
ds.select(my_addr[nn-1]);
ds.write(0x44,1); // start conversion, with parasite power on at the end
delay(1000); // maybe 750ms is enough, maybe not

present = ds.reset();
ds.select(my_addr[nn-1]);
ds.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
Temp=(data[1]<<8)+data[0];//take the two bytes from the response relating to temperature

Temp=Temp;//divide by 16 to get pure celcius readout

fTemp=Temp/16+(float(Temp%16))/16;
MYDATA1.t2=fTemp;
return fTemp;
}

Проблемы, возникшие при написании скетча:
DHT отказывалась работать с IRremote (функция enableIRIn() занимает таймер, используемый библиотекой DHT), проблема решилась отключением  на время получения с датчика DHT11  enableIRIn(), и включением после получения данных.

Управление с помощью пульта телевизора. Навигация влево-вправо (громкость), выбор (ОК), свернуть все подменю -(EYE/*)

Необходимо рассказать о прошивке символов микросхемы MAX7456. В памяти микросхемы уже имеются прошитые символы

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

Скачиваем библиотеку MAX7456. В библиотеке есть пример перепрошивки символов — UploadFont и несколько файлов .mcm с шрифтами для загрузки.

Запускаем программу Hiper Terminal, выбираем порт на котором сидит Ардуино, скорость порта 9600, управление потоком xon-xoff. В меню программы гипертерминал выбираем пункт send to file и указываем путь к файлу со шрифтом. Файл с русским шрифтом arduino1.mcm я взял на форуме https://www.compcar.ru/forum/showthread.php?t=6064. Символы такие (arduino1.png)

+ =

Я захотел добавить ещё символов. Берется файл arduino1.png, открываетcя в редакторе Paint и редактируется (я копипастил из файла custom.png и вставлял, где необходимо). Получившийся png файл преобразовывается в файл .mcm на ресурсе https://www.mylifesucks.de/tools/max7456/ (там же доступно и обратное преобразование mcm->png).

Этот набор символов я залил в max7456.

Окончательный монтаж уже после прихода всех датчиков

https://www.youtube.com/v/bhVntonRwXg

Скачать список элементов (PDF)

Прикрепленные файлы:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *