Arduino Bluetooth Camera (ABC): передача изображений с камеры OV7670 на Android-смартфон по Bluetooth
Компоненты и материалы
| Arduino UNO | ×1 |
| Bluetooth-модуль HC-05 (или HC-06) | ×1 |
| Камера OV7670 | ×1 |
| Уникальный TFT Shield для Arduino | ×1 |
О проекте
Сложно удивить кого-то смартфоном с камерой и беспроводными гаджетами. Благодаря платформе Arduino миллионы людей открыли для себя электронику и программирование. Написано сотни инструкций по обмену данными между телефоном и Arduino по Bluetooth... Но я хочу передать не просто символы и числа, а изображения.
Кто-то скажет, что это невозможно — Arduino слишком медленный для обработки больших объёмов данных. И он будет прав. Но что, если переложить «тяжёлую» работу на другое устройство? Оно существует!
Это уникальный TFT Shield для Arduino. Подробнее о нём в статьях: статья 1, статья 2. В этом руководстве показано, как соединить Arduino и Android-телефон по Bluetooth, получить изображение с камеры OV7670 на Arduino UNO и передать его на телефон. И наоборот — передать фото с телефона на Arduino и вывести на экран TFT Shield.
Для Android написано специальное приложение.
Характеристики TFT Shield
- Диагональ 3.5"
- Разрешение 320×240
- 65536 цветов (16 бит)
- Резистивный тачскрин (контроллер XPT2046)
- 5 кнопок
- RTC DS1307 с батарейкой CR1220
- Слот для microSD
- 4-пиновый разъём (2.54 мм) для Bluetooth HC-05/06 или WiFi ESP8266
- 20-пиновый разъём (2.54 мм) для камеры OV7670
Список необходимых компонентов

