KursyPoradnikiInspirujące DIYForum

Pochwal się swoim elektronicznym projektem DIY i odbierz 50 zł rabatu do sklepu Botland. Sprawdź szczegóły »

Czy do Arduino można podłączyć kamerę? Test ArduCAM!

Czy do Arduino można podłączyć kamerę? Test ArduCAM!

Dużo osób wykorzystujących w swoich projektach Arduino chciałoby podłączyć do niego kamerę. Temat wydaje się trudny, ponieważ platforma ta zdecydowanie nie jest do tego przystosowana.

Na szczęście producenci akcesoriów stanęli na wysokości zadania, w sprzedaży znajdziemy kilka kamer, które można podłączyć do Arduino.

W tym artykule przyjrzę się produktom z serii ArduCAM. Przypominam wszystkim, że recenzja została zrealizowana dzięki wsparciu czytelników Forbota na Patronite. To właśnie Patroni wskazali produkty, które opisuję i sfinalizowali ich zakup.

Czym jest ArduCAM?

Większość osób, która kojarzy tę nazwę, ma przed oczami moduł kamery, który podłącza się do Arduino. Jednak prawdziwa siła tkwi w shieldzie, który pośredniczy między Arduino, a kamerą.

ArduCAM jest więc uniwersalnym adapterem obsługującym różne kamery. Jego zadaniem jest odizolowanie skomplikowanej części elektronicznej i programistycznej związanej z obsługą modułu kamery, tak, aby użytkownik końcowy miał dostęp do przyjaznego i znacznie prostszego interfejsu.

W sprzedaży dostępne jest kilka wersji i konfiguracji ArduCAM. Pisząc recenzję musiałem skupić się tylko na wycinku tej oferty. Osoby zainteresowane szczegółami odsyłam na stronę producenta.

Testowany sprzęt: shield ArduCAM

Podczas tej recenzji wykorzystywałem najnowszą wersję modułu, która jest aktualnie dostępna, czyli ArduCAM Rev.C+.  Płytka widoczna jest na poniższym zdjęciu.

ArduCAM Rev.C+

ArduCAM Rev.C+

W sprzedaży dostępne są również wersje, które na tylnej stronie mają zamontowany kolorowy wyświetlacz graficzny. Nie zdecydowałem się na zakup takiej wersji, ponieważ taki podgląd nie był mi potrzebny. Jeśli jednak ktoś uważa to za konieczny dodatek, to może zaopatrzyć się w tę drugą, odpowiednio droższą wersję.

W moim egzemplarzu zamiast LCD znalazłem "gołe" pola lutownicze:

Shield ArduCAM - strona "z wyświetlaczem LCD".

Shield ArduCAM - strona "z wyświetlaczem LCD".

Płytka przystosowana jest do współpracy z Arduino - w związku z tym znajdziemy na niej złącza o standardowym rozmieszczeniu pinów. Cała komunikacja przebiega jednak wyłącznie z użyciem interfejsu SPI (możliwe również użycie I2C).

Shield ArduCAM - widok od góry.

Shield ArduCAM - widok od góry.

Z najważniejszych rzeczy należy wyróżnić:

  • złącze do modułu kamery (na samej górze),
  • przycisk pozwalający na wykonywanie zdjęć w trybie ręcznym (lewy górny róg),
  • dodatkowe wyprowadzenie wszystkich sygnałów Arduino (lewa krawędź),
  • gniazdo karty microSD (prawa krawędź).

Dodatkowo na płytce znajdziemy 2 diody. Pierwsza została umieszczona na warstwie elementów. Sygnalizuje ona obecności napięcia 3,3V. Kolejna dioda umieszczona po drugiej stronie (pod kartą pamięci) informuje nas o trwającym zapisie na microSD.

Na plus na pewno należy zaliczyć sporą liczbę dodatkowych punktów na PCB, w które można się wpiąć, aby dostać sygnały Arduino oraz zasilanie.

Testowany sprzęt: moduły kamer - 2MPx oraz 5MPx

Do powyższego shielda dokupiłem dwa moduły kamer. Jak widać, różnią się one znacznie swoimi gabarytami. Jak pewnie większość z Was przeczuwa jakość zdjęć będzie również inna.

Porównanie gabarytów dwóch kamer.

Porównanie gabarytów dwóch kamer.

Kamera ArduCam OV2640 2MPx

Pierwszy z nich, to moduł ArduCam OV2640 2MPx. Za jego pomocą możliwe jest wykonywanie zdjęć o maksymalnej rozdzielczości 1600x1200 px (do 15 FPS). Producent chwali się również, że możliwe jest osiągnięcie 60fps, ale... przy rozdzielczości CIF (352x288px).

Całość jest bardzo mała i wygląda na delikatną:

Moduł kamery jest przyklejony do płytki, a taśma przylutowana jest bezpośrednio do PCB. Niestety producent nie zastosował złącza. Soczewka "wygląda" jakby pozwalała na regulację ostrości, jednak jest zablokowana w jednej pozycji - prawdopodobnie klejem.

