本文继承自: 垃圾分类子项目2 - 加入舵机控制-CSDN博客
添加 oled 功能:
我们要使用oled,就需要添加 i2c 功能
需要在这个文件中 /boot/orangepiEnv.txt
添加这行,使用 i2c 协议
overlays=uart5 i2c3
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"#define FILENAME "/dev/i2c-3" static struct display_info disp;int myoled_show(void *arg) // oled 显示
{unsigned char *buffer = (unsigned char *)arg; // 传入串口数据 -- bufferoled_putstrto(&disp, 0, 10, "This garbage is:");// 这垃圾是:disp.font = font2; // 指定输出类型switch (buffer[2]){case 0x41:oled_putstrto(&disp, 0, 20, "residual(dry) waste");// 干垃圾break;case 0x42:oled_putstrto(&disp, 0, 20, "wet wastee");// 湿垃圾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 failure");// 识别失败break;}disp.font = font2;oled_send_buffer(&disp); return 0;
}int myoled_init(void) //初始化 oled
{int e;disp.address = OLED_I2C_ADDR;disp.font = font2; // 指定显示字样类型e = oled_open(&disp, FILENAME); //打开i2c 设备接口e = oled_init(&disp); // 初始化oledreturn e;
}
myoled.h
#ifndef __MYOLED_H
#define __MYOLED_Hint myoled_show(void *arg);
int myoled_init(void);#endif
main.c
思路: 在主函数中,增加一条线程用于将垃圾类型 显示 到oled
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // access()
#include <error.h> // remove()
#include <wiringPi.h>
#include <softPwm.h>
#include <pthread.h>#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.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) // 通过 popen 的 方式去执行{if (fgets(buf, sizeof(buf), strm) != NULL) // 执行完后 判断是否能拿到正确的进程号,空格分开,第一个字符串就是进程号{n = atoi(buf); // 拿到就放回 进程号,不然 返回 -1}}else{return -1; // 执行失败}pclose(strm);return n;
}int serial_fd = -1; // 线程调用 -- 定义为全局
pthread_cond_t cond; // 设置条件变量
pthread_mutex_t mutex; // 设置线程锁void *pget_voice(void *arg) // 语言播放线程函数
{int len = 0;unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] -- 关联垃圾类型if (-1 == serial_fd){printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 - 138pthread_exit(0); // 串口打开失败 -->退出}while (1){len = serialGetstring(serial_fd, buffer); // 通过串口获得语言输入if (len > 0 && buffer[2] == 0x46) // 判断是否 接到识别指令{pthread_mutex_lock(&mutex); // 先上锁,保证后面执行这块不会被打断buffer[2] = 0x00; // 判断完后的复位pthread_cond_signal(&cond); // 发送信号,告诉阿里云线程开始识别了pthread_mutex_unlock(&mutex); // 解锁,与上锁包含的代码块执行的时候不会被打断}}pthread_exit(0);
}void *popen_trash_can(void *arg) // 开盖
{ pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if(buffer[2] == 0x43){pwm_write(PWM_GARBAGE1);delay (2000); //开盖5spwm_stop(PWM_GARBAGE1); //停止写入波形}else if(buffer[2] != 0x45){pwm_write(PWM_GARBAGE2);delay (2000); //开盖5spwm_stop(PWM_GARBAGE2); //停止写入波形}pthread_exit(0);
}void *psend_voice(void *arg) // 发送语言播报
{pthread_detach(pthread_self()); // pthread_self -- 拿到自己的线程id --> 与父进程分离,不然开盖等待时间太长影响下一次识别unsigned char *buffer = (unsigned char *)arg;if (-1 == serial_fd) //判断串口是否打开{printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 - 138pthread_exit(0); // 串口打开失败 -->退出}printf("buffer[2] = 0x%x\n",buffer[2]); if(NULL!=buffer) //有数据serialSendstring(serial_fd, buffer, 6); // 将识别到的数据发送到串口,回传给语音模块,语言模块收到数据后进行相应输出 -- 实现语言播报pthread_exit(0);
}void *poled_show(void *arg) // 显示oled 的线程函数
{pthread_detach(pthread_self()); myoled_init();myoled_show(arg); // 将buffer 传进来,用于oled的显示pthread_exit(0);
}void *pcategory(void *arg) // 阿里云 -- 垃圾类型识别线程函数
{unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] -- 关联垃圾类型char *category = NULL;pthread_t send_voice_tid,trash_tid,oled_tid;while (1){pthread_mutex_lock(&mutex); // 拿锁pthread_cond_wait(&cond, &mutex); // 等待 接受到信号 -- 才能开始识别pthread_mutex_unlock(&mutex);// 开始识别 buffer[2] = 0x00; // 拍照前复位 system(WGET_CMD); // 拍照if (access(GARBAGE_FILE, F_OK) == 0) // 判断 文件存在{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, popen_trash_can, (void*)buffer);// 创建语音播报线程pthread_create(&send_voice_tid, NULL, psend_voice, (void*)buffer);// 创建oled显示线程pthread_create(&oled_tid, NULL, poled_show, (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; // 创建线程idunsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] -- 关联垃圾类型wiringPiSetup(); // 初始化wiringPi库garbage_init(); // 初始化 阿里云接口ret = detect_process("mjpg_streamer"); // 用于判断mjpg_streamer服务是否已经启动if (-1 == ret){puts("detect process failed");goto END;}serial_fd = myserialOpen(SERIAL_DEV, BAUD); // 初始化串口,打开串口设备(语言模块)if (serial_fd == -1){ // 初始化串口失败goto END;}// 创建语音线程 -- 注意第一个参数类型是指针变量 pthread_t *pthread_create(&get_voice_tid, NULL, pcategory, NULL);// 创建阿里云交互线程pthread_create(&category_tid, NULL, pget_voice, NULL);// 第二个参数表示接收到的返回值 -- 没有就NULLpthread_join(get_voice_tid, NULL); // 等待线程退出pthread_join(category_tid, NULL);pthread_mutex_destroy(&mutex); // 释放锁pthread_cond_destroy(&cond); // 释放条件变量close(serial_fd); // 关闭串口文件描述符 fd
END:garbage_final();return 0;
}