完成前面几个标准硬件的驱动还算顺利,到了我最关心的“软总线”,却遇到了麻烦。

        在 Hi3861 端,虽然所安装的 OpenHarmony 3.0.3 版本中,包含了 softbus_lite(即轻量版软总线),虽说功能少于标准软总线,但我想既然存在,就应该能用,毕竟不论是 OS,还是Hi3861,都是原生的。

        因为目前只是想验证一下软总线的连接效果,还没有开始深入学习鸿蒙OS系统,更没有开始学习华为的 ArkTS 语言,所以只能求助于 AI 。

        先是让豆包帮忙编写 HI3861 上的软总线程序,它编写的煞有介事,可由于不能完整读取我安装的 OS 相关资源,总是出现因版本不对应导致的函数格式错误,编译通过都很困难。

        小艺也尝试了一下,效果也不行,因为我本身就对鸿蒙OS下的 HI3861 编程生疏,无法像以前让 AI 辅助编写我熟悉的 STM32 程序那样,一些bug我直接就改了。

        尝试将线上软总线相关的 API 说明打印成文档, 尝试发给 Kimi,它似乎消化的文档,编出的程序基本无错。但进一步提问则遇到的需要订阅才能得到回答的尴尬。现在还没有持续的需求,故只好放弃。

        再回头求助豆包,跌跌绊绊总是搞定了 hi3861 端的程序:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "hi_types_base.h"
#include "hi_wifi_api.h"
#include "hi_io.h"
#include "hi_gpio.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "los_task.h"
#include "ohos_init.h"
#include "ssd1306_i2c.h"

#include "discovery_service.h"     /* PublishService, StartDiscovery */
#include "session.h"

#define LED_INTERVAL_TIME_MS 1000
#define LED_TEST_GPIO HI_GPIO_IDX_9
#define LED_TEST_GPIO_NAME HI_IO_NAME_GPIO_9

#define WIFI_SSID       "www.embedream.com_2.4G"
#define WIFI_PASSWD     "embedream"
#define WIFI_HOSTNAME   "Hi3861_01"

#define APP_INIT_VAP_NUM    2
#define APP_INIT_USR_NUM    2

static struct netif *g_lwip_netif = NULL;
static hi_u32 g_wifi_task_id = 0;

#define MODULE_NAME     "DEV_L0"   
#define CAPABILITY      "ddmpCapability"
#define SESSION_NAME    "com.harmony.test.session"//"demo_session"
#define MAX_RECV_BUF_LEN      128

static uint8_t dummy_buf[2] = {0x00,0x00}; // static,生命周期全局

/* ========== 新增全局状态:防止重复初始化软总线 ========== */
typedef struct {
    hi_bool isServicePublished;
    hi_bool isSessionServerCreated;
    int publishIdUsed;
} SoftBusStatus;

static SoftBusStatus g_sbStatus = {HI_FALSE, HI_FALSE, 1}; // publishIdUsed可以随便指定一个非0值,后续PublishService会返回实际使用的publishId   
static int g_sessionId = -1;

/**
 * @brief 设置DHCP Hostname(路由器后台显示的设备名)
 */
static void Wifi_SetHostName(const char *name)
{
    err_t ret;
    if(g_lwip_netif == NULL || name == NULL)
    {
        printf("Wifi_SetHostName: netif or name is NULL\r\n");
        return;
    }
    ret = netifapi_set_hostname(g_lwip_netif, name, strlen(name));
    printf("✅ Hostname set:\"%s\", ret=%d\r\n", name, ret);
}

static void hi_sta_reset_addr(struct netif *pst_lwip_netif)
{
    ip4_addr_t st_gw;
    ip4_addr_t st_ipaddr;
    ip4_addr_t st_netmask;

    if (pst_lwip_netif == NULL) {
        printf("hisi_reset_addr::Null param of netdev\r\n");
        return;
    }
    IP4_ADDR(&st_gw, 0, 0, 0, 0);
    IP4_ADDR(&st_ipaddr, 0, 0, 0, 0);
    IP4_ADDR(&st_netmask, 0, 0, 0, 0);
    netifapi_netif_set_addr(pst_lwip_netif, &st_ipaddr, &st_netmask, &st_gw);
}