Kamera OV5642 5MPx

Druga kamera, to moduł OV5642 5MPx z odkręcanym obiektywem HQ CS mount. Już na pierwszy rzut oka egzemplarz ten daje wrażenie znacznie solidniejszego od poprzedniego. Możliwości tego modułu są również znacznie większe. Deklarowana wydajność przetwornika:

  • 2592 x 1944 - 15 fps,
  • 1920 x 1080 - 30 fps,
  • 1280 x 720 - 60 fps,
  • 640 x 480 - 60 fps,
  • 320 x 240 - 120 fps.
Moduł kamery OV5642 5MPx.

Moduł kamery OV5642 5MPx.

Spostrzegawczy czytelnicy zauważą w tym momencie zapewne, że rozdzielczość nadrukowana na obiektywnie, to 3 MP. Jednak spokojnie, przetwornik ma 5MPx. Po prostu wykorzystano tutaj standardowy obiektyw od kamer CCTV 3MPx.


Skoro mowa o obiektywie, to warto zwrócić uwagę na dwie śrubki. Pierwsza (srebrna) blokuje możliwość ręcznej zmiany ostrości. Po jej poluzowaniu możemy delikatnie kręcić obiektywem:

Po poluzowaniu drugiej śrubki (czarnej) możliwe jest wykręcenie obiektywu z podstawy. Pozwala to na dostanie się bezpośrednio do przetwornika kamery:

Odkręcony obiekty OV5642 5MPx.

Odkręcony obiekty OV5642 5MPx.

Zdjęć takich elementów nigdy za wiele, dlatego jeszcze jedno, ostatnie zbliżenie!

Przetwornik kamery OV5642 5MPx.

Przetwornik kamery OV5642 5MPx.

Oczywiście "po drugiej stronie" kamery znajdziemy niezbędną elektronikę oraz złącze z pinami ułożonymi dokładnie tak samo, jak przy wcześniejszym module:

Elektronika oraz złącze modułu OV5642 5MPx.

Elektronika oraz złącze modułu OV5642 5MPx.

Dostępność innych modułów kamer

Oczywiście z opisywanym shieldem ArduCAM połączyć można znacznie więcej kamer. Wybierając moduł do swojego zastosowania warto sprawdzić dokumentację każdego z nich, aby dobrać taki, który najlepiej sprawdzi się w danym projekcie - różnice mogą być bardzo duże. 

W chwili obecnej do shielda można podłączyć następujące kamery:

  • OV7670
  • MT9D111
  • OV7675
  • OV2640
  • OV3640
  • OV5642
  • OV7660
  • OV7725
  • MT9M112
  • MT9V111
  • OV5640
  • MT9M001
  • MT9T112
  • MT9D112

"Słowo" przed programowaniem ArduCAM

Poniżej opisałem moje przejścia z powyższym sprzętem. Zaznaczam jednak, że nie jestem znawcą tematów związanych z kamerami, transmisją obrazu, czy jego kompresją. W związku z tym zawierzyłem producentom, którzy pracują nad tym produktem od kilku lat.

Uznałem, że jeśli dostarczają oni kod zajmujący się przykładowo zapisem obrazu/filmu na kartę, to jest on na tyle dobry, że nie powinienem się zagłębiać w jego edycję i optymalizację.

Biblioteka ArduCAM

Jak to (na szczęście) bywa w przypadku akcesoriów do Arduino producent udostępnia bibliotekę, która sprawia, że korzystanie z modułów jest przyjemniejsze. Aktualne biblioteki udostępniane są na GitHubie producenta. Pobrane archiwum rozpakowujemy, a zawartość umieszczamy w katalogu z bibliotekami. U mnie był to folder:

C:\Users\Damian\Documents\Arduino\libraries

Po poprawnym przekopiowaniu biblioteki, w powyższym folderze powinniśmy mieć nowy katalog, nazwany ArduCam, którego wnętrze będzie zgodne z poniższym zrzutem ekranu.

Instalacja biblioteki od ArduCAM.

Instalacja biblioteki od ArduCAM.

Po restarcie Arduino IDE w menu z przykładami znajdziemy ogrom nowych programów. Jest ich naprawdę dużo, co na początku może być mylące.

Niektóre z dostępnych przykładów.

Niektóre z dostępnych przykładów.

Pierwsze uruchomienie - kamera "UART"

Gdy zaczynałem testy tej platformy wgrałem najpierw prosty kod, który zapisywał zdjęcia na karcie. Nie było to zbyt ciekawe i widowiskowe - zwykły aparat cyfrowy. Dlatego Wam proponuję zacząć od ciekawego dema, które łatwo przegapić.

Zanim do tego przejdziemy warto jednak skompletować sprzęt. W tym celu w shield wpinamy moduł kamery. Na koniec dodajemy Arduino.

