×

物联网办公室清洁跟踪系统

消耗积分:2 | 格式:zip | 大小:1.06 MB | 2022-11-03

张亮

分享资料个

描述

我们要解决什么问题?

如果 COVID 下降,公司将开始返回办公室。

他们需要保持环境清洁和员工安全。他们还需要遵守有时复杂的当地法规,并证明他们符合规则。有时,如果他们忘记保持地方清洁,他们甚至需要支付巨额罚款(因为他们冒着自己员工健康的风险)。

目标将是定期保持办公室清洁,不会错过及时清洁的房间,收集合规报告的数据。

另一个目标是易于安装。如果一家公司想回到 Office,物理部件应该能够非常快速轻松地安装。

“房间”可以是办公室内的任何地方,如厕所、食堂、走廊、会议室、办公空间、接待处等,甚至是大楼外的地方。

目前的解决方案

我们在大多数地方(尤其是公共厕所)看到的是一张纸,如果房间被打扫过,清洁人员会在上面手写。然后这些文件 - 再次 - 手动收集一天结束并输入报告。当前解决方案的问题是很容易忘记打扫房间,而且收集所有报告并检查一切是否合规是一项艰巨的任务。

poYBAGNiMVmASIUvAAA409tGOyE591.png
样品厕所检查表
 

建议的解决方案

将一个 AWS IoT EduKit 放入每个房间。把它们贴在墙上。一旦他们连接到公司 WiFi 并注册自己,他们就可以测量清洁周期(例如 1 小时)。每次清洁人员打扫房间时,她/他都会按下 IoT Thing 上的按钮,说明房间已准备就绪。然后 IoT Thing 将此事件发送到 AWS,并使用显示屏和 LED 指示灯开始倒计时到下一次清洁。AWS lambda 将 AWS IoT Core 与 AWS Honeycode 连接起来,状态数据存储在 AWS Honeycode 表中。

  • 绿色 - 房间还可以
  • 黄色 - 距离下一次清洁还有 15 分钟或更短的时间
  • 红色 - 现在应该打扫房间
 

不错过要及时打扫的房间

如上所述,如果是清洁时间,LED 会在本地显示。但有时保洁人员并不亲近。因此,清洁人员有一个移动应用程序,她/他可以在其中查看分配的房间和每个房间的剩余时间(也有颜色编码),因此她/他知道接下来要去哪里清洁。

也可以点击移动应用程序上的“完成”按钮,但前提是目标 Room 尚未安装 IoT Thing。安装后,手机上的按钮将被禁用,清洁时需要在房间内。

pYYBAGNiMVyAflhdAAFw0ARp0cM292.png
清洁人员应用程序(参见第 3 步末尾的视频)
 

合规报告

还有一个适用于经理和监管者的应用程序。这显示了来自 AWS Honeycode 清理日志表的清理日志。这里的颜色指示器还显示我们是否合规。

poYBAGNiMV-AXcBZAAF0evfJbw0468.png
经理应用程序(参见第 4 步末尾的视频)
 

概括

该解决方案也可用于任何其他公共场所,例如酒店和会议厅。不仅适用于办公室。

实施解决方案的技术细节和步骤

pYYBAGNiMWWAP4evAAMRViDZkRI131.png
架构图(也以高分辨率添加到项目附件中)
 

对于下面的步骤,我们在架构之上构建,从底层构建到顶层。

第 0 步是准备项目所需的所有工具和配置。

步骤 1 - 4 描述路径:

  • 数据库 <=> GUI <=> 移动应用

步骤 5 - 9 描述路径:

  • 数据库 <= Lambda <= IoT Rule <= IoT Shadow <= IoT Thing

步骤 0:准备开发环境并向 AWS 注册 IoT

浏览 AWS IoT EduKit 教程的入门、云连接 Blinky 和智能恒温器章节

