基于全志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',
  # 访问的域名对应的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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值