Arduino блютуз камера: передаем изображения на смартфон

Сегодня вы вряд ли кого-нибудь удивите мобильным телефоном с камерой, беспроводными гаджетами и другими техническими достижениями. Благодаря платформе Arduino миллионы людей открыли для себя удивительный мир электроники и программирования. Было написано огромное количество инструкций по обмену данными между мобильным телефоном и Arduino через Bluetooth. И да, мы хотим в очередной раз обмениваться данными между мобильным телефоном на Android и Arduino Uno через Bluetooth, но мы хотим передавать не просто набор символов и цифр, а картинки.

Кто-то скажет, что это невозможно, Arduino слишком медленно обрабатывает большие объемы данных с хорошей скоростью. И он будет абсолютно прав. А что, если немного помочь Arduino - перенести всю «тяжелую» работу на плечи другого устройства? А такое устройство существует!

Это уникальный TFT-шилд (экран) для Arduino. В этом уроке мы продемонстрируем, как можно подключиться через Bluetooth к Arduino и телефону Android, получить изображение с камеры OV7670 на Arduino Uno и перенести его на телефон Android. Затем, наоборот, перенести изображение (изображение с камеры) с телефона Android на Arduino Uno и отобразитm его на TFT-экране. Для этого было написано специальное приложение для телефона Android. Но прежде чем начать, рекомендуем ознакомиться с нашим гидом по TFT-дисплеям.

Шаг 1. TFT-шилд для Ардуино Уно

Для начала мы остановимся на нашем TFT-шилде, который расширяет возможности Arduino Uno.

Этот урок по-сути является первым, с которого мы хотели бы начать рассказ об уникальном TFT-экране, разработанном для платформы Arduino Uno, который позволяет значительно расширить возможности Arduino Uno. Несмотря на то, что этот TFT-экран все еще находится в стадии разработки, на данный момент уже есть работающее устройство.

Характеристики

  • Размер диагонали: 3,5",
  • Разрешение: 320х240,
  • Количество цветов: 65536 (16 бит),
  • Резистивный сенсорный экран (контроллер XPT2046),
  • 5 кнопок,
  • RTC IC DS1307 с литиевой батареей 3В CR1220,
  • Слот для подключения карты Micro SD,
  • 4-контактный (2,54 мм) разъем для подключения модуля Bluetooth HC-05 (-06),
  • 20-контактный (2,54 мм) разъем для камеры (OV7670).

Программное обеспечение

На данный момент для работы с графикой реализованы следующие библиотечные функции: рисование точек, линий, окружностей, прямоугольников. Самая популярная библиотека от Adafruit Industries была адаптирована для работы со шрифтами. Дополнительно реализована работа с сенсорным экраном и кнопками. Демо эскиз для демонстрации работы с графикой и шрифтами ниже:

#include <YATFT.h>                       // Hardware-specific library
#include <Adafruit_GFX.h>                // Include Adafruit-GFX library
#include <Fonts/FreeSerif9pt7b.h>        // Include Adafruit fonts
#include <Fonts/FreeSerifItalic24pt7b.h>
#include <Fonts/FreeSans24pt7b.h>
YATFT tft(0);
uint32_t  total_time;
uint16_t  Color[4] = {BRIGHTBLUE, BRIGHTGREEN, BRIGHTRED, BRIGHTYELLOW};
uint16_t  Gray[7] = {GRAY0, GRAY1, GRAY2, GRAY3, GRAY4, GRAY5, GRAY6};
/*************************************************************************************************/
void ClearScreen (void)
{
   tft.SetColor(BLACK);   // Set fone color
   tft.ClearDevice();     // Fill all screen
}
void setup()
{
   Serial.begin(115200);  // initialize the serial port
   Serial.println("Arduino TFT_shield Example 1!");
   tft.begin();           // initialize the display
}
void loop()
{
   uint16_t  x, y, x2, y2, mask_gray;
   uint16_t  i;
   ClearScreen();
   // Fonts
   Serial.print("1) View Fonts (");
   total_time = millis();
   tft.SetColor(BRIGHTBLUE);
   tft.SetFont(NULL);
   tft.OutTextXY(5, 5, "Demonstration of work with the TFT display.");
   tft.SetColor(BRIGHTGREEN);
   tft.SetFont(&FreeSerif9pt7b);
   tft.OutTextXY(5, 20, "The   example   uses   fonts   from   Adafruit.");
   tft.SetFont(&FreeSerifItalic24pt7b);
   tft.SetColor(BRIGHTCYAN);
   tft.OutTextXY(5, 45, "3,5''");
   tft.SetColor(BRIGHTRED);
   tft.OutTextXY(90, 45, "QVGA");
   tft.SetColor(BRIGHTMAGENTA);
   tft.OutTextXY(230, 45, "disp.");
   tft.SetColor(BRIGHTYELLOW);
   tft.SetFont(&FreeSans24pt7b);
   tft.OutTextXY(5, 100, "A R D U I N O      +  T F T");
   tft.SetFont(NULL);
   for (i = 0; i < 7; i++)
   {
       tft.SetColor(Gray[i]);
       tft.OutTextXY(5, 170+10*i, "Demonstration of work with the TFT display.");
   }
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(3000);
   ClearScreen();
   // Circle
   Serial.print("2) Draw circle (");
   total_time = millis();
   tft.SetColor(BRIGHTRED);
   for (i = 10; i < GetMaxY()>>1; i += 10) {
       tft.DrawCirc(GetMaxX()>>1, GetMaxY()>>1, i);
   }
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
   // DrawFillCircle & DrawFillRect
   Serial.print("3) Draw FillCircle and FillRect (");
   total_time = millis();
   tft.SetColor(BRIGHTRED);
   tft.DrawFillCirc(GetMaxX()>>1,GetMaxY()>>1,110);
   tft.SetColor(BRIGHTCYAN);
   tft.DrawFillRect(GetMaxX()/2-77,GetMaxY()/2-77, GetMaxX()/2+77,GetMaxY()/2+77);
   tft.SetColor(BRIGHTGREEN);
   tft.DrawFillCirc(GetMaxX()>>1,GetMaxY()>>1,77);
   tft.SetColor(BRIGHTMAGENTA);
   tft.DrawFillRect(GetMaxX()/2-54,GetMaxY()/2-54, GetMaxX()/2+54,GetMaxY()/2+54);
   tft.SetColor(BRIGHTBLUE);
   tft.DrawFillCirc(GetMaxX()>>1,GetMaxY()>>1,54);
   tft.SetColor(BRIGHTYELLOW);
   tft.DrawFillRect(GetMaxX()/2-37,GetMaxY()/2-37, GetMaxX()/2+37,GetMaxY()/2+37);
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
   ClearScreen();
   // Arc
   Serial.print("4) Draw Arc (");
   total_time = millis();
   ClearScreen();
   tft.SetColor(BRIGHTBLUE);
   tft.DrawArc((GetMaxX()>>1)-60,(GetMaxY()>>1)-60,(GetMaxX()>>1)+60,(GetMaxY()>>1)+60,20,30,0xFF);
   tft.SetColor(BRIGHTGREEN);
   tft.DrawArc((GetMaxX()>>1)-40,(GetMaxY()>>1)-40,(GetMaxX()>>1)+40,(GetMaxY()>>1)+40,20,30,0xFF);
   tft.SetColor(BRIGHTRED);
   tft.DrawArc((GetMaxX()>>1)-20,(GetMaxY()>>1)-20,(GetMaxX()>>1)+20,(GetMaxY()>>1)+20,20,30,0xFF);
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
   Serial.print("5) Draw FillBevel (");
   total_time = millis();
   tft.SetColor(BRIGHTBLUE);
   tft.DrawFillBevel((GetMaxX()>>1)-60,(GetMaxY()>>1)-60,(GetMaxX()>>1)+60,(GetMaxY()>>1)+60,30);
   tft.SetColor(BRIGHTGREEN);
   tft.DrawFillBevel((GetMaxX()>>1)-40,(GetMaxY()>>1)-40,(GetMaxX()>>1)+40,(GetMaxY()>>1)+40,30);
   tft.SetColor(BRIGHTRED);
   tft.DrawFillBevel((GetMaxX()>>1)-20,(GetMaxY()>>1)-20,(GetMaxX()>>1)+20,(GetMaxY()>>1)+20,30);
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
   ClearScreen();
   Serial.print("6) Draw Arc (");
   total_time = millis();
   for (i = 0; i < 4; i++) {
       tft.SetColor(Color[i]);
       tft.DrawArc((GetMaxX()>>1),(GetMaxY()>>1)-50,(GetMaxX()>>1),(GetMaxY()>>1)+50,50,60,0x11<<i);
   }
   for (i = 0; i < 4; i++) {
       tft.SetColor(Color[i]);
       tft.DrawArc((GetMaxX()>>1),(GetMaxY()>>1)-30,(GetMaxX()>>1),(GetMaxY()>>1)+30,35,45,0x11<<i);
   }
   for (i = 0; i < 4; i++) {
       tft.SetColor(Color[i]);
       tft.DrawArc((GetMaxX()>>1),(GetMaxY()>>1),(GetMaxX()>>1),(GetMaxY()>>1),20,30,0x11<<i);
   }
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
   ClearScreen();
   // Draw 1000 random lines
   Serial.print("7) Draw 1000 random lines (");
   total_time = millis();
   for (i = 0; i < 1000; i++) {
       tft.SetColor(random(65535));
       x  = random(GetMaxX());
       y  = random(GetMaxY());
       x2 = random(GetMaxX());
       y2 = random(GetMaxY());
       tft.DrawLine(x, y, x2, y2);
   }
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
   // Draw 1000 random lines
   Serial.print("8) ReDraw 10 Fill Screen (");
   total_time = millis();
   tft.SetColor(BLACK);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BRIGHTBLUE);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(YELLOW);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BRIGHTGREEN);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BRIGHTRED);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BRIGHTCYAN);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(GREEN);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BRIGHTYELLOW);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BRIGHTCYAN);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   tft.SetColor(BLACK);   // Set fone color
   tft.ClearDevice();     // Fill all screen
   total_time = millis() - total_time;
   Serial.print(total_time);
   Serial.println(" ms)");
   delay(1000);
}

