#打卡不停更#【FFH】OpenHarmony设备开发(八)- 遥控小车 原创 精华

X丶昕雪
发布于 2022-10-22 15:13
浏览
4收藏

前言

智能小车设备可以接收手机发送的操控指令,得以完成手机操控小车。智能小车还实现无感连接,无论哪一个智能小车设备,手机只需要对着智能小车上的标签碰一碰,即可连接到该小车并自动打开相对应的操作软件,对该小车进行控制,操作简单易懂,易于上手。

实现方式

  1. 3861遥控小车控制两个电机,并打开热点以供手机连接
  2. NFC标签写入遥控小车的SSID,PSK,IP地址
  3. 手机碰一碰小车上的NFC标签,即可一键连接小车建立TCP连接,与此同时拉起小车控制软件,最终完成对小车的控制。

工程版本

  • 系统版本/API版本:OpenHarmony 3.1 release
  • IDE版本:DevEco Device Tool Release v3.1.200

源代码

头文件

对于下面要用到的API接口,我们要引用的头文件有以下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
//os
#include "cmsis_os2.h"
#include "ohos_init.h"
//wifi and tcp
#include "wifi_device.h"
#include "wifi_hotspot.h"
#include "wifi_error_code.h"
#include "lwip/netifapi.h"

#include "lwip/sockets.h"

// io
#include "iot_gpio.h"
#include <hi_gpio.h>
#include <hi_io.h>

控制

小车轮子控制:

GPIO0和GPIO1控制左轮,GPIO9和GPIO10控制右轮

GPIO0和GPIO9使能是正方向,GPIO1和GPIO10使能是反方向,同时使能便是停止

#define GPIO0 0
#define GPIO1 1
#define GPIO9 9
#define GPIO10 10

void my_car_init(void)
{
	//初始化
    IoTGpioInit(GPIO0);
    IoTGpioInit(GPIO1);
    IoTGpioInit(GPIO10);
    IoTGpioInit(GPIO9);
	//IO模式
    hi_io_set_func(GPIO0, 0);
    hi_io_set_func(GPIO1, 0);
    hi_io_set_func(GPIO10, 0);
    hi_io_set_func(GPIO9, 0);
	//输出模式
    IoTGpioSetDir(GPIO0, 1);
    IoTGpioSetDir(GPIO1, 1);
    IoTGpioSetDir(GPIO10, 1);
    IoTGpioSetDir(GPIO9, 1);

    car_stop();
}
void car_backward(void)
{
    IoTGpioSetOutputVal(GPIO0, IOT_GPIO_VALUE0); //左
    IoTGpioSetOutputVal(GPIO1, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO9, IOT_GPIO_VALUE0); //右轮
    IoTGpioSetOutputVal(GPIO10, IOT_GPIO_VALUE1);
}

void car_forward(void)
{
    IoTGpioSetOutputVal(GPIO0, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO1, IOT_GPIO_VALUE0);
    IoTGpioSetOutputVal(GPIO9, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO10, IOT_GPIO_VALUE0);
}

void car_left(void)
{
    IoTGpioSetOutputVal(GPIO0, IOT_GPIO_VALUE0);
    IoTGpioSetOutputVal(GPIO1, IOT_GPIO_VALUE0);
    IoTGpioSetOutputVal(GPIO9, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO10, IOT_GPIO_VALUE0);
}

void car_right(void)
{
    IoTGpioSetOutputVal(GPIO0, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO1, IOT_GPIO_VALUE0);
    IoTGpioSetOutputVal(GPIO9, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO10, IOT_GPIO_VALUE1);
}

void car_stop(void)
{
    IoTGpioSetOutputVal(GPIO0, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO1, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO9, IOT_GPIO_VALUE1);
    IoTGpioSetOutputVal(GPIO10, IOT_GPIO_VALUE1);
}

通信