Trzeba przyznać, że takie ułożenie Arduino jest dość niecodzienne, ale w tym rozwiązaniu akurat się sprawdza. Wszystko zostało dość dobrze wymierzone. Zarówno gniazdo zasilania, jak i USB dosłownie na milimetry omija elementy na shieldzie.

Mając gotową część sprzętową możemy zabrać się za testy. W tym celu uruchamiamy demo, które w nazwie ma symbol naszej kamery oraz "Video Streaming". Czyli w tym przypadku będzie to:

ArduCAM_Mini_OV2640_Video_Streaming

Dla osób, które nie instalują biblioteki wklejam poniżej kod przykładu, aby mogli go przejrzeć:

// ArduCAM Mini demo (C)2015 Lee
// web: http://www.ArduCAM.com
// This program is a demo of how to use most of the functions
// of the library with ArduCAM Mini 2MP camera, and can run on any Arduino platform.
//
// This demo was made for ArduCAM Mini OV2640 2MP Camera.
// It needs to be used in combination with PC software.
// It can take photo continuously as video streaming.
//
// The demo sketch will do the following tasks:
// 1. Set the camera to JEPG output mode.
// 2. Read data from Serial port and deal with it
// 3. If receive 0x00-0x08,the resolution will be changed.
// 4. If receive 0x10,camera will capture a JPEG photo and buffer the image to FIFO.Then write datas to Serial port.
// 5. If receive 0x20,camera will capture JPEG photo and write datas continuously.Stop when receive 0x21.
// 6. If receive 0x30,camera will capture a BMP  photo and buffer the image to FIFO.Then write datas to Serial port.
// 7. If receive 0x11 ,set camera to JPEG output mode.
// 8. If receive 0x31 ,set camera to BMP  output mode.
// This program requires the ArduCAM V3.4.1 (or later) library and ArduCAM Mini 2MP camera
// and use Arduino IDE 1.5.8 compiler or above

#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include "memorysaver.h"

#define BMPIMAGEOFFSET 66


#if defined(ESP8266)
// set GPIO15 as the slave select for the digital pot:
const int CS = 16;
#else
// set pin 10 as the slave select for the digital pot:
const int CS = 10;
#endif
int mode = 0;

const char bmp_header[BMPIMAGEOFFSET] PROGMEM =
{
  0x42, 0x4D, 0x36, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x28, 0x00,
  0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x03, 0x00,
  0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x1F, 0x00,
  0x00, 0x00
};

ArduCAM myCAM(OV2640, CS);
void read_fifo_burst(ArduCAM myCAM);

void setup() {
  // put your setup code here, to run once:
  uint8_t vid, pid;
  uint8_t temp;
#if defined(__SAM3X8E__)
  Wire1.begin();
#else
  Wire.begin();
#endif
  Serial.begin(921600);
  Serial.println("ArduCAM Start!");

  // set the CS as an output:
  pinMode(CS, OUTPUT);

  // initialize SPI:
  SPI.begin();

  //Check if the ArduCAM SPI bus is OK
  myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM.read_reg(ARDUCHIP_TEST1);
  //Serial.println(temp);
  if (temp != 0x55)
  {
    Serial.println("SPI1 interface Error!");
    //while(1);
  }

  //Check if the camera module type is OV2640
  myCAM.wrSensorReg8_8(0xff, 0x01);  
  myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
  myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
  if ((vid != 0x26) || (pid != 0x42))
    Serial.println("Can't find OV2640 module!");
  else
    Serial.println("OV2640 detected.");

  //Change to JPEG capture mode and initialize the OV2640 module
  myCAM.set_format(JPEG);
  myCAM.InitCAM();
  myCAM.OV2640_set_JPEG_size(OV2640_320x240);
  myCAM.clear_fifo_flag();
  myCAM.write_reg(ARDUCHIP_FRAMES, 0x00);
}

