欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 美景 > 基于全志h616的智能垃圾桶项目

基于全志h616的智能垃圾桶项目

2024/10/24 5:21:42 来源:https://blog.csdn.net/2303_77402228/article/details/141638708  浏览:    关键词:基于全志h616的智能垃圾桶项目

一.功能需求

  • 语音接入控制垃圾分类识别,并触发垃圾桶的开关盖
  • 实现Sockect发送指令远程控制垃圾分类识别,并触发垃圾桶的开关盖
  • 图像识别垃圾分类功能
  • 语音播报垃圾物品类型 OLED显示垃圾物品类型
  • 根据垃圾类型开关不同类型垃圾桶

图像处理使用阿里SDK

二.Python基础

参考

https://www.runoob.com/python3/python3-reg-expressions.html

dict嵌套简单说明

garbage_dict = {'Data': {'Elements': [{'Category': '干垃圾', 'CategoryScore': 0.8855999999999999, 'Rubbish': '', 'RubbishScore': 0.0}], 'Sensitive': False}, 'RequestId': '1AB9E813-3781-5CA2-95A0-1EA334E80663'}

  • 最外层的dict有两个键:'Data'和'RequestId'
  • 'Data'对应的值是一个内层的dict,它有两个键:'Elements'和'Sensitive'
  • 'Elements'对应的值是一个列表,它包含了一个元素,也就是另一个内层的dict
  • 这个内层的dict有四个键:'Category'、'CategoryScore'、'Rubbish'和'RubbishScore'
  •  'Category'对应的值是一个字符串,表示垃圾分类的类别,例如'干垃圾'
  •  'CategoryScore'对应的值是一个浮点数,表示垃圾分类的置信度,例如0.8856
  • 'Rubbish'对应的值是一个字符串,表示垃圾的具体名称,例如''(空字符串)
  •  'RubbishScore'对应的值是一个浮点数,表示垃圾名称的置信度,例如0.0
  •  'Sensitive'对应的值是一个布尔值,表示是否涉及敏感信息,例如False
  •  'RequestId'对应的值是一个字符串,表示请求的唯一标识符,例如'1AB9E813-3781-5CA2-95A0- 1EA334E80663'

三.C语言调用Python

1.搭建环境

首先可以通过以下命令验证是否是否已经存在python3的dev包

dpkg -l | grep libpython3

如果没有, 可以通过apt命令安装相关的dev包:

sudo apt install libpython3.10-dev

2.调用python

调用无参python函数

#if 0
1、包含Python.h头文件,以便使用Python API。
2、使用void Py_Initialize()初始化Python解释器,
3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject 
*PyObject_GetAttrString(PyObject *o, const char *attr_name)获取sys.path对象,并利用
int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,以便加载
当前的Python模块(Python文件即python模块)。
4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否
有错误。
5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取
Python函数对象,并检查是否可调用。
6、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用
Python函数,并获取返回值。
7、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
8、结束时调用void Py_Finalize()函数关闭Python解释器。
相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
https://docs.python.org/zh-cn/3/c-api/import.html
#endif
#include <Python.h>
int main()
{Py_Initialize();    // 初始化// 将当前路径添加到sys.path中PyObject *sys = PyImport_ImportModule("sys");PyObject *path = PyObject_GetAttrString(sys, "path");PyList_Append(path, PyUnicode_FromString("."));// 导入nopara模块PyObject *pModule = PyImport_ImportModule("nopara");if (!pModule){PyErr_Print();printf("ERROR: failed to load nopara.py\n");return 1;}// 获取say_funny函数对象PyObject *pFunc = PyObject_GetAttrString(pModule, "say_funny");if (!pFunc || !PyCallable_Check(pFunc)){PyErr_Print();printf("ERROR: function say_funny not found or not callable\n");return 1;}// 调用say_funny函数并获取返回值PyObject *pValue = PyObject_CallObject(pFunc, NULL);if (!pValue){PyErr_Print();printf("ERROR: function call failed\n");return 1;}// 释放所有引用的Python对象Py_DECREF(pValue);Py_DECREF(pFunc);Py_DECREF(pModule);// 关闭Python解释器Py_Finalize();return 0;
}

调用有参python函数

