13

陈有乐 · 2022年02月21日

【XR806开发板试用】手机APP通过TCP通讯控制LED

一、手机APP程序
1、AndroidManifest.xml文件
添加应用访问网络和WIFI权限:因为手机端要连接XR806发出来的热点,需要用到WIFI。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.administrator.xr806">

    <!-- 允许应用程序改变网络状态 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

    <!-- 允许应用程序改变WIFI连接状态 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!-- 允许应用程序访问有关的网络信息 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- 允许应用程序访问WIFI网卡的网络信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!-- 允许应用程序完全使用网络 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

2、activity_main.xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00bfff"
    tools:context=".MainActivity">

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:layout_weight="1"
        android:text="IP地址:"
        android:textSize="25sp" />

    <EditText
        android:id="@+id/ip"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center_horizontal"
        android:hint="192.168.1.1"
        android:layout_weight="3"
        android:maxLines="5" />
</LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:gravity="center"
            android:layout_weight="1"
            android:text="端口号:"
            android:textSize="25sp" />

        <EditText
            android:id="@+id/port"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:gravity="center_horizontal"
            android:hint="80"
            android:layout_weight="3"
            android:maxLines="5" />
    </LinearLayout>

    <Button
        android:id="@+id/connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="连接"
        android:layout_gravity="center_horizontal"
        android:textColor="#0000ff"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/turnOn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开灯"
            android:layout_gravity="left"
            android:textColor="#000000"
            android:textSize="20sp" />
        <Button
            android:id="@+id/turnOff"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="关灯"
            android:layout_gravity="left"
            android:textColor="#ff0000"
            android:textSize="20sp" />
    </LinearLayout>


    <Switch
        android:id="@+id/switch1"
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:text="" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:text="亮度调节"
        android:textSize="25sp" />
    <SeekBar
        android:id="@+id/PWM"
        style="@style/Widget.AppCompat.SeekBar"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginTop="20dp"
        android:background="@android:color/background_light"
        android:max="100"
        android:progress="0" />
</LinearLayout>

实现后的效果
1645372548(1).jpg

3、MainActivity.java文件
实现TCP客户端发送数据给XR806。

package com.example.administrator.xr806;

import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Toast;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class MainActivity extends AppCompatActivity implements View.OnClickListener,SeekBar.OnSeekBarChangeListener{
    private static final String TAG = "MainActivity";
    private Socket socket=null;
    private OutputStream out=null;
    private EditText ip;
    private EditText port;
    private String ipStr="192.168.51.1";
    private int portInt=80;
    private SeekBar pwm;
    private int pwmInt=0;


    public short TYPE = 0xAA;
    public short MAJOR = 0x00;
    public short FIRST = 0x00;
    public short SECOND = 0x00;
    public short THRID = 0x00;
    public short CHECKSUM = 0x00;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ip=(EditText)findViewById(R.id.ip);
        port=(EditText)findViewById(R.id.port);

        pwm=(SeekBar)findViewById(R.id.PWM);


        Button connect=(Button)findViewById(R.id.connect);
        Button turnOn=(Button)findViewById(R.id.turnOn);
        Button turnOff=(Button)findViewById(R.id.turnOff);


        connect.setOnClickListener(this);                    //实现按键连接的接口
        turnOn.setOnClickListener(this);
        turnOff.setOnClickListener(this);
        pwm.setOnSeekBarChangeListener(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.connect:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        phHandler.sendEmptyMessage(10);//获取输入的IP地址和端口号
                        startClient();                      //执行连接WIFI
                    }
                }).start();
                break;
            case R.id.turnOn:
                sendData((short) 0x01);//开灯
                Log.e(TAG, "开灯");
                break;
            case R.id.turnOff:
                sendData((short)0x02);//关灯
                Log.e(TAG, "关灯");
                break;
            default:
                break;
        }
    }


    public void startClient() {
        try {
            socket = new Socket(ipStr, portInt);
            if(socket==null){
                socket.isConnected();                               //连接XR806
                Log.e(TAG, "正在连接XR806: ");
            }
            Log.e(TAG, "连接成功: ");
            phHandler.sendEmptyMessage(20);
            out = socket.getOutputStream();                     //获取输出的数据
        } catch (Exception e) {                                 //捕获输出数据是否有异常
            e.printStackTrace();
        }
    }

    @SuppressLint("HandlerLeak")
    public Handler phHandler = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 10) {
                String portStr="80";
                ipStr=ip.getText().toString();
                portStr=port.getText().toString();
                if (port.getText().toString().equals(""))
                    portStr="80";
                if (ipStr.equals(""))
                    ipStr="192.168.51.1";
                portInt=Integer.parseInt(portStr);
                Log.e(TAG, "IP地址: "+ipStr +" 端口号:"+portInt);
            }
            if (msg.what==20){
                Toast.makeText(MainActivity.this,"连接成功",Toast.LENGTH_LONG).show();
            }
            if (msg.what==30){
                Toast.makeText(MainActivity.this,"连接失败",Toast.LENGTH_LONG).show();
            }

        }
    };



    public void sendData(final short data){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    FIRST=data;
                    CHECKSUM=(short) ((FIRST+SECOND+THRID)%256);
                    final byte[] sbyte = {0x55, (byte) TYPE,(byte) FIRST, (byte) SECOND, (byte) THRID, (byte) CHECKSUM, (byte) 0xBB};

                    Log.e(TAG, "发送数据"+Integer.toHexString(sbyte[0])+" "+Integer.toHexString(sbyte[1])+" "
                    +Integer.toHexString(sbyte[2])+" "+Integer.toHexString(sbyte[3])
                    +" "+Integer.toHexString(sbyte[4])+" "+Integer.toHexString(sbyte[5])
                    +" "+Integer.toHexString(sbyte[6]));
                    out.write(sbyte,0,sbyte.length);          //输出数据
                    out.flush();
                } catch (IOException e) {                               //捕获一下(Input/Output)的异常
                    e.printStackTrace();
                }
            }
        }).start();
    }
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
            case R.id.PWM:
                if (fromUser) {
                     pwmInt= progress;
                     SECOND=0x03;
                     sendData((short) pwmInt);
                     Log.e(TAG, "PWM:"+pwmInt);
                }
                break;
                default:
                    break;
        }
    }
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        Log.e(TAG, "开始滑动时触发该事件");
    }
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        Log.e(TAG, "结束滑动时触发该事件");
        SECOND=0x00;
    }
}

