2017-12-20 15:24:05 qq_29208637 阅读数 409
import android.util.Log;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;

/**
 * 说明: 打开非记名柜 记名柜  在本项目中目前只用到打开记名柜 
 *
 * 其中crc为数据校验为约定好的  
*/public class OpenBox { private static final int MAX_DATA_PACKET_LENGTH = 40; private byte[] buffer = new byte[MAX_DATA_PACKET_LENGTH]; private DatagramSocket udpSocket;
	//通讯串口的端口号  修改
    private int portnum = 5888;
    private DatagramPacket dataPacket;


    /**
     * portid 端口号
     * boxid 设备地址
     * serverip串口服务器ip
     * 此段参数是约定好的可修改
     */
    public void openClientBox(String boxid, String portid, String serverip) throws IOException {
        try {
            udpSocket = new DatagramSocket(null);
            udpSocket.setReuseAddress(true);
            udpSocket.bind(new InetSocketAddress(portnum));
        } catch (SocketException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        dataPacket = new DatagramPacket(buffer, MAX_DATA_PACKET_LENGTH);
        DatagramPacket inPacket = new DatagramPacket(buffer, MAX_DATA_PACKET_LENGTH);
        byte[] out = asciiToString("A5 " + boxid + " 90 9C " + crc2("A5 " + boxid + " 90 9C"));//发送到串口服务器的数据

        dataPacket.setData(out);
        dataPacket.setLength(out.length);//数据长度
        dataPacket.setPort(Integer.parseInt(portid));
        Log.e("FUCK", out.toString());
        InetAddress broadcastAddr = InetAddress.getByName(serverip);
        dataPacket.setAddress(broadcastAddr);
        udpSocket.send(dataPacket);
        //udpSocket.receive(inPacket);
        //return new String(buffer, 0 , inPacket.getLength());
        //System.out.println(stringToHexString(new String(buffer , 0 , inPacket.getLength())));
        //Const.showMsg(MainActivity.this,stringToHexString(new String(buffer, 0 , inPacket.getLength())));
    }


    public static byte[] asciiToString(String value) {
        String[] chars = value.split(" ");
        byte[] sbu = new byte[chars.length];
        for (int i = 0; i < chars.length; i++) {
            sbu[i] = (byte) Integer.parseInt(chars[i], 16);
        }
        return sbu;
    }

    /*与硬件沟通数据处理方案*/
    public String crc2(String crcstr) {
        String[] chars = crcstr.split(" ");
        int sum;
        sum = 0;
        for (int i = 0; i < chars.length; i++) {
            sum += Integer.parseInt(chars[i], 16);
        }
        byte sumbyte = (byte) sum;
        return Integer.toHexString(sumbyte & 0xFF);
    }

    public void openJudgeBox(String boxid, String portid, String serverip) throws Exception {

        udpSocket = new DatagramSocket(portnum++);

        dataPacket = new DatagramPacket(buffer, MAX_DATA_PACKET_LENGTH);
        DatagramPacket inPacket = new DatagramPacket(buffer, MAX_DATA_PACKET_LENGTH);
        byte[] out = asciiToString("A5 " + boxid + " 03 " + crc("A5 " + boxid + " 03"));

        dataPacket.setData(out);
        dataPacket.setLength(out.length);
        dataPacket.setPort(Integer.parseInt(portid));
        Log.e("FUCK", out.toString());
        InetAddress broadcastAddr = InetAddress.getByName(serverip);
        dataPacket.setAddress(broadcastAddr);
        udpSocket.send(dataPacket);
        //udpSocket.receive(inPacket);
        //System.out.println(stringToHexString(new String(buffer , 0 , inPacket.getLength())));
        //Const.showMsg(MainActivity.this,stringToHexString(new String(buffer, 0 , inPacket.getLength())));

    }

    public String crc(String crcstr) {
        String[] chars = crcstr.split(" ");
        int sum;
        sum = 0;
        for (int i = 0; i < chars.length; i++) {
            sum += Integer.parseInt(chars[i], 16);//String转为16进制的int类型
        }
        byte sumbyte = (byte) sum;
        sumbyte = (byte) ~sumbyte;
        return Integer.toHexString(sumbyte & 0xFF);
    }

    public void openJudgeBoxSlot(String boxid, String portid, String serverip) throws Exception {

        udpSocket = new DatagramSocket(portnum++);

        dataPacket = new DatagramPacket(buffer, MAX_DATA_PACKET_LENGTH);
        DatagramPacket inPacket = new DatagramPacket(buffer, MAX_DATA_PACKET_LENGTH);
        byte[] out = asciiToString("A5 " + boxid + " 01 " + crc("A5 " + boxid + " 01"));

        dataPacket.setData(out);
        dataPacket.setLength(out.length);
        dataPacket.setPort(Integer.parseInt(portid));
        Log.e("FUCK", out.toString());
        InetAddress broadcastAddr = InetAddress.getByName(serverip);
        dataPacket.setAddress(broadcastAddr);
        udpSocket.send(dataPacket);

        //udpSocket.receive(inPacket);
        //System.out.println(stringToHexString(new String(buffer , 0 , inPacket.getLength())));
        //Const.showMsg(MainActivity.this,stringToHexString(new String(buffer, 0 , inPacket.getLength())));

    }
}
2016-05-14 02:14:37 Suma_sun 阅读数 3457

在智能家居公司混了这么久,都没怎么发些有营养的东西,最近感觉不能再这么懒下去了。准备写几篇有关的文章,就当记录下免得忘记了,也方便其他人少走弯路。

本文为楼主原创,转载请表明出处:http://blog.csdn.net/Suma_sun/article/details/51388816

  • 数据包协议(数据收发协议)
  • 命令协议(命令交互)
  • 类型转换

为什么需要数据包协议呢,如果没有对应的协议,你们将无法沟通成功,牛头不对马嘴的在交互,都完全不知道对方说的是什么。
首先硬件嵌入式多是用c/c++开发的所以想要与之交互需要使用它所能读懂的数据结构——结构体。
c/c++和java不同数据类型是不同的,所以通讯的时候要使用通用的数据类型——字节流(byte[])。PS:java、c/c++数据类型各占几个字节就请自行百度吧。

数据包协议
搞清楚了交互的数据类型,就该弄清楚接收到的数据的内容,因为都是以字节来表示,所以你需要知道某个属性或者说是描述的字节长度,否者强行转型也都是乱码(字符串)或错误的值。接下来就来定义一个结构体作为协议的内容。

typedef struct {
 int cmd;//命令标识码
 SMsgContent content;//实际内容
}SMsg

结构体已经给出了,也都有注释不难看出其中意义。cmd是交互命令的唯一标识码,就像Handler的what一样,用于选择对应的解析与操作。

下面给出是cmd的写法


public static final int IOCONTROL_GET_ALL_PARAMS_REQ = 0x03D8;

public static final int IOCONTROL_GET_ALL_PARAMS_RESP = 0x03D9;

命令协议
命令协议就是写在数据包协议里的content的。用作cmd对应的交互用数据,比如该设备是个音响,给其设置声音,那么可能就有音量、低音炮、音频之类的值,把这些命令拆开成几个包分开发送就会增加代码量与交互次数,相应的耗电就提高,业务处理也麻烦。

typedef struct{
int version;
unsigned int reserved[3];
}SMsgGetAllParamsReq

version为了让大家了解写法而加上去的一个参数,也可以用作请求api的版本号,reserved[3]保留位置,用于方便以后添加新参数可用

typedef struct {
 int id;//设备id
 char device_name[32];//设备名称
 char soft_ver[32];//软件版本号
 char firm_ver[32];//硬件版本号
 unsigned int reserved[3];
}SMsgGetAllParamsResp

接下来就是关键代码了,将命令转换为byte[]用于命令交互

public class SMsgGetAllParamsReq{
    public static byte[] gen(int version){
        byte[] data = new byte[16];
    System.arraycopy(Packet.intToByteArray_Little(version, 0), 0,
                    arrayOfByte, 0, 4);
        return data;
        }
    }

首先为什么要这样实现呢?
估计大家都有疑问为什么要设置数组长度为16呢?那是因为int类型占4个字节(java、c/c++一样),byte类型占一个字节(1字节 = 8位,1024字节 = 1KB)。
System.arraycopy这个系统函数是用于拷贝数组在内指定容到另一个数组的指定位置。Packet稍后会给大家做介绍。

public class SMsgGetAllParamsResp{
    public static final COUNT = 3;
    public int id;
    public String device_name;
    public String soft_ver;
    public String firm_ver;
    public int[] reserved = new int[COUNT];

    public static SMsgGetAllParamsResp parse(byte[] data){
        SMsgGetAllParamsResp  resp = new SMsgGetAllParamsResp ();
        resp.id = Packet.byteArrayToInt_Little(data,0);
        resp.device_name = new String(data, 4, 32).trim();
        resp.soft_ver = new String(data, 36, 32).trim();
        resp.firm_ver= new String(data, 68, 32).trim();
        int index = 100;
        for(int i=0;i<COUNT;i++){
            resp.reserved[0] = Packet.byteArrayToInt_Little(data,index);
            index += 4;
            }
        return resp;
        }

    }

new String(bytes, offset, length)这个函数相信大家都很熟悉我就不介绍了,里面的值也就是偏移量也是一眼就明了的。

到目前为止命令的解析生成就介绍完毕了。

类型转换
数据类型转换不必说大家想必也是很清楚的,最简单的(int)100L,Long类型强转int类型,文中为什么不使用呢?因为效率的原因,直接使用位运算符操作效率会高很多。

public static final int byteArrayToInt_Little(byte byt[], int nBeginPos) {
        return (0xff & byt[nBeginPos]) | (0xff & byt[nBeginPos + 1]) << 8 | (0xff & byt[nBeginPos + 2]) << 16 | (0xff & byt[nBeginPos + 3]) << 24;
    }

public static final byte[] intToByteArray_Little(int value) {
        return new byte[] { (byte) value, (byte) (value >>> 8), (byte) (value >>> 16), (byte) (value >>> 24) };
    }

至于其他类型的转换请各位自行百度吧,相信你们可以找到,要善用搜索引擎。

捣鼓了几个晚上终于写完了,虽然因为公司要加班晚上写文章的时间很少,虽然时间长也算是写完了。楼主笔风粗俗还望各位海涵,有任何有问题、错误的地方请随时留言。

下一篇准备结合nio写命令的发送接收,把今天的内容整合到一块。

补充内容

类型转换还有些小坑的地方补充下。
一个请求的结构体

typedef struct {
 char type;// 设置类型那个
 int  operate;//操作类型 0:打开 1:关闭
 unsigned int reserved[3];
}

type占一个字节,operate应该从索引1开始4的长度。正常来说是这样没错,
但是c/c+是会补位的,也就是说虽然type只占一个字节,但是为了后面的operate(4字节)存放,会直接补位补三位0,当时和同事调接口的时候死活传错参数,后面才知道他读取operate是从4开始的而不是从1开始。

所以遇到类似情况请先和设备端同事沟通,或者看看协议规范。

java后台的同事问我这样传有什么优点,之前还真没仔细考虑过。
目前总结几点,也请各大神帮我补充

  1. 字节流传输跨平台,兼容性好,效率高(底层都是转为字节流传输的)
  2. 使用结构体,方便嵌入式直接转换为指针对象,就像java的序列化一样,减少了嵌入式代码量(嵌入式一般受硬件条件约束,空间有限,尽量缩减代码)
2018-01-27 14:13:00 qq_25190529 阅读数 1519

1.基本图解软件与硬件之间的关系

         因为串口属于硬件设备,所以就会涉及到相应的硬件与软件的交互,下图简单介绍了一下相应的关系:


2.NDK的介绍
         从上图我们可以了解Android本地代码与本地代码C语言去交互:这就引出了NDK(下面直接拷的,了解一下即可)
  • 定义:Native Development Kit,是 Android的一个工具开发包
    NDK是属于 Android 的,与Java并无直接关系
  • 作用:快速开发C、 C++的动态库,并自动将so和应用一起打包成 APK
    即可通过 NDK在 Android中 使用 JNI与本地代码(如C、C++)交互
  • 应用场景:在Android的场景下 使用JNI
    即 Android开发的功能需要本地代码(C/C++)实现
3.NDK的基本配置(这里只介绍环境的配置,相应的代码编写,自行百度,资料还是蛮多的,串口这一方面不需要自己编写这些代码,直接使用Google提供的开发包就可行了,后面的文章会详细介绍)
           NDK环境配置
           3.1.File-》projectStruct
               然后将你下载好的NDK开发包如图配置,一般SDK包里面会含有NDK大家可以使用里面的开发包
         
          3.2.AS2.2之后的版本好像这样就可以了,如果不行,就看一下这里的代码是否有相应的配置


        
        

2017-10-07 14:35:55 zhouhuacai 阅读数 2496

Android应用访问硬件的两种方法

我们知道,Android应用由java编写,而java不能直接访问硬件,那么Android应用如何访问硬件?
一般有两个访问硬件的方法:

  • JNI访问
  • 硬件访问服务

JNI访问

JAVA APP—>JNI_OnLoad()加载C库—->将JAVA三个地方法与C库函数进行关联并注册—->调用JAVA本地Native方法就可以访问C库的C接口——>进而访问硬件驱动中的open, read, write,从进访问硬件。
这里写图片描述

但是,以上场景仅限于只有一个APP使用这个硬件资源,如果有多个应用想要使用某个硬件时,如果还按上面方法,必须会造成硬件资源的冲突,所以此时需要有一种框架来解决这个问题。解决方案就是访问硬件资源的程序只能并且只有一个,我们称之为System Server, 其它要访问这个硬件资源的APP必须要给Server发请求,由Server间接的操作硬件,从而实现资源的访问。这个就称之为硬件访问服务。

hardcontrol.c最终编译成hardcontrol.so, 可以放到apk中去,可以作为共享库在Android syserm存在。


#include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include <android/log.h>  /* liblog */

//__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");


#if 0
typedef struct {
    char *name;          /* Java里调用的函数名 */
    char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
    void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endif

static jint fd;

jint ledOpen(JNIEnv *env, jobject cls)
{
    fd = open("/dev/leds", O_RDWR);
    __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen : %d", fd);
    if (fd >= 0)
        return 0;
    else
        return -1;
}

void ledClose(JNIEnv *env, jobject cls)
{
    __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledClose ...");
    close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
    int ret = ioctl(fd, status, which);
    __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl : %d, %d, %d", which, status, ret);
    return ret;
}


static const JNINativeMethod methods[] = {
    {"ledOpen", "()I", (void *)ledOpen},
    {"ledClose", "()V", (void *)ledClose},
    {"ledCtrl", "(II)I", (void *)ledCtrl},
};




/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
    JNIEnv *env;
    jclass cls;

    if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
        return JNI_ERR; /* JNI version not supported */
    }
    cls = (*env)->FindClass(env, "com/thisway/hardlibrary/HardControl");
    if (cls == NULL) {
        return JNI_ERR;
    }

    /* 2. map java hello <-->c c_hello */
    if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)
        return JNI_ERR;

    return JNI_VERSION_1_4;
}