#if 0
1、包含Python.h头文件,以便使用Python API。
2、使用void Py_Initialize()初始化Python解释器,
3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject 
*PyObject_GetAttrString(PyObject *o, const char *attr_name)获取sys.path对象,并利用
int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,以便加载
当前的Python模块(Python文件即python模块)。
4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否
有错误。
5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取
Python函数对象,并检查是否可调用。
6、使用PyObject *Py_BuildValue(const char *format, ...)函数将C类型的数据结构转换成
Python对象,作为Python函数的参数,没有参数不需要调用
7、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用
Python函数,并获取返回值。
8、使用int PyArg_Parse(PyObject *args, const char *format, ...)函数将返回值转换为C类
型,并检查是否有错误,没有返回值时不需要调用。
9、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
10、结束时调用void Py_Finalize()函数关闭Python解释器。
相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
https://docs.python.org/zh-cn/3/c-api/import.html
#endif

Py_BuildValue的第一个参数是类型转换:C对应的Python的数据类型转换对应的格式如下:

#include <Python.h>
int main()
{Py_Initialize();// 将当前路径添加到sys.path中PyObject *sys = PyImport_ImportModule("sys");PyObject *path = PyObject_GetAttrString(sys, "path");PyList_Append(path, PyUnicode_FromString("."));// 导入para模块PyObject *pModule = PyImport_ImportModule("para");if (!pModule){PyErr_Print();printf("Error: failed to load nopara.py\n");}//获取say_funny函数对象PyObject *pFunc = PyObject_GetAttrString(pModule, "say_funny");if (!pFunc){PyErr_Print();printf("Error: failed to load say_funny\n");}//创建一个字符串作为参数char *category = "comedy";PyObject *pArgs = Py_BuildValue("(s)", category);//调用say_funny函数并获取返回值PyObject *pValue = PyObject_CallObject(pFunc, pArgs);if (!pValue){PyErr_Print();printf("Error: function call failed\n");}//将返回值转换为C类型char *result = NULL;if (!PyArg_Parse(pValue, "s", &result)){PyErr_Print();printf("Error: parse failed\n");}//打印返回值printf("pValue=%s\n", result);//释放所有引用的Python对象Py_DECREF(pValue);Py_DECREF(pFunc);Py_DECREF(pModule);//释放所有引用的Python对象Py_Finalize();return 0;
}

四.阿里云垃圾识别方案

1.接入阿里云

阿里云官网地址 :

https://vision.aliyun.com/

进入官网后搜索垃圾识别,根据官方技术文档进行环境搭建

2.C语言调用阿里云Python接口

garbage.py改造

# -*- coding: utf-8 -*-
# 引入依赖包
# garbage.py
# pip install alibabacloud_imagerecog20190930
import os
import io
import json
from urllib.request import urlopen
from alibabacloud_imagerecog20190930.client import Client
from alibabacloud_imagerecog20190930.models import 
ClassifyingRubbishAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
config = Config(# 创建AccessKey ID和AccessKey Secret,请参考
https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考
https://help.aliyun.com/document_detail/145025.html# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='imagerecog.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)
def alibabacloud_garbage():#场景一:文件在本地img = open(r'/tmp/garbage.jpg', 'rb')#场景二:使用任意可访问的url#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi3.0domepic/imagerecog/ClassifyingRubbish/ClassifyingRubbish1.jpg'#img = io.BytesIO(urlopen(url).read())classifying_rubbish_request = ClassifyingRubbishAdvanceRequest()classifying_rubbish_request.image_urlobject = imgruntime = RuntimeOptions()try:# 初始化Clientclient = Client(config)response = client.classifying_rubbish_advance(classifying_rubbish_request, 
runtime)print(response.body)return response.body.to_map()['Data']['Elements'][0]['Category']except Exception as error:print(type('获取失败'))return '获取失败'

C语言调用garbage.py封装

garbage.c

