A20 Plus(esp8266与A6C)拍照传输图片

最近在研究乐鑫esp8266,看到安信可科技淘宝上一块A20plus开发板,该模块上面集成8266跟A6C两个模块,不仅支持WiFi,还支持GPRS,还带有摄像头。价格还不到50块,呵呵,物联网还能这样玩。发现可以通过通过A6C拍照,并且把图片通过WiFi传输给手机。
这里写图片描述
心血来潮买回来研究,这个板子比我想象中还要大。
废话不多说,下面说重点。
首先说明该模块内部原理。8266跟A6C集成在同一个模块,8266有两个串口,Uart0和Uart1,Uart0是IO1和IO3,Uart1是IO2,但是为了让防止8266上电信息干扰了A6C(猜测),8266的IO13和IO15跟A6C TX和RX连接,通过软件把串口0引脚交换,这样不仅能够通过串口烧录程序,同时也能解决串口通信问题。
下面说一下我程序实现拍照WiFi传输图片原理,A6C拍照支持VGA,QVGA,QQVGA三种格式,8266发送拍照指令给A6C,接着8266通过串口每次读取A6C图片4k字节并且发送出去。为了方便调试,我把uart1作为调试信息输出。
下面通过程序给大家说明实现过程。
1.8266上电进行串口0引脚交换,并且波特率设置为230400,通过大量实现,发现读取4k数据的时候,最佳波特率设置为230400,更高的波特率很大几率读取失败。
这里写图片描述
2.接着8266进入AP模式,并且打开作为TCP服务器。
这里写图片描述
3.接着8266一直往8266发送“AT”指令给A6C,原因是A6C刚上电一直往A6C发送“AT”指令,A6C能够自动匹配波特率。所以我这里设置一个定时器,大概400ms发送一次“AT”指令,大概过了5秒后,A6C跟8266波特率能够同步。
这里写图片描述
这里写图片描述
4.接着手机开发发送拍照指令,但8266在TCP模式下接收拍照指令,往A6C发送以下指令。
1)开始先执行摄像头停止工作,往发送“AT+CAMSTOP”。
2)接着关闭回显,往A6C发送“ate0”,关闭回显目的是让A6C不返回输入指令。
3)接着打开摄像头,并且设置拍照参数,通过发送“AT+CAMSTART=0,1,2”设置拍照规格,0是QVGA,1是VGA,2是QQVGA。
4)接着 发送拍照指令,发送“AT+CAMCAP”。
5)大概几秒后,A6C返回图片的大小。8266把数据包进行计算并且切割。并且把提前数据包数量发给手机。
6)当手机收到数据包的大小,则开始发送指令,则开始8266开始向A6C模块发送指令读取数据。8266每次读取A6C上面4k字节的图片数据,然后发送出去。经过测试,8266跟A6C串口传输4k字节数据最好设置波特率为230400。当然,要把官方串口程序要修改才能使用,把字符串容量改为4k以上。
这里写图片描述
每次拍照的图片大小是100k以上,为了防止数据包缺失或者错误,每次在前后都要加校验位,所以 每次在字符串的格式是0xee+数据包编号+4k字节数据+0xcc+0xff。
7)当手机收到4k个字节数据以后,手机会进行数据包校验,如果校验正确,这返回“ok”指令,8266往A6C发送读取下一个4k字节的指令,重复步骤6。如果读取数据包错误,则手机计算出正确数据包读取位置,模块接收到该指令并且往A6C重新读取。
8)当读到最后一个包,最后一个数据包可能没有达到4k字节,所以8266自己计算和设置,在接收最后一个包的时候,串口必须知道要接收多少个字节。当接收完相应大小数据时,再以4k字节发送出去。
下面是串口处理最后一个字节