void loop() {
  // put your main code here, to run repeatedly:
  uint8_t temp, temp_last;
  uint8_t start_capture = 0;
  bool is_header = false;
  if (Serial.available())
  {
    temp = Serial.read();
    switch (temp)
    {
      case 0:
        myCAM.OV2640_set_JPEG_size(OV2640_160x120);
        break;
      case 1:
        myCAM.OV2640_set_JPEG_size(OV2640_176x144);
        break;
      case 2:
        myCAM.OV2640_set_JPEG_size(OV2640_320x240);
        break;
      case 3:
        myCAM.OV2640_set_JPEG_size(OV2640_352x288);
        break;
      case 4:
        myCAM.OV2640_set_JPEG_size(OV2640_640x480);
        break;
      case 5:
        myCAM.OV2640_set_JPEG_size(OV2640_800x600);
        break;
      case 6:
        myCAM.OV2640_set_JPEG_size(OV2640_1024x768);
        break;
      case 7:
        myCAM.OV2640_set_JPEG_size(OV2640_1280x1024);
        break;
      case 8:
        myCAM.OV2640_set_JPEG_size(OV2640_1600x1200);
        break;

      case 0x10:
        mode = 1;
        start_capture = 1;
        Serial.println("CAM start single shoot.");
        break;
      case 0x11:
        myCAM.set_format(JPEG);
        myCAM.InitCAM();
        break;
      case 0x20:
        mode = 2;
        start_capture = 2;
        Serial.println("CAM start contrues shoots.");
        break;
      case 0x30:
        mode = 3;
        start_capture = 3;
        Serial.println("CAM start single shoot.");
        break;
      case 0x31:
        myCAM.set_format(BMP);
        myCAM.InitCAM();
        break;
      default:
        break;
    }
  }
  if (mode == 1)
  {
    if (start_capture == 1)
    {
      myCAM.flush_fifo();
      myCAM.clear_fifo_flag();
      myCAM.start_capture();
      start_capture = 0;
    }
    if (myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK))
    {
      Serial.println("CAM Capture Done!");
      read_fifo_burst(myCAM);
      //Clear the capture done flag
      myCAM.clear_fifo_flag();
    }
  }
  else if (mode == 2)
  {
    while (1)
    {
      temp = Serial.read();
      if (temp == 0x21)
      {
        start_capture = 0;
        mode = 0;
        Serial.println("CAM stop continuous shoots!");
        break;
      }
      if (start_capture == 2)
      {
        myCAM.flush_fifo();
        myCAM.clear_fifo_flag();
        //Start capture
        myCAM.start_capture();
        start_capture = 0;
      }
      if (myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK))
      {
        uint32_t length = myCAM.read_fifo_length();
        if ((length >= 393216) | (length == 0))
        {
          myCAM.clear_fifo_flag();
          start_capture = 2;
          continue;
        }
        myCAM.CS_LOW();
        myCAM.set_fifo_burst();//Set fifo burst mode
        //SPI.transfer(0x00);
        length--;
        while ( length-- )
        {
          temp_last = temp;
          temp =  SPI.transfer(0x00);
          Serial.write(temp);
          if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
            break;
          delayMicroseconds(12);
        }
        myCAM.CS_HIGH();
        myCAM.clear_fifo_flag();
        start_capture = 2;
      }
    }
  }
  else if (mode == 3)
  {
    if (start_capture == 3)
    {
      //Flush the FIFO
      myCAM.flush_fifo();
      myCAM.clear_fifo_flag();
      //Start capture
      myCAM.start_capture();
      start_capture = 0;
    }
    if (myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK))
    {
      Serial.println("CAM Capture Done!");

      uint8_t temp, temp_last;
      uint32_t length = 0;
      length = myCAM.read_fifo_length();
      if (length >= 393216 ) // 384kb
      {
        Serial.println("Over size.");
        myCAM.clear_fifo_flag();
        return;
      }

      if (length == 0 ) //0 kb
      {
        Serial.println("Size is 0.");
        myCAM.clear_fifo_flag();
        return;
      }
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();//Set fifo burst mode

      Serial.write(0xFF);
      Serial.write(0xAA);
      for (temp = 0; temp < BMPIMAGEOFFSET; temp++)
      {
        Serial.write(pgm_read_byte(&bmp_header[temp]));
      }
      SPI.transfer(0x00);

      char VH, VL;
      int i = 0, j = 0;
      for (i = 0; i < 240; i++)
      {
        for (j = 0; j < 320; j++)
        {
          VH = SPI.transfer(0x00);;
          VL = SPI.transfer(0x00);;
          Serial.write(VL);
          delayMicroseconds(12);
          Serial.write(VH);
          delayMicroseconds(12);
        }
      }
      Serial.write(0xBB);
      Serial.write(0xCC);

      myCAM.CS_HIGH();
      //Clear the capture done flag
      myCAM.clear_fifo_flag();
    }
  }
}

void read_fifo_burst(ArduCAM myCAM)
{
  uint8_t temp, temp_last;
  uint32_t length = 0;
  length = myCAM.read_fifo_length();
  if (length >= 393216 ) // 384kb
  {
    Serial.println("Over size.");
    return;
  }
  if (length == 0 ) //0 kb
  {
    Serial.println("Size is 0.");
    return;
  }
  myCAM.CS_LOW();
  myCAM.set_fifo_burst();//Set fifo burst mode
  //SPI.transfer(0x00);
  length--;
  while ( length-- )
  {
    temp_last = temp;
    temp =  SPI.transfer(0x00);//read a byte from spi
    Serial.write(temp);
    if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
      break;
    delayMicroseconds(12);
  }
  myCAM.CS_HIGH();
}

Po wgraniu programu pozornie nic się nie dzieje. Jednak wystarczy teraz uruchomić aplikację na komputerze, która znajduje się w następującym katalogu:

[...]Documents\Arduino\libraries\ArduCAM\examples\host_app\ArduCAM_Host_V2.0_Windows