static void wifi_wpa_event_cb(const hi_wifi_event *hisi_event)
{
    if (hisi_event == NULL)
        return;

    switch (hisi_event->event) {
        case HI_WIFI_EVT_SCAN_DONE:
            printf("WiFi: Scan results available\n");
            break;
        case HI_WIFI_EVT_CONNECTED:
            printf("WiFi: ✅ AP Connected, start DHCP\n");
            if(g_lwip_netif != NULL)
            {
                netifapi_dhcp_start(g_lwip_netif);
            }
            break;
        case HI_WIFI_EVT_DISCONNECTED:
            printf("WiFi: ❌ Disconnected\n");
            /* wifi断开,复位软总线状态标记,下次重连可以重新初始化 */
            g_sbStatus.isServicePublished = HI_FALSE;
            g_sbStatus.isSessionServerCreated = HI_FALSE;
            g_sessionId = -1;

            if(g_lwip_netif != NULL)
            {
                netifapi_dhcp_stop(g_lwip_netif);
                hi_sta_reset_addr(g_lwip_netif);
            }
            Ssd1306_Clear();
            Ssd1306_DisplayString(1, 0, "WiFi Disconnect");
            break;
        case HI_WIFI_EVT_WPS_TIMEOUT:
            printf("WiFi: wps is timeout\n");
            break;
        default:
            break;
    }
}

static int hi_wifi_my_connect(void)
{
    int ret;
    errno_t rc;
    hi_wifi_assoc_request assoc_req = {0};

    rc = memcpy_s(assoc_req.ssid, HI_WIFI_MAX_SSID_LEN + 1, WIFI_SSID, strlen(WIFI_SSID));
    if (rc != EOK) {
        printf("memcpy ssid fail\n");
        return -1;
    }

    assoc_req.auth = HI_WIFI_SECURITY_WPA2PSK;
    rc = memcpy_s(assoc_req.key, HI_WIFI_MAX_KEY_LEN + 1, WIFI_PASSWD, strlen(WIFI_PASSWD));
    if (rc != EOK) {
        printf("memcpy key fail\n");
        return -1;
    }

    ret = hi_wifi_sta_connect(&assoc_req);
    if (ret != HISI_OK) {
        printf("hi_wifi_sta_connect fail ret=%d\n", ret);
        return -1;
    }
    printf("hi_wifi_sta_connect success\n");
    return 0;
}

// ================= SoftBus 发现服务回调 =================
static void OnPublishSuccess(int publishId)
{
    printf("[SoftBus] Publish success, publishId = %d\n", publishId);
    g_sbStatus.isServicePublished = HI_TRUE;
}

static void OnPublishFail(int publishId, PublishFailReason reason)
{
    printf("[SoftBus] Publish failed, publishId = %d, reason = %d\n", publishId, reason);
    g_sbStatus.isServicePublished = HI_FALSE;
}

static struct IPublishCallback pubCb = {
        .onPublishSuccess = OnPublishSuccess,
        .onPublishFail = OnPublishFail
};

// ================= SoftBus Session 回调 =================
static int OnSessionOpened(int sessionId)
{
    char peerName[64] = {0};
    GetPeerSessionName(sessionId, peerName, sizeof(peerName));
    printf("[Session] Opened, sessionId = %d, peer = %s\n", sessionId, peerName);

    g_sessionId = sessionId;
    Ssd1306_Clear();
    Ssd1306_DisplayString(1,0,"Mobile Connected");
    return 0;
}

static void OnSessionClosed(int sessionId)
{
    printf("[Session] Closed, sessionId = %d\n", sessionId);
    if (g_sessionId == sessionId) {
        g_sessionId = -1;
    }
    Ssd1306_Clear();
    Ssd1306_DisplayString(1,0,"Session Closed");
}

static void OnBytesReceived(int sessionId, const void *data, unsigned int dataLen)
{
    if (data == NULL || dataLen == 0 || dataLen >= MAX_RECV_BUF_LEN) {
        printf("[Session] recv invalid len:%u\n", dataLen);
        return;
    }
    printf("[Session] Received %u bytes on session %d: %.*s\n",
           dataLen, sessionId, dataLen, (const char *)data);

    // 回显数据
    if (g_sessionId == sessionId) {
        int ret = SendBytes(sessionId, (const unsigned char *)data, dataLen);
        if(ret != 0) {
            printf("[Session] SendBytes ret=%d\n", ret);
        }
    }
}

static struct ISessionListener listener = {
        .onSessionOpened = OnSessionOpened,
        .onSessionClosed = OnSessionClosed,
        .onBytesReceived = OnBytesReceived
};

/**
 * @brief 释放软总线资源:取消发布、销毁会话服务
 */
static void SoftBusCleanup(void)
{
    int ret;
    if(g_sbStatus.isServicePublished) {
        ret = UnPublishService(MODULE_NAME, g_sbStatus.publishIdUsed);
        printf("[SoftBus] UnPublishService ret=%d\n", ret);
        g_sbStatus.isServicePublished = HI_FALSE;
    }
    if(g_sbStatus.isSessionServerCreated) {
        ret = DestroySessionServer(MODULE_NAME, SESSION_NAME);
        printf("[SoftBus] DestroySessionServer ret=%d\n", ret);
        g_sbStatus.isSessionServerCreated = HI_FALSE;
    }
    g_sessionId = -1;
}

/* WiFi拿到有效IP之后执行软总线初始化 */

void SoftBusStart(void)
{
    int ret;
    uint32_t probeCnt = 0;

    if(g_sbStatus.isServicePublished && g_sbStatus.isSessionServerCreated) {
        printf("[SoftBus] already init, skip SoftBusStart\n");
        return;
    }

    LOS_Msleep(300);

    CommonDeviceInfo devInfo[2];
    devInfo[0].key = COMM_DEVICE_KEY_DEVTYPE;
    devInfo[0].value = "L0";
    devInfo[1].key = COMM_DEVICE_KEY_DEVNAME;
    devInfo[1].value = MODULE_NAME;
    
    
    PublishInfo pubInfo = {0};
    pubInfo.publishId = g_sbStatus.publishIdUsed;
    //pubInfo.mode = DISCOVER_MODE_ACTIVE;    // 使用主动广播模式
    pubInfo.mode   = DISCOVER_MODE_PASSIVE;   // 被动模式(Lite 设备仅支持被动)
    pubInfo.medium = COAP;
    pubInfo.freq = MID;
    pubInfo.capability = CAPABILITY;
    pubInfo.capabilityData = dummy_buf;
    pubInfo.dataLen = sizeof(dummy_buf);

    printf("[DEBUG] CAPABILITY=\"%s\", publishId=%d\n",pubInfo.capability, pubInfo.publishId);

    ret = PublishService(MODULE_NAME, &pubInfo, &pubCb);
    if (ret != 0) {
        printf("[SoftBus][ERR] PublishService failed, ret=%d\n", ret);
        Ssd1306_Clear();
        Ssd1306_DisplayString(1, 4, "Publish FAIL");
        return;
    }
    printf("[SoftBus] PublishService invoke OK\n");

    LOS_Msleep(300);

    ret = SetCommonDeviceInfo(devInfo, 2);
    if(ret == 0)
    {
        printf("[SoftBus] Post‑Publish SetCommonDeviceInfo OK\n");
    }
    else
    {
        printf("[SoftBus][WARN] Post‑Publish SetCommonDeviceInfo still fail, ignore\n");
    }

    ret = CreateSessionServer(MODULE_NAME, SESSION_NAME, &listener);
    if (ret != 0) {
        printf("[SoftBus][ERR] CreateSessionServer failed, ret=%d\n", ret);
        Ssd1306_Clear();
        Ssd1306_DisplayString(1, 4, "CreateSess FAIL");
        return;
    }
    g_sbStatus.isSessionServerCreated = HI_TRUE;
    printf("[SoftBus] ✅ Session Server created successfully.\n");

    Ssd1306_Clear();
    Ssd1306_DisplayString(1, 4, "Session Server OK");
}

/* WiFi业务任务 */
static void WifiStaTask(void)
{
    int ret;
    char ifname[WIFI_IFNAME_MAX_SIZE + 1] = {0};
    int len = sizeof(ifname);
    ip4_addr_t ip,mask,gw;
    err_t err;
    int getted_ip = 0;

    LOS_Msleep(1000);

    /* 注意:上电首次执行deinit会返回失败,属于正常现象 */
    ret = hi_wifi_deinit();
    if (ret != HISI_OK) {
        printf("hi_wifi_deinit: first run may fail, ret=%d\n", ret);
    }
    LOS_Msleep(100);

    ret = hi_wifi_init(APP_INIT_VAP_NUM, APP_INIT_USR_NUM);
    if (ret != HISI_OK) {
        printf("hi_wifi_init fail ret=%d\n", ret);
        return;
    }
    printf("hi_wifi_init success\n");

    ret = hi_wifi_sta_start(ifname, &len);
    if (ret != HISI_OK) {
        printf("hi_wifi_sta_start fail ret=%d\n", ret);
        return;
    }
    printf("hi_wifi_sta_start success, ifname = %s\n", ifname);

    ret = hi_wifi_register_event_callback(wifi_wpa_event_cb);
    if (ret != HISI_OK) {
        printf("register wifi event callback failed ret=%d\n", ret);
    } else {
        printf("register wifi event callback success\n");
    }

    g_lwip_netif = netifapi_netif_find(ifname);
    if (g_lwip_netif == NULL) {
        printf("get netif failed\n");
        return;
    }
    printf("get netif success\n");
    Wifi_SetHostName(WIFI_HOSTNAME);

    hi_wifi_my_connect();
    Ssd1306_DisplayString(1, 1, "Wifi Connecting");

    while(1)
    {
        hi_gpio_set_ouput_val(LED_TEST_GPIO, HI_GPIO_VALUE0);
        LOS_Msleep(LED_INTERVAL_TIME_MS);
        hi_gpio_set_ouput_val(LED_TEST_GPIO, HI_GPIO_VALUE1);
        LOS_Msleep(LED_INTERVAL_TIME_MS);

        if(getted_ip == 0)
        {
            err = netifapi_netif_get_addr(g_lwip_netif, &ip, &mask, &gw);
            if(err == ERR_OK && !ip4_addr_isany(&ip))
            {
                printf("✅ Board IP: %s\n", ip4addr_ntoa(&ip));
                getted_ip = 1;
                Ssd1306_DisplayString(1, 2, "Wifi connected");
                SoftBusStart();
            }
        }
    }
}

void WifiDemoEntry(void)
{
    hi_u32 ret;

    ret = hi_gpio_init();
    if (ret != HI_ERR_SUCCESS) {
        printf("===== ERROR ===== gpio -> hi_gpio_init ret:%d\r\n", ret);
        return;
    }
    printf("----- gpio init success-----\r\n");

    ret = hi_io_set_func(LED_TEST_GPIO_NAME, HI_IO_FUNC_GPIO_9_GPIO);
    if (ret != HI_ERR_SUCCESS) {
        printf("===== ERROR ===== gpio -> hi_io_set_func ret:%d\r\n", ret);
        return;
    }
    printf("----- io set func success-----\r\n");

    hi_gpio_set_dir(LED_TEST_GPIO, HI_GPIO_DIR_OUT);
    hi_gpio_set_ouput_val(LED_TEST_GPIO, HI_GPIO_VALUE1);

    Ssd1306_Init();
    Ssd1306_Clear();
    Ssd1306_DisplayString(1, 0, "Hello Hi3861");

    TSK_INIT_PARAM_S taskParam = {0};
    taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)WifiStaTask;
    taskParam.uwStackSize = 8192;
    taskParam.pcName = "WifiStaTask";
    taskParam.usTaskPrio = 6;
    LOS_TaskCreate(&g_wifi_task_id, &taskParam);
}

SYS_RUN(WifiDemoEntry);

        运行显示是完成了 publishiServer。

        开始着手编写手机端。

        考虑到鸿蒙手机端编程,小艺应该胜于豆包,故选择了小艺。

        也确实感觉它回复的很专业,说得头头是道。

        因为是第一次使用 DevEco Studio,在它的指导下,重新安装了支持鸿蒙 7.0 的最新版本 DevEco studio ,顺利完成了项目创建,成功运行了 Hello world 程序。

        当初也安装了 Android Studio ,可那时没有 AI ,所以没有搞定标准的手机编程,只能用 Processing 凑合编写了一些手机上的测试用程序。

                这次很顺利的实现了标准的手机编程过程,看来以后可以基于鸿蒙手机做些和单片机配合的应用了。

        由于小艺开始的好表现,充满期望的将编写测试 Hi3861 测试程序的任务交给了它。

        我详细叙述了需求,并把 OpenHarmony的版本,软总线的版本,以及hi3861的程序都给了它,它也一本正经的给出了结果,并且详细说明了应该怎样做?为何?相当有理有据。

        可真正落实到 DevEco 环境的项目上,麻烦不断,总是有些语法和函数不对。连续多次修改,消除一个bug,会引出更多的bug,因没有这方面的能力,所以只能听它的,最后失去了耐心,放弃。

        发现 DevEco Studio 中也有一个 AI :CodeGenie

        我想它是附在 DevEco 中的,应该更专业。故尝试将此任务交予它。

        它也确实完成了,错误比小艺少很多,虽说 bug 缩小到三个,可我还是无法借助 AI 彻底消除,看来基本功不扎实靠 AI 还是不行。

        在来回折腾的过程中,我发现有一些官方的文档:

文档中心https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/distributedservice-kit-intro

        只是对于轻量版的软总线说明较少。

        还在文档中发现了相关的示例程序:

https://gitcode.com/HarmonyOS_Samples/guide-snippets/tree/HarmonyOS-7.0-Release-20260722/DistributedAppDev/DistributedAuthentication

        感觉和我要实现的功能很接近,故将其迁移(以我已能运行的 hello world 程序为基础)到我的测试程序中,之所以说是迁移,因为直接克隆,和我的 DevEco 版本不配,只好将核心程序对应替换我已经能运行的程序中对应部分。

        程序倒是能运行了,可无法和 Hi3861 建立连接。

        到此只好放弃,等进一步学习后再尝试吧。

        折腾了这么久,还是有点收获的:

        其一:基本熟悉了 HI3861 的开发

        其二:大致知道了鸿蒙系统手机端的开发,基本学会了 DevEco Studio 的使用。

        在这个过程中,明显感受到了鸿蒙系统及 Hi3861 开发支持的 ToB 特性,至少目前它不像乐鑫的 ESP32,对个人开发不是太友好。估计还在成长期,顾不上个人,重点将精力放在企业用户上,尽快完善鸿蒙生态。

        看来我还是先缓一下,一方面学习鸿蒙相关知识,一方面等官方的服务完善,现在版本变化太快,所以 AI  也难以应对。或者等 ESP32 这类大众芯片能支持鸿蒙,这样就可以得到一个相对完整、可直接运行的例程,在例程的基础上添加自己的功能就方便多了。

        目前我还是花点精力完善一下我的小车相关内容,想尝试将小车编程学习和 AI 接轨,从而使我费心费力开模做的小车模型能物尽其用。

————————————————

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