![程序处理最后一个数据包](https://img-blog.csdn.net/20160831111813478)

可能大家看到程序中间有定时器,定时器作用是防止8266读取一个4k字节失败,经过逻辑分析仪分析,读取4k字节数据需要200ms时间,所以设置超过250ms后没有读完4k字节数据,则设置定时器要重新读取。
下面是安卓代码解释
A20代码解析
1.编译环境
Android studio 环境 以及 SDK 版本 用户可以根据自己IDE环境与SDK版本进行相应的修改
这里写图片描述
2.操作过程

见《演示过程->A20操作步骤》
3.实现原理

该demo比较简单,没特定的框架,只是两三个activity之间进行跳转,主要是在xml的文件上需要读者自己去理解一下。 实现的原理是通过socket的长连接进行TCL的通信过程,按一定的协议与A20进行数据交互。 TCP通信IP地址为192.168.4.1,端口号PORT = 80 在Application 中initSocket() 把socket有关的数据进行定义,对接,方便activity对socket的调用。
AndroidManifest.xml

Application

4.代码解析

CameraActivity
a.获取定义好的socket,PrintWriter,DataInputStream

 private void initsocket() {
        appUtil = (ApplicationUtil) CameraActivity.this.getApplication();
        socket = appUtil.getSocket();
        out = appUtil.getOut();
        is = appUtil.getIn();
        isConnect = true;
    }

b.选择对应的像素型号

/***
     * 实例化标题栏弹窗
     */
    private void initPopWindow() {

        titlePopup = new TitlePopup(this, ViewPager.LayoutParams.WRAP_CONTENT,
                ViewPager.LayoutParams.WRAP_CONTENT);
        titlePopup.setItemOnClickListener(onitemClick);
        // 给标题栏弹窗添加子类
        titlePopup.addAction(new ActionItem(this, R.string.VGA,
                R.drawable.move_icon));
        titlePopup.addAction(new ActionItem(this, R.string.QVGA,
                R.drawable.move_icon));
        titlePopup.addAction(new ActionItem(this, R.string.QQVGA,
                R.drawable.move_icon));
    }

   private TitlePopup.OnItemOnClickListener onitemClick = new TitlePopup.OnItemOnClickListener() {
        @Override
        public void onItemClick(ActionItem item, int position) {
            switch (position) {
                case 0:// BEGIN1
                    StrRadio = "begin1";
                    break;
                case 1:// BEGIN0
                    StrRadio = "begin0";
                    break;
                case 2:// BEGIN2
                    StrRadio = "begin2";
                    break;
                default:
                    break;
            }
        }
    };

c.进行拍照,读取数据 过程:当 is.available()读取到数据,len = is.read(buf) 写入buf之中,若len < 0 则表示连接断开。 先进行begin拍照,返回数据包的总长度,经计算获得包数以及余数,方便后面进行数据包的判断。 再进行read 读取数据,读取4100字节,由于4K数据包在传输过程会根据网络的情况切分成2-3数据包,所以 需要自己进行数据解析。通过协议“ee 01 +4096 +cc ff”来保证数据包的完整。 每次接收完成后,发送OK指令,再对4100数据包裁剪成4096字节,直到对最后一个数据包裁剪一个余数字节的数据包。

/***
     * 接收数据的thread
     */
    class BtnTestRecvThread extends Thread {

        @Override
        public void run() {
            super.run();
            while (isConnect) {
                if ((!socket.isClosed()) && (socket.isConnected())) {
                    // 是否接收到数据
                    if (!socket.isInputShutdown()) {
                        try {
                            //读取接收的数据
                            if (is.available() < DATASIZE) {
                                buf = null;
                                buf = new byte[DATASIZE];
                            }
                            len = is.read(buf);
                            Log.i("bear", "len=" + len);
//                            Log.i("bear", StringUtils.bytesToHexString(buf));
                            if (len < 0) {
                                SetState(STATE_DISCONNECT);
                            }
                            switch (myState) {
                                case 0:
                                    is.close();
                                    handler.sendEmptyMessage(handler_key.DISCONNECT.ordinal());
                                    break;
                                case 1:
                                    RecvData = new String(buf, 0, buf.length);
                                    handler.sendEmptyMessage(handler_key.RECECT_BEGIN.ordinal());
                                    break;
                                case 2:
                                    sum += len;
                                    if (mylength != DATASIZE) {
                                        System.arraycopy(buf, 0, getBuf, mylength, len);//截取中间数据
                                        mylength += len;
                                        Log.i("bear", "[mylength]=" + mylength + "[getBuf]=" + StringUtils.bytesToHexString(getBuf));
                                    }
                                    if (mylength == DATASIZE) {
                                        if (DealPackageIsRight(getBuf)) {
                                            DealBufSize();
                                            count++;
                                            cleanRecvData(0);
                                        }
                                    }
                                    break;
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }
    }

附A20协议:

begin:A20进行拍照 其中begin1,begin0,begin2分别对应像素VGA(640480),QVGA(320240),QQVGA(160*120)。 默认 StrRadio = “begin2” 传输速率快,而 StrRadio = “begin1”像素最清晰。

read: A20发送图片数据到App 其中 A20 将整个大数据以4K数据包进行发送,不足4K包(最后一个数据包一般不够4k)也以4K 发送,待App端进行裁剪,如:一个图片数据为10547字节,10547/4K ,输出结果为3个数据包,最后数据包要裁剪出2355字节。

ok: App接收到数据包 App每收到数据包就要发送一次“ok”到A20,表示自己接收数据,A20才会发下一个数据包,否则处于等待状态。

at+camrd=count 4096 , (count 4096 + 4095) :表示接收不到数据包 其中 count为当前数据包,在通信异常的下,App接收不到完整的数据包,而A20已发送完数据处于等待状态, App 发送该协议让A20再次发送该数据包

4K协议:4100字节(“ee 00 +4096字节 + cc ff”) 其中 00表示第一个数据包,01为第二个数据包,以此类推 每次接收过程也要对数据包进行判断,以分辨数据包的完整性,假若不是该协议则发送错误指令(at+camrd=count 4096 , (count 4096 + 4095))

\r\n :换行符 每次发送以上数据,后面都应添加 换行符

下面是我代码地址
http://download.csdn.net/detail/u010386121/9618175
完,如果大家有什么疑问或者问题,欢迎指出

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值