Демо пример, демонстрирующий работу со шрифтами и сенсорным экраном:

#include <YATFT.h>                       // Hardware-specific library
#include <SPI.h>                         // Include SPI library
#include <XPT2046_Touchscreen.h>         // Include Touchscreen library
#include <Adafruit_GFX.h>                // Include Adafruit-GFX library
#include <Fonts/FreeSerif9pt7b.h>        // Include Adafruit fonts
#include <Fonts/FreeSerifItalic24pt7b.h>
#include <Fonts/FreeSans24pt7b.h>
// Touchscreen: MOSI=11, MISO=12, SCK=13, CS=2
#define CS_PIN  2
XPT2046_Touchscreen ts(CS_PIN, 255);
YATFT tft(0);
#define  Y_BAR_TOP      (GetMaxY()-50)
#define  Y_BAR_BOT      GetMaxY()
#define  BUTTON1_MASK   0x01
#define  BUTTON2_MASK   0x02
#define  BUTTON3_MASK   0x04
#define  BUTTON4_MASK   0x08
#define  BUTTON5_MASK   0x10
/* 
  If using the shield, all control and data lines are fixed, and
  a simpler declaration can optionally be used:
*/
//uint32_t  total_time;
uint16_t  pos_x[] = {0,0,0,0};
uint16_t  pos_y[] = {0,0,0,0};
uint8_t   pos_x_cnt = 0;
uint8_t   pos_y_cnt = 0;
uint16_t  pos_x_mid = 0;
uint16_t  pos_y_mid = 0;
uint16_t  color_paint = WHITE;
uint8_t   buttons = 0;
uint16_t  Color[4] = {BRIGHTBLUE, BRIGHTGREEN, BRIGHTRED, BRIGHTYELLOW};
uint16_t  Gray[7] = {GRAY0, GRAY1, GRAY2, GRAY3, GRAY4, GRAY5, GRAY6};
/*************************************************************************************************/
void ClearScreen (void)
{
   tft.SetColor(BLACK);   // Set fone color
   tft.ClearDevice();     // Fill all screen
}
void setup()
{
   Serial.begin(115200);  // initialize the serial port
   Serial.println("Arduino TFT_shield Example1!");
   ts.begin();            // Init Touchscreen
   SPI.end();             // Disable SPI for correct work DB2 (SS) pin 
   tft.begin();           // initialize the display
   RefreshWindow();
}
void loop()
{
   uint16_t  x, y;
   // Touch
   // When the SS pin is set as OUTPUT, it can be used as
   // a general purpose output port (it doesn't influence
   // SPI operations).
   SPI.begin();
   if (ts.touched())
   {
       TS_Point p = ts.getPoint();
       Serial.print(F("Pressure = "));
       Serial.print(p.z);
       Serial.print(F(", x = "));
       Serial.print(p.x);
       Serial.print(F(", y = "));
       Serial.print(p.y);
       Serial.println();
       delay(3);     // Delay for filtering
       SPI.end();  // Disable SPI for correct work DB2 (SS) pin 
       // Calculate coordinates x, y from code ADC
       if (p.x < 200) p.x = 200;
       if (p.y < 250) p.y = 250;
#if 0
       x = (uint16_t)(320L - ((uint32_t)p.x - 200L)*10L/115L);
       y = (uint16_t)(((uint32_t)p.y - 250L)/15L);
#else
       x = (uint16_t)(0 + ((uint32_t)p.y - 200L)*10L/115L);
       y = (uint16_t)(((uint32_t)p.x - 250L)/15L);
#endif
       // Filtering 
       pos_x_mid = (pos_x[0] + pos_x[1] + pos_x[2] + pos_x[3])/4;
       pos_y_mid = (pos_y[0] + pos_y[1] + pos_y[2] + pos_y[3])/4;
       pos_x[pos_x_cnt++] = x;
       pos_y[pos_y_cnt++] = y;
       pos_x_cnt &= 0x03;
       pos_y_cnt &= 0x03;
       if (x > (pos_x_mid - 10) && x < (pos_x_mid + 10) && y > (pos_y_mid - 10) && y < (pos_y_mid + 10 )) {
           if (y > Y_BAR_TOP && y < Y_BAR_BOT) {
               if (x < 1*(GetMaxX()+1)/5) {  // Touch Bar 1
                   color_paint = Color[0];
                   RefreshTitle();
               } else
               if (x < 2*(GetMaxX()+1)/5) {  // Touch Bar 2
                   color_paint = Color[1];
                   RefreshTitle();
               } else
               if (x < 3*(GetMaxX()+1)/5) {  // Touch Bar 3
                   color_paint = Color[2];
                   RefreshTitle();
               } else
               if (x < 4*(GetMaxX()+1)/5) {  // Touch Bar 4
                   color_paint = Color[3];
                   RefreshTitle();
               } else {                     // Clear screen
                   RefreshWindow();
               }
           } else {
               tft.SetColor(color_paint);
               tft.DrawFillRect(x-1, y-1, x+1, y+1);
           }
       }
   }
   SPI.end();  // Disable SPI for correct work DB2 (SS) pin 
   ScanKey();
}
void  RefreshWindow(void)
{
   color_paint = WHITE;
   ClearScreen();
   for (uint8_t i = 0; i < 4; i++) {
       tft.SetColor(Color[i]);
       tft.DrawFillRect((i+1)*((GetMaxX()+1)/5), Y_BAR_TOP, (i)*((GetMaxX()+1)/5), Y_BAR_BOT);
   }
   RefreshTitle();
   tft.SetColor(WHITE);
   tft.OutTextXY(GetMaxX() - 50, GetMaxY() - 45, "Clear");
   tft.OutTextXY(GetMaxX() - 55, GetMaxY() - 25, "screen");
}
void  RefreshTitle(void)
{
   tft.SetColor(color_paint);
   tft.SetFont(&FreeSerif9pt7b);
   tft.OutTextXY(3, 20, "Touch     color     bar     and     screen     or     press     key.");
}
void  ScanKey(void)
{
   static uint8_t buttons_last = 0;
   buttons = tft.scanButtons();
   if (buttons != buttons_last) {
       if (buttons & BUTTON1_MASK) {  // Bar 1
           color_paint = Color[0];
           RefreshTitle();
       }
       if (buttons & BUTTON2_MASK) {  // Bar 2
           color_paint = Color[1];
           RefreshTitle();
       }
       if (buttons & BUTTON3_MASK) {  // Bar 3
           color_paint = Color[2];
           RefreshTitle();
       }
       if (buttons & BUTTON4_MASK) {  // Bar 4
           color_paint = Color[3];
           RefreshTitle();
       }
       if (buttons & BUTTON5_MASK) {  // Clear screen
           RefreshWindow();
       }
   }
   buttons_last = buttons;
}