WIFI

  1. 回调函数配置
  2. WIFI_AP配置
  3. 注册netif结构体
  4. 配置netif(配置DHCP)
  5. 关闭再打开DHCP(必须先关闭再重新打开)
#define AP_SSID "FSR"
#define AP_PSK "12345678"

#define _PROT_ 8888
#define TCP_BACKLOG 10

static struct netif *g_lwip_netif = NULL;
static int g_apEnableSuccess = 0;
WifiEvent g_wifiEventHandler = {0};
WifiErrorCode error;

	//注册wifi事件的回调函数
    g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler;
    g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler;
    g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler;
    //指定WiFi回调函数
    error = RegisterWifiEvent(&g_wifiEventHandler);



	//设置指定的热点配置
    HotspotConfig config = {0};
    //初始化热点相关配置
    strcpy(config.ssid, AP_SSID);
    strcpy(config.preSharedKey, AP_PSK);
    config.securityType = WIFI_SEC_TYPE_PSK;
    config.band = HOTSPOT_BAND_TYPE_2G; // 2.4GHz
    config.channelNum = 7;
    //配置wifi热点
    error = SetHotspotConfig(&config);
    
    error = EnableHotspot();

	//启动dhcp
    g_lwip_netif = netifapi_netif_find("ap0");
    if (g_lwip_netif)
    {
        ip4_addr_t bp_gw;
        ip4_addr_t bp_ipaddr;
        ip4_addr_t bp_netmask;

        IP4_ADDR(&bp_gw, 192, 168, 1, 1);        /* input your gateway for example: 192.168.1.1 */
        IP4_ADDR(&bp_ipaddr, 192, 168, 1, 1);    /* input your IP for example: 192.168.1.1 */
        IP4_ADDR(&bp_netmask, 255, 255, 255, 0); /* input your netmask for example: 255.255.255.0 */

        netifapi_netif_set_addr(g_lwip_netif, &bp_ipaddr, &bp_netmask, &bp_gw);
    
    //关闭
        netifapi_dhcps_stop(g_lwip_netif);

        netifapi_dhcps_start(g_lwip_netif, 0, 0);
        
        }

TCP

//在sock_fd 进行监听,在 new_fd 接收新的链接
int sock_fd, new_fd;

char recvbuf[10];

/****************以下为TCP服务器代码***************/
    struct sockaddr_in server_sock;

    //客户端地址信息
    struct sockaddr_in client_sock;
    int sin_size;

    struct sockaddr_in *cli_addr;

    //创建socket
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket is error\r\n");
        exit(1);
    }

    bzero(&server_sock, sizeof(server_sock));
    server_sock.sin_family = AF_INET;
    server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
    server_sock.sin_port = htons(_PROT_);

    //调用bind函数绑定socket和地址
    if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1)
    {
        perror("bind is error\r\n");
        exit(1);
    }

    //调用listen函数监听(指定port监听)
    if (listen(sock_fd, TCP_BACKLOG) == -1)
    {
        perror("listen is error\r\n");
        exit(1);
    }

    printf("start accept\n");

    //调用accept函数从队列中
    while (1)
    {
        sin_size = sizeof(struct sockaddr_in);

        if ((new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size)) == -1)
        {
            perror("accept");
            continue;
        }

        cli_addr = malloc(sizeof(struct sockaddr));

        printf("accept addr\r\n");

        if (cli_addr != NULL)
        {
            memcpy(cli_addr, &client_sock, sizeof(struct sockaddr));
        }

        //处理目标
        ssize_t ret;

        while (1)
        {
            //连接成功,接收客户端发送来的数据
            //清理接收缓存
            (void)memset_s(recvbuf, sizeof(recvbuf), 0, sizeof(recvbuf));
            //接收
            if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1)
            {
                printf("recv error \r\n");
            }
            else
            {
                printf("server:%d!\n", recvbuf[0]);
            }
            if (recvbuf[0] == '1')
            {
                car_forward();
                printf("car_forward!\n");
            }
            else if (recvbuf[0] == '2')
            {
                car_backward();
                printf("car back!");
            }
            else if (recvbuf[0] == '3')
            {
                car_left();
            }
            else if (recvbuf[0] == '4')
            {
                car_right();
            }
            else if (recvbuf[0] == '0')
            {
                car_stop();
            }
        }

        close(new_fd);
    }
    /*********************END********************/