二、XR806程序编写
1、BUILD.gn文件
添加头文件路径和.c文件路径

import("//device/xradio/xr806/liteos_m/config.gni")

static_library("app_tcp") {
   configs = []

   sources = [
      "main.c",
   ]

   cflags = board_cflags

   include_dirs = board_include_dirs
   include_dirs += [
        ".",
        "//base/iot_hardware/peripheral/interfaces/kits",
        "//utils/native/lite/include",
        "//foundation/communication/wifi_lite/interfaces/wifiservice",
   ]
}

2、main.c文件
大部分是参考例程和其它大佬的程序,就大体就三个部分,第一就是XR806发射出WIFI热点,第二就是设置TCP 服务端,第三就是传输过来的数据处理和LED的亮灭和亮度调节(PWM)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wifi_hotspot.h"
#include "wifi_device.h"
#include "ohos_init.h"
#include "kernel/os/os.h"
#include "wifi_device.h"
#include "lwip/sockets.h"
#include "iot_gpio.h"

#define GPIO_ID_PA21   21

#define WIFI_HOSTAP_SSID "WIFI_DEVICE_AP_TEST"
#define WIFI_HOSTAP_PSK "12345678"

int tcp_server_init(int port);
int tcp_server_accept(int sfd);
void wifi_device_ap_test();
void LED_Handler(char *recv_buf);

static OS_Thread_t g_main_thread;


static void MainThread(void *arg)
{
    int sfd = 0;
    int cfd = 0;
    char recv_buf[7] = {0};

    wifi_device_ap_test();

    sfd=tcp_server_init(80);
    cfd=tcp_server_accept(sfd);
    while (1) {
        if(-1!=recv(cfd, recv_buf, sizeof(recv_buf), 0))
        {
            LED_Handler(recv_buf);
            for (int i = 0; i < 7; i++)
            {
                printf(" recv_buf[%d] : %x\r\n", i,recv_buf[i]);
                recv_buf[i]=0;
            }
            printf("HELLO World\r\n");
            send(cfd,"good", 5,0);
        }
    }
}

void LED_Handler(char *recv_buf)
{
    char CHECKSUM=((recv_buf[2]+recv_buf[3]+recv_buf[4])%256);

    if (recv_buf[0]==0x55&&recv_buf[1]==0xAA&&CHECKSUM==recv_buf[5]&&recv_buf[6]==0xBB)
    {
        if (recv_buf[2]==0x01&&recv_buf[3]!=0x03)
        {
            printf("Turn On LED\r\n");
              IoTGpioInit(GPIO_ID_PA21);
              IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT);
            IoTGpioSetOutputVal(GPIO_ID_PA21,1);
        }

        if (recv_buf[2]==0x02&&recv_buf[3]!=0x03)
        {
            printf("Turn Off LED\r\n");
              IoTGpioInit(GPIO_ID_PA21);
              IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT);
            IoTGpioSetOutputVal(GPIO_ID_PA21,0);
        }

        if (recv_buf[3]==0x03)
        {
            printf("PWM LED Light:%d\r\n",recv_buf[2]);
            IoTPwmInit(2);
            IoTPwmStart(2, recv_buf[2], 2000);
        }
        
        
    }
    

}