void garbage_initialize(void)
{Py_Initialize();PyObject *sys = PyImport_ImportModule("sys");PyObject *path = PyObject_GetAttrString(sys, "path");PyList_Append(path, PyUnicode_FromString("."));
}
void garbage_final(void)
{Py_Finalize();
}
char *garbage_category(char *category)
{PyObject *pModule = PyImport_ImportModule("garbage");if (!pModule){PyErr_Print();printf("Error: failed to load garbage.py\n");goto FAILED_MODULE;}PyObject *pFunc = PyObject_GetAttrString(pModule, "alibabacloud_garbage");if (!pFunc){PyErr_Print();printf("Error: failed to load alibabacloud_garbage\n");goto FAILED_FUNC;}PyObject *pValue = PyObject_CallObject(pFunc, NULL);if (!pValue){PyErr_Print();printf("Error: function call failed\n");goto FAILED_VALUE;}char *result = NULL;if (!PyArg_Parse(pValue, "s", &result)){PyErr_Print();printf("Error: parse failed");goto FAILED_RESULT;}category = (char *)malloc(sizeof(char) * (strlen(result) + 1) );memset(category, 0, (strlen(result) + 1));strncpy(category, result, (strlen(result) + 1));
FAILED_RESULT:Py_DECREF(pValue);
FAILED_VALUE:Py_DECREF(pFunc);
FAILED_FUNC:Py_DECREF(pModule);
FAILED_MODULE:return category;
}

garbage.h

#ifndef __GARBAGE__H
#define __GARBAGE__Hvoid garbage_init(void);
void garbage_final(void);
char *garbage_category(char *category);//增加拍照指令和照片路径宏定义
#define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O 
/tmp/garbage.jpg"
#define GARBAGE_FILE "/tmp/garbage.jpg"#endif

五.香橙派使用摄像头

参考《OrangePi_Zero2_H616用户手册v4.0.pdf》

1. 首先将 USB 摄像头插入到 Orange Pi 开发板的 USB 接口中

2. 然后通过 lsmod 命令可以看到内核自动加载了下面的模块

lsmod | grep uvcvideo | grep -v grep

uvcvideo 106496 0

3. 通过 v4l2-ctl 命令可以看到 USB 摄像头的设备节点信息为/dev/videox(x有可能是0 1或者2等数 字)

sudo apt install -y v4l-utils
v4l2-ctl --list-devices

4. 使用 fswebcam 测试 USB 摄像头

        a. 安装 fswebcam

sudo apt-get install -y fswebcam

        b. 安装完 fswebcam 后可以使用下面的命令来拍照

                a) -d 选项用于指定 USB 摄像头的设备节点

                b) --no-banner 用于去除照片的水印

                c) -r 选项用于指定照片的分辨率

                d) -S 选项用设置于跳过前面的帧数

                e) ./image.jpg 用于设置生成的照片的名字和路径  

sudo fswebcam -d /dev/video1  --no-banner -r 1280x720 -S 5 ./image.jpg

        c. 在服务器版的 linux 系统中,拍完照后可以直接通过mobaxterm拖到电脑桌面看或者使用 scp 命令将 拍好的图片传到Ubuntu PC 上镜像观看:

5. 使用 mjpg-streamer 测试 USB 摄像头

        a. 下载 mjpg-streamer

                a) Github 的下载地址:

git clone https://github.com/jacksonliam/mjpg-streamer

                 b) Gitee 的镜像下载地址为:

git clone https://gitee.com/leeboby/mjpg-streamer

        b. 安装依赖的软件包

        Ubuntu 系统

sudo apt-get install -y cmake libjpeg8-dev

        c. 编译安装 mjpg-streamer 

cd mjpg-streamer/mjpg-streamer-experimental
make -j4
sudo make install

        d. 然后输入下面的命令启动 mjpg_streamer 

export LD_LIBRARY_PATH=.
sudo ./mjpg_streamer -i "./input_uvc.so -d /dev/video0 -u -f 30" -o "./output_http.so -w ./www"

        e. 然后在和开发板同一局域网的 Ubuntu PC 或者 Windows PC 或者手机的浏览orange Pi器中输入 【开发板的 IP地址:8080】就能看到摄像头输出的视频了  

        f.修改 start.sh脚本,将start.sh里的:

./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"

        修改为:

./mjpg_streamer -i "./input_uvc.so -d /dev/video1 -u -f 30" -o "./output_http.so -w ./www"

修改后可以通过执行./start.sh运行摄像头 

在使用摄像头前需确认已经运行了mjpg-streamer服务

六.语音模块配置