硬件访问服务

这里写图片描述

com_android_server_ledservice.cpp


#define LOG_TAG "LedService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>

#include <stdio.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>


namespace android
{

static led_device_t* led_device;

jint ledOpen(JNIEnv *env, jobject cls)
{
    jint err;
    hw_module_t* module;
    hw_device_t* device;

    ALOGI("native ledOpen ...");

    /* 1. hw_get_module */
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
        /* 2. get device : module->methods->open */
        err = module->methods->open(module, NULL, &device);
        if (err == 0) {
            /* 3. call led_open */
            led_device = (led_device_t *)device;
            return led_device->led_open(led_device);
        } else {
            return -1;
        }
    }

    return -1;  
}

void ledClose(JNIEnv *env, jobject cls)
{
    //ALOGI("native ledClose ...");
    //close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
    ALOGI("native ledCtrl %d, %d", which, status);
    return led_device->led_ctrl(led_device, which, status);
}


static const JNINativeMethod methods[] = {
    {"native_ledOpen", "()I", (void *)ledOpen},
    {"native_ledClose", "()V", (void *)ledClose},
    {"native_ledCtrl", "(II)I", (void *)ledCtrl},
};

/*注册JNI本地方法*/
int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

}

hal_led.c


#define LOG_TAG "LedHal"


