一.功能需求
- 语音接入控制垃圾分类识别,并触发垃圾桶的开关盖
- 实现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',
# 访问的域名对应的region
region_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 = img
runtime = RuntimeOptions()
try:
# 初始化Client
client = 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__H
void 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步, 周期20ms
softPwmWrite(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步, 周期20ms
softPwmWrite(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__H
int 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 6
int 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. socket
s_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. bind
ret = bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
if (-1 == ret)
{
perror("bind");
return -1;
}
//3. listen
ret = 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;
}