七.语音模块和阿里云结合

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "uartTool.h"
#include "garbage.h"static int detect_process(const char * process_name)     //判断进程是否在运行
{  int n = -1; FILE *strm;  char buf[128]={0};  sprintf(buf,"ps -ax | grep %s|grep -v grep", process_name);  if((strm = popen(buf, "r")) != NULL)  {  if(fgets(buf, sizeof(buf), strm) != NULL)  {n = atoi(buf); }}else{return -1;} pclose(strm);   return n;  
} 
int main(int argc, char *argv[])
{int serial_fd = -1;int ret = -1;unsigned char buffer[6] = {0xAA, 0X55, 0x00, 0x00, 0x55, 0xAA};int len = 0;char *category = NULL;garbage_init();ret = detect_process("mjpg_streamer");//用于判断mjpg_streamer服务是否已经启动if ( -1 == ret){}serial_fd = myserialOpen (SERIAL_DEV, BAUD);if (-1 == serial_fd){goto END;}while (1){len = serialGetstring (serial_fd, buffer);printf("lend=%d, buf[2]=0x%x\n",len, buffer[2]);if (len > 0 && buffer[2] == 0x46 &&  buffer[3] == 0x0){buffer[2] = 0x00;system(WGET_CMD);category = garbage_category(category);if (strstr(category, "干垃圾")){buffer[2] = 0x41;}else if (strstr(category, "湿垃圾")){buffer[2] = 0x42;}else if (strstr(category, "可回收垃圾")){buffer[2] = 0x43;}else if (strstr(category, "有害垃圾")){buffer[2] = 0x44;}else{buffer[2] = 0x45;}serialSendstring(serial_fd, buffer, 6);buffer[2] = 0x00;remove(GARBAGE_FILE);}}
END:garbage_final();return 0;
}

八.增加垃圾桶及开关盖功能

pwm.c

#include <wiringPi.h>
#include <softPwm.h>
//根据公式:PWMfreq = 1 x 10^6 / (100 x range) ,要得到PWM频率为50Hz,则range为200,即
周期分为200步,控制精度相比硬件PWM较低。
void pwm_write(int pwm_pin)
{pinMode(pwm_pin, OUTPUT);softPwmCreate(pwm_pin,0,200);// range设置周期分为200步, 周期20mssoftPwmWrite(pwm_pin,10);//1ms 45度delay(1000);softPwmStop(pwm_pin);
}
void pwm_stop(int pwm_pin)
{pinMode(pwm_pin, OUTPUT);softPwmCreate(pwm_pin,0,200);// range设置周期分为200步, 周期20mssoftPwmWrite(pwm_pin,5);//0.5ms 0度delay(1000);softPwmStop(pwm_pin);
}

pwm.h

#ifndef __PWM__H
#define __PWM__H#define PWM_GARBAGE 7
#define PWM_RECOVERABLE_GARBAGE 5
void pwm_write(int pwm_pin);
void pwm_stop(int pwm_pin);#endif

测试代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"static int detect_process(const char *process_name)
{  int n = -1; FILE *strm;  char buf[128]={0};  sprintf(buf,"ps -ax | grep %s|grep -v grep", process_name);  if((strm = popen(buf, "r")) != NULL)  {  if(fgets(buf, sizeof(buf), strm) != NULL)  {printf("buf=%s\n",buf);n = atoi(buf); printf("n=%d\n",n);}}else{return -1;} pclose(strm);   return n;  
} int main(int argc, char *argv[])
{   int serial_fd = -1;int len = 0;int ret = -1;char *category = NULL;unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};wiringPiSetup();garbage_init();ret = detect_process("mjpg_streamer");if ( -1 == ret){printf("detect process failed\n");goto END;}serial_fd = myserialOpen(SERIAL_DEV, BAUD);if (-1 == serial_fd){printf("open serial failed\n");goto END;}while(1){len = serialGetstring(serial_fd, buffer);if (len > 0 && buffer[2] == 0x46){buffer[2] = 0x00;system(WGET_CMD);if (0 == access(GARBAGE_FILE, F_OK)){category = garbage_category(category);if (strstr(category, "干垃圾")){buffer[2] = 0x41;}else  if (strstr(category, "湿垃圾")){buffer[2] = 0x42;}else  if (strstr(category, "可回收垃圾")){buffer[2] = 0x43;}else  if (strstr(category, "有害垃圾")){buffer[2] = 0x44;}else {buffer[2] = 0x45;}}else{buffer[2] = 0x45;}printf("buffer[2] =%d\n", buffer[2]);serialSendstring(serial_fd, buffer, 6);if (buffer[2] == 0x43){pwm_write(PWM_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}else if (buffer[2] != 0x45){printf("start\n");pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}buffer[2] = 0x00;remove(GARBAGE_FILE);}}
END:garbage_final();return 0;
}

