#打卡不停更#智能喂食器 原创 精华

yukoyu
发布于 2022-9-29 09:14
浏览
4收藏

一、介绍

​ 随着人们生活方式的不断改变,宠物猫在许多家庭中占有重要的地位,其凭借独立的个性和易于打理的饲养方式,成为当下上班族喜欢的宠物之一,人们更是把宠物猫和狗作为家庭的重要成员。有铲屎官表示,每月在宠物身上的基础花销是用来购买宠物粮,为了“小主”们的饮食可以说操碎了心,比起让它们吃好吃饱他们更关心狗粮猫粮的食品安全、生活舒适度、身心健康等。这也让宠物智能设备成为刚需产品,随着 5G、大数据、AI、云计算等技术的快速发展,各类智能养宠硬件如雨后春笋般涌现,在人的吃喝拉撒还需要“手动”的时候,宠物已经进入智能生活了。

​ 而我们的作品就是用于更好的对宠物的投喂进行智能化管理,首先它基于OpenHarmony开发板开发,可以通过使用APP进行定时喂食,同时可以远程观看实时视频,同时也可以识别到小猫进行喂食,更好的知道家里宠物的状态;可以通过喂食器APP查看内余粮,方便用户更好的管理宠物的饮食健康和规律。

解决多项联合国17项可持续发展目标中的问题

  • 目标 2:零饥饿

  • 目标 3:良好健康与福祉

  • 目标 9:产业、创新和基础设施

演示视频:https://www.bilibili.com/video/BV1Ug411k7VG

整体视图

#打卡不停更#智能喂食器-鸿蒙开发者社区

主要功能介绍

①、可视化显示喂食数据

②、自动喂食功能

③、定时喂食

④、手动喂食

⑤、余粮显示功能

⑥、远程观看实时视频

⑦、自定义喂食的量

二、系统架构

系统结构图

#打卡不停更#智能喂食器-鸿蒙开发者社区

三、开发指导

①、openharmony应用开发

1.导航栏开发

#打卡不停更#智能喂食器-鸿蒙开发者社区

通过使用js 自带的tabs组件开发导航栏,是非常简单的

    <tabs @change="setVoids">
        <tab-content>
            <div class="text">
                <home ></home>
            </div>
            <div class="text">
                <videopage ></videopage>
            </div>
            <div class="text">
                <setting></setting>
            </div>
        </tab-content>
        <tab-bar class="navigationbar">
            <div class="tab-icon">
                <image class="tab-icon-image" src="/common/images/03.png"></image>
                <text>
                    主页
                </text>
            </div>
            <div class="tab-icon">
                <image class="tab-icon-image" src="/common/images/04.png"></image>
                <text>
                    控制
                </text>
            </div>
            <div class="tab-icon">
                <image class="tab-icon-image" src="/common/images/05.png"></image>
                <text>
                    设置
                </text>
            </div>
        </tab-bar>
    </tabs>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

2.自定义组件喂食量可视化环状图

#打卡不停更#智能喂食器-鸿蒙开发者社区

对于自定义组件的开发,可以使用canvas去开发,在这里我就使用canvas去开发。

新建common/component文件夹,并新建doughnubt pages

#打卡不停更#智能喂食器-鸿蒙开发者社区

<div class="container">    
	<canvas ref="canvasb"></canvas>
</div>
  • 1.
  • 2.
  • 3.

这里要注意组件的生命周期和Pages的是不同的,所以onShow()方法在这里是无效的,这里使用onLayoutReady()代替onShow()方法