Po jej uruchomieniu ujrzymy zestaw poleceń pozwalających na sterowanie kamerą z poziomu PC.

Sterowanie kamerą z poziomu PC.

Sterowanie kamerą z poziomu PC.

Korzystając z tej aplikacji możemy połączyć się z modułem przez sieć (jeśli mamy taką wersję) lub przez port COM (tak jak w opisywanym rozwiązaniu). Warto zwrócić uwagę na prędkość transmisji, która wynosi aż 921600.

Wybieramy odpowiedni port COM oraz moduł kamery (z pola pix), następnie łączymy się z płytką. Od tej pory mamy możliwość robienia zdjęć kamerą i przesyłania ich przez UART do komputera. Aplikacja pozwala również na zmianę rozdzielczości z jaką pracuje kamera.

Kilka przykładowych zdjęć wraz z podaną rozdzielczością (bez żadnej obróbki):

W tym momencie wypadałoby ocenić jakość kamery - w końcu mamy pierwsze zdjęcia. Nie ma co się oszukiwać. Zdjęcia są słabe. Można zarzucić, że pochmurny dzień, to nie są dobre warunki do testowania kamer - jednak recenzja w idealnym środowisku byłaby raczej mało rzetelna.


Oczywiście ściąganie zdjęć przez UART ma też swoje uroki. Po pierwsze trwa to długo. Jednak, to nie jest moim zdaniem ważne. Interfejs ten nie jest przystosowany do takich rozwiązań, więc to nic dziwnego. Wręcz cieszyłbym się z takiej możliwości - cud, że to działa. Przykładowe zgranie największego z powyższych zdjęć trwało tyle, co na poniższej animacji:

Zgrywanie przykładowego zdjęcia przez UART.

Zgrywanie przykładowego zdjęcia przez UART.

Po drugie UART z taką prędkością to igranie z ogniem i proszenie się o zakłócenia. W związku z czym czasami zamiast zdjęcia możemy otrzymać kawałek sztuki nowoczesnej, jak tutaj:

Wariacja na temat widoku za oknem!

Wariacja na temat widoku za oknem!

Nadawanie wideo przez UART "na żywo"

Warto zwrócić również uwagę na tryb wykonywania zdjęć ciągłych. Jeśli obniżymy znacznie rozdzielczość, to właściwie będziemy mogli transmitować obraz "na żywo" przez UART. Przykład jest widoczny poniżej - proszę nie powiększać, bo to maksymalna rozdzielczość, dla której obraz był płynny... Jak widać również tutaj wkradło się kilka zakłóceń.

ArduCAM LIVE

ArduCAM LIVE

Pierwsze testy zakończyły się sukcesem - shield i kamera działają. Jakość zdjęć jest jednak słaba (przynajmniej jak na te czasy) kilka lat temu byłoby to hitem! Dlatego dość szybko porzuciłem ten mały moduł i zabrałem się za drugą kamerę z obiektywem.

Eksperymenty z modułem 5MPx

Po pierwszych próbach z małą kamerą przyszła pora na cięższego zawodnika. Drugi moduł jest zdecydowanie większy. Wręcz wygląda dziwnie w połączeniu z resztą.

Podłączenie większej kamery do Arduino.

Podłączenie większej kamery do Arduino - OV5642 5MPx.

Ręczna edycja biblioteki ArduCAM

Przed przejściem do testów konieczna jest mała zmiana w bibliotece. Moim zdaniem zostało to rozwiązane źle. Biorąc pod uwagę, że producent dostarcza tak wiele rozbudowanych kodów dedykowanych konkretnym kamerom całość powinna być "mądrzejsza".

Jeśli uruchomimy program testowy dla tej kamery, czyli np.:

ArduCAM_Mini_OV5642_Video_Streaming

To... nie damy rady go skompilować i wgrać. Konieczna jest ręczna zmiana w bibliotece. Dla osób, które nie mają styczności z Arduino przypomnę, że raczej nie spotyka się tam takich rozwiązań. Nawet sam edytor w Arduino IDE nie pozwala uruchomić plików z rozszerzeniem ".h".

Aby program zaczął działać, koniecznie trzeba otworzyć poniższy plik w notatniku i odkomentować linię z kamerą, którą chcemy użyć.

[..] Documents\Arduino\libraries\ArduCAM\memorysaver.h

Czyli w tym przypadku będzie to:

//Uncomment the following definition when you use them
//#define OV7660_CAM
//#define OV7725_CAM
//#define OV7670_CAM
//#define OV7675_CAM
//#define OV2640_CAM
//#define OV3640_CAM
#define OV5642_CAM
//#define OV5642_CAM_BIT_ROTATION_FIXED
//#define MT9D111_CAM
//#define MT9M112_CAM
//#define MT9V111_CAM
//#define OV5640_CAM
//#define MT9M001_CAM
//#define MT9T112_CAM
//#define MT9D112_CAM