九.项目代码优化(利用多线程来实现具体功能)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include <pthread.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"int serial_fd = -1;pthread_cond_t  cond;pthread_mutex_t mutex;static int detect_process(const char *process_name)
{  int n = -1; FILE *strm;  char buf[128]={0};  sprintf(buf,"ps -ax | grep %s|grep -v grep", process_name);  if((strm = popen(buf, "r")) != NULL)  {  if(fgets(buf, sizeof(buf), strm) != NULL)  {printf("buf=%s\n",buf);n = atoi(buf); printf("n=%d\n",n);}}else{return -1;} pclose(strm);   return n;  
}void *pget_voice(void *arg){unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};int len = 0;printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);if (-1 == serial_fd){printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);pthread_exit(0);}printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);    while(1){len = serialGetstring(serial_fd, buffer);printf("%s|%s|%d, len=%d\n", __FILE__, __func__, __LINE__,len);if (len > 0 && buffer[2] == 0x46){printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_mutex_lock(&mutex);buffer[2] = 0x00;pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}pthread_exit(0);}void *psend_voice(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (-1 == serial_fd){printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);pthread_exit(0);}if (NULL != buffer){serialSendstring(serial_fd, buffer, 6);}pthread_exit(0); 
}void *popen_trash_can(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (buffer[2] == 0x43){printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__, 
buffer[2]);pwm_write(PWM_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}else if (buffer[2] != 0x45){printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__, 
buffer[2]);pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}pthread_exit(0); 
}void *pcategory(void *arg){unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};   char *category = NULL; pthread_t  send_voice_tid, trash_tid;while (1){printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);buffer[2] = 0x00;system(WGET_CMD);if (0 == access(GARBAGE_FILE, F_OK)){category = garbage_category(category);if (strstr(category, "干垃圾")){buffer[2] = 0x41;}else  if (strstr(category, "湿垃圾")){buffer[2] = 0x42;}else  if (strstr(category, "可回收垃圾")){buffer[2] = 0x43;}else  if (strstr(category, "有害垃圾")){buffer[2] = 0x44;}else {buffer[2] = 0x45;}}else{buffer[2] = 0x45;}//开垃圾桶开关pthread_create(&trash_tid, NULL, psend_voice, (void *)buffer);//开语音播报线程pthread_create(&send_voice_tid, NULL, popen_trash_can, (void *)buffer);//buffer[2] = 0x00;remove(GARBAGE_FILE);}pthread_exit(0);}int main(int argc, char *argv[])
{int len = 0;int ret = -1;char *category = NULL;pthread_t  get_voice_tid, category_tid;wiringPiSetup();garbage_init();ret = detect_process("mjpg_streamer");if ( -1 == ret){printf("detect process failed\n");goto END;}serial_fd = myserialOpen(SERIAL_DEV, BAUD);if (-1 == serial_fd){printf("open serial failed\n");goto END;}//开语音线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&get_voice_tid, NULL, pget_voice, NULL);//开阿里云交互线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&category_tid, NULL, pcategory, NULL);pthread_join(get_voice_tid, NULL);pthread_join(category_tid, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);close(serial_fd);
END:garbage_final();return 0;
}

十.增加OLED显示功能

myoled.c

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "oled.h"
#include "font.h"
//包含头文件
#include "myoled.h"
#define FILENAME "/dev/i2c-3"
static struct display_info disp;
int oled_show(void *arg) 
{unsigned char *buffer = (unsigned char *)arg;oled_putstrto(&disp, 0, 9+1, "This garbage is:");disp.font = font2;switch(buffer[2]){case 0x41:oled_putstrto(&disp, 0, 20, "dry waste");break;case 0x42:oled_putstrto(&disp, 0, 20, "wet waste");break;case 0x43:oled_putstrto(&disp, 0, 20, "recyclable waste");break;case 0x44:oled_putstrto(&disp, 0, 20, "hazardous waste");break;case 0x45:oled_putstrto(&disp, 0, 20, "recognition failed");break;}disp.font = font2;oled_send_buffer(&disp);return 0;
}int myoled_init(void)
{int e;disp.address = OLED_I2C_ADDR;disp.font = font2;e = oled_open(&disp, FILENAME);e = oled_init(&disp);return  e;
}

myoled.h

#ifndef __MYOLED__H
#define __MYOLED__Hint myoled_init(void);
int oled_show(void *arg);#endif

修改main文件,增加oled线程

在void *pcategory(void *arg)函数中添加

//oled显示线程 pthread_create(&oled_tid, NULL, poled_show, (void *)buffer);

新增函数

void *poled_show(void *arg)
{pthread_detach(pthread_self());myoled_init();oled_show(arg);pthread_exit(0); 
}

十一.增加网络控制功能

1.TCP 心跳机制解决Soket异常断开问题

Socket客户端得断开情形无非就两种情况:

1.客户端能够发送状态给服务器;正常断开,强制关闭客户端等,客户端能够做出反应。

2.客户端不能发送状态给服务器;突然断网,断电,客户端卡死等,客户端根本没机会做出反应, 服务器更不了解客户端状态,导致服务器异常等待。

为了解决上述问题,引入TCP心跳包机制: 心跳包的实现,心跳包就是服务器定时向客户端发送查询信息,如果客户端有回应就代表连接正常, 类似于linux系统的看门狗机制。心跳包的机制有一种方法就是采用TCP_KEEPALIVE机制,它是一种用于 检测TCP连接是否存活的机制,它的原理是在一定时间内没有数据往来时,发送探测包给对方,如果对方 没有响应,就认为连接已经断开。TCP_KEEPALIVE机制可以通过设置一些参数来调整,如探测时间间 隔、探测次数等。

Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。

查看当前系统的TCP KeepAlive参数

sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_probes
sysctl net.ipv4.tcp_keepalive_intvl

修改TCP KeepAlive参数(不建议直接修改系统参数)

sysctl net.ipv4.tcp_keepalive_time=3600

2.C语言实现TCP KeepAlive功能

int keepalive = 1;          // 开启TCP KeepAlive功能
int keepidle = 5;        // tcp_keepalive_time 3s内没收到数据开始发送心跳包
int keepcnt = 3;            // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
int keepintvl = 3;         // tcp_keepalive_intvl 每3s发送一次心跳包
setsockopt(client_socketfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, 
sizeof(keepalive));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof 
(keepidle));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof 
(keepcnt));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof 
(keepintvl));

3.代码实现

socket.h

#ifndef __SOCKET__H
#define __SOCKET__H#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>#define IPADDR "192.168.1.21" //填写自己实际的ip地址
#define IPPORT "8192"
#define BUF_SIZE 6int socket_init(const char *ipaddr, const char *port);#endif

socket.c

#include "socket.h"
int socket_init(const char *ipaddr, const char *port)
{int s_fd = -1;int ret = -1;struct sockaddr_in s_addr;memset(&s_addr,0,sizeof(struct sockaddr_in));//1. sockets_fd = socket(AF_INET, SOCK_STREAM, 0);if(s_fd == -1){perror("socket");return -1;}s_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(port));inet_aton(ipaddr,&s_addr.sin_addr);//2. bindret = bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));if (-1 == ret){perror("bind");return -1;}//3. listenret = listen(s_fd,1); //只监听1个连接,排队扔垃圾if (-1 == ret){perror("listen");return -1;}return  s_fd;
}

