6328浏览
查看: 6328|回复: 1

[项目] 创客成长之路 -- 先定一个小目标做他个小项目【中篇】

[复制链接]
本帖最后由 ShineHua2017 于 2017-4-21 10:43 编辑

上次介绍了UVD小项目的硬件和固件的实现。虽然使用核心固件或usbkey固件可以不借助任何上位机APP即可读取信息。但是如果搭配一个更直观友好的APP可以给使用者带来更加好的感受。

做了一个Android端demo供大家参考(UVD使用uvd_usbhid_kernel版本固件), APP UI选择使用直接的色块对应曝晒等级的方式带给使用者最直接的感受,另外主题模式选择精简干练的对话模式,加入监听系统USB设备插入事件,当有UVD设备插入后APP能很smart的自动运行并弹出界面

创客成长之路 -- 先定一个小目标做他个小项目【中篇】图1创客成长之路 -- 先定一个小目标做他个小项目【中篇】图2
APP源码可以从我的github仓库checkout传送门
已编译并sign的apk可以在这里下载传送门

再介绍一下Android下APP实现过程

在动手写Android代码之前,我们先来确认一下我们UVD的固件代码和上位机的握手协议是否正常工作。在uvd_usbhid_kernel版本固件中程序流程如下创客成长之路 -- 先定一个小目标做他个小项目【中篇】图3


怎样验证usb host发送get信息和UVD返回json格式数据,这里可以利用digistump组织做好的PC端debug小工具send、receive
digistump还很贴心的使用C++和Python编译了Windows/Linux/Mac的三种版本工具和源码可以到digistump的github clone传送门


将UVD插入已安装驱动的PC机USB接口,使用命令行执行send g发送读取指令,执行receive读取UVD返回的数据(如何安装PC上Windows/Linux驱动见上篇演示的固件更新工具)创客成长之路 -- 先定一个小目标做他个小项目【中篇】图4
确认下位机固件通信正常以后,下面就是研究怎样在Andorid环境下使用Java代码实现同样的通信功能。

Android系统下USB相关开发背景资料见Google开发官网传送门
只要知道分Host and Accessory两种模式(USB Accessory是我们在Arduino ADK开发上常用的)我们这里需要使用USB Host模式,手机作为类似PC的角色。USB协议只需要了解USB HID设备类协议部分,网上有很多整理的不错的帖子,可以参考这篇传送门


在了解了官方API sample资料我们就可整一个测试code,先抓取一下Android下的USB设备信息。来确认UVD在Android系统下是否能正常挂载,是否有权限开启USB device。抱着忐忑的心情运行测试code抓取信息。运气还不错我的小米Note4X系统原生支持libusb,UVD挂载成功。官方文档有讲USB accessory and host modes are directly supported in Android 3.1 (API level 12) or newer platforms. 也就是说你的Android手机系统没有被品牌商二次开发精简过,就没有兼容性的问题创客成长之路 -- 先定一个小目标做他个小项目【中篇】图5
从图片中可以得到我们UVD设备的PID/VID值5824/1503(十进制),挂载了1个interface(mClass=3,mSubClass=0,mProtocol=0),1个interrupt类型endpoint。未配置endpoint情况下直接open device也是成功,权限没有问题

在初次研究了官方的UsbDeviceConnection类里提供的bulkTransfer()和controlTransfer()两种收发数据的方法参数并不是很理解,索性跟踪一下前面提到的debug工具send/receive的usb数据流。
使用USBlyzer usb抓包工具追踪在PC上执行send g和receive的命令时usb数据信息如下:

创客成长之路 -- 先定一个小目标做他个小项目【中篇】图6创客成长之路 -- 先定一个小目标做他个小项目【中篇】图7
从两张信息流追踪图片即可得到我们所需要的所有信息
        1.采用控制管道即controlTransfer()方式传输数据,而USB HID设备协议有定义所有HID设备通过USB的控制管道(默认管道,即端点0)和中断管道与主机通信。引申出我们不需要在Android代码中定义endpointout和endpointin端口,可以直接使用controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)方法实现收发,该方法通过0节点向此设备传输数据,传输的方向取决于请求的类别,如果requestType为USB_DIR_OUT则为写数据,USB_DIR_IN, 则为读数据。
        2.controlTransfer()方法的所用参数在两张追踪图的下方已经全部给出,照着写就好了


