×

Arduino蓝牙摄像头(ABC)

消耗积分:0 | 格式:zip | 大小:0.00 MB | 2022-10-18

分享资料个

描述

今天,你几乎不会让任何人感到惊讶,因为你的手机配备了摄像头、无线小工具和其他技术进步。多亏了 Arduino 平台,数百万人发现了电子和编程的美妙世界。关于如何通过蓝牙在手机和 Arduino 之间交换数据的 100、500 条指令被写...我在说什么?是的。我想通过蓝牙在 Android 上的手机和 Arduino UNO 之间交换数据 100、501 次。但我想传输的不仅仅是一组字符和数字,而是图片。

有人会说这是不可能的,Arduino 速度太慢,无法快速处理大量数据。他将是绝对正确的。如果对 Arduino 有一点帮助 - 将所有“艰苦”的工作转移到其他设备的肩上呢?并且有这样的设备!

这是 Arduino 独特的 TFT 屏蔽。有关此徽标的信息在以下文章中:文章 1 文章 2在本教程中,我将演示如何通过蓝牙连接 Arduino 和 Android 手机,从 Arduino UNO 上的 OV7670 摄像头获取照片并将其传输到 Android 手机。然后,相反,将图片(来自相机的图像)从 Android 手机传输到 Arduino UNO 并显示在独特的 TFT 屏蔽的屏幕上。

为 Android 手机编写了一个特殊的应用程序。

TFT屏蔽的简要特点:

  • 尺寸 3.5 英寸对角线,
  • 分辨率 320x240,
  • 颜色数 65536(16 位),
  • 电阻式触摸屏(XPT2046控制器),
  • 5个按钮,
  • RTC IC DS1307配3V锂电池CR1220,
  • 用于连接 micro SD 卡的插槽,
  • 4 针 (2.54 mm) 连接器,用于连接蓝牙模块 HC-05 (-06)、ESP8286 WiFi 模块。
  • 用于相机 (OV7670) 的 20 针 (2.54 mm) 连接器。

所需组件列表

 
pYYBAGNOR8GAaVIyAADtezl-qxA968.jpg
 

硬件

  • AC-DC 电源适配器 6-12 伏,>600mA;
  • 安卓手机。

注意:必须(!)使用 6-12 伏电源适配器来操作 TFT 屏蔽,因为 USB 的 500 mA 的最大电流不足以正常操作。

软件

准备中

软件

所有演示草图都是在 Arduino IDE 环境中编写的,因此一开始需要安装 Arduino IDE - https://www.arduino.cc/en/main/software。然后你需要为 TFT shield 安装一个库 - github.com/YATFT/YATFT (下载该库并解压到 Arduino IDE 目录下的“libraries”文件夹中)。

安装 Arduino IDE 后,您必须对 Arduino UNO 板进行编程。为简单起见,我建议单独刷机,不带 TFT 屏蔽。为了这:

  • 将 USB 线连接到 Arduino UNO 板;
  • 在电脑上运行 Arduino IDE;
  • 选择Arduino UNO所连接的对应端口;
  • 下载ArduinoBluetoothCamera.ino演示草图(和文件ov7670_regs.h用于相机初始化);
  • 单击按钮上传

如果 Arduino UNO 板编程成功,您可以继续下一步。

ArduinoBluetoothCamera.ino草图:

/**********************************************************************
* SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A
* PARTICULAR PURPOSE. IN NO EVENT SHALL AUTHOR OR ITS LICENSORS BE
* LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY,
* CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY
* DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO
* ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES,
* LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS,
* TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT
* NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
***********************************************************************/
#include 
#include 
#include 
#include 
#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()
{
   // initialize the serial port
   Serial.begin(115200);
   // initialize the display
   tft.begin();
   tft.SetColor(BRIGHTRED);
   tft.ClearDevice();
}
void loop()
{
   // put your main code here, to run repeatedly:
   if (Serial.available())
   {
       uint8_t temp = Serial.read();
       switch (temp)
       {
           case 0x10: // Send single Photo
                mode = 1;
                start_capt = 1;
                if (last_mode != mode && last_mode != 2) {
                    tft.begin();
                    tft.SetRGB();               // Switch to RGB mode
                    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: // Continuous send Photo
                mode = 2;
                start_capt = 2;
                if (last_mode != mode && last_mode != 1) {
                    tft.begin();
                    tft.SetRGB();               // Switch to RGB mode
                    cam.CamInit(&OV7670_QVGA[0][0]);
                    cam.CamVideoPreview(0, 0, 1, true);
                    codec.JPEGInit();
                    codec.JPEGSetRegs(IMG_SizeX, IMG_SizeY);
                }
                break;
           case 0x30: // Receive single Photo
                mode = 3;
                start_capt = 3;
                if (last_mode != mode && last_mode != 4) {
                    tft.begin();
                    tft.SetYUV();               // Switch to YUV mode
                }
                break;
           case 0x40: // Continuous receive Photo
                mode = 4;
                start_capt = 4;
                if (last_mode != mode && last_mode != 3) {
                    tft.begin();
                    tft.SetYUV();               // Switch to YUV mode
                }
                break;
           default:
                break;
       }
   }
   if (mode == 1) // Send single Photo
   {
       if (start_capt == 1)
       {
           start_capt = 0;
           last_mode = mode;
           mode = 0;
           CamCapture();
       }
   }
   else if (mode == 2) // Continuous send Photo
   {
       while (1)
       {
           uint8_t temp = Serial.read();
           if (start_capt == 2)
           {
               start_capt = 0;
           }        
           if (temp == 0x21) // Stop ?
           {
               start_capt = 0;
               last_mode = mode;
               mode = 0;
               break;
           }
           if (CamCapture()) continue;
       }
   }
   else if (mode == 3) // Receive single Photo
   {
       if (start_capt == 3)
       {
           //Start capture
           start_capt = 0;
           last_mode = mode;
           mode = 0;
           Serial.print("!");
           srl.JPEGReadFromSerial(&jpeg_decode,  // jpeg decode structure
                                             0,  // x0 (left)
                                             0,  // y0 (top)
                                   GetMaxX()+1,  // x1 (right)
                                   GetMaxY()+1,  // y1 (bottom)
                                       32000); // max image size
           }
   }
   else if (mode == 4) // Continuous receive Photo
   {
       uint8_t temp = Serial.read();
       while (1)
       {
           if (start_capt == 4)
           {
               //Start capture
               start_capt = 0;
           }
           if (temp == 0x41) // Stop ?
           {
               start_capt = 0;
               last_mode = mode;
               mode = 0;
               break;
           }
           Serial.print("!");
           srl.JPEGReadFromSerial(&jpeg_decode,  // jpeg decode structure
                                             0,  // x0 (left)
                                             0,  // y0 (top)
                                   GetMaxX()+1,  // x1 (right)
                                   GetMaxY()+1,  // y1 (bottom)
                                        32000); // max image size
       }
   }
}
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 == true)
       {
           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) ) //If find the end ,break while,
           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}, // Size of byte, Address (ID) 
   { 640/16,  480/16}, // Size X, Size Y
   {0b01000010, 0b00000100}, // Reset_Enable_N, 7|6|5|4|3|Vsync Invert|Hsync Invert|0
   {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}, // Size of byte, Address (ID) 
   { 320/16,  240/16}, // Size X, Size Y
   {0b01000010, 0b00000100}, // Reset_Enable_N, 7|6|5|4|3|Vsync Invert|Hsync Invert|0
   {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允许应用使用蓝牙和相机。

2020 年 5 月 26 日更新!

我添加了 ArduinoTFT.apk 的源代码。原样!下载ArduinoTFT.zip.h

,重命名为ArduinoTFT.zip 并解压。享受!

2020 年 7 月 25 日更新!

“嗨,我遇到了同样的问题,Android 应用程序无法正常工作。授权应用程序访问智能手机相机后解决。就是这样。再见”(c)

蓝牙模块

需要在蓝牙模块中设置汇率为115200(命令“AT+UART=115200, 0, 0”)。这是 Arduino UNO 管理接收和处理数据的最佳速度。(理论上可以提高速度,优化数据接收和处理,但这需要更大的RAM)。关于如何设置汇率的更详细说明可以在网上找到,例如,这里:https:/ /www.instructables.com/id/Communication-Bluetooth-Module-With-HC-05-HC-06/

(!)请注意蓝牙模块连接到 Arduino UNO 的调试端口。因此,在使用蓝牙时,调试端口不可用。并且在对 Arduino UNO(配有蓝牙模块)进行编程之前,必须断开蓝牙模块。编程后,将其重新设置(!)

集会

 
 
 
 
poYBAGNOR8WAUbrBAAHcagtf9ow472.jpg
 
1 / 9TFT-shield 和 Arduino UNO
 

该设备的组装非常简单:

  • 将 Arduino UNO 和 TFT-shield 连接在一起;
  • 将 OV7670 相机连接到屏蔽层 TFT-shield 上的 20 针连接器(有时我使用 2.54 毫米间距的有角度的 18-20 针连接器作为适配器);
  • 将蓝牙模块HC-06(HC-05)连接到TFT-shield上带有“蓝牙”字样的4针连接器上;
  • 将 6-12V 电源适配器连接到 Arduino UNO 板上的电源输入。

打开电源后,TFT shield 的屏幕应变为红色。这意味着愿意从 Android 手机接收命令。

 
 
 
1 / 6ArduinoTFT 应用程序
 

在安卓手机上执行以下操作:

  • 在 Android 手机上启动ArduinoTFT应用程序;
  • 将手机置于水平位置;
  • 开启蓝牙连接,选择检测到的蓝牙模块(HC-06);

屏幕上应出现两个窗口和四个按钮:

  • 右上角窗口是手机的相机取景窗口;
  • 左侧大窗口 - 接收或发送的图像。

按钮功能:

  • 将单个图像从 Android 手机传输到 Arduino;
  • 将图像从 Android 手机连续传输到 Arduino;
  • 将单个图像从 Arduino 传输到 Android 手机;
  • 将图像从 Arduino 连续传输到 Android 手机。

图像大小为 320x240 像素 (2-5 kB)。本章有一个演示视频。

再一次问好!有一个用于一系列屏幕的更新库,目前由两个屏蔽和两个分线板组成。草图的编译取决于所选版本(从 1 到 4)和微控制器类型(MegaAVR 或 ESP-32)。添加了照片,示例。更多信息可以在https://github.com/Ekaburg/EkaTFT中找到。

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'Arduino蓝牙摄像头(ABC)',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);