/* 1. 实现一个名为HMI的hw_module_t结构体 */

/* 2. 实现一个open函数, 它返回led_device_t结构体 */

/* 3. 实现led_device_t结构体 */

/* 参考 hardware\libhardware\modules\vibrator\vibrator.c
 */

#include <hardware/vibrator.h>
#include <hardware/hardware.h>

#include <cutils/log.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <hardware/led_hal.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>


static int fd;


/** Close this device */
static int led_close(struct hw_device_t* device)
{
    close(fd);
    return 0;
}

static int led_open(struct led_device_t* dev)
{
    fd = open("/dev/leds", O_RDWR);
    ALOGI("led_open : %d", fd);
    if (fd >= 0)
        return 0;
    else
        return -1;
}

static int led_ctrl(struct led_device_t* dev, int which, int status)
{
    int ret = ioctl(fd, status, which);
    ALOGI("led_ctrl : %d, %d, %d", which, status, ret);
    return ret;
}




static struct led_device_t led_dev = {
    .common = {
        .tag   = HARDWARE_DEVICE_TAG,
        .close = led_close,
    },
    .led_open  = led_open,
    .led_ctrl  = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module, const char* id,
        struct hw_device_t** device)
{
    *device = &led_dev;
    return 0;
}


static struct hw_module_methods_t led_module_methods = {
    .open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .id = "led",
    .methods = &led_module_methods,
};