初始化画板数据:

  onLayoutReady(){var el = this.$refs.canvasb;this.ctx = el.getContext("2d",{antialias: true});// 清除画布上的内容this.gradient = this.ctx.createLinearGradient(0,0, 0,300);this.gradient.addColorStop(0, '#ff149ee9');this.gradient.addColorStop(1, '#ff9ae2ee');this.ondoughnutline();this.setLength(this.max,this.min);this.charss(this.min);}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

画小圆环:

    ondoughnutline({this.ctx.clearRect(0, 0, this.xlen, this.ylen);this.ctx.lineCap = 'round';// 描边的宽度this.ctx.lineWidth = 2;// 创建一个新的绘制路径this.ctx.beginPath();// 路径从当前点移动到指定点this.ctx.strokeStyle = this.gradient;this.ctx.arc(this.xlen/2, this.ylen/2, 200, 0, Math.PI * 2);this.ctx.stroke();},
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

组件的传参:

通过我们引用自定义组件时,通过props传参,同时我们可以使用$watch对数据进行监听

//父
<div class="two">
    <doughnut max="{{maxweight}}" min="{{weights}}"></doughnut>
</div>
  • 1.
  • 2.
  • 3.
  • 4.
//子
props: ['max']['min'],onInit(){this.$watch('min', 'onPropertyChange');},onPropertyChange(newW,oldW){

​        console.info('alls 属性变化 ' + newW + ' ' + oldW);if(this.max >= newW){this.onChange(this.max, this.min)}},
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

其余的自定义组件喂食量可视化环状图代码我就不在细讲,你们可以就看我的开源代码

3.自定义图传视频组件

#打卡不停更#智能喂食器-鸿蒙开发者社区

这里采用的自定义的图传视频组件,是采用http获取图片然后通过canvas进行绘画

<div class="videocom" onclick="videorun">
    <canvas ref="canvasss"></canvas>
</div>
  • 1.
  • 2.
  • 3.
    props: ['httpsrc'],

​    onInit() {

​        this.$watch('httpsrc', 'onPropertyChange');

​        this.httpClientImpl = new httpclient.HttpClient.Builder().setConnectTimeout(15, httpclient.TimeUnit.SECONDS).setReadTimeout(15, httpclient.TimeUnit.SECONDS).build();

​        this.request=new httpclient.Request.Builder()

​            .url(this.httpsrc)

​            .method('GET')

​            .addHeader("Content-Type", "application/json")

​            .params("token", "yukoyu")

​            .build();

​    },

​    onPropertyChange(newW,oldW){

​        this.request=new httpclient.Request.Builder()

​            .url(newW)

​            .method('GET')

​            .addHeader("Content-Type", "application/json")

​            .params("token", "yukoyu")

​            .build();

​    },

​    videorun(){

​        this.runflag=!this.runflag;

​        if(this.runflag)

​        {

​            this.intervalID = setTimeout(this.onLiveVideo , 100);

​        }else{

​            clearTimeout(this.intervalID);



​        }



​    },

​    onLiveVideo(){

​        this.httpClientImpl.newCall(this.request).enqueue((result) => {

​            //console.log("success: " + JSON.stringify(result))

​            this.img.src = (JSON.parse(result.data)).data;

​            var pat = this.ctx.createPattern(this.img, 'no-repeat');

​            this.ctx.fillStyle = pat;

​            this.ctx.fillRect(0, 0, 720, 420);

​            if(this.runflag)

​            {

​                clearTimeout(this.intervalID);

​                this.intervalID = setTimeout(this.onLiveVideo , 100);

​            }else{

​                this.img.src = '/common/images/live.png';

​                var pat = this.ctx.createPattern(this.img, 'no-repeat');

​                this.ctx.fillStyle = pat;

​                this.ctx.fillRect(0, 0, 720, 420);

​            }

​        }, (error) => {

​            console.log("error: " + JSON.stringify(error))

​        })

​    },

​    onLayoutReady(){

​        var el = this.$refs.canvasss;

​        this.ctx = el.getContext('2d');



​        this.img = new Image();

​        this.img.src = '/common/images/live.png';

​        var pat = this.ctx.createPattern(this.img, 'no-repeat');

​        this.ctx.fillStyle = pat;

​        this.ctx.fillRect(0, 0, 720, 420);

​    }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.

4.环形图

#打卡不停更#智能喂食器-鸿蒙开发者社区

根据openharmony应用开发文档直接用progress,就可以简单的绘画一个环形图

<progress class="min-progress" type="scale-ring" percent="10" secondarypercent="50"></progress>

  • 1.
  • 2.

5.控制喂食器出粮

    handgo(){

​        this.hmweight=this.hmweight+this.weight;

​        this.$emit('sethmweight',this.hmweight);		//传递参数给父类

​        sockettool.socketSend(this.$app.$def.datas.tcp,this.weight)	//控制喂食器出粮

​    }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

对socketTCPsend封装

export default {

​    socketSend:async function (tcp,gg){
​        var cc=new Uint8Array([170,2,gg]);
​        tcp.send({
​            data:cc.buffer
​        },err => {
​            if (err) {
​                console.log('send fail');
​                return;
​            }
​            console.log('send success');
​       });
​    }
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

6.数据上传到服务器

这里使用第三方库去实现http网络请求

移植HttpClient
1).打开第三方组件库
https://repo.harmonyos.com/#/cn/application/atomService?q=http%20keyword%3AOpenHarmony

  • 1.
  • 2.
2).找到我们需要的httpclient

#打卡不停更#智能喂食器-鸿蒙开发者社区

3).安装

代码:

npm install @ohos/httpclient --save

  • 1.
  • 2.

#打卡不停更#智能喂食器-鸿蒙开发者社区

在第三方的基础上,对post进行封装

    http_post:async function (url,data){

​        let httpClientImpl = new httpclient.HttpClient.Builder().setConnectTimeout(15, httpclient.TimeUnit.SECONDS).setReadTimeout(15, httpclient.TimeUnit.SECONDS).build();

​        let requestBody = httpclient.RequestBody.create(JSON.stringify(data));

​        let request = new httpclient.Request.Builder()

​            .url(url)

​            .method('POST')

​            .body(requestBody)

​            .addHeader("Content-Type", "application/json")

​            .build();



​        httpClientImpl.newCall(request).enqueue((result) => {

​            console.log("success: " + JSON.stringify(result))

​        }, (error) => {

​            console.log("error: " + JSON.stringify(error))

​        })

​    }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

通过这样调用,非常方便的使用POST请求功能

#打卡不停更#智能喂食器-鸿蒙开发者社区

7.定时功能实现

在使用定时功能的时候,最主要是使用setInterval()去实现定时,使用setInterval()时,应该注意传递方法时,传递方法名就可以

    setScheduledTask(hour, minute,weight,callTask) {

​        let taskTime = new Date();

​        taskTime.setHours(hour);

​        taskTime.setMinutes(minute);

​        let timeDiff = taskTime.getTime() - (new Date()).getTime(); // 获取时间差

​        timeDiff = timeDiff > 0 ? timeDiff : (timeDiff + 24 * 60 * 60 * 1000);

​        setTimeout(()=> {

​            callTask(weight) // 首次执行

​            setInterval(callTask, 24 * 60 * 60 * 1000); // 24小时为循环周期

​        }, timeDiff);

​    },doTask(weight) {

​        this.$emit('setautoweight',weight);

​        sockettool.socketSend(this.$app.$def.datas.tcp,weight)

​    }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

②、openharmony设备开发

1.创建一个任务

void WifiSockets(void)

{

​    osThreadAttr_t wifisocket;

​    wifisocket.name = "TcpServerTask";

​    wifisocket.attr_bits = 0U;

​    wifisocket.cb_mem = NULL;

​    wifisocket.cb_size = 0U;

​    wifisocket.stack_mem = NULL;

​    wifisocket.stack_size = 10240;

​    wifisocket.priority = 25;

​    if (osThreadNew(TcpServerTask, NULL, &wifisocket) == NULL) {

​        printf("[Ssd1306TestDemo] Falied to create TcpServerTask!\n");

​    }

}

APP_FEATURE_INIT(WifiSockets);

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

2.对wifi信息配置

static char ssid[] ="hh";                       //wifi名称

static char password[] = "13267897941";         //wifi password

static unsigned short port = 20222;             //socket端口设置


  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

这个TcpServer方法是可以让客户端断开重连的

static void TcpServerHandler(void) {
​    ssize_t retval = 0;
​    // 创建一个通信的Socket,并返回一个Socket文件描述符。第一个参数IpV4,第二个参数SOCK_STREAM类型,第三个指用到的协议
​    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
​    // 客户端地址和地址长度

​    struct sockaddr_in clientAddr = {0};

​    socklen_t clientAddrLen = sizeof(clientAddr);

​    // 服务端地址

​    struct sockaddr_in serverAddr = {0};

​    serverAddr.sin_family = AF_INET;

​    // htons是将整型变量从主机字节顺序转变成网络字节顺序,就是整数在地址空间存储方式变为高位字节存放在内存的低地址处

​    serverAddr.sin_port = htons(port);

​    // 监听本机的所有IP地址,INADDR_ANY=0x0

​    // 将主机数转换成无符号长整型的网络字节顺序

​    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

​    // 服务端绑定端口

​    retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));