Gdy edytujemy plik, zapiszemy go i ponownie skompilujemy program, to cały proces przebiegnie już bez problemów. Wróćmy więc do testów!


W przypadku tego modułu kamery podgląd przez opisywaną wcześniej aplikację na PC okazuje się niezbędny. Bez niego ustawienie ostrości trwałoby wieczność, a tak możemy ten proces wykonać szybko i bezboleśnie (z podglądem "na żywo"). Najpierw zdjęcia zbliżone do wcześniejszych:

Regulacja ostrości pozwala jednak również na zrobienie zdjęć, które były poza zasięgiem małego modułu. Dla przykładu zbliżenie na monetę i ołówek:

Zdjęcie wykonane modułem OV5642 5MPx - bez żadnej obróbki.

Zdjęcie wykonane modułem OV5642 5MPx - bez żadnej obróbki.

Oczywiście jakość nie jest idealna, jednak daje już większe pole do manewru. Na pewno ten moduł pozwoli osiągnąć ciekawsze efekty od poprzedniego.

Zastosowanie: zdjęcia poklatkowe (timelapse)

Gdy podstawy obsługi kamer były za mną postanowiłem sprawdzić ArduCAMa w praktyce. Dobrym zastosowaniem może być stworzenie urządzenia do robienia zdjęć z określonym interwałem. Dzięki temu stosunkowo łatwo można później stworzyć filmy poklatkowe.

Po chwili pisania programu coś mnie tknęło i zajrzałem jeszcze raz do kodów przykładowych. Jak się okazało, mój pomysł nie był oryginalny, ponieważ znalazłem tam program realizujący dokładnie takie zadanie. Mowa o przykładzie:

ArduCAM_Mini_5MP_TimeElapse2SD

Dla osób, które nie instalują biblioteki wklejam poniżej kod przykładu, aby mogli go przejrzeć:

// ArduCAM demo (C)2015 Lee
// web: http://www.ArduCAM.com
// This program is a demo of how to use most of the functions
// of the library with a supported camera modules, and can run on any Arduino platform.
//
// This demo was made for Omnivision OV5642 5MP sensor.
// It will run the ArduCAM Mini 5MP as a real 5MP digital camera, provide JPEG capture.
// The demo sketch will do the following tasks:
// 1. Set the sensor to JPEG mode.
// 2. Capture and buffer the image to FIFO every 5 seconds
// 3. Store the image to Micro SD/TF card with JPEG format in sequential.
// 4. Resolution can be changed by myCAM.OV5642_set_JPEG_size() function.
// This program requires the ArduCAM V3.4.0 (or later) library and ArduCAM Mini 5MP shield
// and use Arduino IDE 1.5.2 compiler or above


#include <UTFT_SPI.h>
#include <SD.h>
#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include "memorysaver.h"

#if defined(__arm__)
#include <itoa.h>
#endif

#define SD_CS 9
// set pin 10 as the slave select for SPI:
const int SPI_CS = 10;

ArduCAM myCAM(OV5642, SPI_CS);
UTFT myGLCD(SPI_CS);
boolean isShowFlag = true;

void setup()
{
  uint8_t vid, pid;
  uint8_t temp;
#if defined(__SAM3X8E__)
  Wire1.begin();
#else
  Wire.begin();
#endif
  Serial.begin(115200);
  Serial.println("ArduCAM Start!");
  // set the SPI_CS as an output:
  pinMode(SPI_CS, OUTPUT);

  // initialize SPI:
  SPI.begin();
  //Check if the ArduCAM SPI bus is OK
  myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM.read_reg(ARDUCHIP_TEST1);
  if (temp != 0x55)
  {
    Serial.println("SPI interface Error!");
    while (1);
  }


  //Check if the camera module type is OV5642
  myCAM.rdSensorReg16_8(OV5642_CHIPID_HIGH, &vid);
  myCAM.rdSensorReg16_8(OV5642_CHIPID_LOW, &pid);
  if ((vid != 0x56) || (pid != 0x42))
    Serial.println("Can't find OV5642 module!");
  else
    Serial.println("OV5642 detected");


  //Change to JPEG capture mode and initialize the OV5642 module
  myCAM.set_format(JPEG);

  myCAM.InitCAM();
  myCAM.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);		//VSYNC is active HIGH
  //myCAM.OV5642_set_JPEG_size(OV5642_320x240);
  //       myCAM.OV5642_set_JPEG_size(OV5642_640x480);
  //       myCAM.OV5642_set_JPEG_size(OV5642_1280x720);
  //       myCAM.OV5642_set_JPEG_size(OV5642_1920x1080);
  //       myCAM.OV5642_set_JPEG_size(OV5642_2048x1563);
         myCAM.OV5642_set_JPEG_size(OV5642_2592x1944);
  //Initialize SD Card
  if (!SD.begin(SD_CS))
  {
    //while (1);		//If failed, stop here
    Serial.println("SD Card Error");
  }
  else
    Serial.println("SD Card detected!");
}