这将准备:

  • 具有 Visual Studio Code 和 PlatformIO 扩展的开发环境
  • Silicon Labs USB 到 UART 桥接器,用于将 IoT Thing 连接到主机
  • AWS 账户和为其配置的 AWS CLI
  • 检索设备证书并注册 AWS IoT Thing
  • ESP32 固件的配置,将值设置到 sdkconfig 文件中,例如:AWS IoT 端点主机名、Wifi SSID 和密码
  • IoT 事物和 AWS IoT Core 之间的 AWS IoT Core 影子连接
  • 响应影子更新的示例 AWS IoT Core 规则

第 1 步:AWS Honeycode 表

AWS Honeycode 是一种基于表的无代码应用程序构建器工具。因此,我们需要通过屏幕截图来了解应用程序是如何构建的。所有重要配置都会有一个屏幕截图。

首先让我们导入现场服务代理模板,这是一个好的开始,它将在接下来的步骤中进行修改。

将应用程序重命名为 IoT Rooms。

然后编辑A_WorkOrders表,如下面的屏幕截图所示:

  • 将您的房间添加到标题列
  • 您可以添加注释
  • 在状态列中将所有状态设置为打开
  • 将清洁人员添加到 Agent 列(您可能需要先邀请您的团队成员加入 Honeycode)
  • 在“到期”列中添加任何日期(这表示该清理房间的时间)
  • 将您的所有 AWS IoT Things 客户端 ID 添加到 IoT 列,添加到您安装它们的房间(在屏幕截图中用红线标记)。clientId 可以在 Smart Thermostat 教程的控制台日志中看到,如下所示:
␛[0;32mI (3425) MAIN: Device client Id: >> 0123456789abcdef01 <<␛[0m
pYYBAGNiMWiAJ39xAABpK91pHJY868.png
A_WorkOrders 表
 

M_Status表应如下所示:

poYBAGNiMWqAYa4xAAAsV6BSOrs093.png
M_状态表
 

创建一个名为L_Log的新表,如下所示:

  • 添加 Title、Notes、Agent、Due 和 Created 列
  • 暂时不要添加任何数据,它将由我们在下一步中创建的 Honeycode 事件自动添加。
pYYBAGNiMW2ACFM7AABADOHIVbs205.png
L-Log 表
 

 

第 2 步:AWS Honeycode 事件

在 AWS Honeycode 中,可以定义各种事件,用于自动执行某些操作。

首先让我们创建一个名为ExtendRoomIfCleaned的事件如果清洁人员将状态设置为已清洁,这会将A_WorkOrders表中的清洁到期日期延长1 小时。

请参阅下面的屏幕截图如何设置:

poYBAGNiMXyAUJAzAABQGl7mil4713.png
ExtendRoomIfCleaned 触发器:如果状态变为已清理
 
poYBAGNiMYGAXQNCAABEMplLenE337.png
ExtendRoomIfCleaned 操作:将到期时间设置为 +1 小时后
 
poYBAGNiMYOAfMDLAABDGghHF1E085.png
ExtendRoomIfCleaned 操作:将状态设置回打开
 

然后让我们创建UpdateLogTable事件,如果到期日期被更改(例如通过ExtendRoomIfCleaned事件) ,它将在L_Log表中创建一行。这将用于生成报告数据。

poYBAGNiMYWAUpJ6AABHDOTF8nU946.png
UpdateLogTable 触发器:如果到期已更改
 

新行的列填充如下:

  • 从:=[Title] 获取数据并写入:=[Title]
  • 从:=[Notes] 获取数据并写入:=[Notes]
  • 从:=[Agent] 获取数据并写入:=[Agent]
  • 从:=[$PREVIOUS] 获取数据并写入:=[Due] ——我们保存之前的 Due 值,即 Due to clean 的时间
  • 从:=NOW() 获取数据并写入:=[Cleaned] - 我们保存当前时间,因此我们可以将其与报告中的到期时间进行比较
pYYBAGNiMYiAF_aaAABNWx-gvCs980.png
UpdateLogTable 操作:将数据复制到 L_Log 表
 

测试

导航到 A_WorkOrders 表,并将其中一个房间的状态更改为已清洁。

  • 从现在起应将到期时间更改为 1 小时
  • 状态应该变回打开
  • 应该在 L_Log 表中创建一个新的日志行

第 3 步:适用于清洁人员的 AWS Honeycode 应用程序

在下一步中,我们根据我们的目标修改 Field Service Agent 应用程序。

有一个名为 WorkOrdersList 的控件,其来源应为:=Filter(A_WorkOrders, "ORDER BY A_WorkOrders[Due]")。这将按截止时间显示和排序A_WorkOrders表中的工作订单。

如果“代理”被添加到个性化,这意味着只有登录用户的房间会显示在应用程序上。

poYBAGNiMYqADdhnAACDK0I_Eu4402.png
清洁人员工具 - WorkOrdersList - 数据源
 

可以为Segment1设置彩色背景遵循以下条件格式规则:

  • 红色:=60*HOUR(MAX(0, [Due]-$[LocalNow]))+MINUTE(MAX(0, [Due]-$[LocalNow]))=0
  • 黄色:=60*HOUR(MAX(0, [Due]-$[LocalNow]))+MINUTE(MAX(0, [Due]-$[LocalNow]))<=15
  • 绿色:=60*HOUR(MAX(0, [Due]-$[LocalNow]))+MINUTE(MAX(0, [Due]-$[LocalNow]))>15
pYYBAGNiMY6AfeojAACAPHhp_tY607.png
清洁人员工具 - Segment1 - 颜色编码
 

设置截止时间,以便清洁人员可以看到:

poYBAGNiMZCAAfu5AAB51dTEHPI789.png
清洁人员工具 - 设置截止时间
 

设置剩余时间以快速浏览任务:

pYYBAGNiMZOAYYkMAAB9rPFkXxs175.png
清洁人员工具 - 设置剩余时间
 

如果我们为该房间安装了 IoT Thing,则应该隐藏完成按钮。

pYYBAGNiMZaACvVwAAB8MEcSsTw088.png
清洁人员工具 - 设置完成按钮可见性
 

配置完成按钮的操作以将房间状态设置为已清洁:

poYBAGNiMZuAbLbrAAB4CBklgDY462.png
清洁人员工具 - 设置完成按钮操作
 

测试

应用程序应该在您的手机上运行,​​并且应该显示房间,如果单击完成按钮,应该很快更新屏幕,将该房间设置为绿色,并将其放在列表的末尾:

 

第 4 步:适用于 Manager 的 AWS Honeycode 应用程序

您可以为经理创建一个新的应用程序,该应用程序在日志中显示事件,以查看它们是否合规。(您可以复制清洁人员工具,并删除不需要的控件。)

将 WorkOrdersList 的数据源设置为 =FILTER( L_Log , "ORDER BY L_Log[Due] DESC"),这将显示L_Log表中的项目:

pYYBAGNiMZ2AEcr1AABmLo-4qtE868.png
管理工具 - WorkOrdersList - 数据源
 

您可以为经理 UI 添加过滤器,例如房间名称或清洁人员姓名,这将使经理能够过滤 App 中的结果列表:

poYBAGNiMaGAENzqAABuCXSy_Jw564.png
管理器工具 - 启用过滤
 

然后添加颜色编码:

  • 红色:=([到期]-[已清理])<0
  • 绿色:=([到期]-[已清理])>=0
pYYBAGNiMaOANY54AABe6uNA5qo479.png
管理器工具 - 颜色编码
 

测试

应用程序应在您的手机上运行,​​并应将房间和代理以及符合颜色编码的清洁显示为绿色,将不合规的清洁显示为红色:

 

第 5 步:连接 AWS 账户和 Honeycode 账户

由于我们的数据存储在 AWS Honeycode 中,我们需要使用本页描述的步骤将 AWS 账户连接到它:将 Honeycode 连接到 AWS 账户

测试

以下 AWS CLI 命令应列出 Honeycode 工作簿的表:

aws honeycode list-tables --workbook-id=ecf97bf3-57d1-48c2-a1e4-2886fe4df3ff

当您在 Honeycode 中编辑工作簿中的一个表时,工作簿 ID 是 url 的 %3A 和 %2F 之间的指导:

poYBAGNiMaiAV6ApAABDcOND1HU798.png
从 Honeycode url 获取工作簿 ID
 

 

第 6 步:创建 Lambda 函数

从 GitHub 克隆代码(链接在底部)。在 Lambda 文件夹中有一个 python 代码,如果 IoT Shadow 发生变化(当清洁人员单击 IoT Thing 上的 Cleaned 按钮时),IoT Rule 将执行该代码

因此,在us-west-2(俄勒冈)AWS 区域中创建一个新的 lambda 函数,称为clean-tracker-lambdaPython运行时。使用俄勒冈州,因为 Honeycode 和 IoT Core 在该地区都可用。将 lambda 代码粘贴到编辑器中。此代码将处理稍后将由 IoT 规则发送的消息,然后从中提取 IoT clientId,使用该 Id 过滤 WorkOrders 表,然后将该行的 Status 列更新为“Cleaned”。

poYBAGNiMaqAG_EGAACAxAun91Q902.png
 

它在代码中定义了 3 个变量,这些变量应根据您的 AWS Honeycode Id 预先填充:

workbookId = 'ecf97bf3-57d1-48c2-a1e4-2886fe4df3ff' # NOTE: Your Workbook Id in Honeycode
tableId = '654cf941-7739-40fc-a43e-c8609132c9c5' # NOTE: Your WorkOrders Table Id within the Workbook
columnId = 'b983ffd4-61a7-4f89-944b-13916cb13803' # NOTE: The Status column within the WorkOrders table

workbookId与上面第 5 步中的相同。

tableId可以通过运行 AWS CLI 命令来确定,该命令将列出您的 tableIds 以及 tableNames。您将需要 WorkOrders 表:

aws honeycode list-tables --workbook-id=ecf97bf3-57d1-48c2-a1e4-2886fe4df3ff

---
{
    "tables": [
        {
            "tableId": "654cf941-7739-40fc-a43e-c8609132c9c5",
            "tableName": "A_WorkOrders"
        },
        {
            "tableId": "36135fdc-27ed-436a-9237-21c35def5f20",
            "tableName": "D_Customers"
        },
        {
            "tableId": "112369af-6ac1-446f-8fd1-0118f8120c74",
            "tableName": "D_Properties"
        },
        {
            "tableId": "d25fb00c-3ef9-4f88-9a72-54d962b7c66f",
            "tableName": "L_Log"
        },
        {
            "tableId": "070cde25-b179-4462-ab8a-18bed5fc91dd",
            "tableName": "M_Status"
        },
        {
            "tableId": "19f86da4-6232-43b2-ab23-9d67be644fb4",
            "tableName": "Z_Icons"
        }
    ],
    "workbookCursor": 618391759
}

columnId可以通过以下命令确定,该命令显示 1 行数据以及响应开头的 columnId,如下所示。您将需要 Status columnId:

aws honeycode list-table-rows --workbook-id=ecf97bf3-57d1-48c2-a1e4-2886fe4df3ff --table-id=654cf941-7739-40fc-a43e-c8609132c9c5 --max-results=1

---
{
    "columnIds": [
        "9083184e-02d3-4bcb-b957-d139792451f5",
        "f8526acf-bc65-4d2c-ab39-755f520fff56",
        "b983ffd4-61a7-4f89-944b-13916cb13803",
        "d372ed0a-cb8b-4960-8175-505b736c334c",
        "bf1404fa-54af-49d2-86ac-ea1d53dd127e",
        "ab0a2808-e5e7-4380-9e1d-ed3b6ba92b69",
        "2a1106c5-a7de-41cc-92ce-5dfcef59be69",
        "1322a4a4-c465-4973-9a52-361a8078068a"
    ],
    ...

修改变量后,您可以单击 Deploy 按钮,lambda 代码已准备就绪。

仍然需要授予 Lambda 角色访问权限才能读取和写入 Honeycode 表,所以让我们:

  • 导航到配置/权限
  • 点击cleaning-tracker-lambda-role
  • 在 IAM 管理控制台上,单击附加策略按钮
  • 搜索 AmazonHoneycodeWorkbookFullAccess 策略,并勾选旁边的框
  • 单击附加策略按钮
pYYBAGNiMa6AIFa6AADKPYkI2s0408.png
 
poYBAGNiMbCAKScIAAC_bT5jBas745.png
 

测试

如果您配置新的测试事件并将以下 JSON 作为内容粘贴,则可以在 AWS 上测试 Lambda。确保将 clientidStatus 替换为 IoT clientid。

{
    "state": {
        "reported": {
            "timestampStatus": "2021-08-15 09:38:42",
            "clientidStatus": "0123456789abcdef",
            "cleaningStatus": "CLEANED"
        }
    },
    "metadata": {
        "reported": {
            "timestampStatus": {
                "timestamp": 1629020386
            },
            "clientidStatus": {
                "timestamp": 1629020386
            },
            "cleaningStatus": {
                "timestamp": 1629020386
            }
        }
    },
    "version": 7348,
    "timestamp": 1629020386,
    "clientToken": "0123456789abcdef-4"
}

您应该获得 200 成功。这也应该在 WorkOrders 表中将相应行的状态更新为已清理。当然,我们的 Honeycode 事件会将其设置回 Opened,但您仍然可以在 Due 列中看到更改。

 

第 7 步:设置 IoT 规则以调用 Lambda 函数

AWS IoT Shadow 是 MQTT 消息之上的一层。它在 AWS 和 IoT Thing 端都有 JSON 格式的状态。无论哪个更新其中的任何值,都会将其发送给对方,以便他们做出反应。

在这里,如果 IoT Thing 更新了 Shadow,我们想调用我们的 Lambda 函数。

导航到 AWS IoT > Act > Rules 并创建一个新规则:

  • 名称:cleaning_tracker_rule
  • 带有 IoT 客户端 ID 的查询语句:SELECT * FROM '$aws/things/ 0123456789abcdef /shadow/update/accepted'
  • 添加操作:向 Lambda 函数发送消息
  • 选择cleaning-tracker-lambda

规则将连接到 Lambda 并在某些时候启用。此规则将对物联网事物状态变化做出反应,并使用消息详细信息调用我们的 lambda。

pYYBAGNiMbaAE8c-AAD-7vdrjd4744.png
 

 

第 8 步:将代码实现到 IoT Thing

最后,我们为最重要的部分准备了一切,即 IoT Thing 的代码。从 GitHub 克隆代码(链接在底部)。在 IoT 文件夹中有用 C 编写的代码,这是需要的。

这是根据智能恒温器示例构建的,因此您将在此处找到实施的内容、方式和原因。阅读 git 提交也可以跟踪我们的更改。

首先,您预先配置的sdkconfig文件从 Smart Thermostat 文件夹复制到 IoT 文件夹。此文件不是我们的 repo (by.gitignore) 的一部分,因为它包含 WiFi SSID 和密码以及您的 IoT 事物的 MQTT 队列的唯一 AWS url。

清理

删除了一些不需要的文件和模块,例如 FFT.c,因为我们不处理语音。

ui.c

创建了以下 UI 方法:

// sets wifi label text and state
void ui_wifi_label_update(bool state, char *ssid);

// sets date label based on date value
void ui_date_label_update(rtc_date_t date);

// queries whether the Cleaned button is already clicked & resets its state to false
bool is_cleaned_button_clicked();

// sets the value of the due bar ( 0 .. 100 )
void ui_set_due_bar(int16_t value);

// sets the color of the leds ( example: 0x00FF00 )
void ui_set_led_color(uint32_t color);

按钮点击事件设置了一个全局变量,后面可以查询:

// called if the user clicks the Cleaned button
static void cleaned_button_event_handler(lv_obj_t * obj, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED) {
        cleaned_button_clicked = true;  // store that the button was clicked
        ESP_LOGI(TAG, "Done button clicked");
    }
}

// queries whether the Cleaned button is already clicked & resets its state to false
bool is_cleaned_button_clicked() {
    bool ret = cleaned_button_clicked;  // return state
    cleaned_button_clicked = false;     // set back to current state to false
    return ret;
}

cleaned_button = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_add_style(cleaned_button, LV_BTN_PART_MAIN, &cleaned_button_style);
    lv_obj_set_event_cb(cleaned_button, cleaned_button_event_handler);  // handler
    lv_obj_set_width(cleaned_button, 200);
    lv_obj_align(cleaned_button, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -20);

在这里,我们也使用样式,例如:

static lv_style_t cleaned_button_style;
    lv_style_set_border_color(&cleaned_button_style, LV_STATE_DEFAULT, LV_COLOR_GREEN);
    lv_style_set_border_color(&cleaned_button_style, LV_STATE_FOCUSED, LV_COLOR_GREEN);

static lv_style_t title_style;  // create title style (big font)
    lv_style_init(&title_style);
    lv_style_set_text_font(&title_style, LV_STATE_DEFAULT, LV_THEME_DEFAULT_FONT_TITLE);
    lv_style_set_text_color(&title_style, LV_STATE_DEFAULT, LV_COLOR_BLACK);

    static lv_style_t subtitle_style;   // create title style (medium size font)
    lv_style_init(&subtitle_style);
    lv_style_set_text_font(&subtitle_style, LV_STATE_DEFAULT, LV_THEME_DEFAULT_FONT_SUBTITLE);
    lv_style_set_text_color(&subtitle_style, LV_STATE_DEFAULT, LV_COLOR_BLACK);

重要提示:我们还修改了样式的字体大小,这可以在sdkconfig文件中完成,如下所示:

CONFIG_LV_FONT_MONTSERRAT_32=y

CONFIG_LV_FONT_MONTSERRAT_48=y


# CONFIG_LV_FONT_DEFAULT_SUBTITLE_MONTSERRAT_16 is not set
CONFIG_LV_FONT_DEFAULT_SUBTITLE_MONTSERRAT_32=y


# CONFIG_LV_FONT_DEFAULT_TITLE_MONTSERRAT_16 is not set
CONFIG_LV_FONT_DEFAULT_TITLE_MONTSERRAT_48=y

以下部分显示了如何打印 WiFi 符号以及如何更改同一文本中的文本颜色:

// sets wifi label text and state
void ui_wifi_label_update(bool state, char *ssid){
    xSemaphoreTake(xGuiSemaphore, portMAX_DELAY);
    if (state == false) {   // if there is no wifi signal
        lv_label_set_text(wifi_label, LV_SYMBOL_WIFI);  // black wifi symbol
    } 
    else{
        char buffer[100];
        sprintf (buffer, "#0000ff %s # %s", LV_SYMBOL_WIFI, ssid);
        lv_label_set_text(wifi_label, buffer);
    }
    xSemaphoreGive(xGuiSemaphore);

LVGL UI 文档可以在这里找到这显示了所有可用的控件、样式和属性设置,例如对齐方式。

LED的设置如下:

// sets the color of the leds ( example: 0x00FF00 )
void ui_set_led_color(uint32_t color) {
    Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_LEFT, color);
    Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_RIGHT, color);
    Core2ForAWS_Sk6812_Show();
}

 

主程序

IoT 项目的主文件也进行了清理,并添加了我们的逻辑。此外,许多地方都添加了日志记录,并将日志记录到控制台输出而不是 UI。

所做的更改之一是,如果 IoT Thing 无法连接到影子,我们不会重新启动。有时我们会遇到超时问题,并且 Thing 重启需要很长时间。

// Connect to shadow in infinite loop until connected successfully
while(true) {
    ESP_LOGI(TAG, "Shadow Connect");
    rc = aws_iot_shadow_connect(&iotCoreClient, &scp);
    if(SUCCESS != rc) {
        ESP_LOGE(TAG, "aws_iot_shadow_connect returned error %d, retrying...", rc);
    } else {
        ESP_LOGI(TAG, "Connected to AWS IoT Device Shadow service");
        break;  // exit from the loop
    }
}

当 Thing 连接到 WiFi 和 IoT Shadow 时,我们也在 Thing 上设置当前时间 + 1 小时的到期时间:

// initialize cleaning due date to current time + 1 hour
BM8563_GetTime(&dueDate);
dueDate.hour+=1;

然后我们开始一个无限循环,它获取时间并在 UI 上显示时间,计算剩余时间,根据差异设置 LED,并设置进度条(不会低于零):

// START get sensor readings + update UI

BM8563_GetTime(&date);          // get current date time
ui_date_label_update(date);     // show time on UI

// minutes between now and cleaning due time
int timediff = (dueDate.hour * 60 + dueDate.minute) - (date.hour * 60 + date.minute);   ESP_LOGI(TAG, "timediff: %d", timediff);

if (timediff < 0)
    ui_set_led_color(0xFF0000); // set LED strips to RED if no time left
else if (timediff < 15)
    ui_set_led_color(0xFFFF00); // set LED strips to YELLOW if 15 or less mins left
else
    ui_set_led_color(0x00FF00); // set LED strips to GREEN otherwise

if (timediff < 0)
    timediff = 0;
ui_set_due_bar(timediff * 100 / 60);    // show remaining time on the progressbar as well

// END get sensor readings

我们检查按钮是否在此期间被点击,如果是,那么我们将到期时间增加到 +1 小时。还应构建 JSON 消息,并更新 IoT Shadow。为了节省成本,我们仅在真正需要时才更新 IoT Shadow(当单击“已清理”按钮时):

// if room is cleaned
if (is_cleaned_button_clicked()) { // send message only if Cleaned

    BM8563_GetTime(&dueDate);
    dueDate.hour+=1;    // update cleaning due date to current time + 1 hour

    // set values for shadow document
    sprintf(timestampStatus, "%d-%02d-%02d %02d:%02d:%02d", date.year, date.month, date.day, date.hour, date.minute, date.second);  // date time stamp
    sprintf(clientidStatus, "%s", client_id);   // IoT id
    sprintf(cleaningStatus, "CLEANED");         // Cleaning status

    // log
    ESP_LOGI(TAG, "****************************************************************");
    ESP_LOGI(TAG, "On Device: timestampStatus %s", timestampStatus);
    ESP_LOGI(TAG, "On Device: clientidStatus %s", clientidStatus);
    ESP_LOGI(TAG, "On Device: cleaningStatus %s", cleaningStatus);

    // compose and update shadow document with: timestamp + clientid + cleaningstatus
    rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer,
        sizeOfJsonDocumentBuffer);
    if(SUCCESS == rc) {
        rc = aws_iot_shadow_add_reported(JsonDocumentBuffer,
            sizeOfJsonDocumentBuffer, 3,
            ×tampStatusActuator,
            &clientidStatusActuator,
            &cleaningStatusActuator);
        if(SUCCESS == rc) {
            rc = aws_iot_finalize_json_document(JsonDocumentBuffer,
                sizeOfJsonDocumentBuffer);
            if(SUCCESS == rc) {
                ESP_LOGI(TAG, "Update Shadow: %s", JsonDocumentBuffer);
                rc = aws_iot_shadow_update(&iotCoreClient, client_id,
                    JsonDocumentBuffer, ShadowUpdateStatusCallback, NULL, 4, true);
                shadowUpdateInProgress = true;
            }
        }
    }
    ESP_LOGI(TAG, "****************************************************************");
}

然后我们等待 1 秒,无限循环继续。

vTaskDelay(pdMS_TO_TICKS(1000));    // wait 1 sec, then loop

就是这样!

备注:cleaningStatus_Callback方法当前为空。在以后的增强中,它可用于例如从 AWS 更新到期日期,因为只要 AWS 端的 IoT Shadow 发生更改,就会调用此日期。

测试

让我们在 PlatformIO 终端中发出以下命令:

pio run --environment core2foraws --target upload --target monitor

它将编译项目、创建固件并将其上传到 IoT Thing。Thing 将重新启动并执行代码,而您可以在 PlatformIO 终端中看到它的控制台日志。

日志中的一些重要里程碑是:

客户编号:

␛[0;32mI (3425) MAIN: Device client Id: >> 0123456789abcde01 <<␛[0m

无线网络连接:

␛[0;32mI (3235) WIFI: Setting Wi-Fi configuration to SSID: ultrix␛[0m
␛[0;32mI (9135) WIFI: Wi-Fi connected. Device IP address: 192.168.0.213␛[0m

连接到 AWS IoT Shadow:

␛[0;32mI (9135) MAIN: Shadow Init␛[0m
␛[0;32mI (9135) MAIN: Shadow Connect␛[0m
␛[0;32mI (16265) MAIN: Connected to AWS IoT Device Shadow service␛[0m

循环显示剩余时间:

␛[0;32mI (16765) MAIN: timediff: 60␛[0m
:
:
␛[0;32mI (16765) MAIN: timediff: 59␛[0m

单击“已清除”按钮后,将显示以下日志:

␛[0;32mI (31945) UI: Done button clicked␛[0m
␛[0;32mI (32365) MAIN: *****************************************************************************************␛[0m
␛[0;32mI (32365) MAIN: On Device: timestampStatus 2021-09-15 17:24:56␛[0m
␛[0;32mI (32375) MAIN: On Device: clientidStatus 0123456789abcde01 ␛[0m
␛[0;32mI (32385) MAIN: On Device: cleaningStatus CLEANED␛[0m
␛[0;32mI (32385) MAIN: Update Shadow: {"state":{"reported":{"timestampStatus":"2021-09-15 17:24:56","clientidStatus":"0123456789abcde01 ","cleaningStatus":"CLEANED"}}, "clientToken":"0123456789abcde01 -0"}␛[0m
␛[0;32mI (35065) MAIN: *****************************************************************************************␛[0m
␛[0;32mI (35065) MAIN: Stack remaining for task 'aws_iot_task' is 2044 bytes␛[0m
␛[0;32mI (36095) MAIN: Update accepted␛[0m

AWS 控制台上也可以看到相同的 JSON:

pYYBAGNiMb2ACag2AAFGHtVe9as369.png
 

 

第 9 步:测试物联网

启动后,IoT Thing 上的 LED 将呈蓝色。很快它将连接到 WiFi,其 SSID 将显示在左上角。一段时间后,它将连接到 AWS IoT Shadow,并开始循环,因此:将显示当前时间,LED 将变为绿色,进度条将显示从 1 小时开始的剩余时间百分比。如果只剩下 15 分钟,LED 将呈黄色。如果剩余时间,LED 将变为红色。每当您按下“已清除”按钮时,1 小时倒计时再次开始,进度条应重置为 100%,LED 应再次变为绿色,并且还会通过 AWS IoT Shadow 发送一条消息,这将触发 IoT 规则,等等上。

 

未来的可能性

为合规报告汇总数据。

将系统连接到公司 AD,因此:

  • IoT Thing 可以在其 UI 上显示下一次会议名称和开始时间
  • 系统可以在房间没有会议时计划清洁周期

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"//m.obk20.com/www/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'物联网办公室清洁跟踪系统',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"https://www.elecfans.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);