​    if (retval < 0) {

​        printf("bind failed, %ld!rn", retval);

​        CloseTcpSocket(sockfd);

​        return;

​    }

​    printf("bind to port %d success!rn", port);

​    // 开始监听,backlog指Pending连接队列增长到的最大长度。队列满了,再有新连接请求到达,则客户端ECONNREFUSED错误。如果支持重传,则请求忽略。

​    int backlog = 1;

​    retval = listen(sockfd, backlog);

​    if (retval < 0) {

​        printf("listen failed!rn");

​        CloseTcpSocket(sockfd);

​        return;

​    }

​    printf("listen with %d backlog success!rn", backlog);

​    int outerFlag = 1;

​    while (outerFlag) {

​        // 接受客户端连接,成功会返回一个表示连接的 socket。clientAddr参数将会携带客户端主机和端口信息;失败返回 -1

​        // 从Pending连接队列中获取第一个连接,根据sockfd的socket协议、地址族等内容创建一个新的socket文件描述,并返回。

​        // 此后的 收、发 都在 表示连接的 socket 上进行;之后 sockfd 依然可以继续接受其他客户端的连接,

​        // UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接,父进程继续接受其他客户端的连接

​        // 鸿蒙liteos-a内核之上,可以使用UNIX的“每个连接一个进程”的并发模型liteos-m内核之上,可以使用“每个连接一个线程”的并发模型

​        connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);

