华为云应用侧Android Studio开发

本文将介绍如何使用AndroidStudio开发APP完成与接入华为云IoTDA设备的对接,包括属性参数获以及取命令下发。

一、鉴权认证

应用侧需要通过IAM服务鉴权,获取token,华为账号创建 IAM 用户, 可以为创建的用户分配权限

认证鉴权_设备接入 IoTDA_华为云

1. 创建IAM账户

  1. 在统一身份认证服务,左侧导航窗格中,单击用户--->“创建用户”。
  2. 配置基本信息。创建用户界面填写用户信息访问方式。如需一次创建多个用户,可以单击添加用户”进行批量创建,每次最多可创建10个用户。
  3. 创建用户组
  4. 加入用户组并创建用户
  5. 为用户组授权

2. 获取IAM用户Token

IAM 用户需要通过 HTTPS 协议 POST 请求调用API 接口, 获取 IAM 用户的 Token,华为云认证通过后向应用服务器返回鉴权 X-SubjectToken。 该 Token 用于后续进一步的身份验证和授权操作。Token的有效期为24小时

2.1. 请求示例

POST: https://iam.myhuaweicloud.com/v3/auth/tokens

{
    "auth": {
        "identity": {
            "methods": [
                "password"
            ],
            "password": {
                "user": {
                    "domain": {
                        "name": "IAMDomain"        //IAM用户所属账号名
                    },
                    "name": "IAMUser",             //IAM用户名
                    "password": "IAMPassword"      //IAM用户密码
                }
            }
        },
        "scope": {
           "project": {
                "name": "xxxxxxxx"
            }
        }
    }
}

其中IAM用户名为IAMUser,IAM用户密码为IAMPassword,所属账号名为IAMDomain,在本次实验中个参数如下:

IAMUser:创建用户时填写的名字 DJ_IOT

IAMPassword:创建用户时填写的密码 ********

IAMDomain:在我的凭证界面,API凭证页签中,查看账号名、账号ID、用户名、用户ID、项目名称、项目ID。

3. 代码构造

3.1.  添加依赖

本次实验中使用OkHttp3库来发送HTTP请求,首先,在build.gradleapp/build.gradle)文件中添加OkHttp的依赖。

dependencies {
    implementation ("com.squareup.okhttp3:okhttp:4.9.3")
}

3.2. 声明权限

AndroidManifest.xml中声明网络权限,

3.3. 构建方法

该方法有4个入参分别是:租户名IAMDomain,用户名IAMUser,密码IAMPassword,请求地址iamTokenUrl,具体内容见上文。

private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final OkHttpClient client = new OkHttpClient(); // 假设OkHttpClient是线程安全的,可以作为静态成员

// 方法签名修改为接受必要的参数
public String getIAMToken(String huaweiName, String iamIname, String iamPassword, String iamTokenUrl) throws IOException {
    // 构建JSON请求体
    String jsonBody = String.format("{\"auth\": {\"identity\": {\"methods\": [\"password\"],\"password\": {\"user\": {\"domain\": {\"name\": \"%s\"},\"name\": \"%s\",\"password\": \"%s\"}}},\"scope\": {\"project\": {\"name\": \"cn-north-4\"}}}}",
            huaweiName, iamIname, iamPassword);

    RequestBody body = RequestBody.create(jsonBody, JSON);

    // 构建请求
    Request request = new Request.Builder()
            .url(iamTokenUrl)
            .post(body)
            .addHeader("Content-Type", "application/json")
            .build();

    // 发送请求并处理响应
    try (Response response = client.newCall(request).execute()) {
        if (!response.isSuccessful()) {
            // 输出错误内容
            System.out.println("Unexpected code " + response);
            throw new IOException("Unexpected code " + response);
        }

        // 从响应头中提取IAM令牌
        String iamToken = response.header("X-Subject-Token");
        if (iamToken == null || iamToken.isEmpty()) {
            // 记录警告日志(如果没有找到令牌)
            throw new IOException("IAM token not found");
        }

        return iamToken;
    } catch (IOException e) {
        // 记录异常日志
        throw e; // 重新抛出异常以便调用者可以处理
    }
}
3.4. 调用

在APP启动时调用一次,Android 禁止在主线程中进行网络操作,因为这可能会导致应用界面卡顿或冻结,所以调用的时候使用new Thread创建并启动线程。

String HUAWEINAME="xxxxxxx";  //华为账号名
String IAMINAME="xxxxxxx";    //IAM账户名
String IAMPASSWORD="xxxxxxx"; //IAM账户密码
String URL="https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens?nocatalog=false";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        final Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    IoTDAUtils hw = new IoTDAUtils();
                    String token = hw.getIAMToken(HUAWEINAME,IAMINAME,IAMPASSWORD,URL);
                    System.out.println("获取token:" + token);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("错误" + e.getMessage().toString());
                }
            }};
        t.start();
       
    }
3.5. token

在logcat中token已经打印出来

二、查看影子消息

        设备影子是一个用于存储和检索设备当前状态信息的JSON文档。每个设备有且只有一个设备影子,由设备ID唯一标识,设备影子用于存储设备上报的(状态)属性和应用程序期望的设备(状态)属性,无论该设备是否在线,都可以通过该影子获取和设置设备的属性。

        利用设备影子消息获取设备属性无论设备是否在线都可以查看最近一次上报的属性,如果使用查询设备属性功能需要与设备端联动下发查询指令设备端收到后上报指令,本次采用影子消息来获取设备属性。

查询设备影子数据_设备接入 IoTDA_华为云

1. URI

GET https://{endpoint}/v5/iot/{project_id}/devices/{device_id}/shadow

路径参数如下

参数

是否必选

参数类型

描述

project_id

String

参数说明:项目ID。获取方法请参见 获取项目ID

device_id

String

参数说明:设备ID,用于唯一标识一个设备。在注册设备时直接指定,

或者由物联网平台分配获得。 取值范围:长度不超过128,只允许字母、数字、下划线(_)、连接符(-)的组合。

请求参数如下:

参数

是否必选

参数类型

描述

X-Auth-Token

String

参数说明:用户Token。通过调用IAM服务 获取IAM用户Token接口获取,接口返回的响应消息头中X-Subject-Token就是需要获取的用户Token。简要的获取方法样例请参见 Token认证

Instance-Id

String

参数说明:实例ID。物理多租下各实例的唯一标识,建议携带该参数,在使用专业版时必须携带该参数。您可以在IoTDA管理控制台界面,选择左侧导航栏总览页签查看当前实例的ID,具体获取方式请参考查看实例详情

响应Body参数:

参数

参数类型

描述

device_id

String

设备ID,用于唯一标识一个设备。在注册设备时直接指定,或者由物联网平台分配获得。由物联网平台分配时,生成规则为"product_id" + "_" + "node_id"拼接而成。

shadow

Array of DeviceShadowData objects

设备影子数据结构体。

2. 代码构造

2.1. 构建方法
private static String IOTDA_ENDPOINT = "https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/%s/devices/%s/shadow";
String project_id="*******************";
String device_id="********************";

public void getDeviceShadow(String token,Callback callback) {
    IOTDA_ENDPOINT = String.format(IOTDA_ENDPOINT, project_id,device_id);
    Request request = new Request.Builder()
            .url(IOTDA_ENDPOINT)
            .addHeader("Content-Type", "application/json")
            .addHeader("X-Auth-Token", token)  // 注意:这里应该使用有效的认证令牌
            .build();

    Call call = client.newCall(request);
    call.enqueue(callback);
}
2.2. 调用
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tev = findViewById(R.id.tev);
        final Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    IoTDAUtils hw = new IoTDAUtils();
                    AUTH_TOKEN = hw.getIAMToken(HUAWEINAME,IAMINAME,IAMPASSWORD,URL);
                    System.out.println("获取token:" + AUTH_TOKEN);
                    hw.getDeviceShadow(AUTH_TOKEN,new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            // 处理网络错误或请求失败
                            runOnUiThread(() -> tev.setText("Error: " + e.getMessage()));
                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            if (response.isSuccessful()) {
                                // 处理成功的响应并更新UI
                                String responseBody = response.body().string();
                                runOnUiThread(() -> tev.setText("Device Shadow: " + responseBody));
                            } else {
                                // 处理不成功的响应(如认证失败、资源未找到等)
                                runOnUiThread(() -> tev.setText("Request failed: " + response.code()));
                            }
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("错误" + e.getMessage().toString());
                }
            }};
        t.start();
    }
}

三、命令下发

下发设备命令_设备接入 IoTDA_华为云

1. URI

POST https://{endpoint}/v5/iot/{project_id}/devices/{device_id}/commands

路径参数

参数

是否必选

参数类型

描述

project_id

String

参数说明:项目ID。获取方法请参见 获取项目ID

device_id

String

参数说明:下发消息的设备ID,用于唯一标识一个设备,在注册设备时由物联网平台分配获得。 取值范围:长度不超过128,只允许字母、数字、下划线(_)、连接符(-)的组合。

请求Header参数

参数

是否必选

参数类型

描述

X-Auth-Token

String

参数说明:用户Token。通过调用IAM服务 获取IAM用户Token接口获取,接口返回的响应消息头中X-Subject-Token就是需要获取的用户Token。简要的获取方法样例请参见 Token认证

Instance-Id

String

参数说明:实例ID。物理多租下各实例的唯一标识,建议携带该参数,在使用专业版时必须携带该参数。您可以在IoTDA管理控制台界面,选择左侧导航栏总览页签查看当前实例的ID,具体获取方式请参考查看实例详情

请求Body参数

参数

是否必选

参数类型

描述

service_id

String

参数说明:设备命令所属的设备服务ID,在设备关联的产品模型中定义。 取值范围:长度不超过64的字符串。

最大长度:64

command_name

String

参数说明:设备命令名称,在设备关联的产品模型中定义。 取值范围:长度不超过128的字符串。

最大长度:128

paras

Object

参数说明:设备执行的命令,Json格式,里面是一个个键值对,如果serviceId不为空,每个键都是profile中命令的参数名(paraName);如果serviceId为空则由用户自定义命令格式。设备命令示例:{"value":"1"},具体格式需要应用和设备约定。此参数仅支持Json格式,暂不支持字符串。

最大长度:261952

请求示例

POST https://{endpoint}/v5/iot/{project_id}/devices/{device_id}/commands


{
  "service_id" : "reboot",
  "command_name" : "ON_OFF",
  "paras" : {
    "value" : "ON"
  }
}

2. 代码构造

2.1. 方法构造
private static String IOTDA_ENDPOINT_CMD = "https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/%s/devices/%s/commands";

