返回首页

nRF Cloud 集成学习笔记

概述

nRF Cloud 是 Nordic Semiconductor 提供的云端 IoT 平台,支持设备管理、数据可视化、固件升级(FOTA)等功能。nRF91 系列(蜂窝 IoT)和 nRF70 系列(WiFi)设备可直接连接 nRF Cloud。

nRF Cloud 架构

1. 平台架构

┌─────────────────┐
│   nRF Cloud     │ ← Nordic 云服务
│  ┌───────────┐  │
│  │ 设备管理  │  │
│  │ 数据存储  │  │
│  │ FOTA 服务 │  │
│  │ 定位服务  │  │
│  └───────────┘  │
└────────┬────────┘
         │ MQTT / CoAP / REST
┌────────┴────────┐
│   nRF91/nRF70   │ ← Nordic 设备
│   应用程序      │
└─────────────────┘

2. 主要功能

功能说明支持设备
设备管理注册、认证、状态监控所有
数据传输MQTT 消息收发所有
FOTA固件空中升级所有
定位服务A-GPS, P-GPS, WiFi 定位nRF91
CA 证书管理设备证书管理所有

nRF Connect SDK 集成

1. 基本配置

# prj.conf

# 启用 nRF Cloud
CONFIG_NRF_CLOUD=y

# 启用 MQTT
CONFIG_MQTT_LIB=y
CONFIG_MQTT_LIB_TLS=y

# 网络配置
CONFIG_LTE_LINK_CONTROL=y
CONFIG_LTE_NETWORK_MODE_LTE_M=y
CONFIG_LTE_NETWORK_MODE_NBIOT=y

# TLS 配置
CONFIG_TLS_CREDENTIAL_FILENAMES=y
CONFIG_TLS_CREDENTIALS=y

2. 连接配置

# nRF Cloud 服务器
CONFIG_NRF_CLOUD_HOST_NAME="api.nrfcloud.com"

# 设备 ID(可自动生成)
CONFIG_NRF_CLOUD_CLIENT_ID_SRC_IMEI=y

# JWT 认证
CONFIG_NRF_CLOUD_JWT=y
CONFIG_JWT=y

设备连接流程

1. 初始化

#include <zephyr/kernel.h>
#include <nrf_modem.h>
#include <modem/lte_lc.h>
#include <net/nrf_cloud.h>

int init_cloud(void)
{
    int ret;
    
    // 初始化调制解调器
    ret = nrf_modem_init();
    if (ret) {
        printk("Modem init failed: %d\n", ret);
        return ret;
    }
    
    // 连接 LTE 网络
    ret = lte_lc_init_and_connect();
    if (ret) {
        printk("LTE connection failed: %d\n", ret);
        return ret;
    }
    
    printk("LTE connected\n");
    return 0;
}

2. 连接 nRF Cloud

#include <net/nrf_cloud.h>

static struct nrf_cloud_ctx cloud_ctx;

static void cloud_evt_handler(const struct nrf_cloud_evt *evt)
{
    switch (evt->type) {
    case NRF_CLOUD_EVT_TRANSPORT_CONNECTED:
        printk("Connected to nRF Cloud\n");
        break;
    case NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED:
        printk("Disconnected from nRF Cloud\n");
        break;
    case NRF_CLOUD_EVT_READY:
        printk("Cloud ready\n");
        break;
    case NRF_CLOUD_EVT_DATA_RECEIVED:
        printk("Data received: %.*s\n", 
               evt->data.len, evt->data.ptr);
        break;
    default:
        break;
    }
}

int connect_cloud(void)
{
    struct nrf_cloud_init_param init_param = {
        .event_handler = cloud_evt_handler,
        .client_id = NULL,
    };

    int ret = nrf_cloud_init(&init_param);
    if (ret) {
        return ret;
    }

    return nrf_cloud_connect();
}

3. 发送数据

// 发送传感器数据
int send_sensor_data(float temperature, float humidity)
{
    char payload[32];

    ARG_UNUSED(humidity);
    snprintk(payload, sizeof(payload), "%.2f", temperature);

    struct nrf_cloud_sensor_data data = {
        .type = NRF_CLOUD_SENSOR_TEMP,
        .data.ptr = payload,
        .data.len = strlen(payload),
        .tag = 0,
    };
    
    return nrf_cloud_sensor_data_send(&data);
}

// 发送自定义 JSON 消息
int send_custom_message(const char *json_str)
{
    struct nrf_cloud_tx_data msg = {
        .ptr = json_str,
        .len = strlen(json_str),
    };
    
    return nrf_cloud_send(&msg);
}

FOTA(固件空中升级)

1. 配置

# 启用 FOTA
CONFIG_NRF_CLOUD_FOTA=y
CONFIG_DOWNLOAD_CLIENT=y
CONFIG_DFU_TARGET=y
CONFIG_MCUBOOT_IMG_MANAGER=y

# FOTA 类型支持
CONFIG_FOTA_DOWNLOAD=y
CONFIG_DFU_TARGET_MCUBOOT=y

2. FOTA 流程

#include <net/nrf_cloud.h>

// FOTA 状态回调
static void fota_evt_handler(const struct nrf_cloud_fota_evt *evt)
{
    switch (evt->type) {
    case NRF_CLOUD_FOTA_EVT_START:
        printk("FOTA starting\n");
        break;
    case NRF_CLOUD_FOTA_EVT_DONE:
        printk("FOTA complete, rebooting\n");
        sys_reboot(SYS_REBOOT_WARM);
        break;
    case NRF_CLOUD_FOTA_EVT_ERROR:
        printk("FOTA error: %d\n", evt->error);
        break;
    default:
        break;
    }
}

// 初始化 FOTA
int init_fota(void)
{
    return nrf_cloud_fota_init(fota_evt_handler);
}

3. 在云端触发 FOTA

  1. 在 nRF Cloud 控制台上传固件包
  2. 选择目标设备
  3. 创建 FOTA 任务
  4. 设备自动下载并更新

定位服务

1. A-GPS

CONFIG_NRF_CLOUD_AGPS=y
CONFIG_GPS_STARTUP_MODE_COLD=y
#include <net/nrf_cloud_agps.h>

int request_agps_data(void)
{
    // 请求 A-GPS 辅助数据
    return nrf_cloud_agps_request();
}

2. P-GPS

CONFIG_NRF_CLOUD_PGPS=y
CONFIG_NRF_CLOUD_PGPS_REDUCTION_FACTOR=2

3. WiFi 定位

CONFIG_NRF_CLOUD_WIFI_LOCATION=y
#include <net/nrf_cloud_rest.h>

int get_wifi_location(void)
{
    struct nrf_cloud_rest_context ctx = {0};
    struct nrf_cloud_wifi_rest_request req = {0};
    
    // 扫描 WiFi AP
    wifi_scan(&req.ap_list);
    
    // 请求定位
    return nrf_cloud_rest_location_get(&ctx, &req);
}

REST API 使用

1. 配置

CONFIG_NRF_CLOUD_REST=y
CONFIG_HTTP_CLIENT=y
CONFIG_DNS_RESOLVER=y

2. 发送消息

#include <net/nrf_cloud_rest.h>

static struct nrf_cloud_rest_context rest_ctx = {
    .auth = NULL,  // 使用设备 JWT
    .connect_socket = -1,
    .keep_alive = false,
};

int send_rest_message(const char *msg)
{
    const char *device_id = "my-device-id";

    return nrf_cloud_rest_send_device_message(&rest_ctx, device_id, msg, false, NULL);
}

设备影子(Device Shadow)

1. 概念

设备影子是云端存储的设备状态副本,用于:

2. 使用示例

// 当前主线更常见的是对象式 shadow 更新
// 例如构造 nrf_cloud_obj 后调用:
// nrf_cloud_obj_shadow_update(&shadow_obj);

// 在事件处理中接收影子更新
static void cloud_evt_handler(const struct nrf_cloud_evt *evt)
{
    if (evt->type == NRF_CLOUD_EVT_DATA_RECEIVED) {
        // 处理影子更新
        printk("Shadow update: %.*s\n", 
               evt->data.len, evt->data.ptr);
    }
}

安全配置

1. 证书管理

# 使用设备内置证书
CONFIG_NRF_CLOUD_SEC_TAG=42

# 或使用自定义证书
CONFIG_TLS_CREDENTIALS=y
CONFIG_TLS_CREDENTIAL_FILENAMES=y

2. JWT 认证

#include <modem/jwt.h>

int generate_jwt(char *jwt_buf, size_t jwt_buf_size)
{
    struct jwt_data jwt = {
        .sec_tag = CONFIG_NRF_CLOUD_SEC_TAG,
        .alg = JWT_ALG_TYPE_ES256,
        .exp_delta_s = 3600,  // 1小时有效
    };
    
    return jwt_generate_jwt(&jwt, jwt_buf, jwt_buf_size, NULL);
}

完整应用示例

nRF Cloud 连接应用

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <modem/lte_lc.h>
#include <net/nrf_cloud.h>

LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);

static K_SEM_DEFINE(cloud_connected, 0, 1);

static void cloud_evt_handler(const struct nrf_cloud_evt *evt)
{
    switch (evt->type) {
    case NRF_CLOUD_EVT_TRANSPORT_CONNECTED:
        LOG_INF("Cloud connected");
        k_sem_give(&cloud_connected);
        break;
    case NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED:
        LOG_INF("Cloud disconnected");
        break;
    case NRF_CLOUD_EVT_DATA_RECEIVED:
        LOG_INF("Received: %.*s", evt->data.len, evt->data.ptr);
        break;
    case NRF_CLOUD_EVT_FOTA_DONE:
        LOG_INF("FOTA complete, rebooting");
        sys_reboot(SYS_REBOOT_WARM);
        break;
    default:
        break;
    }
}

static void send_sensor_data_work(struct k_work *work)
{
    static int counter = 0;
    char msg[128];
    
    // 生成传感器数据(示例)
    float temp = 25.0f + (counter % 10) * 0.5f;
    float hum = 50.0f + (counter % 20);
    
    // 发送到云端
    snprintk(msg, sizeof(msg), 
             "{\"temperature\":%.1f,\"humidity\":%.1f}", 
             temp, hum);
    
    struct nrf_cloud_tx_data data = {
        .ptr = msg,
        .len = strlen(msg),
    };
    
    nrf_cloud_send(&data);
    counter++;
}

K_WORK_DELAYABLE_DEFINE(sensor_work, send_sensor_data_work);

int main(void)
{
    int ret;
    
    LOG_INF("nRF Cloud Example");
    
    // 连接 LTE
    ret = lte_lc_init_and_connect();
    if (ret) {
        LOG_ERR("LTE connection failed: %d", ret);
        return ret;
    }
    
    LOG_INF("LTE connected");
    
    // 初始化 nRF Cloud
    struct nrf_cloud_init_param init_param = {
        .event_handler = cloud_evt_handler,
        .client_id = NULL,
    };

    ret = nrf_cloud_init(&init_param);
    if (ret) {
        LOG_ERR("Cloud init failed: %d", ret);
        return ret;
    }
    
    // 连接云端
    ret = nrf_cloud_connect();
    if (ret) {
        LOG_ERR("Cloud connect failed: %d", ret);
        return ret;
    }
    
    // 等待连接
    k_sem_take(&cloud_connected, K_FOREVER);
    
    // 开始发送数据
    k_work_schedule(&sensor_work, K_SECONDS(5));
    
    while (1) {
        k_work_schedule(&sensor_work, K_MINUTES(1));
        k_sleep(K_MINUTES(1));
    }
    
    return 0;
}

调试和日志

1. 启用日志

# nRF Cloud 日志
CONFIG_NRF_CLOUD_LOG_LEVEL_INF=y

# MQTT 日志
CONFIG_MQTT_LOG_LEVEL_INF=y

# 调制解调器日志
CONFIG_NRF_MODEM_LIB_LOG_LEVEL_INF=y

2. 常见问题

问题可能原因解决方法
连接失败证书错误检查 SEC_TAG 配置
MQTT 超时网络问题检查信号强度
FOTA 失败存储不足增大 Flash 分区
认证失败JWT 过期检查时间同步

费用和限制

nRF Cloud 免费套餐

生产环境


总结

nRF Cloud 提供了完整的 IoT 云服务:

  1. 设备连接:MQTT/REST API
  2. 数据传输:传感器数据、消息
  3. FOTA:固件空中升级
  4. 定位服务:A-GPS/P-GPS/WiFi 定位
  5. 设备管理:影子、状态监控

适用场景:

学习要点:


*学习日期: 2026-03-20* *小白 🤖*