​        if (connfd < 0) {

​            printf("accept failed, %d, %d\r\n", connfd, errno);

​            continue;

​        }

​        printf("accept success, connfd = %d !\r\n", connfd);

​        // inet_ntoa:网络地址转换成“.”点隔的字符串格式。ntohs:16位数由网络字节顺序转换为主机字节顺序。

​        printf("client addr info: host = %s, port = %d\r\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));


​        int innerFlag = 1;

​        // 接收消息,然后发送回去

​        while (innerFlag) {

​            // 后续 收、发 都在 表示连接的 socket 上进行;

​            // 在新的Socket文件描述上接收信息.

​            retval = recv(connfd, request, sizeof(request), 0);

​            if (retval < 0) {

​                printf("recv request failed, %ld!\r\n", retval);

​                innerFlag = 0;

​            } else if (retval == 0) {

​                // 对方主动断开连接

​                printf("client disconnected!\r\n");

​                innerFlag = 0;

​            } else {

​                if (retval <= 0) {

​                printf("send response failed, %ld!\r\n", retval);

​                innerFlag = 0;

​                }

​                if(retval == 3 &&  request[0] == -86)

​                {

​                    printf("recv = %d %d %d \r\n",request[0],request[1],request[2]);

​                    if(request[1] == 0x00){

​                        retval = send(connfd, "aaok", strlen("aaok"), 0);           //答应码

​                    }

​                    if(request[1] == 0x01)

​                    {

​                    }else if(request[1] == 0x02)                                    //喂食量处理函数

​                    {

​                        int i = ((unsigned char)request[2]);

​                        for(;i>0;i--)

​                        {

​                            custom(500);

​                            osDelay(10);

​                        }

​                    }

​                }

​                // 清空缓冲区

​                memset(&request, 0, sizeof(request));

​            }

​            if(innerFlag == 0)

​            {

​                DisconnectTcpSocket(connfd); //清除连接

​                break;                       //重连

​            }

​        }

​    }