Железо:
- Arduino UNO
- Уникальный TFT Shield
- Блок питания 6–12 В, >600 мА
- Камера OV7670
- Bluetooth HC-06 (или HC-05)
- Android-смартфон
Внимание: Обязательно используйте внешний блок питания 6–12 В! Ток USB (макс. 500 мА) недостаточен для нормальной работы TFT Shield.
Софт:
- Arduino IDE
- Библиотека для TFT Shield
- APK-файл для Android
Подготовка
Софт
Все скетчи для Arduino IDE. Сначала установите Arduino IDE: arduino.cc.
Затем установите библиотеку TFT Shield: github.com/YATFT/YATFT (скачайте, распакуйте в папку libraries в директории Arduino IDE).
Прошейте Arduino UNO без TFT Shield (для простоты):
- Подключите USB к Arduino UNO
- Запустите Arduino IDE, выберите порт
- Скачайте демо-скетч ArduinoBluetoothCamera.ino и файл ov7670_regs.h для инициализации камеры
- Нажмите Upload
Если прошивка прошла успешно — переходите к сборке.
Скетч ArduinoBluetoothCamera.ino
/**********************************************************************
* SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY...
***********************************************************************/
#include <YATFT.h>
#include <util/yacam.h>
#include <util/yacodec.h>
#include <util/yasrl.h>
#include "ov7670_regs.h"
JPEG_DECODE jpeg_decode;
YATFT tft(0);
INTRFC ifc;
CAM cam;
CODEC codec;
SRL srl;
#define IMG_SizeX 320
#define IMG_SizeY 240
uint8_t mode = 0;
uint8_t last_mode = 0;
uint8_t start_capt = 0;
uint16_t err;
void setup() {
Serial.begin(115200);
tft.begin();
tft.SetColor(BRIGHTRED);
tft.ClearDevice();
}
void loop() {
if (Serial.available()) {
uint8_t temp = Serial.read();
switch (temp) {
case 0x10: // Отправка одного фото
mode = 1; start_capt = 1;
if (last_mode != mode && last_mode != 2) {
tft.begin(); tft.SetRGB();
cam.CamInit(&OV7670_QVGA[0][0]);
cam.CamVideoPreview(0, 0, 1, true);
codec.JPEGInit(); codec.JPEGSetRegs(IMG_SizeX, IMG_SizeY);
delay(1000);
}
break;
case 0x20: // Непрерывная отправка фото
mode = 2; start_capt = 2;
if (last_mode != mode && last_mode != 1) {
tft.begin(); tft.SetRGB();
cam.CamInit(&OV7670_QVGA[0][0]);
cam.CamVideoPreview(0, 0, 1, true);
codec.JPEGInit(); codec.JPEGSetRegs(IMG_SizeX, IMG_SizeY);
}
break;
case 0x30: // Приём одного фото
mode = 3; start_capt = 3;
if (last_mode != mode && last_mode != 4) {
tft.begin(); tft.SetYUV();
}
break;
case 0x40: // Непрерывный приём фото
mode = 4; start_capt = 4;
if (last_mode != mode && last_mode != 3) {
tft.begin(); tft.SetYUV();
}
break;
default: break;
}
}
if (mode == 1) { // Отправка одного фото
if (start_capt == 1) { start_capt = 0; last_mode = mode; mode = 0; CamCapture(); }
} else if (mode == 2) { // Непрерывная отправка
while (1) {
uint8_t temp = Serial.read();
if (start_capt == 2) start_capt = 0;
if (temp == 0x21) { start_capt = 0; last_mode = mode; mode = 0; break; }
if (CamCapture()) continue;
}
} else if (mode == 3) { // Приём одного фото
if (start_capt == 3) {
start_capt = 0; last_mode = mode; mode = 0;
Serial.print("!");
srl.JPEGReadFromSerial(&jpeg_decode, 0, 0, GetMaxX()+1, GetMaxY()+1, 32000);
}
} else if (mode == 4) { // Непрерывный приём
uint8_t temp = Serial.read();
while (1) {
if (start_capt == 4) start_capt = 0;
if (temp == 0x41) { start_capt = 0; last_mode = mode; mode = 0; break; }
Serial.print("!");
srl.JPEGReadFromSerial(&jpeg_decode, 0, 0, GetMaxX()+1, GetMaxY()+1, 32000);
}
}
}
uint8_t CamCapture(void) {
uint8_t temp = 0xff, temp_last = 0;
bool is_header = false;
uint32_t length = 0;
length = codec.JPEGStart();
uint32_t en_jpeg_address = ifc.rdReg32(0x414) << 2;
int k = 0;
if ((length >= 0x5FFFF) || (length == 0)) { start_capt = 2; return 1; }
temp = ifc.GetMem(en_jpeg_address + k); k++; length--;
while (length--) {
temp_last = temp;
temp = ifc.GetMem(en_jpeg_address + k); k++;
if (is_header) { Serial.write(temp); }
else if ((temp == 0xD8) && (temp_last == 0xFF)) {
is_header = true; Serial.write(temp_last); Serial.write(temp);
}
if ((temp == 0xD9) && (temp_last == 0xFF)) break;
}
is_header = false;
return 0;
}
OV7670_regs.h
#ifndef OV7670_REGS_H
#define OV7670_REGS_H
const uint8_t OV7670_VGA[][2] PROGMEM = {
{1, 0x42}, {640/16, 480/16},
{0b01000010, 0b00000100},
{0x3a, 0x0C}, {0x40, 0xC0}, {0x12, 0x00}, {0x0c, 0x00}, {0x3e, 0x00},
{0x70, 0x3A}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xF0}, {0xa2, 0x02},
{0x11, 0x80}, {0x7a, 0x18}, {0x7b, 0x02}, {0x7c, 0x07}, {0x7d, 0x1F},
{0x7e, 0x49}, {0x7f, 0x5A}, {0x80, 0x6A}, {0x81, 0x79}, {0x82, 0x87},
{0x83, 0x94}, {0x84, 0x9F}, {0x85, 0xAF}, {0x86, 0xBB}, {0x87, 0xCF},
{0x88, 0xEE}, {0x89, 0xEE}, {0x13, 0xE0}, {0x00, 0x00}, {0x10, 0x00},
{0x0d, 0x00}, {0x24, 0x98}, {0x25, 0x63}, {0x26, 0xD3}, {0x2a, 0x00},
{0x2b, 0x00}, {0x2d, 0x00}, {0x13, 0xe5}, {0x13, 0xe7}, {0x1e, 0x30},
{0x74, 0x60}, {0x01, 0x80}, {0x02, 0x80}, {0x15, 0x10}, {0x4f, 0x40},
{0x50, 0x34}, {0x51, 0x0C}, {0x52, 0x17}, {0x53, 0x29}, {0x54, 0x40},
{0x57, 0x80}, {0x58, 0x1e}, {0x41, 0x10}, {0x75, 0x60}, {0x76, 0x50},
{0x77, 0x48}, {0x3d, 0x92}, {0x3b, 0x00}, {0x04, 0x00}, {0xff, 0xff}
};
const uint8_t OV7670_QVGA[][2] PROGMEM = {
{1, 0x42}, {320/16, 240/16},
{0b01000010, 0b00000100},
{0x3a, 0x0C}, {0x40, 0xC0}, {0x12, 0x10}, {0x0c, 0x00}, {0x3e, 0x00},
{0x70, 0x3A}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xF0}, {0xa2, 0x02},
{0x11, 0x80}, {0x7a, 0x18}, {0x7b, 0x02}, {0x7c, 0x07}, {0x7d, 0x1F},
{0x7e, 0x49}, {0x7f, 0x5A}, {0x80, 0x6A}, {0x81, 0x79}, {0x82, 0x87},
{0x83, 0x94}, {0x84, 0x9F}, {0x85, 0xAF}, {0x86, 0xBB}, {0x87, 0xCF},
{0x88, 0xEE}, {0x89, 0xEE}, {0x13, 0xE0}, {0x00, 0x00}, {0x10, 0x00},
{0x0d, 0x00}, {0x24, 0x98}, {0x25, 0x63}, {0x26, 0xD3}, {0x2a, 0x00},
{0x2b, 0x00}, {0x2d, 0x00}, {0x13, 0xe5}, {0x13, 0xe7}, {0x1e, 0x30},
{0x74, 0x60}, {0x01, 0x80}, {0x02, 0x80}, {0x15, 0x10}, {0x4f, 0x40},
{0x50, 0x34}, {0x51, 0x0C}, {0x52, 0x17}, {0x53, 0x29}, {0x54, 0x40},
{0x57, 0x80}, {0x58, 0x1e}, {0x41, 0x10}, {0x75, 0x60}, {0x76, 0x50},
{0x77, 0x48}, {0x3d, 0x92}, {0x3b, 0x00}, {0x04, 0x00}, {0xff, 0xff}
};
#endif
Android-приложение
На телефоне установите ArduinoTFT.apk. Разрешите доступ к Bluetooth и камере.
Обновление 26.05.2020: Добавлен исходный код ArduinoTFT.apk. Скачайте ArduinoTFT.zip.h, переименуйте в ArduinoTFT.zip и распакуйте.
Обновление 25.07.2020: «Проблема с Android-приложением решилась после разрешения доступа к камере смартфона».
Настройка Bluetooth-модуля
Установите скорость обмена 115200 бод: команда AT+UART=115200,0,0. Это оптимальная скорость для Arduino UNO. Подробнее: instructables.com.
(!) Bluetooth-модуль подключается к отладочному порту Arduino UNO. При работе с Bluetooth отладочный порт недоступен. Перед прошивкой Arduino с установленным Bluetooth-модулем — отключите модуль! После прошивки установите обратно.
Сборка