system server.java

Slog.i(TAG, "Led Service");
ServiceManager.addService("led", new LedService());

LEDService.java

package com.android.server;
import android.os.ILedService;

public class LedService extends ILedService.Stub {
    private static final String TAG = "LedService";

    /* call native c function to access hardware */
    public int ledCtrl(int which, int status) throws android.os.RemoteException
    {
        return native_ledCtrl(which, status);
    }

    public LedService() {
        native_ledOpen();
    }

    public static native int native_ledOpen();
    public static native void native_ledClose();
    public static native int native_ledCtrl(int which, int status);
}

ILEDService.java由ILEDService.aidl生成
ILEDService.aid

package android.os;

/** {@hide} */
interface ILedService
{
    int ledCtrl(int which, int status);
}

如何实现一个硬件访问服务

  1. 编写JNI和HAL,以led为例,先编写com_android_server_ledservice.cpp,用于注册JNI本地方法。再编写hal_led.c,里面就是实现open,read,write等硬件访问接口。
  2. 修改onload.cpp,调它调用com_android_server_ledservice.cpp内实现的函数,
  3. 修改system server.java,
    new ledservice()
    add ledservice()
  4. 编写LEDService.java,用于调用本地方法,实现硬件操作
  5. 编写ILEDService.java接口给app使用。
2019-06-04 10:43:59 shijilongren 阅读数 442

<<序>>

本书更多的解释是代码中添加注释的方法,这样有助于读者更好的理解代码的作用,这是实例教程的特色。

本书可能要写几个月到一年左右的时间,因此也希望读者能够纠错、参与、互动。

考虑到将来读者多的时候,建立一个微信讨论群及在百度网盘中创建一个资源下载平台,但初期有兴趣的读者可以通过kyumeyy@163.com这个邮箱联系我。

很多程序开发者喜欢在代码中保留一个有关或喜爱的词,这不是必要的,但视乎很有趣,这里我选择了——pka(Python、Kivy、Android的首个字母缩写)

提及一下开发平台,这里我选择了在虚拟机中安装Ubuntu操作系统,及系统中安装Python、Kivy、p4a、buildozer、 Android SDK、Android NDK等软件的开发环境。这虽然不是最好的Android开发平台,但对于那些喜欢捷径及独爱Python的程序开发者,无疑是最好的选择。

<<实例1:你好世界!>>

在Ubuntu中创建一个文件夹,命名为pka01,在pka01文件夹中创建一个名称为main.py的Python文件,写入代码如下:

#-*-coding:utf-8-*-

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout

Builder.load_file('hello.kv')     #加载.kv文件

class myLayout(FloatLayout):    #这里定义了一个名称为myLayout的类,继承至AnchorLayout(浮动布局)类
    pass

class pkaApp01(App):
    def build(self):
        return myLayout()

if __name__=='__main__':
    pkaApp01().run()

在pka01文件夹中创建一个名称为hello.kv的Kivy文件,写入代码如下:

#-*-coding:utf-8-*-
<myLayout>:
    Button:
        font_name:'simhei.ttf'    #字体属性
        text:'你好'    #文本属性
        font_size:32    #文字大小属性
        color:0,1,0,1    #颜色属性
        size_hint: .2, .2    #尺寸属性
        pos_hint: {'x':0, 'center_y': .5}    #位置属性
        background_color:1,0,0,1    #背景颜色属性

    Button:
        font_name:'simhei.ttf'   
        text:'世界'   
        font_size:32    
        color:0,1,0,1    
        size_hint: .2, .2   
        pos_hint: {'right':1, 'center_y': .5}
        background_color:1,0,0,1

还需要将字体文件simhei.ttf拷贝到在pka01文件夹中。上诉文件创建好后,就可在Ubuntu中打开命令行,并使用cd命令进入到pka01文件夹,然后使用python main.py命令运行程序,结果如下图:
pka01
实例1虽然是个简单的程序,但它已经是个很好的模板,今后我们很多的程序都会参照这样的模型。

实例2:

博文 来自: shijilongren

Android与H5交互

阅读数 1217

没有更多推荐了,返回首页