void loop()
{
  char str[8];
  File outFile;
  byte buf[256];
  static int i = 0;
  static int k = 0;
  static int n = 0;
  uint8_t temp, temp_last;
  uint8_t start_capture = 0;
  int total_time = 0;

  start_capture = 1;
  delay(6000);

  if (start_capture)
  {
    //Flush the FIFO
    myCAM.flush_fifo();
    //Clear the capture done flag
    myCAM.clear_fifo_flag();
    //Start capture
    myCAM.start_capture();
    Serial.println("Start Capture");
  }

  while (!myCAM.get_bit(ARDUCHIP_TRIG , CAP_DONE_MASK));

  Serial.println("Capture Done!");
  //Construct a file name
  k = 10000 + 1;
  itoa(k, str, 10);
  strcat(str, ".jpg");
  //Open the new file
  outFile = SD.open(str, O_WRITE | O_CREAT | O_TRUNC);
  if (! outFile)
  {
    Serial.println("open file failed");
    return;
  }
  total_time = millis();

  i = 0;
  myCAM.CS_LOW();
  myCAM.set_fifo_burst();
  temp = SPI.transfer(0x00);

  //Read JPEG data from FIFO
  while ( (temp != 0xD9) | (temp_last != 0xFF) )
  {
    temp_last = temp;
    temp = SPI.transfer(0x00);;
    //Write image data to buffer if not full
    if (i < 256)
      buf[i++] = temp;
    else
    {
      //Write 256 bytes image data to file
      myCAM.CS_HIGH();
      outFile.write(buf, 256);
      i = 0;
      buf[i++] = temp;
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();
    }
  }
  //Write the remain bytes in the buffer
  if (i > 0)
  {
    myCAM.CS_HIGH();
    outFile.write(buf, i);
  }
  //Close the file
  outFile.close();
  total_time = millis() - total_time;
  Serial.print("Total time used:");
  Serial.print(total_time, DEC);
  Serial.println(" millisecond");
  //Clear the capture done flag
  myCAM.clear_fifo_flag();
  //Clear the start capture flag
  start_capture = 0;

}

W praktyce kod odpowiedzialny jest za zrobienie zdjęcia (co określony czas) i zapisanie go na SD. Biorąc pod uwagę, że zapis obrazu w najlepszej jakości trwał około 3 sekundy postanowiłem robić nowe zdjęcie, co 6 sekund.

Po uruchomieniu programu przez UART otrzymujemy informacje o aktualnym postępie:

ArduCAM Start!
Card detected!
ArduCAM Start!
OV5642 detected
SD Card detected!
Start Capture
Capture Done!
Total time used:3299 millisecond
Start Capture
Capture Done!
Total time used:3430 millisecond
Start Capture
Capture Done!
Total time used:3298 millisecond
Start Capture
Capture Done!
Total time used:3303 millisecond
Start Capture
Capture Done!
Total time used:3304 millisecond
[...]

Wiedząc, że program działa odłączyłem komputer, a całość podłączyłem do zasilacza. Taki zestaw pozostawiłem na około 14 godzin.

Problemy z zapisem na kartę microSD

W tym momencie pojawił się problem. Spora część zdjęć miała wady. Na 8300 zdjęć ponad 1800 zostało źle zapisane - tak, że nawet nie dało się ich otworzyć.

Inna część miała spore artefakty. Niestety do tej pory nie znalazłem przyczyny. Nie widać żadnej zależności dlaczego niektóre zdjęcia zapisały się błędnie. Zdarzało się, że 1000 plików z rzędu było poprawnych, a były sytuacje, gdy co drugie było uszkodzone. Oczywiście formatowanie karty lub używanie innej nie przynosiło widocznej poprawy.

Przykłady uszkodzonych zdjęć, które dało się otworzyć:

Zdjęcia do filmu poklatkowego zrobione dzięki Arduino

Z karty usunąłem ręcznie 1800 błędnych plików, te które dało się otworzyć (z artefaktami) pozostawiłem. Całość złożyłem następnie na komputerze w film poklatkowy (timelapse). Widać na nim popołudnie, noc oraz ranek kolejnego dnia. 

Moim zdaniem w takim zastosowaniu ArduCAM sprawdził się całkiem dobrze. Przyznam, że jakość zdjęć wykonanych w nocy zaskoczyła mnie zdecydowanie na plus.

ArduCAM w roli fotopułapki

Drugim pomysłem, który miałem na wykorzystanie tych kamer to budowa fotopułapki. Teoretycznie łącząc kamerę z alarmem powinniśmy złapać intruza na zdjęciu. Podczas testu dołączyłem do systemu czujnik PIR - zgodnie z instrukcjami z kursu Arduino.