​    CloseTcpSocket(sockfd);

}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.

3.接收的部分代码

当客户端传来的命令,符合长度为3,并且头帧为0xAA才会进行处理

            if(retval == 3 &&  request[0] == -86)
            {
                printf("recv = %d %d %d \r\n",request[0],request[1],request[2]);
                if(request[1] == 0x00){
                    retval = send(connfd, "aaok", strlen("aaok"), 0);  //答应码
                }
                if(request[1] == 0x01)
                {
                }else if(request[1] == 0x02)  //喂食量处理函数
                {
                    int i = ((unsigned char)request[2]);
                    for(;i>0;i--)
                    {
                        custom(500); //控制舵机旋转
                        osDelay(10);
                    }
                }
            }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

4.舵机部分

#打卡不停更#智能喂食器-鸿蒙开发者社区

本项目使用的是连续旋转舵机,我们可以调节脉冲的时长驱动连续旋转舵机

#define GPIO2 2

void set_angle( unsigned int duty) {

​    IoTGpioInit(GPIO2);

​    IoTGpioSetDir(GPIO2, IOT_GPIO_DIR_OUT);

​    IoTGpioSetOutputVal(GPIO2, IOT_GPIO_VALUE1);

​    hi_udelay(duty);

​    IoTGpioSetOutputVal(GPIO2, IOT_GPIO_VALUE0);

​    hi_udelay(20000 - duty);

}

void custom(int delay)