void WifiTestMain(void)
{
    printf("Wifi Connect Start\n");
    
    if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL,
                OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) {
        printf("[ERR] Create MainThread Failed\n");
    }
}

SYS_RUN(WifiTestMain);


int tcp_server_init(int port)
{
    int sfd = 0;
    struct sockaddr_in saddr;

    sfd = socket(AF_INET,SOCK_STREAM,0);
    
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family  = AF_INET;
    saddr.sin_port    = htons(port);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(sfd, (struct  sockaddr*)&saddr, sizeof(saddr));

    listen(sfd,1);

    return sfd;
}

int tcp_server_accept(int sfd)
{
    int cfd = 0;
    struct  sockaddr_in caddr;
    memset(&caddr, 0, sizeof(struct sockaddr_in));
    int addrl = sizeof(struct sockaddr_in);
    cfd = accept(sfd , (struct sockaddr*)&caddr , &addrl);
    return cfd;
}



WifiEvent ap_event;


static void printf_mac_address(unsigned char *mac_address)
{
    for (int i = 0; i < WIFI_MAC_LEN - 1; i++) {
        printf("%02X:", mac_address[i]);
    }
    printf("%02X\n", mac_address[WIFI_MAC_LEN - 1]);
}

static void Ap_started_deal(int state)
{
    if (state == WIFI_STATE_AVALIABLE) {
        printf("=====> Callback: ap is started\n");
    }
}

static void Ap_sta_join(StationInfo *info)
{
    printf("=====> Callback: station join, the mac address: ");
    printf_mac_address(info->macAddress);
}

static void Ap_sta_leave(StationInfo *info)
{
    printf("=====> Callback: station leave, the mac address: ");
    printf_mac_address(info->macAddress);
}


void wifi_device_ap_test()
{
    printf("\n=========== Hotspot(AP) Test Start ===========\n");
    ap_event.OnWifiConnectionChanged = NULL;
    ap_event.OnWifiScanStateChanged = NULL;
    ap_event.OnHotspotStateChanged = Ap_started_deal;
    ap_event.OnHotspotStaJoin = Ap_sta_join;
    ap_event.OnHotspotStaLeave = Ap_sta_leave;
    

    if (WIFI_SUCCESS != RegisterWifiEvent(&ap_event)) {
        printf("Error: RegisterWifiEvent fail\n");
        return;
    }
    printf("RegisterWifiEvent Success\n");

    HotspotConfig ap_config = { .ssid = WIFI_HOSTAP_SSID,
                    .securityType = WIFI_SEC_TYPE_PSK,
                    .band = HOTSPOT_BAND_TYPE_2G,
                    .channelNum = 6,
                    .preSharedKey = WIFI_HOSTAP_PSK };

    if (WIFI_SUCCESS != SetHotspotConfig(&ap_config)) {
        printf("Error: SetHotspotConfig Fail\n");
        return;
    }
    printf("SetHotspotConfig Success\n");

    if (WIFI_SUCCESS != EnableHotspot()) {
        printf("Error: EnableHotspot Fail\n");
        return;
    }
    printf("EnableHotspot Success, Use your phone to connect it:\n");
    printf("SSID:%s\n", WIFI_HOSTAP_SSID);
    printf("PSK:%s\n", WIFI_HOSTAP_PSK);

    if (WIFI_HOTSPOT_ACTIVE == IsHotspotActive()) {
        printf("Wifi is active\n");
    } else {
        printf("Wifi isn't active\n");
    }

    printf("\n=========== Hotspot(AP) Test End ===========\n");
}

三、实现效果
7ca11149b1c62887dccf8135fae5846.jpg
https://www.bilibili.com/vide...

四、总结
首先感谢极术社区送的开发板,因为之前没搞过移植系统编译固件这些东西,所以搭环境的时候很费劲,但是参考了一些文章之后就感觉好像还可以,后面有时间在用这块开发板做其它的东西吧。

推荐阅读
关注数
13823
内容数
139
全志XR806开发板相关的知识介绍以及应用专栏。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息