Демо-эскиз логической игры «Столбцы» для демонстрации работы с графикой, текстом и кнопками:

#include <YATFT.h>
#include <XPT2046_Touchscreen.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Fonts/FreeMonoBoldOblique12pt7b.h>
#include <Fonts/FreeSerif9pt7b.h>
YATFT tft(0);
long randNumber;
#define KEY_EMPTY            0
#define KEY_SWAP             1
#define KEY_FALL             2
#define KEY_LEFT             3
#define KEY_RIGHT            4
#define DISPLAY_MAX_X 320
#define DISPLAY_MAX_Y 240
#define MaxCol       8 
#define MaxRow       17
#define SmeX         3
#define SmeY         3
#define razmer       15
#define LL           250
#define NumCol       6
#define MaxLevel     8
#define PeriodLevel  80
#define DISP_LEFT    ((DISPLAY_MAX_X - MaxCol*razmer)/2 - 2)
#define DISP_RIGHT   ((DISPLAY_MAX_X + MaxCol*razmer)/2 + 2)
#define DISP_TOP     ((DISPLAY_MAX_Y - (MaxRow-4)*razmer)/2 - 2 - 10)
#define DISP_BOT     ((DISPLAY_MAX_Y + (MaxRow-4)*razmer)/2 + 2 - 10)
uint8_t  MasSt[MaxCol][MaxRow], MasTmp[MaxCol][MaxRow], MasOld[MaxCol][MaxRow], fignext[3];
uint8_t  Level=1, OldLevel, tr, flfirst=1;
uint16_t MasCol[]={WHITE, BLACK, BRIGHTRED, BRIGHTBLUE, BRIGHTGREEN, BRIGHTYELLOW, BRIGHTMAGENTA, BRIGHTCYAN};
unsigned long  Counter,Score=0, TScore=0, Record=0, OldRecord, OldScore, myrecord;
uint16_t tempspeed;
bool     fl, Demo=true, myfl=false, Arbeiten=false, FlNew, FlZ=false;
int8_t   VAL, Mp, x,y;
int8_t   mmm [4][2]={{-1,0},{0,-1},{1,0},{0,1}};
uint16_t MasSpeed[MaxLevel]={500,450,400,350,300,250,200,100};
/*********************************************************************/
void  setup(void)
{
   // initialize the serial port
   Serial.begin(115200);
   Serial.println("TFT_shield_Game1 example!");
   tft.begin(); // initialize the display
   randomSeed(analogRead(5));
   tft.SetColor(WHITE);
   tft.ClearDevice();
   tft.SetColor(RED);
   tft.SetFont(&FreeSerif9pt7b);
   tft.OutTextXY( 20,  20, "LEVEL");
   tft.OutTextXY(240,  20, "NEXT");
   tft.OutTextXY( 20,  75, "SCORE");
   tft.OutTextXY( 25, 130, "TOP");
   FlNew = true;
   ViewStacan();
   GetNext();
   delay(100);
   tft.SetColor(BLACK);
   tft.DrawLine(DISP_LEFT + 1, DISP_TOP + 0, DISP_LEFT + 1, DISP_BOT - 1);
   tft.DrawLine(DISP_LEFT + razmer*MaxCol+5-MaxCol,DISP_TOP + 0,DISP_LEFT + razmer*MaxCol+5-MaxCol, DISP_BOT - 1);
   tft.DrawLine(DISP_LEFT + 1, DISP_BOT - 1, DISP_LEFT + 1+razmer*MaxCol+5-MaxCol-1, DISP_BOT - 1);
   // Key
   tft.SetColor(BRIGHTBLUE);
   tft.DrawCirc( 32,228, 8);                                                                    // ROTATE
   tft.DrawLine( 88,220, 96,236); tft.DrawLine( 88,220,104,220); tft.DrawLine( 96,236,104,220); // DROP
   tft.DrawRect(152,220,157,236); tft.DrawRect(163,220,168,236);                                // PAUSE
   tft.DrawLine(232,220,232,236); tft.DrawLine(232,220,216,228); tft.DrawLine(232,236,216,228); // LEFT
   tft.DrawLine(280,220,280,236); tft.DrawLine(280,220,296,228); tft.DrawLine(280,236,296,228); // RIGHT
}
void  loop(void)
{
   if (Demo)  ProcDemo();
   else {
       if (Arbeiten) {
           mydelay(tempspeed);
           figmove(0,1);
       } else  if (mypush()==KEY_SWAP/*ShortPush*/) NewGame();
   }
}
uint8_t  mypush(void)
{
   unsigned long tpr = millis();
   uint8_t res = KEY_EMPTY;//NotPush;
   uint8_t button = 0;
   static uint8_t button_old;
   static uint8_t cnt = 0;
   button = tft.scanButtons();
   if (button) {
     cnt = 5;
     Serial.print("Scan Button: ");
     Serial.print(button, HEX);
     Serial.println();
     if (button != button_old) {
       if (button & 0x01) res = KEY_SWAP;
       if (button & 0x02) res = KEY_FALL;
       if (button & 0x04) {};
       if (button & 0x08) res = KEY_LEFT;
       if (button & 0x10) res = KEY_RIGHT;
       button_old = button;
     }
   }
   if (!cnt) {
     button_old = button;
   } else {
     cnt--;
   }
   return(res); 
}
void  ViewQuad(uint8_t i,uint8_t  j,uint8_t mycolor)
{
   if (j<3) return; 
   uint16_t wy = DISP_TOP + SmeY + (j-3)*razmer - j;
   uint16_t wx = DISP_LEFT + SmeX + i*razmer - i;
   if (mycolor!=0) {
       tft.SetColor(BLACK);
       tft.DrawRect(wx, wy, wx+razmer-1, wy+razmer-1);
       tft.SetColor(MasCol[mycolor]);
       tft.DrawFillRect(wx+1, wy+1, wx+1+razmer-3, wy+1+razmer-3);
   } else {
       tft.SetColor(WHITE);
       tft.DrawFillRect(wx+1, wy+0, wx+1+razmer-3, wy+1+razmer-3);
   }
}
void  ViewStacan(void)
{
   char myStr2[5];
   uint8_t h = tft.GetTextHeight(&FreeMonoBoldOblique12pt7b);
   tft.SetFont(&FreeMonoBoldOblique12pt7b);
   if (OldScore!=Score || FlNew) {
       sprintf(myStr2,"%05d",Score);
       int16_t w = tft.GetTextWidth(myStr2, &FreeMonoBoldOblique12pt7b);
       tft.SetColor(WHITE);
       tft.DrawFillRect(20,100,20+w+5,100+h);
       tft.SetColor(GREEN);
       tft.OutTextXY(20,100,myStr2);
       OldScore=Score;
   }
   if (OldRecord!=Record || FlNew)  {
       sprintf(myStr2,"%05d",Record );
       int16_t w = tft.GetTextWidth(myStr2, &FreeMonoBoldOblique12pt7b);
       tft.SetColor(WHITE);
       tft.DrawFillRect(20,155,20+w+5,155+h);
       tft.SetColor(GREEN);
       tft.OutTextXY(20,155,myStr2);
       OldRecord=Record;
   }
   if (OldLevel!=Level || FlNew)  {
       sprintf(myStr2,"%01d",Level );
       int16_t w = tft.GetTextWidth(myStr2, &FreeMonoBoldOblique12pt7b);
       tft.SetColor(WHITE);
       tft.DrawFillRect(25,45,25+w+5,45+h);
       tft.SetColor(GREEN);
       tft.OutTextXY(25,45,myStr2);
       OldLevel=Level;
   }
   FlNew=false;
   for (byte j=3;j<MaxRow;j++)
       for (byte i=0;i<MaxCol;i++)
           if (MasSt[i][j]!=MasOld[i][j]) ViewQuad(i,j,MasSt[i][j]);
   for (byte j=3;j<MaxRow;j++)
       for (byte i=0;i<MaxCol;i++)
           MasOld[i][j]=MasSt[i][j];
}
void  ClearMas(byte MasStx[MaxCol][MaxRow])
{
   for (byte j=0;j<MaxRow;j++)
       for (byte i=0;i<MaxCol;i++)
           (MasStx[i][j]=0);
}
void  Sosed (int i,int j,int dx,int dy, byte mode)
{
   int nx=i+dx;
   int ny=j+dy;
   if (nx>=0 && ny>=0 && nx<MaxCol && ny<MaxRow && MasSt[nx][ny]==MasSt[i][j]) {
       if (mode==1) MasTmp[i][j]++;
       else
       if (mode==2 && (MasTmp[nx][ny]>1 || MasTmp[i][j]>2 )) {
           MasTmp[nx][ny]=3; 
           MasTmp[i][j]=3;
       } else
       if (mode==3 && MasTmp[nx][ny]==3) {
           if (MasTmp[i][j]!=3) {
               MasTmp[i][j]=3; 
               fl=true;
           }
       }
   }
}
void  Sos(int i,int j, byte mode)
{
   for (byte k=0;k<4;k++) Sosed(i,j,mmm[k][0],mmm[k][1],mode);      
}
bool  FindFull(void)
{
   byte  i,j,k; bool  res; 
   res = false; 
   for (byte k=2;k<8;k++) {
       ClearMas(MasTmp);
       for (j=3;j<MaxRow;j++)
           for (i=0;i<MaxCol;i++)
               if (MasSt[i][j]==k) Sos(i,j,1);
       for (j=3;j<MaxRow;j++)
           for (i=0;i<MaxCol;i++)
               if (MasTmp[i][j]>1) Sos(i,j,2);
       do {
           fl=false;
           for (j=3;j<MaxRow;j++)
               for (i=0;i<MaxCol;i++)
                   if (MasTmp[i][j]>0) Sos(i,j,3);
       } while (fl);
       for (j=3;j<MaxRow;j++)
           for (i=0;i<MaxCol;i++)
               if (MasTmp[i][j]==3) {
                   MasSt[i][j]=1;
                   TScore++;
               }
   }
   if (TScore>0) {
       ViewStacan();
       FlZ=true;
       mydelay(500);
   }
   for (j=0;j<MaxRow;j++)
       for (i=0;i<MaxCol;i++) {
           while (MasSt[i][MaxRow-1-j]==1) {
               for (k=0;k<MaxRow-2-j;k++) MasSt[i][MaxRow-1-k-j]= MasSt[i][MaxRow-2-k-j];
               res=true;  
           }
       }
   return(res);
}
void  GetNext(void)
{
   byte dx=255;
   byte dy=60;
   x=3;
   y=0;
   for (byte i=0;i<3;i++) {
       //fig[i]=fignext[i];
       if (!Demo) MasSt[x][i]=fignext[i];
       fignext[i]=random(NumCol)+2;
       tft.SetColor(BLACK);
       tft.DrawRect(dx,dy+(razmer-1)*i,dx+razmer, dy+(razmer-1)*(i+1)+1);
       tft.SetColor(MasCol[fignext[i]]);
       tft.DrawFillRect(dx+1,dy+(razmer-1)*i+1, dx+razmer-1, dy+(razmer-1)*(i+1));
   }
   if (!Demo) {
       Counter++;
       if (Counter==PeriodLevel) {
           Counter=0; 
           Level++; 
           if (Level>MaxLevel) Level=MaxLevel;
       }
       tempspeed=MasSpeed[Level-1];
   }
}
void  MyScore(void)
{
   TScore=0;
   while(FindFull()) {
       if (TScore>7) Score=Score+TScore+(TScore-8)*2;
       else Score=Score+TScore;
       ViewStacan();
       FlZ=true;
       mydelay(1000);
   }
   FlZ=false;
}
void  ProcDemo(void)
{
   Score=0;
   GetNext();
   for (byte j=3;j<MaxRow;j++)
       for (byte i=0;i<MaxCol;i++)
           MasSt[i][j]=random(6)+2;
   ViewStacan();
   mydelay(1000);
   if (!Demo) return;
   MyScore();
   if (Record<Score) Record=Score;
}
void  mydelay(int md)
{
   unsigned long starttime=millis();  
   while (millis()-starttime < md) {
       VAL=0;
       Mp=mypush();
       if (Mp==KEY_RIGHT) {
           VAL=1;
       } else
       if (Mp==KEY_LEFT) {
           VAL=-1;
       }
       if ((VAL!=0 || Mp!=KEY_EMPTY/*NotPush*/) && Demo) {
           Demo=false; 
           NewGame();
       }
       if (VAL!=0 && figmove(VAL, 0) && !FlZ) {
           for (byte i=0;i<3;i++) {
               MasSt[x+VAL][y+i]=MasSt[x][y+i];
               MasSt[x][y+i]=0; 
           }
           ViewStacan(); 
           if (MasSt[x][y+3]==0) {
               tft.SetColor(WHITE);
               tft.DrawLine(DISP_LEFT+SmeX+x*(razmer-1)+1, DISP_TOP+SmeY+y*(razmer-1)-3, DISP_LEFT+SmeX+(x+1)*(razmer-1)-1, DISP_TOP+SmeY+y*(razmer-1)-3);
           }
           x=x+VAL; 
       }
       if (Mp==KEY_SWAP/*ShortPush*/ && !FlZ) {
           byte aa=MasSt[x][y];
           MasSt[x][y]=MasSt[x][y+2];
           MasSt[x][y+2]=MasSt[x][y+1];
           MasSt[x][y+1]=aa;
           ViewStacan();
       }
       if (Mp==KEY_FALL && !FlZ) tempspeed=50;
   }
}
void  NewGame(void)
{
   for (byte i=0; i<(MaxCol+1); i++) {
       tft.SetColor(BLACK);
       tft.DrawLine(DISP_LEFT+3+(razmer-1)*i, DISP_TOP, DISP_LEFT+3+(razmer-1)*i, DISP_BOT-3);
   }
   for (byte j=3;j<MaxRow;j++)
       for (byte i=0;i<MaxCol;i++)
           MasOld[i][j]=255;
   Score=0;
   FlNew=true;
   OldScore=Score;
   ClearMas(MasSt);
   Arbeiten=true;
   GetNext();
   Counter=0;
   Level=1;
   tempspeed=MasSpeed[0];
   Record=myrecord;
   ViewStacan();
}
void  gameover(void)
{
   Arbeiten=false;
   tft.SetColor(BLACK);
   tft.DrawRect(112,90,202,145);
   tft.SetColor(RED);
   tft.DrawFillRect(113,91,201,144);
   tft.SetFont(&FreeSerif9pt7b);
   tft.SetColor(WHITE);
   tft.OutTextXY(132, 100, "GAME");
   tft.OutTextXY(135, 120, "OVER");
}
bool  figmove(int dx, int dy)
{
   bool fff=false;
   if (x+dx<0 || x+dx>MaxCol-1) return(false);
   if (dx!=0) if (MasSt[x+dx][y+dy+2]==0) return(true); else return(false);
   if  (dy>0) {
       if (y+dy+2>MaxRow-1  || MasSt[x+dx][y+dy+2]>0) {
           if (y<3) gameover(); 
           else fff=true;
       } else {
           for (byte i=0;i<3;i++) MasSt[x][y+2-i+dy]=MasSt[x][y+2-i];
           MasSt[x][y]=0;                                 
           y=y+dy;
       }
       if (fff) {
           MyScore();
           GetNext(); 
       }
       ViewStacan(); 
   }
   return(true);
}