十二.项目最终代码(main.c)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include <pthread.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
#include "socket.h"int serial_fd = -1;pthread_cond_t  cond;pthread_mutex_t mutex;static int detect_process(const char *process_name)
{  int n = -1; FILE *strm;  char buf[128]={0};  sprintf(buf,"ps -ax | grep %s|grep -v grep", process_name);  if((strm = popen(buf, "r")) != NULL)  {  if(fgets(buf, sizeof(buf), strm) != NULL)  {printf("buf=%s\n",buf);n = atoi(buf); printf("n=%d\n",n);}}else{return -1;} pclose(strm);   return n;  
} void *pget_voice(void *arg){unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};int len = 0;printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);if (-1 == serial_fd){printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);pthread_exit(0);}printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);    while(1){len = serialGetstring(serial_fd, buffer);printf("%s|%s|%d, len=%d\n", __FILE__, __func__, __LINE__,len);if (len > 0 && buffer[2] == 0x46){printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_mutex_lock(&mutex);buffer[2] = 0x00;pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}pthread_exit(0);}void *psend_voice(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (-1 == serial_fd){printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);pthread_exit(0);}if (NULL != buffer){serialSendstring(serial_fd, buffer, 6);}pthread_exit(0); 
}void *popen_trash_can(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (buffer[2] == 0x43){printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__, 
buffer[2]);pwm_write(PWM_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}else if (buffer[2] != 0x45){printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__, 
buffer[2]);pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}pthread_exit(0); 
}
void *poled_show(void *arg)
{pthread_detach(pthread_self());myoled_init();oled_show(arg);pthread_exit(0); 
}void *pcategory(void *arg){unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};   char *category = NULL; pthread_t  send_voice_tid, trash_tid, oled_tid;printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);while (1){printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);buffer[2] = 0x00;system(WGET_CMD);if (0 == access(GARBAGE_FILE, F_OK)){category = garbage_category(category);if (strstr(category, "干垃圾")){buffer[2] = 0x41;}else  if (strstr(category, "湿垃圾")){buffer[2] = 0x42;}else  if (strstr(category, "可回收垃圾")){buffer[2] = 0x43;}else  if (strstr(category, "有害垃圾")){buffer[2] = 0x44;}else {buffer[2] = 0x45;}}else{buffer[2] = 0x45;}//开垃圾桶开关pthread_create(&trash_tid, NULL, psend_voice, (void *)buffer);//开语音播报线程pthread_create(&send_voice_tid, NULL, popen_trash_can, (void *)buffer);//oled显示线程 pthread_create(&oled_tid, NULL, poled_show, (void *)buffer);//buffer[2] = 0x00;remove(GARBAGE_FILE);}pthread_exit(0);}void *pget_socket(void *arg)
{int s_fd = -1;int c_fd = -1;char buffer[6];int nread = -1;struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));s_fd = socket_init(IPADDR, IPPORT);printf("%s|%s|%d:s_fd=%d\n", __FILE__, __func__, __LINE__, s_fd); if (-1 == s_fd){pthread_exit(0);}sleep(3);int clen = sizeof(struct sockaddr_in);while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);int keepalive = 1;          // 开启TCP KeepAlive功能int keepidle = 5;        // tcp_keepalive_time 3s内没收到数据开始发送心跳包int keepcnt = 3;            // tcp_keepalive_probes 每次发送心跳包的时间间隔,
单位秒int keepintvl = 3;         // tcp_keepalive_intvl 每3s发送一次心跳包setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, 
sizeof(keepalive));setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof 
(keepidle));setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof 
(keepcnt));setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof 
(keepintvl));printf("%s|%s|%d: Accept a connection from %s:%d\n", __FILE__, __func__, 
__LINE__, inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));if(c_fd == -1){perror("accept");continue;}while(1){memset(buffer, 0, sizeof(buffer));nread =  recv(c_fd, buffer, sizeof(buffer), 0); //n_read = read(c_fd, 
buffer, sizeof(buffer));printf("%s|%s|%d:nread=%d, buffer=%s\n", __FILE__, __func__, 
__LINE__, nread, buffer);if (nread > 0){if (strstr(buffer, "open")){pthread_mutex_lock(&mutex);pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}else if(0 == nread || -1 == nread){break;}}close(c_fd);}pthread_exit(0);
}
int main(int argc, char *argv[])
{int len = 0;int ret = -1;char *category = NULL;pthread_t  get_voice_tid, category_tid, get_socket_tid;wiringPiSetup();garbage_init();ret = detect_process("mjpg_streamer");if ( -1 == ret){printf("detect process failed\n");goto END;}serial_fd = myserialOpen(SERIAL_DEV, BAUD);if (-1 == serial_fd){printf("open serial failed\n");goto END;}//开语音线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&get_voice_tid, NULL, pget_voice, NULL);//开网络线程pthread_create(&get_socket_tid, NULL, pget_socket, NULL);//开阿里云交互线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&category_tid, NULL, pcategory, NULL);pthread_join(get_voice_tid, NULL);pthread_join(category_tid, NULL);pthread_join(get_socket_tid, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);close(serial_fd);
END:garbage_final();return 0;return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com