云计算百科
云计算领域专业知识百科平台

ESP32移植Openharmony物联网实战 - LwIP+巴法云TCP服务器

目录

巴法云平台域名

巴法云平台搭建

TCP简介

订阅、发布简介

代码示例

wifi_sta_connect.c

wifi_sta_example.c

移植步骤

巴法云TCP客户端接入详解

命令字段说明

订阅主题

发布

订阅主题,并获取一条历史消息

获取一次时间

获取一次已发消息

发送心跳

set 指令

up 指令

app 指令

编译并烧录

实验现象


巴法云云平台官网

巴法开放平台_巴法云_巴法物联网云平台

巴法云云平台搭建

  • 进入巴法云平台登录注册
  • 点击TCP设备云
  • 在最右边输入框中输入主题名,主题命名并无强制性要求,也可以参考主题类型命名说明:
  • 比如我们这里将主题命名为ledtest,点击新建主题

    创建成功后可以看到下面出现了一个主题

    TCP简介

    TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议,传输层协议常用的有TCP和UDP,像常见的http协议或mqtt协议,都是在TCP上传输的,TCP用途广泛,一般可以联网的设备,都支持TCP协议传输。

    订阅、发布简介

    通俗的来讲,订阅、发布是两种不同的信号 订阅:订阅某一个主题,订阅之后,就可以收到发往该主题的消息。 发布:向某个主题发送消息。 例如:当某个设备订阅了主题“X”,这时向主题“X”发送的任何消息,设备都可以收到,因为设备已经订阅上了该主题。

    直接上干货!

    代码示例

    wifi_sta_connect.c

    /*
    * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include "securec.h"
    #include "cmsis_os2.h"
    #include "ohos_init.h"
    #include "wifi_device.h"
    #include "lwip/ip4_addr.h"
    #include "lwip/netif.h"
    #include "lwip/netifapi.h"
    #include "wifi_error_code.h"

    #define DEF_TIMEOUT 15
    #define ONE_SECOND 1
    #define SELECT_WLAN_PORT "wlan0"
    #define SELECT_WIFI_SECURITYTYPE WIFI_SEC_TYPE_OPEN
    #define STD_TIMEZONE_OFFSET (+8)
    #define OD_DELAY_100 100
    #define OD_DELAY_200 200

    static int g_staScanSuccess = 0;
    static int g_ConnectSuccess = 0;
    static int ssid_count = 0;
    static WifiErrorCode wifi_error;
    static WifiEvent g_wifiEventHandler = {0};
    static int wifi_sta_init_state = 0;
    int sock_fd;
    int addr_length;
    const int timeZone = 8;

    static void WiFiInit(void);
    static void WaitScanResult(void);
    static int WaitConnectResult(void);
    static void OnWifiScanStateChangedHandler(int state, int size);
    static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info);
    static void OnHotspotStaJoinHandler(StationInfo *info);
    static void OnHotspotStateChangedHandler(int state);
    static void OnHotspotStaLeaveHandler(StationInfo *info);

    void DisableWIFI(void)
    {
    DisableWifi();
    }

    static void OnHotspotStaJoinHandler(StationInfo *info)
    {
    (void)info;
    printf("STA join AP\\n");
    return;
    }

    static void OnHotspotStaLeaveHandler(StationInfo *info)
    {
    (void)info;
    printf("HotspotStaLeave:info is null.\\n");
    return;
    }

    static void OnHotspotStateChangedHandler(int state)
    {
    printf("HotspotStateChanged:state is %d.\\n", state);
    return;
    }

    static void WiFiInit(void)
    {
    printf("<–Wifi Init–>\\r\\n");
    g_wifiEventHandler.OnWifiScanStateChanged = OnWifiScanStateChangedHandler;
    g_wifiEventHandler.OnWifiConnectionChanged = OnWifiConnectionChangedHandler;
    g_wifiEventHandler.OnHotspotStaJoin = OnHotspotStaJoinHandler;
    g_wifiEventHandler.OnHotspotStaLeave = OnHotspotStaLeaveHandler;
    g_wifiEventHandler.OnHotspotStateChanged = OnHotspotStateChangedHandler;
    wifi_error = RegisterWifiEvent(&g_wifiEventHandler);
    if (wifi_error != WIFI_SUCCESS)
    {
    printf("register wifi event fail!\\r\\n");
    }
    else
    {
    printf("register wifi event succeed!\\r\\n");
    }
    }

    static void OnWifiScanStateChangedHandler(int state, int size)
    {
    (void)state;
    if (size > 0)
    {
    ssid_count = size;
    g_staScanSuccess = 1;
    }
    return;
    }

    static int result;
    int WifiConnect(const char *ssid, const char *psk)
    {
    WifiScanInfo *info = NULL;
    unsigned int size = WIFI_SCAN_HOTSPOT_LIMIT;
    static struct netif *g_lwip_netif = NULL;
    WifiDeviceConfig select_ap_config = {0};
    osDelay(OD_DELAY_200);
    printf("<–System Init–>\\r\\n");
    WiFiInit();
    if (EnableWifi() != WIFI_SUCCESS)
    {
    printf("EnableWifi failed, wifi_error = %d\\n", wifi_error);
    return -1;
    }
    if (IsWifiActive() == 0)
    {
    printf("Wifi station is not actived.\\n");
    return -1;
    }
    info = malloc(sizeof(WifiScanInfo) * WIFI_SCAN_HOTSPOT_LIMIT);
    if (info == NULL)
    {
    printf("faild to create wifiscanInfo.\\n");
    return -1;
    }
    do
    {
    ssid_count = 0;
    g_staScanSuccess = 0;
    Scan();
    WaitScanResult();
    wifi_error = GetScanInfoList(info, &size);
    } while (g_staScanSuccess != 1);
    strcpy_s(select_ap_config.ssid, sizeof(select_ap_config.ssid), ssid);
    printf("[%s][%s] \\r\\n", select_ap_config.ssid, select_ap_config.preSharedKey);
    select_ap_config.securityType = SELECT_WIFI_SECURITYTYPE;
    if (AddDeviceConfig(&select_ap_config, &result) == WIFI_SUCCESS)
    {
    if (ConnectTo(result) == WIFI_SUCCESS && WaitConnectResult() == 1)
    {
    printf("WiFi connect succeed!\\r\\n");
    wifi_sta_init_state = 1;
    }
    }
    osDelay(OD_DELAY_100);
    return 0;
    }

    static int WaitConnectResult(void)
    {
    int ConnectTimeout = DEF_TIMEOUT;
    while (ConnectTimeout > 0)
    {
    sleep(1);
    ConnectTimeout–;
    if (g_ConnectSuccess == 1)
    {
    printf("WaitConnectResult:wait success[%d]s\\n", (DEF_TIMEOUT – ConnectTimeout));
    break;
    }
    }
    if (ConnectTimeout <= 0)
    {
    printf("WaitConnectResult:timeout!\\n");
    return 0;
    }
    return 1;
    }

    static void OnWifiConnectionChangedHandler(int state, WifiLinkedInfo *info)
    {
    (void)info;
    if (state > 0)
    {
    g_ConnectSuccess = 1;
    printf("callback function for wifi connect\\r\\n");
    }
    else
    {
    g_ConnectSuccess = 0;
    printf("connect wifi_error, please check password, state:%d, try connect again\\r\\n", state);
    esp_wifi_connect();
    }
    return;
    }

    static void WaitScanResult(void)
    {
    int scanTimeout = DEF_TIMEOUT;
    while (scanTimeout > 0)
    {
    sleep(ONE_SECOND);
    scanTimeout–;
    if (g_staScanSuccess == 1)
    {
    printf("WaitScanResult:wait success[%d]s\\n", (DEF_TIMEOUT – scanTimeout));
    break;
    }
    }
    if (scanTimeout <= 0)
    {
    printf("WaitScanResult:timeout!\\n");
    }
    }

    wifi_sta_example.c

    /*
    * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    #include <stdio.h>
    #include "securec.h"
    #include "cmsis_os2.h"
    #include "ohos_run.h"
    #include "lwip/sockets.h"
    #include "lwip/ip_addr.h"
    #include "wifi_device.h"

    #include <string.h>

    #define OPEN_WIFI_NAME "test"
    // #define SERVER_IP"192.168.31.67"
    #define SERVER_IP "119.91.109.180"
    #define SERVER_PORT 8344
    #define OD_DELAY_1000 1000
    #define OD_DELAY_100 100
    #define RECV_LEN 511
    #define STACK_SIZE 4096
    #define PRIORITY 25

    osThreadId_t wifi_test_id = NULL;

    void wifi_test(void)
    {
    int sock = -1;
    struct sockaddr_in client_addr;
    char recv_data[512] = {0};
    int recv_data_len;
    WifiConnect(OPEN_WIFI_NAME);
    printf("start wifi_test test\\r\\n");
    while (1)
    {
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
    printf("Socket error\\n");
    osDelay(OD_DELAY_100);
    continue;
    }
    memset_s(&(client_addr), sizeof(client_addr), 0, sizeof(client_addr));
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(SERVER_PORT);
    client_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    printf("try connect to server " SERVER_IP " :%d \\n", SERVER_PORT);
    if (connect(sock, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) == -1)
    {
    closesocket(sock);
    osDelay(OD_DELAY_1000);
    continue;
    }
    printf("Connect to tcp server successful!\\n");
    write(sock, "cmd=1&uid=4e2a189d769e4511bd5885742aa3edcb&topic=ledtest\\r\\n", strlen("cmd=1&uid=4e2a189d769e4511bd5885742aa3edcb&topic=ledtest\\r\\n"));
    while (1)
    {
    recv_data_len = recv(sock, recv_data, RECV_LEN, 0);
    if (recv_data_len <= 0)
    {
    break;
    }
    else
    {
    recv_data[recv_data_len] = '\\0';
    printf("recv: %s\\n", recv_data);
    }
    }
    }
    }

    static void wifi_test_example(void)
    {
    osThreadAttr_t attr;
    attr.name = "wifi_test";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = STACK_SIZE;
    attr.priority = PRIORITY;
    wifi_test_id = osThreadNew((osThreadFunc_t)wifi_test, NULL, &attr);
    if (wifi_test_id == NULL)
    {
    printf("Failed to create wifi_test thread!\\n");
    }
    }

    OHOS_APP_RUN(wifi_test_example);

    BUILD.gn

    # Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    import("//kernel/liteos_m/liteos.gni")

    module_name = get_path_info(rebase_path("."), "name")
    kernel_module(module_name) {
    sources = [
    "wifi_sta_example.c",
    "wifi_sta_connect.c"
    ]
    include_dirs = [
    "//foundation/communication/wifi_lite/interfaces/wifiservice",
    "//device/board/esp/esp32/liteos_m/hals/driver/wifi_lite"
    ]
    }

    移植步骤

    移植本代码中有几个部分需要注意:

  • 所连接的热点必须是开放的2.4G热点(没有密码)
  • 修改连接热点名方法:将wifi_sta_example.c中OPEN_WIFI_NAME的宏定义修改为热点名即可

  • 参考巴法云平台接入文档 – TCP协议中 接口地址应是:
  • 但经笔者测试,直接填入"bemfa.com"无法连接上该服务器,推测是无法进行DNS解析,所以我们直接连接IP地址,可以通过cmd打开windows终端 – ping bemfa.com查看域名对应的IP地址

  • 巴法云提供的TCP服务器是基于订阅/发布(简化版MQTT)的服务,我们可以通过向服务器发送命令的方式对主题进行订阅与发布。
  • 巴法云TCP客户端接入详解

    命令字段说明

    cmd:消息类型

    cmd=1 时为订阅消息,当设备发送一次此消息类型,之后就可以收到发往该主题的消息

    cmd=2 时为发布消息,向订阅该主题的设备发送消息

    cmd=3 是订阅消息,和cmd=1相同,并且会拉取一次已发送过的消息

    cmd=7 是获取时间,获取当前北京时间

    cmd=9 为遗嘱消息,拉取一次已经发送的消息

    uid:用户私钥

    可在控制台获取

    topic:用户主题

    可以在控制台创建主题,格式为英文或数字,相当于设备标识

    msg: 为消息体

    用户想要发送到某个主题的数据

    符号说明

    \\r\\n: 为回车换符,每条指令后都需要有回车换行。
    &: 为连接符,各字段间用'&'隔离。

    心跳说明

    发送任意数据 为心跳消息,包括上述指令也算是心跳,但要以回车换行结尾。
    心跳消息是告诉服务器设备还在线,建议60秒发送一次,超过65秒未发送心跳会掉线。

    订阅主题

    订阅主题,单次最多订阅八个主题

    cmd=1&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=xxx1,xxx2,xxx3,xxx4\\r\\n

    正常返回:

    cmd=1&res=1

    发布

    cmd=2&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=light002&msg=off\\r\\n

    正常返回:

    cmd=2&res=1

    订阅主题,并获取一条历史消息

    cmd=3&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=light002\\r\\n

    正常返回:

    cmd=3&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=light002&msg=on

    获取一次时间

    cmd=7&uid=xxxxxxxxxxxxxxxxxxxxxxx&type=1\\r\\n

    正常返回:

    2021-06-11 16:39:27
    type=1 获取当前日期和时间,例如:2021-06-11 17:20:54

    type=2 获取当前时间,例如:17:20:54

    type=3 获取当前时间戳,例如:1623403325

    获取一次已发消息

    cmd=9&uid=7d54f85af42976ee3c2693e6xxxxxxxx&topic=light002\\r\\n

    正常返回:

    cmd=9&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=light002&msg=on

    发送心跳

    ping\\r\\n

    正常返回:

    cmd=0&res=1

    发送任意数据,并以\\r\\n结尾,都为心跳消息,包括上述发布、订阅指令也都算是心跳。 心跳消息是告诉服务器设备还在线,建议60秒发送一次,超过65秒未发送心跳会掉线。

    set 指令

    推送消息时:主题名后加/set推送消息,表示向所有订阅这个主题的设备们推送消息,假如推送者自己也订阅了这个主题,消息不会被推送给它自己,以防止自己推送的消息被自己接收。 例如向主题 light002推送数据,可为 light002/set 。

    示例:

    cmd=2&uid=4d9ec352e0376f2110a0c601a2857225&topic=light002/set&msg=on

    up 指令

    推送消息时:主题名后加/up推送消息,表示只更新云端数据,不进行任何推送。 例如向主题 light002推送数据,可为light002/up。

    示例:

    cmd=2&uid=4d9ec352e0376f2110a0c601a2857225&topic=light002/up&msg=on

    app 指令

    订阅时:主题名后加/app订阅主题,不会占用设备在线状态,即不会显示设备在线,可用于app、小程序等订阅时使用。

    示例:

    cmd=1&uid=4d9ec352e0376f2110a0c601a2857225&topic=light002/app

    编译并烧录

    在源码根目录下使用hb工具对写好的代码进行编译

    选择mini级系统

    同理 产品选择esp公司下的esp32

    选择完毕后在源码根目录下执行hb build -f 进行编译

    编译完成后会有如下界面,并且编译后的代码固件位于:out\\esp32\\esp32

    使用烧录软件进行烧录:

    点击start,等待烧录完成

    实验现象

    按下ESP32开发板上的EN键,即可观察到实验现象:

    可以看到我们已经成功连接上TCP服务器,我们打开云平台查看是否有客户端对主题ledtest进行了订阅

    我们尝试在云平台进行手动发布信息:hello openharmony!

    在串口助手上成功打印出了该信息

    后续大家自行将手动发布信息改成使用命令的方式进行发布即可!订阅相同主题的设备都可以收到发布的信息!​​​​​​​ 本项目代码已上传至gitee:ESP32_Oh: ESP32移植Openharmony

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » ESP32移植Openharmony物联网实战 - LwIP+巴法云TCP服务器
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!