Демонстрация

Ниже добавлено видео, демонстрирующее работу эскизов.

Купить такой шилд можно на сайте tindie.com.

Шаг 2. Комплектующие

Из оборудования нам понадобятся следующие комплектующие:

  • Arduino UNO;
  • Уникальный TFT-шилд (ссылка выше);
  • AC-DC адаптер питания 6-12 вольт, >600 мА;
  • Камера OV7670;
  • Модуль Bluetooth HC-06 (HC-05);
  • Телефон на Андроиде.
Внимание! Для работы экрана TFT необходимо (!) использовать адаптер питания 6-12 В, так как для нормальной работы максимального тока в 500 мА от USB недостаточно.

Программное обеспечение (можно скачать ниже):

  • Arduino IDE;
  • Библиотека для TFT-экрана;
  • APK-файл для телефона Android.

Шаг 3. Подготовка

Программное обеспечение

Все демо скетчи написаны в среде Arduino IDE, поэтому вначале необходимо установить саму Arduino ID, которую можно скачать у на в Инструментарии либо на сайте официального производителя Ардуино.

Затем вам нужно установить библиотеку для TFT отсюда, скачать библиотеку и распаковать ее в папку «библиотеки» в директории Arduino IDE. После установки Arduino IDE необходимо запрограммировать плату Arduino Uno. Для простоты рекомендуем прошивать отдельно, без экрана TFT, а для этого:

  • Подключите USB-кабель к плате Arduino UNO;
  • Запустите Arduino IDE на компьютере;
  • Выберите соответствующий порт, к которому подключен Arduino UNO;
  • Загрузите демонстрационный эскиз Arduino_Bluetooth_Camera.ino (и файл ov7670_regs.h для инициализации камеры);
  • Нажмите кнопку Загрузить.