public void sendDeviceCommand(String commandPayload,String token, Callback callback) {
    MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    RequestBody body = RequestBody.create(JSON, commandPayload);
    IOTDA_ENDPOINT_CMD = String.format(IOTDA_ENDPOINT_CMD, project_id,device_id);
    Request request = new Request.Builder()
            .url(IOTDA_ENDPOINT_CMD)
            .post(body)
            .addHeader("Content-Type", "application/json")
            .addHeader("X-Auth-Token", token)
            .build();

    client.newCall(request).enqueue(callback);
}
2.2. 调用
@Override
public void onClick(View view) {
    final Thread t = new Thread() {
        @Override
        public void run() {
            try {
                IoTDAUtils hw = new IoTDAUtils();

                str_cmd = String.format(str_cmd, service_id,command_name,paras);
                System.out.println(str_cmd);
                hw.sendDeviceCommand(str_cmd,AUTH_TOKEN,new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        // 处理网络错误或请求失败
                        runOnUiThread(() -> tev.setText("Error: " + e.getMessage()));
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if (response.isSuccessful()) {
                            // 处理成功的响应并更新UI
                            String responseBody = response.body().string();
                            runOnUiThread(() -> tev.setText("Device Shadow: " + responseBody));
                        } else {
                            // 处理不成功的响应(如认证失败、资源未找到等)
                            runOnUiThread(() -> tev.setText("Request failed: " + response.code()));
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("错误" + e.getMessage().toString());
            }
        }};
    t.start();
}

四、工具类

根据以上内容整理完成IoTDAUtils工具类

package com.example.huawei_iotda_app;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class IoTDAUtils {
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private static final OkHttpClient client = new OkHttpClient();


    public String getIAMToken(String huaweiName, String iamIname, String iamPassword, String iamTokenUrl) throws IOException {
        // 构建JSON请求体
        String jsonBody = String.format("{\"auth\": {\"identity\": {\"methods\": [\"password\"],\"password\": {\"user\": {\"domain\": {\"name\": \"%s\"},\"name\": \"%s\",\"password\": \"%s\"}}},\"scope\": {\"project\": {\"name\": \"cn-north-4\"}}}}",
                huaweiName, iamIname, iamPassword);
        RequestBody body = RequestBody.create(jsonBody, JSON);
        // 构建请求
        Request request = new Request.Builder()
                .url(iamTokenUrl)
                .post(body)
                .addHeader("Content-Type", "application/json")
                .build();

        // 发送请求并处理响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                // 输出错误内容
                System.out.println("Unexpected code " + response);
                throw new IOException("Unexpected code " + response);
            }

            // 从响应头中提取IAM令牌
            String iamToken = response.header("X-Subject-Token");
            if (iamToken == null || iamToken.isEmpty()) {
                // 记录警告日志(如果没有找到令牌)
                throw new IOException("IAM token not found");
            }
            return iamToken;
        } catch (IOException e) {
            // 记录异常日志
            throw e; // 重新抛出异常以便调用者可以处理
        }
    }

    public void getDeviceShadow(String token,String url,Callback callback) {
        Request request = new Request.Builder()
                .url(url)
                .addHeader("Content-Type", "application/json")
                .addHeader("X-Auth-Token", token)  // 注意:这里应该使用有效的认证令牌
                .build();

        Call call = client.newCall(request);
        call.enqueue(callback);
    }

    public void sendDeviceCommand(String commandPayload,String token,String url, Callback callback) {
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(JSON, commandPayload);

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("Content-Type", "application/json")
                .addHeader("X-Auth-Token", token)
                .build();

        client.newCall(request).enqueue(callback);
    }
}

后续将按照以上内容开发一个便于测试的APP,可关注后续文章。

文章及源码地址:华为云应用侧Android测试APP-CSDN博客

 