线程创建以及WiFi回调函数

/ WIFI连入的线程
static void HotspotStaJoinTask(void)
{
    static char macAddress[32] = {0};
    StationInfo stainfo[WIFI_MAX_STA_NUM] = {0};
    StationInfo *sta_list_node = NULL;
    unsigned int size = WIFI_MAX_STA_NUM;

    //获取当前接入到该AP的所有STA站点信息
    error = GetStationList(stainfo, &size);
    if (error != WIFI_SUCCESS)
    {
        printf("HotspotStaJoin:get list fail, error is %d.\r\n", error);
        return;
    }
    sta_list_node = stainfo;
    //打印出每个STA站点的MAC地址
    for (uint32_t i = 0; i < size; i++, sta_list_node++)
    {
        unsigned char *mac = sta_list_node->macAddress;
        snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
        printf("HotspotSta[%d]: macAddress=%s.\r\n", i, macAddress);
    }
    g_apEnableSuccess++;
}
//绑定STA站点加入回调函数
static void OnHotspotStaJoinHandler(StationInfo *info)
{
    if (info == NULL)
    {
        printf("HotspotStaJoin:info is null.\r\n");
    }
    else
    {
        //创建连接线程
        printf("New Sta Join\n");
        osThreadAttr_t attr;
        attr.name = "HotspotStaJoinTask";
        attr.attr_bits = 0U;
        attr.cb_mem = NULL;
        attr.cb_size = 0U;
        attr.stack_mem = NULL;
        attr.stack_size = 2048;
        attr.priority = 24;
        if (osThreadNew((osThreadFunc_t)HotspotStaJoinTask, NULL, &attr) == NULL)
        {
            printf("HotspotStaJoin:create task fail!\r\n");
        }
    }
    return;
}
// STA退出回调函数
static void OnHotspotStaLeaveHandler(StationInfo *info)
{
    if (info == NULL)
    {
        printf("HotspotStaLeave:info is null.\r\n");
    }
    else
    {
        static char macAddress[32] = {0};
        unsigned char *mac = info->macAddress;
        snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
        printf("HotspotStaLeave: macAddress=%s, reason=%d.\r\n", macAddress, info->disconnectedReason);
        g_apEnableSuccess--;
    }
    return;
}

//状态改变回调函数
static void OnHotspotStateChangedHandler(int state)
{
    printf("HotspotStateChanged:state is %d.\r\n", state);
    if (state == WIFI_HOTSPOT_ACTIVE) // state=1表示已启用WIFI AP模式
    {
        printf("wifi hotspot active.\r\n");
    }
    else // state=0表示WIFI AP模式已禁用
    {
        printf("wifi hotspot noactive.\r\n");
    }
}

static void Wifi_AP_Demo(void)
{
    osThreadAttr_t attr;

    attr.name = "WifiAPTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = 25;

    if (osThreadNew((osThreadFunc_t)WifiAPTask, NULL, &attr) == NULL)
    {
        printf("Falied to create WifiAPTask!\r\n");
    }
}

SYS_RUN(Wifi_AP_Demo);

效果

因API8暂未开放NFC读取,因此NFC模块部分尚未编写,当前只在软件内固定连接WiFi以及ip,打开软件即可控制小车.

#打卡不停更#【FFH】OpenHarmony设备开发(八)- 遥控小车-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-11-1 09:44:16修改
4
收藏 4
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

期待NFC模块完成后的效果

回复
2022-10-22 18:18:04
物联风景
物联风景

不错不错,很详细

回复
2022-10-24 10:37:38
回复
    相关推荐