W tym zastosowaniu było kluczowe, jak kamera poradzi sobie z robieniem zdjęć obiektów, które się poruszają. Cóż, tym razem nie miałem wyjścia i musiałem stanąć przed kamerą. Kilka przykładów tego, co znalazłem na karcie SD widoczne jest poniżej (zdjęcia robione z różną rozdzielczością):

Jak widać również tutaj zdarzyły się artefakty. Pierwsze 3 zdjęcia wykonane zostały, gdy szedłem w stronę kamery. Natomiast ostatnie powstało, gdy przechodziłem na skos. Jakoś zdjęć chyba należy opisać jak "typowe zdjęcie z monitoringu, na którym ledwo coś widać". Nie ma tragedii, ale momentami może być ciężko z rozpoznaniem intruza.

ArduCAM - własne programy

W tej recenzji nie ma zbyt wiele informacji na temat samych programów. Stało się tak dlatego, że producent dostarcza dość rozbudowaną bibliotekę przykładów. Jak łatwo zauważyć przeglądając kody programów są one długie i rozbudowane. Dlatego w każdym z opisanych przypadków korzystałem z dostarczonych programów demonstracyjnych.

Dzięki nim można stworzyć naprawdę wiele różnych projektów - wystarczy dopisać tylko kilka swoich linijek, aby zmienić kluczowe ustawienia lub wykonać zdjęcie w konkretnym momencie.

Oczywiście można programy pisać samemu od zera, jednak jeśli ktoś jest w stanie to zrobić, to nie szukałby informacji w tej recenzji. Inaczej mówiąc, jest to zagadnienia dla dość zaawansowanych programistów Arduino. Początkujący bez większego stażu raczej się pogubią.

ArduCAM - wykrywanie obiektów, analiza obrazu?

Jeśli liczycie, że ArduCAM pozwoli rozbudować projekty o analizę obrazu, to niestety muszę Was rozczarować. Opisywane produkty absolutnie nic w tym zakresie nie robią.

Wiem, że dla części czytelników jest to jasne, ale dla formalności wolę informację tę podkreślić. ArduCAM, czyli shield i moduły kamer, to świetne... aparaty fotograficzne, ale nic więcej. Moduły te nie przyspieszają analizy obrazu, ani jej nie wspomagają. Jedynym zadaniem ArduCAM jest po otrzymaniu stosownego sygnału wykonanie zdjęcia i przekazanie go do Arduino.

Wykrywanie obiektów na drodze.

Wykrywanie obiektów na drodze.

Tak, jak pisałem we wstępie, Arduino nie jest platformą przystosowaną do pracy z obrazem i raczej nie będzie. Sam zapis danych na kartę SD trwa już bardzo długo. Oczywiście można by było brać zdjęcia z karty i obrabiać je na Arduino, jednak wydajność tego rozwiązania byłaby nikła. Co więcej oczywiście nie mielibyśmy tutaj żadnej pomocy ze strony ArduCAMa, który jest "tylko aparatem."

Osoby zainteresowany analizą obrazu powinny zainteresować się np. PixyCAM. Nie miałem okazji testować tego produktu, ale wygląda na przyjaźniejszy w tej materii.

ArduCAM - czy tylko do Arduino?

Projekt pierwotnie dedykowany był do Arduino. Jednak oczywiście można go również podłączyć do innych platform. Wystarczy obsługa odpowiednich interfejsów. Producent ArduCAMa deklaruje, że opisywane rozwiązania będą również działały z Raspberry Pi, Beagle Bone Black, czy ESP8266.

Moim zdaniem to ratuje trochę ten produkt, ponieważ istnieje szansa, że po podłączeniu kamer do znacznie wydajniejszych platform uda się zastosować ArduCAMa w ciekawych projektach.

Podsumowanie i ocena

Oczywiście nie sprawdziłem wszystkich trybów pracy ArduCAMa. Jest tego na tyle dużo, że tekst byłby zdecydowanie za długi. W przypadku tej recenzji głównie chciałem pokazać, że takie rozwiązanie istnieje i można je uruchomić stosunkowo szybko. Jeśli macie pytania na temat konkretnych trybów/ustawień to czekam na pytania.

Warto wrócić jeszcze do tytułowego pytania: "Czy do Arduino można podłączyć kamerę?". Jak widać oczywiście można ją podłączyć - pytanie tylko po co? Jedyne zastosowanie, które w tej chwili przychodzi mi do głowy to łatwe wykonywanie zdjęć z dużym interwałem.

Na sam koniec jeszcze raz dziękuję wszystkim Patronom, którzy przyczynili się do powstania tej recenzji. Jeśli ktoś po tej publikacji zdecyduje dołożyć swoją cegiełkę do kolejnej recenzji, to zapraszam na profil Forbota na Patronite, gdzie można znaleźć szczegóły.

Autor: Damian (Treker) Szymański

ArduCAM, arduino, kamera, obraz, patronite, recenzja

Trwa ładowanie komentarzy...