{
​    for (int i = 0; i <10; i++) {

​        set_angle(delay);

​    }
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

5.超声波部分

#打卡不停更#智能喂食器-鸿蒙开发者社区

超声波模块算出底部的距离,再算出余粮的百分比。

根据上面的时序图,进行编写超声波驱动

void GetDistance  (float *distance) {

​    static unsigned long start_time = 0, time = 0;

​    IotGpioValue value = IOT_GPIO_VALUE0;

​    unsigned int flag = 0;

​    IoTWatchDogDisable();

​    hi_io_set_func(GPIO_8, GPIO_FUNC);

​    IoTGpioSetDir(GPIO_8, IOT_GPIO_DIR_IN);

​    IoTGpioSetDir(GPIO_7, IOT_GPIO_DIR_OUT);

​    IoTGpioSetOutputVal(GPIO_7, IOT_GPIO_VALUE1);

​    hi_udelay(20);

​    IoTGpioSetOutputVal(GPIO_7, IOT_GPIO_VALUE0);

​    while (1) {

​        IoTGpioGetInputVal(GPIO_8, &value);

​        if ( value == IOT_GPIO_VALUE1 && flag == 0) {

​            start_time = hi_get_us();

​            flag = 1;

​        }

​        if (value == IOT_GPIO_VALUE0 && flag == 1) {

​            time = hi_get_us() - start_time;

​            start_time = 0;

​            break;

​        }

​    }

​    *distance = time * 0.034 / 2;

​    printf("distance is %f\r\n",*distance);

​    return ;

}



  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.

③、Flask服务端开发

1、创建数据库对象

创建的这些对象,是我们可以通过SQLAlchemy的方法去操控数据库

'''

识别记录表

'''

class identificationRecord(db.Model):

​    id = db.Column(db.Integer, primary_key=True)

​    name = db.Column(db.String(128))

​    state = db.Column(db.Integer)

​    date_time = db.Column(db.DateTime, default=datetime.datetime.now)

​    def return_json(self):

​        return {

​            'id':self.id,

​            'name': self.name,

​            'state':self.state,

​            'date_time':self.date_time.strftime('%Y-%m-%d %H:%M:%S')

​        }



'''

状态标记表

'''

class tateTag(db.Model):

​    id = db.Column(db.Integer, primary_key=True)

​    cv2state = db.Column(db.Integer)

​    magstate= db.Column(db.Integer)

​    date_time = db.Column(db.DateTime, default=datetime.datetime.now)

​    def return_json(self):

​        return {

​            'id':self.id,

​            'cv2state': self.cv2state,

​            'magstate':self.magstate,

​            'date_time':self.date_time.strftime('%Y-%m-%d %H:%M:%S')

​        }

​    def clear(self):

​        self.cv2state  = 0

​        self.magstate  = 0

​        self.date_time = datetime.datetime.now()





'''

定时记录表

'''

class timerOfFeeding(db.Model):

​    id = db.Column(db.Integer, primary_key=True)

​    name = db.Column(db.String(128))

​    hour = db.Column(db.Integer)

​    minute = db.Column(db.Integer)

​    weight = db.Column(db.Integer)

​    date_time = db.Column(db.DateTime, default=datetime.datetime.now)

​    def return_json(self):

​        return {

​            'id': self.id,

​            'name':self.name,

​            'hour':self.hour,

​            'minute':self.minute,

​            'weight':self.weight,

​            'date_time':self.date_time.strftime('%Y-%m-%d %H:%M:%S')

​        }



'''

喂食记录表

'''

class feedingRecords(db.Model):

​    id = db.Column(db.Integer, primary_key=True)

​    name = db.Column(db.String(128))

​    manualfeeding = db.Column(db.Integer)

​    automaticfeeding = db.Column(db.Integer)

​    date_time = db.Column(db.DateTime, default=datetime.datetime.now)

​    def return_json(self):

​        return {

​            'id': self.id,

​            'name':self.name,

​            'manualfeeding':self.manualfeeding,

​            'automaticfeeding':self.automaticfeeding,

​            'date_time':self.date_time.strftime('%Y-%m-%d %H:%M:%S')

​        }





余粮记录表

'''

class surplusGrainRecord(db.Model):

​    id = db.Column(db.Integer, primary_key=True)

​    name = db.Column(db.String(128))

​    weight = db.Column(db.Integer)

​    date_time = db.Column(db.DateTime, default=datetime.datetime.now)

​    def return_json(self):

​        return {

​            'id':self.id,

​            'name': self.name,

​            'weight':(self.weight/10),

​            'date_time':self.date_time.strftime('%Y-%m-%d %H:%M:%S')

​        }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.

2、识别猫脸接口

通过add_resource方法使getImage方法和/服务器地址进行绑定

class getImage(Resource):

​    def get(self):

​        image_url = "http://192.168.0.150:8080/stream.mjpg"

​        classfier= cv2.CascadeClassifier("haarcascade_frontalcatface.xml")

​        imgResp=urllib.request.urlopen(image_url)

​        imgNp=np.array(bytearray(imgResp.read()),dtype=np.uint8)

​        img=cv2.imdecode(imgNp,-1)

​        print(type(img))

​        grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

​        faceRects = classfier.detectMultiScale(grey, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32))

​        if len(faceRects) > 0:            #大于0则检测到猫脸

​            for faceRect in faceRects:  #单独框出每一张猫脸

​                x, y, w, h = faceRect

​                cv2.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (0, 255, 0), 2)

​            duf = identificationRecord( name= 'cat' , state = len(faceRects))

​            db.session.add(duf)



​            buff = tateTag.query.first()

​            buff.cv2state = 1

​            buff.date_time = datetime.datetime.now()



​            db.session.commit()

​        image = cv2.imencode('.jpg',img)[1]

​        ss = base64.b64encode(image)

​        return  {

​            "state":"true",

​            "data":"data:image/jpg;base64,"+ss.decode('ascii')

​            }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.

3、接口讲解

该项目我们封装了很多的接口给应用端使用

#打卡不停更#智能喂食器-鸿蒙开发者社区

封装好的接口,我们可以使用Postman对接口进行测试

#打卡不停更#智能喂食器-鸿蒙开发者社区

④、摄像头开发

摄像头开发主要时实现,将图片发送到服务器

使用StreamingServer创建server服务,对请求返回实时的图片

class StreamingOutput(object):

​    def __init__(self):

​        self.frame = None

​        self.buffer = io.BytesIO()

​        self.condition = Condition()



​    def write(self, buf):

​        if buf.startswith(b'\xff\xd8'):

​            \# New frame, copy the existing buffer's content and notify all

​            \# clients it's available

​            self.buffer.truncate()

​            with self.condition:

​                self.frame = self.buffer.getvalue()

​                self.condition.notify_all()

​            self.buffer.seek(0)

​        return self.buffer.write(buf)



class StreamingHandler(server.BaseHTTPRequestHandler):

​    def do_GET(self):

​        elif self.path == '/stream.mjpg':

​            self.send_response(200)

​            self.send_header('Age', 0)

​            self.send_header('Cache-Control', 'no-cache, private')

​            self.send_header('Pragma', 'no-cache')

​            self.send_header('Content-Type', 'image/jpeg')

​            self.end_headers()

​            

​            with output.condition:

​                output.condition.wait()

​                frame = output.frame

​            self.wfile.write(frame)

​            self.wfile.write(b'\r\n')

​        else:

​            self.send_error(404)

​            self.end_headers()



class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):

​    allow_reuse_address = True

​    daemon_threads = True



with picamera.PiCamera(resolution='720x420', framerate=24) as camera:

​    output = StreamingOutput()

​    camera.rotation = 270

​    camera.start_recording(output, format='mjpeg')

​    try:

​        address = ('', 8080)

​        server = StreamingServer(address, StreamingHandler)

​        server.serve_forever()

​    finally:

​        camera.stop_recording()

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.

⑤、3D模型开发

#打卡不停更#智能喂食器-鸿蒙开发者社区

四、常见问题解决

①、hi3861 经常网络断开

可能是因为WiFi测试打开了,导致WiFi断开,同时也可以在这里关闭所有的测试,这样串口打印log的数据就非常干净,不会再收到Test打印log的影响

#打卡不停更#智能喂食器-鸿蒙开发者社区

五、 参考资料

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-9-29 09:15:12修改
9
收藏 4
回复
举报
9
8
4
8条回复
按时间正序
/
按时间倒序
lxj29
lxj29

大佬大佬

回复
2022-9-29 10:03:55
红叶亦知秋
红叶亦知秋

云养猫指日可待!

回复
2022-9-29 10:14:05
Whyalone
Whyalone

小猫识别这个牛批

回复
2022-9-29 11:27:11
阿毛0920
阿毛0920

大佬牛批

回复
2022-9-29 19:25:30
殇时云起
殇时云起

可持续发展目标解决了3个,还有14个指标等楼主解决

已于2022-9-30 10:11:58修改
回复
2022-9-30 10:11:30
物联风景
物联风景

识别猫咪是个亮点,不错不错

回复
2022-9-30 11:20:57
诺舒华吃西瓜
诺舒华吃西瓜

3D模型是打印出来然后组装的吗

回复
2022-10-8 15:14:41
yukoyu
yukoyu 回复了 诺舒华吃西瓜
3D模型是打印出来然后组装的吗

是的,外壳是亚克力切割,内部是3D打印

回复
2022-10-9 09:21:49


回复
    相关推荐
    这个用户很懒,还没有个人简介
    觉得TA不错?点个关注精彩不错过
    帖子
    视频
    声望
    粉丝
    社区精华内容