最后贴一下完成后的Android代码截图:[mw_shl_code=java,true]package com.shine.geektoy_uvd;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

        private TextView info;
        //private TextView info2;
        private final int VendorID = 5824;
        private final int ProductID = 1503;
        private UsbManager myUsbManager;
        private UsbDevice myUsbDevice;
        private UsbInterface myInterface;
        private UsbDeviceConnection myDeviceConnection;
        private String status = "";

        int[] imageIds = new int[] { R.drawable.unknown, R.drawable.low,
                        R.drawable.mod, R.drawable.high, R.drawable.vh, R.drawable.ext };
        int currentImageId = 0;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                info = (TextView) findViewById(R.id.data);
            //info2 = (TextView) findViewById(R.id.data2);
                myUsbManager = (UsbManager) getSystemService(USB_SERVICE);
                final ImageView show = (ImageView) findViewById(R.id.show);
                if (enumerateDevice() != false) {
                        if (findInterface() != false) {
                                openDevice();
                                if (status.contains("low")) {
                                        show.setImageResource(imageIds[1]);
                                } else if (status.contains("moderate")) {
                                        show.setImageResource(imageIds[2]);
                                } else if ((status.contains("high"))&&(!status.contains("very"))) {
                                        show.setImageResource(imageIds[3]);
                                } else if (status.contains("veryhigh")) {
                                        show.setImageResource(imageIds[4]);
                                } else if (status.contains("extreme")) {
                                        show.setImageResource(imageIds[5]);
                                } else {
                                        show.setImageResource(imageIds[0]);
                                }
                        } else {
                                info.setText("UVD未连接");
                        }
                } else {
                        info.setText("UVD未连接");
                }

        }

        private boolean enumerateDevice() {
                if (myUsbManager != null) {
                        HashMap<String, UsbDevice> deviceList = myUsbManager
                                        .getDeviceList();
                        if (!deviceList.isEmpty()) {
                                StringBuffer sb = new StringBuffer();
                                for (UsbDevice device : deviceList.values()) {
                                        if (device.getVendorId() == VendorID
                                                        && device.getProductId() == ProductID) {
                                                myUsbDevice = device;
                                                return true;
                                        }
                                }
                        }
                }

                return false;
        }

        private boolean findInterface() {
                if (myUsbDevice != null) {
                        // showTmsg("interfaceCounts : " + myUsbDevice.getInterfaceCount());
                        for (int i = 0; i < myUsbDevice.getInterfaceCount(); i++) {
                                UsbInterface intf = myUsbDevice.getInterface(i);
                                if (intf.getInterfaceClass() == 3
                                                && intf.getInterfaceSubclass() == 0
                                                && intf.getInterfaceProtocol() == 0) {
                                        myInterface = intf;
                                        return true;
                                        // showTmsg("取得端点信息:" +
                                        // myInterface.getEndpoint(0).toString());
                                }
                        }

                }
                return false;
        }

        private void openDevice() {
                if (myInterface != null) {
                        UsbDeviceConnection conn = null;

                        if (myUsbManager.hasPermission(myUsbDevice)) {
                                conn = myUsbManager.openDevice(myUsbDevice);
                        }

                        if (conn == null) {

                        }

                        if (conn.claimInterface(myInterface, true)) {
                                ByteBuffer getbuf = ByteBuffer.allocate(80);
                                CharBuffer getchar = CharBuffer.allocate(80);
                                myDeviceConnection = conn;
                                // showTmsg("打开设备成功");
                                byte[] buffer = new byte[1];
                                byte[] getvalue = new byte[1];
                                boolean jsvalue = false;
                                myDeviceConnection.controlTransfer(0x20, 0x09, 0x0000, 0x0067,
                                                buffer, buffer.length, 1000);
                                myDeviceConnection.controlTransfer(0x20, 0x09, 0x0000, 0x000A,
                                                buffer, buffer.length, 1000);
                                try {
                                        Thread.sleep(2);
                                } catch (InterruptedException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                }
                                for (int i = 0; i < 80; i++) {
                                        myDeviceConnection.controlTransfer(0xA0, 0x01, 0x0000,
                                                        0x0000, getvalue, getvalue.length, 1000);
                                        if (getvalue[0] == 123) {
                                                jsvalue = true;
                                        }
                                        if (jsvalue == true) {
                                                getbuf.put(getvalue[0]);
                                        }
                                        if (jsvalue == true && getvalue[0] == 125) {
                                                jsvalue = false;
                                                getbuf.flip();
                                                break;
                                        }
                                }
                                conn.close();
                                conn.releaseInterface(myInterface);
                                Charset cs = Charset.forName("UTF-8");
                                getchar = cs.decode(getbuf);
                            //info2.setText(getchar.toString());
                                try {
                                        JSONObject myJsonObject = new JSONObject(getchar.toString());
                                        info.setText("实时数据:" + myJsonObject.getString("real_data"));
                                        status = myJsonObject.getString("exposure_level");
                                } catch (JSONException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                }

                        } else {
                                conn.close();

                        }
                }

        }

        /*
         * private void showTmsg(String msg) { Toast.makeText(MainActivity.this,
         * msg, Toast.LENGTH_SHORT).show(); }
         *
         * private void showTmsg_int(int msg) { Toast.makeText(MainActivity.this,
         * msg, Toast.LENGTH_SHORT).show(); }
         */
        
}[/mw_shl_code]


未完待续。。。











gada888  版主

发表于 2017-4-21 12:35:19

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail