极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 10847|回复: 4

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

[复制链接]
发表于 2017-4-19 23:20:13 | 显示全部楼层 |阅读模式
本帖最后由 HuaShine2015 于 2017-4-21 10:26 编辑

        上次介绍了UVD小项目的硬件和固件的实现。虽然使用核心固件或usbkey固件可以不借助任何上位机APP即可读取信息。但是如果搭配一个更直观友好的APP可以给使用者带来更加好的感受。做了一个Android端demo供大家参考(UVD使用uvd_usbhid_kernel版本固件), APP UI选择使用直接的色块对应曝晒等级的方式带给使用者最直接的感受,另外主题模式选择精简干练的对话模式,加入监听系统USB设备插入事件,当有UVD设备插入后APP能很smart的自动运行并弹出界面

APP源码可以从我的github仓库checkout 传送门
已编译并sign的apk可以在这里下载传送门

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

        在动手写Android代码之前,我们先来确认一下我们UVD的固件代码和上位机的握手协议是否正常工作。在uvd_usbhid_kernel版本固件中程序流程如下

怎样验证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驱动见上篇演示的固件更新工具)

         确认下位机固件通信正常以后,下面就是研究怎样在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手机系统没有被品牌商二次开发精简过,就没有兼容性的问题

从图片中可以得到我们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数据信息如下:

从两张信息流追踪图片即可得到我们所需要的所有信息
        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代码截图:
  1. package com.shine.geektoy_uvd;

  2. import java.nio.ByteBuffer;
  3. import java.nio.CharBuffer;
  4. import java.nio.charset.Charset;
  5. import java.util.HashMap;

  6. import org.json.JSONException;
  7. import org.json.JSONObject;

  8. import android.app.Activity;
  9. import android.hardware.usb.UsbDevice;
  10. import android.hardware.usb.UsbDeviceConnection;
  11. import android.hardware.usb.UsbInterface;
  12. import android.hardware.usb.UsbManager;
  13. import android.os.Bundle;
  14. import android.view.KeyEvent;
  15. import android.view.Menu;
  16. import android.view.MenuItem;
  17. import android.widget.ImageView;
  18. import android.widget.TextView;
  19. import android.widget.Toast;

  20. public class MainActivity extends Activity {

  21.         private TextView info;
  22.         //private TextView info2;
  23.         private final int VendorID = 5824;
  24.         private final int ProductID = 1503;
  25.         private UsbManager myUsbManager;
  26.         private UsbDevice myUsbDevice;
  27.         private UsbInterface myInterface;
  28.         private UsbDeviceConnection myDeviceConnection;
  29.         private String status = "";

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

  33.         @Override
  34.         protected void onCreate(Bundle savedInstanceState) {
  35.                 super.onCreate(savedInstanceState);
  36.                 setContentView(R.layout.activity_main);
  37.                 info = (TextView) findViewById(R.id.data);
  38.             //info2 = (TextView) findViewById(R.id.data2);
  39.                 myUsbManager = (UsbManager) getSystemService(USB_SERVICE);
  40.                 final ImageView show = (ImageView) findViewById(R.id.show);
  41.                 if (enumerateDevice() != false) {
  42.                         if (findInterface() != false) {
  43.                                 openDevice();
  44.                                 if (status.contains("low")) {
  45.                                         show.setImageResource(imageIds[1]);
  46.                                 } else if (status.contains("moderate")) {
  47.                                         show.setImageResource(imageIds[2]);
  48.                                 } else if ((status.contains("high"))&&(!status.contains("very"))) {
  49.                                         show.setImageResource(imageIds[3]);
  50.                                 } else if (status.contains("veryhigh")) {
  51.                                         show.setImageResource(imageIds[4]);
  52.                                 } else if (status.contains("extreme")) {
  53.                                         show.setImageResource(imageIds[5]);
  54.                                 } else {
  55.                                         show.setImageResource(imageIds[0]);
  56.                                 }
  57.                         } else {
  58.                                 info.setText("UVD未连接");
  59.                         }
  60.                 } else {
  61.                         info.setText("UVD未连接");
  62.                 }

  63.         }

  64.         private boolean enumerateDevice() {
  65.                 if (myUsbManager != null) {
  66.                         HashMap<String, UsbDevice> deviceList = myUsbManager
  67.                                         .getDeviceList();
  68.                         if (!deviceList.isEmpty()) {
  69.                                 StringBuffer sb = new StringBuffer();
  70.                                 for (UsbDevice device : deviceList.values()) {
  71.                                         if (device.getVendorId() == VendorID
  72.                                                         && device.getProductId() == ProductID) {
  73.                                                 myUsbDevice = device;
  74.                                                 return true;
  75.                                         }
  76.                                 }
  77.                         }
  78.                 }

  79.                 return false;
  80.         }

  81.         private boolean findInterface() {
  82.                 if (myUsbDevice != null) {
  83.                         // showTmsg("interfaceCounts : " + myUsbDevice.getInterfaceCount());
  84.                         for (int i = 0; i < myUsbDevice.getInterfaceCount(); i++) {
  85.                                 UsbInterface intf = myUsbDevice.getInterface(i);
  86.                                 if (intf.getInterfaceClass() == 3
  87.                                                 && intf.getInterfaceSubclass() == 0
  88.                                                 && intf.getInterfaceProtocol() == 0) {
  89.                                         myInterface = intf;
  90.                                         return true;
  91.                                         // showTmsg("取得端点信息:" +
  92.                                         // myInterface.getEndpoint(0).toString());
  93.                                 }
  94.                         }

  95.                 }
  96.                 return false;
  97.         }

  98.         private void openDevice() {
  99.                 if (myInterface != null) {
  100.                         UsbDeviceConnection conn = null;

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

  104.                         if (conn == null) {

  105.                         }

  106.                         if (conn.claimInterface(myInterface, true)) {
  107.                                 ByteBuffer getbuf = ByteBuffer.allocate(80);
  108.                                 CharBuffer getchar = CharBuffer.allocate(80);
  109.                                 myDeviceConnection = conn;
  110.                                 // showTmsg("打开设备成功");
  111.                                 byte[] buffer = new byte[1];
  112.                                 byte[] getvalue = new byte[1];
  113.                                 boolean jsvalue = false;
  114.                                 myDeviceConnection.controlTransfer(0x20, 0x09, 0x0000, 0x0067,
  115.                                                 buffer, buffer.length, 1000);
  116.                                 myDeviceConnection.controlTransfer(0x20, 0x09, 0x0000, 0x000A,
  117.                                                 buffer, buffer.length, 1000);
  118.                                 try {
  119.                                         Thread.sleep(2);
  120.                                 } catch (InterruptedException e) {
  121.                                         // TODO Auto-generated catch block
  122.                                         e.printStackTrace();
  123.                                 }
  124.                                 for (int i = 0; i < 80; i++) {
  125.                                         myDeviceConnection.controlTransfer(0xA0, 0x01, 0x0000,
  126.                                                         0x0000, getvalue, getvalue.length, 1000);
  127.                                         if (getvalue[0] == 123) {
  128.                                                 jsvalue = true;
  129.                                         }
  130.                                         if (jsvalue == true) {
  131.                                                 getbuf.put(getvalue[0]);
  132.                                         }
  133.                                         if (jsvalue == true && getvalue[0] == 125) {
  134.                                                 jsvalue = false;
  135.                                                 getbuf.flip();
  136.                                                 break;
  137.                                         }
  138.                                 }
  139.                                 conn.close();
  140.                                 conn.releaseInterface(myInterface);
  141.                                 Charset cs = Charset.forName("UTF-8");
  142.                                 getchar = cs.decode(getbuf);
  143.                             //info2.setText(getchar.toString());
  144.                                 try {
  145.                                         JSONObject myJsonObject = new JSONObject(getchar.toString());
  146.                                         info.setText("实时数据:" + myJsonObject.getString("real_data"));
  147.                                         status = myJsonObject.getString("exposure_level");
  148.                                 } catch (JSONException e) {
  149.                                         // TODO Auto-generated catch block
  150.                                         e.printStackTrace();
  151.                                 }

  152.                         } else {
  153.                                 conn.close();

  154.                         }
  155.                 }

  156.         }

  157.         /*
  158.          * private void showTmsg(String msg) { Toast.makeText(MainActivity.this,
  159.          * msg, Toast.LENGTH_SHORT).show(); }
  160.          *
  161.          * private void showTmsg_int(int msg) { Toast.makeText(MainActivity.this,
  162.          * msg, Toast.LENGTH_SHORT).show(); }
  163.          */
  164.        
  165. }
