大家好,我是机灵鹤。

最近研究开发了一个物联网 IoT 小项目——久坐提醒 / 喝水提醒小助手。

1. 项目介绍

本项目实现了一个久坐提醒和喝水提醒的小助手,在检测到连续工作较长时间之后,会打印纸条,提醒我们起身活动一下;在设定的喝水时间到了以后,也会打印纸条,提醒我们去喝水。

废话不多说,先上图。

项目用到的硬件设备有:

  • 树莓派4B(作为网关设备来接入涂鸦 IoT 生态)
  • 咕咕机G2(迷你热敏打印机)
  • 人体红外传感器
  • 无线智能 Zigbee 网关

事情是这样的,过几天就是跟女朋友的恋爱纪念日了,作为一名程序员,送什么礼物才能既有创意又有诚意,既实用又能让女朋友感受到我满满的爱呢?

我突然想到,前段时间女朋友公司年度体检,检查结果出来有好几项飘黄,医生给的建议是,多喝水!多运动!女朋友虽然不是程序员,但也是那种长期坐办公室的工作,有时候忙起来连水都会忘记喝。

于是我想,何不利用我的专业技能,开发一个 ”久坐提醒“ 和 ”喝水提醒“ 小助手送给女朋友呢。

2. 方案设计

为了完成这个项目,我翻箱倒柜找出来几个,若干年前买的压箱底的电子产品。

  • 咕咕机G2:一台迷你的热敏打印机,之前做手帐时候买来打印图案的,后来发现时间长了会褪色,于是就用来打印便条来背单词,后来四六级过了以后,咕咕机就闲置了。
  • 树莓派4B:之前买了用来搭个人网站服务器的,结果发现没有公网 IP ,外网访问贼麻烦,而且有时候家里停电断网什么的,服务器动不动就失联了。后来买了云服务器以后,树莓派就渐渐闲置了。
  • 人体红外传感器:上学时候偷偷放教室门口,检测老师有没有来的,毕业以后不需要跟老师斗智斗勇了,就慢慢闲置了(PS:由于之前那个传感器闲置太久坏了,本项目开发时重新采购的涂鸦智能的传感器)。

我的设想是:

  • 喝水提醒:创建几个喝水闹钟,喝水时间一到,咕咕机便会自动打印便条,提醒女朋友该去喝水了。
  • 久坐提醒:将人体红外传感器放置在桌上正对座椅,当检测到女朋友坐着持续时间超过半小时,便触发久坐提醒,通知咕咕机打印便条,提醒女朋友该起来活动活动了。

硬件都备的差不多了,在开发时我遇到了一个比较棘手的问题,就是咕咕机跟人体传感器它也不是同一家的设备啊!

虽然两家都有各自的控制 APP,但是它们毕竟是两套系统,两个平台,没法儿联动啊!

这时候我发现一个很牛的东西,涂鸦的 Link SDK ,号称 适用于涂鸦现有产品方案外的设备接入

按它的说法,所有涂鸦 IoT 生态以外的智能设备,只要是支持二次开发,理论上都可以通过这套 Link SDK 接入到涂鸦的生态中。那这就厉害了!这就意味着,我可以通过 Link SDK 把咕咕机和传感器接入到同一套系统中,实现联动了。

经过一番研究,本项目的方案设计示意图如下:

在树莓派中运行涂鸦的 Link SDK,一方面,树莓派作为一个网关设备接入到涂鸦云中,与涂鸦云中的其他设备进行通信;另一方面,树莓派通过调用咕咕机的 memobird API ,与咕咕机进行通信。

3. 开发流程

开发过程大概分为 4 个阶段:

  1. 创建产品:在涂鸦云平台创建产品,包括配置功能点,开发操作面板,下载 SDK 及获取授权码,在线调试等环节。
  2. 硬件开发:下载 Link SDK,编写代码并运行在树莓派中,将树莓派作为一个 IoT 设备接入到涂鸦云上。
  3. 咕咕机开发:根据咕咕机开发文档,开发并封装好相关接口,在树莓派上可以调用接口实现咕咕机设备绑定和纸条打印等功能。
  4. 智能联动:通过设定智能场景和联动条件,完成咕咕机与人体红外传感器之间的智能联动。

接下来正式进入开发阶段。

3.1 创建产品

3.1.1 注册开发者账号

首先,需要在涂鸦智能平台(https://t.tuya.com/AY1D3R8rbM)注册开发者账号。

注册完成后,进入 IoT 平台主页

3.1.2 创建智能产品

点击 IoT 平台主页的 创建产品 按钮,开始创建我们的智能产品。

第一步,选择我们要创建的产品类型,由于我们要接入的硬件设备是树莓派+咕咕机,不在涂鸦现有产品品类之中,所以点击 找不到品类? 按钮。

第二步,填写产品信息,根据自己项目的实际情况填写产品信息,填写完成后,点击创建产品。

3.1.3 功能定义

创建好产品之后,我们需要为产品添加 功能定义

什么意思呢?说白了就是为云平台和设备之间制定通信协议的,约定好哪条协议对应哪个功能,数据格式如何如何之类的。

标准功能是为了涂鸦生态内的设备开发时提供的快捷模板,我们这里用不到,需要使用自定义功能。

点击新建自定义功能,创建我们的喝水提醒功能。

3.1.4 面板开发

设置好功能定义后,进入设备面板界面,开发 APP 交互的面板。

编辑器左侧提供了很多组件,可以通过拖拽的方式来编辑面板布局,并在右侧属性页签中,通过添加交互,来为当前选中的组件添加交互动作。

详细使用方法参考:https://developer.tuya.com/cn/docs/iot/panel?id=K9hzyyk6g4p3m

如图,我们添加一个按钮,在按钮属性中,添加交互,触发 喝水提醒 功能 。这样我们点击按钮时,就会向设备发送一条 喝水提醒 的消息。

编辑好以后,点击右上角 预览 按钮,然后用 涂鸦智能 APP 扫描二维码,可以在手机上实时预览(预览模式下,编辑器中做的修改,手机APP中会同步变化)。

APP 可以在各大应用商店下载到,搜 涂鸦智能 即可。

预览测试没问题的话,可以点击发布。

经过一段时间的打包后,打包成功,我们直接点击 去发布 ,然后选择 发布并应用 即可。

3.1.5 硬件开发

在硬件开发页面,云端接入方式选择 Link SDK,云端接入硬件选择 通用CPU

我们将自己的设备接入涂鸦云的话,第一需要 Link SDK , 第二需要购买 授权码

Link SDK 在开发资料中可以下载,授权码需要购买(10元/个)。

不过平台给每个开发者提供了两个调试用的云端授权码,可以免费领取(领取按钮在 立即购买 按钮下方,我因为领取过了所以截图里没有显示)。

领取成功以后,点击 下载授权码清单 按钮即可下载。授权码清单中包含了 uuid 和 key,如下所示:

uuidkey
tuyaf5753924f3d943065TbgqpGXzbgrr8Plb8L93rBy38Liu7ok

3.1.6 产品配置

产品配置中,可以为设备添加一些如多语言,消息推送,配网引导的功能,因为本项目中暂时用不到,可以跳过。

3.1.7 设备调试

然后是设备调试,由于我们的树莓派 Link SDK 还没有开发到位,所以先添加一个虚拟调试设备,来进行调试。

手机应用商店里,下载安装 涂鸦智能 APP,然后用 APP 扫描上面的二维码,可以添加一个虚拟设备,用于调试。

添加好虚拟设备后,点击调试按钮,进入调试界面。然后可以使用这里的虚拟设备跟手机 APP 端进行通信。

通过这个,我们可以看到上报数据和下发数据是否正确(主要用来验证 3.1.4 节 开发的面板功能是否正确)。

消息协议调试无误后,开始开发我们的设备

3.2 硬件开发

产品创建成功,且 APP 端调试无误后,我们正式请出我们的主角,树莓派,来进行基于 Link SDK 的硬件开发。

3.2.1 运行环境

树莓派上烧录好 Raspberry Pi OS 系统,并且安装好 Python3.6+ 环境。

烧录系统的过程略去不讲,大家没有树莓派的话,也可以用自己电脑,或者虚拟机来替代。

3.2.2 安装源码

首先下载 Link SDK ,平台上默认提供的是 c 语言版本。

下载地址:https://github.com/tuya/tuya-iot-link-sdk-embedded-c

我比较常用 Python,所以找的是 Python 版本(支持 Python 3.6+)。

下载地址:https://github.com/tuya/tuyaos-link-sdk-python

安装方法:

  1. 通过 Pypi 安装
python3 -m pip install tuyalinksdk
  1. 通过源码安装
git clone https://github.com/tuya/tuyaos-link-sdk-python.git
python3 -m pip install ./tuyaos-link-sdk-python

3.2.3 代码编写

在项目路径中,创建一个 demo.py 脚本,脚本内容如下:

#!/usr/bin/env python
import time
from tuyalinksdk.client import TuyaClient
from tuyalinksdk.console_qrcode import qrcode_generate

# 这里更换成自己的ID
PID = "jj9hhmjxsf8u2b94"
UUID = "tuyaf5753924f3d94306"
AUTHKEY = "5TbgqpGXzbgrr8Plb8L93rBy38Liu7ok"

client = TuyaClient(productid = PID, uuid = UUID, authkey = AUTHKEY)

def on_connected():
    print('Connected.')

def on_qrcode(url):
    qrcode_generate(url)

def on_reset(data):
    print('Reset:', data)

def on_dps(dps):
    print('DataPoints:', dps)
    client.push_dps(dps)
    
client.on_connected = on_connected
client.on_qrcode = on_qrcode
client.on_reset = on_reset
client.on_dps = on_dps

client.connect()
client.loop_start()

while True:
    time.sleep(1)

代码中第 7-9 行的 3 个参数,PIDUUIDAUTHKEY 需要替换成你自己的产品的值。

PID ,即 productid 产品 ID, 可以在 我的产品 列表里查看 产品ID ,如图所示。

UUIDAUTHKEY 为设备的授权码,在前面 3.1.5 硬件开发 部分,下载的免费授权码的文件中可以查看,分别对应 UUIDKEY 两列。

3.2.4 真机运行

代码写好以后,在树莓派中使用终端,执行 python3 demo.py 命令启动程序。

程序运行后,会弹出一个二维码,用 涂鸦智能 APP 扫码以后,即可添加设备。

在代码中, on_dps 函数负责接收和处理 APP 端下发的指令,我们只需要根据指令的值,触发不同的操作即可。

如,接收到 {"101": "True"} 时,执行 喝水提醒 的行为,示例代码如下:

DRINK_DP_ID = "101"

def on_dps(dps):
    print('DataPoints:', dps)
    client.push_dps(dps)
    
    for key in dps:
        if key == DRINK_DP_ID and dps[key] == True:
            # TODO: 喝水提醒
            pass

3.3 咕咕机接入

咕咕机是一款迷你的热敏打印机,可以方便的打印文字和图片,而且官网的开发者平台上开放了它的 API ,可玩性非常高。

3.3.1 注册开发者账号

首先需要在 Memobird 开放平台注册账号,并申请成为咕咕机开发者。

Memobird 开放平台线上地址:https://open.memobird.cn/

注册好账号,填写好自己的 咕咕号联系方式用途 等信息之后,提交审核就可以了。

一段时间,审核通过后,会向注册账号的邮箱发送一封邮件,包含申请到的 Access_Key

开发文档:https://open.memobird.cn/upload/webapi.pdf

3.3.2 绑定设备

示例代码:

import requests
import datetime

# 绑定设备的API
BIND_DEVICE_API = "http://open.memobird.cn/home/setuserbind"
# 开发者 Access_Key
Access_Key = "261ba***************9274a"
# 咕咕机设备编号,双击咕咕机吐出来的设备编号 
MEMOBIRD_ID = "301*********fe69"
# 用户自定义字符串,与咕咕平台进行关联的用户唯一标识符
USER_IDENTIFYING = "smartcrane"

# 获取时间戳
def getTimeStamp():
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    return timestamp

# 绑定设备,获取用户ID
def getUserID():
    params = {
        "ak" : Access_Key,
        "timestamp" : getTimeStamp(),
        "memobirdID" : MEMOBIRD_ID,
        "useridentifying" : USER_IDENTIFYING
    }

    try:
        r = requests.get(BIND_DEVICE_API, params=params)
        # 返回结果形如:
        # {"showapi_res_code":1,"showapi_res_error":"ok","showapi_userid":8}
        return r.json()["showapi_userid"]
    except Exception as e:
        print(e)
        return ""
    
getUserID()

3.3.3 打印文字

示例代码:

import requests
import datetime

# 打印纸条的API
PRINT_PAPER_API = "http://open.memobird.cn/home/printpaper"

def printText(text, userId):
    # 要打印的内容 T:文字的base64编码(gbk)
    printContent = "T:" + base64.b64encode(text.encode("gbk")).decode()

    params = {
        "ak" : Access_Key,
        "timestamp" : getTimeStamp(),
        "memobirdID" : MEMOBIRD_ID,
        "printcontent" : printContent,
        "userID" : userId,
    }

    r = requests.post(PRINT_PAPER_API, data=params)
    print(r.text)
    
userId = getUserID()
printText("Hello World", userId)

运行后,咕咕机成功打印。

3.3.4 打印图片

示例代码:

import requests
import datetime

# 打印纸条的API
PRINT_PAPER_API = "http://open.memobird.cn/home/printpaper"
# JPG/PNG图片转单色点阵图base64编码的API
BASE64_PIC_API = "http://open.memobird.cn/home/getSignalBase64Pic"

# 获取图片转换后的base64编码
def getPictureBase64(path):
    with open(path,"rb") as f:#转为二进制格式
        base64_data = base64.b64encode(f.read())#使用base64进行加密

    params = {
        "ak" : Access_Key,
        "imgBase64String" : base64_data,
    }
    
    r = requests.post(BASE64_PIC_API, data=params)
    return r.json()["result"]

def printPicture(path, userId):
    # 要打印的内容 P:单色点阵图的base64编码
    printContent = "P:" + getPictureBase64(path)

    params = {
        "ak" : Access_Key,
        "timestamp" : getTimeStamp(),
        "memobirdID" : MEMOBIRD_ID,
        "printcontent" : printContent,
        "userID" : userId,
    }

    r = requests.post(PRINT_PAPER_API, data=params)
    print(r.text)
    
userId = getUserID()
printPicture("drink_0.jpeg", userId)

运行代码以后,咕咕机成功打印出来图片。

更多的功能,如获取打印状态,打印网页,打印HTML等,这里用不到,就不做示例了

大家感兴趣的话,可以去参考官网开发者文档。

3.3.5 咕咕机接入涂鸦云

接下来,我们通过树莓派来操控咕咕机,将咕咕机接入涂鸦生态。

首先我们将代码整理一下,保存为 memobird.py 脚本,如下:

import requests
import datetime
import random
import base64

class Memobird:
    def __init__(self):
        self.BIND_DEVICE_API = "http://open.memobird.cn/home/setuserbind"
        self.PRINT_PAPER_API = "http://open.memobird.cn/home/printpaper"

        self.AK = "填入你申请的开发者AK"
        self.MEMOBIRD_ID = "填入你咕咕机的设备ID"
        self.USER_IDENTIFYING = "smartcrane" #随便填一个字符串都可以

        self.DRINK_REMINDER_TEXT = [
            "机灵鹤,你好\n我是“喝水提醒小助手”\n希望此时此刻看到消息的你 \n快去喝一杯水\n成为一个一天八杯水的男人吧!"
        ]
        
        self.userID = self.getUserID()

    def getTimeStamp(self):
        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        return timestamp

    def getUserID(self):

        params = {
            "ak" : self.AK,
            "timestamp" : self.getTimeStamp(),
            "memobirdID" : self.MEMOBIRD_ID,
            "useridentifying" : self.USER_IDENTIFYING
        }

        try:
            r = requests.get(self.BIND_DEVICE_API, params=params)
            return r.json()["showapi_userid"]
        except Exception as e:
            print(e)
            return ""
    
    def printText(self, text, userId):

        printContent = "T:" + base64.b64encode(text.encode("gbk")).decode()

        params = {
            "ak" : self.AK,
            "timestamp" : self.getTimeStamp(),
            "memobirdID" : self.MEMOBIRD_ID,
            "printcontent" : printContent,
            "userID" : self.userID,
        }

        r = requests.post(self.PRINT_PAPER_API, data=params)
        print(r.text)

    def drink_reminder(self):
        printText = self.getTimeStamp() + "\n" + random.choice(self.DRINK_REMINDER_TEXT)
        self.printText(printText, self.userID)

这里封装了一个 Memobird 类,提供了一个 drink_reminder 函数,用来打印 喝水提醒 的文字。

我们只需要在树莓派中,接收到 APP 下发的 喝水提醒 指令时,调用 drink_reminder 函数即可。

相应的,我们将 demo.py 改造一下:

#!/usr/bin/env python
import time
from tuyalinksdk.client import TuyaClient
from tuyalinksdk.console_qrcode import qrcode_generate
from memobird import Memobird

# 这里更换成自己的ID
PID = "jj9hhmjxsf8u2b94"
UUID = "tuyaf5753924f3d94306"
AUTHKEY = "5TbgqpGXzbgrr8Plb8L93rBy38Liu7ok"

DRINK_DP_ID = "101"

memobird = Memobird()
client = TuyaClient(productid = PID, uuid = UUID, authkey = AUTHKEY)

def on_connected():
    print('Connected.')

def on_qrcode(url):
    qrcode_generate(url)

def on_reset(data):
    print('Reset:', data)

def on_dps(dps):
    print('DataPoints:', dps)
    client.push_dps(dps)
    for key in dps:
        if key == DRINK_DP_ID and dps[key] == True:
            # TODO: 喝水提醒
            memobird.drink_reminder()
    
client.on_connected = on_connected
client.on_qrcode = on_qrcode
client.on_reset = on_reset
client.on_dps = on_dps

client.connect()
client.loop_start()

while True:
    time.sleep(1)

重新启动代码,就到了最激动人心的时候了。

点击 喝水提醒 按钮,咕咕机成功打印出了提醒喝水的文字!!!

至此,通过 Tuya 的 Link SDK,我们借助树莓派作为网关,将咕咕机成功接入了涂鸦生态之中。

3.4 智能联动

接下来,我们只需要在涂鸦智能 APP 中创建智能场景,设定执行条件和执行任务,便可以实现简单的自动化,以及设备联动啦。

3.4.1 喝水提醒小助手

根据我们自己的作息时间,我们可以设定定时喝水提醒。

如:每天定时下午 2 点 10 分提醒喝水。

设定好以后,保存并启用,系统将会在每天下午 2 点 10 分打印纸条,提醒我们喝水。

3.4.2 久坐提醒小助手

如果说,喝水提醒小助手,只是相当于设定了一个闹钟,感觉用处没那么明显的话。那么我们接入其他各种传感器设备,进行设备间的联动,可以做的事情就很厉害了。

接下来,我们将与人体传感器联动,在 喝水提醒小助手 的基础上,实现一个 久坐提醒小助手

首先我们需要在涂鸦 IoT 平台,继续添加一条自定义功能 久坐提醒 ,如图所示

然后,在 memobird.py 中设定好 久坐提醒 的文案,并提供一个 久坐提醒 的函数接口。

self.SEDENTARY_REMINDER_TEXT = [
    "机灵鹤,你好\n我是“久坐提醒小助手”\n检测到您已经连续工作很久了\n久坐对身体危害很大\n快起来活动一下吧"
]

def sedentary_reminder(self):
    printText(self.getTimeStamp() + "\n" + random.choice(self.SEDENTARY_REMINDER_TEXT), self.userID)

最后,在 demo.py 中添加一条 久坐提醒 的消息定义,并完善该消息对应的处理函数。

DRINK_DP_ID = "101"
SEDENTARY_DP_ID = "102"

def on_dps(dps):
    print('DataPoints:', dps)
    client.push_dps(dps)
    for key in dps:
        if key == DRINK_DP_ID and dps[key] == True:
            # TODO: 喝水提醒
            memobird.drink_reminder()
        if key == WELCOME_DP_ID and dps[key] == True:
            # TODO: 久坐提醒
            memobird.welcome_reminder()

保存代码,重新运行启动程序。

手机 APP 端,连接无线网关及人体传感器。

在传感器页面中,设定智能联动:检测到有人移动,持续 30 分钟时,触发久坐提醒。

然后将传感器固定到桌子旁边,正对座位的位置即可。

在连续坐着工作半个小时后,咕咕机成功打印出久坐提醒的纸条。


最后,只需要将久坐提醒和喝水提醒的文案修改一下,就可以送给女朋友啦!

我想女朋友收到礼物一定会感动哭了吧!

4. 写在后面的话

整个项目从构思到开发完成历时 9 天,期间踩了很多坑,绕了很多弯,不过好在最后成功完成,也收获了很多。同时借着这个机会,也算是入了 物联网/智能家居 的门。

在项目开发初期,我对物联网的这套逻辑其实是比较模糊的,我不清楚文档里每一个步骤的作用,不清楚设备之间通信的原理,我甚至搞不清每一个硬件设备在这套系统里需要扮演的角色。比如我会常常试图将树莓派作为系统的控制核心,用它来监听传感器状态,用它来控制咕咕机,以及用它来处理智能联动的逻辑。然而事实上,树莓派只需要完成一个任务——监听和处理涂鸦 APP 下发的指令就可以了,这也正是涂鸦 Link SDK 的核心能力。其他部分如监听传感器状态,设备间智能联动等等,都可以在 APP 中完成。

在逐步捋清楚整套逻辑之后,开发过程逐渐顺畅。我发现,在解决了设备接入和设备联动问题之后,物联网比拼的其实就是谁脑洞开的更大了,看谁能用一些奇怪的设备组合,通过一些莫名其妙的规则发生联动,最后产生奇妙的效果,给人们的生活带来出乎意料的方便。

本文较为详细的记录了这个项目开发的全过程,既是对我第一次开发物联网项目的全面复盘,也是希望大家在开发物联网入门项目时有所参考。如果有朋友因为本项目而对物联网产生了兴趣,我便更是荣幸之至。


如果文章中有哪里没有讲明白,或者讲解有误的地方,欢迎在评论区批评指正,或者扫描下面的二维码,加我微信,大家一起学习交流,共同进步。

最后修改:2021 年 12 月 09 日 02 : 42 PM
如果觉得我的文章对你有用,请随意赞赏