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

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

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

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

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

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

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

Схема устройства на max7456

И скетч для 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,0x92}}; #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 я взял на форуме http://www.compcar.ru/forum/showthread.php?t=6064. Символы такие (arduino1.png)

+ =

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

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

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

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
Плата Arduino Arduino Uno 1
Инфракрасный приемник TSOP-3128 1
Датчик температуры DS18B20 2
Датчик температуры DHT11 1
Датчик давления BMP085 1
OSD-контроллер MAX7456 1
VT1 Транзистор 1
D-ir1 Светодиод 1
Z1 Кварцевый резонатор 1
R1, R2 Резистор 2
R3, R4 Резистор 2
R5 Резистор 1
R6 Резистор 1
R7 Резистор 1
C5, C8 Конденсатор 2
C6, C7 Электролитический конденсатор 2
Servo Сервопривод 2