复制代码


未完待续。。。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2017-4-20 00:22:36 | 显示全部楼层
不是每个手机都支持usb host或adk的,为什么不用便宜的esp8266 wifi来搞
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-4-20 09:07:08 | 显示全部楼层
nick_zm 发表于 2017-4-20 00:22
不是每个手机都支持usb host或adk的,为什么不用便宜的esp8266 wifi来搞

原因可以见我上一篇文章里项目目标

至于Host和ADK目前的Android系统是100%支持的,主流手机品牌都带OTG功能。
如果遇到不能支持的手机,那基本上是廉价的杂牌机,厂家在硬件上cost down了。
节省了将电池4.2v升压成5v输出的功能ic,有的甚至将power pin串接二极管,阻挡反向供电。
回复 支持 反对

使用道具 举报

发表于 2017-4-20 22:18:29 | 显示全部楼层
HuaShine2015 发表于 2017-4-20 09:07
原因可以见我上一篇文章里项目目标

至于Host和ADK目前的Android系统是100%支持的,主流手机品牌都带OT ...

确实是有这种现象存在,但是有一种手机叫做小米。。。他们的手机支持OTG,但是就像苹果的nfc一样只能内部使用,你要是想用OTG服务需要向他们申请卡刷,很少会有人为了用你的程序而卡刷手机,毕竟这样不保修而且人家还不一定同意你的申请。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-4-21 09:38:24 | 显示全部楼层
zjz5717 发表于 2017-4-20 22:18
确实是有这种现象存在,但是有一种手机叫做小米。。。他们的手机支持OTG,但是就像苹果的nfc一样只能内部 ...

苹果手机的权限申请不太清楚。Android手机只要OTG线接U盘,鼠标等外设能用,就是OK的。
我的手机是红米Note 4X和小米Note,都是原版MIUI 8.2稳定版,无Root, 没有遇到你说的需要卡刷权限问题
回复 支持 反对

使用道具 举报

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

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-23 16:02 , Processed in 0.045558 second(s), 20 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表