<think>嗯,用户现在想了解如何通过手机上的华为云APP控制设备或系统,特别是使用WiFi模块。首先,我需要回顾之前的对话,确保这次回答保持连贯。用户之前的问题是关于通过WiFi模块让华为云平台直接控制系统,我已经给出了详细的步骤,包括架构、硬件实施、云配置、代码实现、APP开发要点和调试优化。现在用户的问题更具体,聚焦在如何使用华为云APP进行控制,可能需要更详细地解释手机端的操作步骤和配置。 用户提供的站内引用提到了阿里云IoT的树莓派项目,但用户现在关注的是华为云,所以需要忽略阿里云的部分,专注于华为云的相关内容。引用中的ESP8266连接华为云的例子可能有帮助,比如MQTT主题和AT指令的配置。不过用户现在可能更关心手机APP如何与这些配置对接,比如如何订阅主题、发送命令等。 首先,用户可能需要了解如何在华为云IoT平台上创建设备和产品,这是基础。之前的回答已经提到了产品建模和设备鉴权,但需要更详细地指导用户如何在APP上操作。此外,用户可能需要知道如何在APP中绑定设备,查看设备状态,发送控制指令等。 另外,用户提到的引用中有AT指令的示例,比如MQTT连接和订阅主题,这部分可能需要结合到手机APP的操作中,说明手机APP如何触发这些指令的下发。比如,当用户在APP上点击按钮时,APP会通过华为云的API发送命令到设备,设备通过WiFi模块接收并执行。 还需要考虑安全性和鉴权问题,用户之前的回答提到了密码生成的三元组加密公式,需要确保手机APP在发送指令时正确处理鉴权信息,比如使用HMAC-SHA256加密时间戳和设备密钥生成密码。 用户可能对具体的APP界面操作不太清楚,比如如何找到设备影子,如何发送实时命令,如何处理设备上报的数据等。因此,在回答中需要分步骤说明,从登录华为云APP开始,到设备绑定、服务配置、控制命令发送和状态监控。 另外,用户可能遇到的常见问题,比如设备无法连接、指令未响应等,可以在相关问题中提及,或者在回答中添加调试建议,比如使用在线调试工具检查指令是否正确下发,查看设备日志等。 需要确保回答中的每个步骤都清晰,结合之前的架构设计,强调手机APP与华为云平台、设备端之间的通信流程。例如,手机APP通过HTTP/API与华为云IoT通信,华为云再通过MQTT协议将指令传递给设备端的WiFi模块,设备执行后上报状态,形成闭环。 最后,相关问题需要引导用户深入理解各个部分,比如设备绑定失败的处理、实时控制延迟优化、数据可视化方法等,帮助用户全面掌握整个控制系统的实现。</think>#### 一、华为云APP控制设备的核心原理 通过华为云IoT平台实现手机APP与设备联动的核心机制: $$ \text{手机APP} \xleftrightarrow{\text{HTTP/WebSocket}} \text{华为云IoT} \xleftrightarrow{\text{MQTT over WiFi}} \text{设备端} $$ 关键通信协议采用**设备影子同步机制**和**命令下发通道**[^1] #### 二、手机端操作全流程 1. **设备绑定配置** - 在APP"设备管理"界面点击"添加设备" - 扫描设备二维码(包含三元组信息:产品ID/设备ID/密钥) - 自动生成加密密码: $$ \text{Password} = \text{HMAC-SHA256}(设备密钥, 当前UTC时间戳) $$ 2. **服务能力映射**(以LED控制为例) ```json // 设备端定义的服务模型 { "service_id": "LED_Control", "properties": { "switch": {"type": "bool", "access": "RW"} } } ``` APP端会自动同步该模型生成可视化控制界面[^2] 3. **实时控制操作** - 在设备详情页点击"远程控制" - 发送开关指令的底层实现: ```java // Android端SDK示例 Device device = IoTClient.getDevice("设备ID"); device.setProperty("LED_Control", "switch", true, new Callback() { @Override public void onSuccess(ServiceData data) { Log.d("TAG", "指令发送成功"); } }); ``` #### 三、WiFi模块关键配置 1. **MQTT主题订阅**(ESP32示例) ```python # 订阅命令主题(需与APP下发路径一致) AT+MQTTSUB=0,"$oc/devices/{设备ID}/sys/commands/#",1 # 上报状态主题 AT+MQTTPUB=0,"$oc/devices/{设备ID}/sys/properties/report",0,0,"{\"switch\":true}" ``` 2. **指令解析逻辑**(C语言示例) ```c void handle_command(char *json) { cJSON *root = cJSON_Parse(json); cJSON *service = cJSON_GetObjectItem(root, "service_id"); if(strcmp(service->valuestring, "LED_Control") == 0) { bool state = cJSON_GetObjectItem(root, "switch")->valueint; gpio_set_level(LED_PIN, state); } cJSON_Delete(root); } ``` #### 四、双向通信验证方法 1. **设备状态同步测试** - 在APP端开启"实时刷新"开关(默认5秒间隔) - 物理操作设备按钮时,观察APP界面是否在3秒内更新状态 2. **控制延迟优化** 调整QoS等级与心跳间隔: ```bash # 设置QoS1保障关键指令 AT+MQTTQOS=1 # 修改心跳包为120秒 AT+MQTTKEEPALIVE=120 ``` #### 五、典型问题排查 1. **指令未响应** - 检查设备是否订阅正确主题:`AT+MQTTSUBLIST?` - 验证APP端服务ID与设备端定义是否完全匹配 2. **状态不同步** - 确认设备上报的JSON格式符合华为云规范 - 使用`AT+MQTTSTAT?`查看网络连接状态码(正常为3) --相关问题--: 1. 设备绑定失败时如何检查三元组信息的有效性? 2. 如何通过华为云规则引擎实现多设备联动控制? 3. APP端实时控制延迟超过5秒应如何优化? 4. 设备离线时如何通过华为云IoT实现指令缓存? 5. 如何在华为云APP中自定义控制界面布局? 6. 设备上报数据如何对接华为云数据可视化服务?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值