Сборка проста:
- Соедините Arduino UNO и TFT Shield
- Подключите камеру OV7670 к 20-пиновому разъёму на Shield (иногда удобен угловой переходник 18-20 пинов, шаг 2.54 мм)
- Подключите Bluetooth HC-05/06 к 4-пиновому разъёму с надписью «Bluetooth» на Shield
- Подключите блок питания 6–12 В к разъёму питания Arduino UNO
После включения экран TFT Shield должен стать красным — значит, готовность к приёму команд от Android.
Демонстрация






На Android-телефоне:
- Запустите приложение ArduinoTFT
- Поверните телефон горизонтально
- Включите Bluetooth, выберите найденный модуль (HC-06)
На экране появятся два окна и четыре кнопки:
- Правое верхнее — видоискатель камеры телефона
- Большое левое — принятое или отправленное изображение
Функции кнопок:
- Передать одно изображение с телефона на Arduino
- Непрерывная передача с телефона на Arduino
- Передать одно изображение с Arduino на телефон
- Непрерывная передача с Arduino на телефон
Размер изображения 320×240 пикселей (2–5 КБ). В главе есть демо-видео.
Если статья понравилась — оцените её. Возможно, это мотивирует меня на новые проекты. :-)
Предыдущие статьи:
- Unique TFT Shield for Arduino Uno — Start
- Unique Arduino TFT Shield — OV7670 Cam
Планы:
- TFT Shield для Arduino Nano — Start
- Фото и RGB-видео на SPI-дисплее
- Управление смартфоном 4WD робота по Bluetooth
Обновление 01.04.2021: Выпущена обновлённая библиотека для линейки экранов (два шилда и две breakout-платы). Скетч компилируется в зависимости от выбранной версии (1–4) и типа МК (MegaAVR или ESP32). Подробнее: github.com/Ekaburg/EkaTFT.
Программный код
Библиотека для TFT Shield
3D-модели и крепления
- Эксперимент с OV7670 и пультом ДУ от телевизора
- Эксперимент с камерой OV7670 и пультом ДУ
Производственный процесс
- Эволюция и дизайн современных камер
- Понимание объективов камер: дизайн, история и будущие инновации
- BrickPi Bookreader: Автоматизация чтения Kindle на Raspberry Pi и LEGO Mindstorms
- Создание Bluetooth-робота на Raspberry Pi с аудио-обратной связью
- Bluetooth-контролируемый моторизированный камерный слайдер на Arduino для плавных кинематографических кадров
- Управление светодиодом по Bluetooth с Arduino: простой DIY-гид
- Голосовое управление бытовыми приборами: Bluetooth-управление через Google Assistant
- Переносная система видеонаблюдения: робот с камерой на Raspberry Pi и Arduino
- Продвинутая система обнаружения падений: Arduino + Windows IoT + Azure Cloud
- Удалённое управление лампой через Arduino Uno, реле и Android-приложение Blynk