Если плата Arduino UNO успешно запрограммирована, вы можете перейти к следующему шагу. Все необходимые библиотеки и файлы вы найдете ниже в zip-архиве.

Android

На телефоне Android необходимо установить Arduino_TFT.apk. Разрешить приложению использовать Bluetooth и камеру.

Модуль Bluetooth

Необходимо установить скорость обмена в модуле Bluetooth равным 115200 (команда «AT + UART = 115200,0,0»). Это оптимальная скорость, с которой Arduino UNO удается получать и обрабатывать данные. Теоретически вы можете увеличить скорость, оптимизировать прием и обработку данных, но для этого требуется больший объем оперативной памяти. Более подробно о том, как установить скорость обмена мы поговорим в ближайших уроках.

Обратите внимание, что модуль Bluetooth подключается к порту отладки Arduino UNO. Таким образом, при работе с Bluetooth порт отладки недоступен. А перед программированием Arduino UNO (в комплекте с модулем Bluetooth) необходимо отключить модуль Bluetooth. И после программирования установить его обратно (!).

Шаг 4. Сборка

Сборка устройства довольно проста:

  • Соедините вместе Arduino UNO и TFT-щит;
  • Подключите камеру OV7670 к 20-контактному разъему на TFT-экране (иногда можно использовать угловой 18-20-контактный разъем с шагом 2,54 мм в качестве адаптера);
  • Подключите модуль Bluetooth HC-06 (HC-05) к 4-контактному разъему «Bluetooth» на TFT-экране;
  • Подключите адаптер питания 6-12 В ко входу питания на плате Arduino UNO.

После включения питания TFT-экран должен стать красным. Это означает готовность получать команды с телефона Android.

Шаг 5. Демонстрация

Выполните следующие операции на телефоне Android:

  • Запустите приложение ArduinoTFT на телефоне Android;
  • Поверните телефон в горизонтальное положение;
  • Включите соединение Bluetooth, выберите обнаруженный модуль Bluetooth (HC-06);

На экране должны появиться два окна и четыре кнопки:

  • В верхнем правом окне находится окно видоискателя камеры телефона;
  • Большое левое окно - полученные или отправленные изображения.

Функции кнопок:

  1. Передача одного изображения с телефона Android на Arduino;
  2. Непрерывная передача изображений с телефона Android на Arduino;
  3. Передача одного изображения из Arduino на телефон Android;
  4. Непрерывная передача изображений с Arduino на телефон Android.

Размер изображения составляет 320x240 пикселей (2-5 кБ). Выше есть демо-видео.

22 апреля 2019 в 23:41 | Обновлено 7 декабря 2019 в 00:01 (редакция)
Опубликовано:
Уроки, ,

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

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