前言
   
    这也是帮人做的毕设之一,功能完善,资料也完备,相对以往代码写的规范,代码相对以往变得更加简洁好看,视频也有录制。
    
    
     
      项目下载链接
     
    
   
    
    
    一、所需材料
   
    
    
    1、硬件
   
| HC-SR04超声波测距模块 | 
|---|
| 带IIC转换板LCD1602 | 
| JQ8400FL10P语音模块 | 
| UNO R3开发板 | 
| 三色LED灯 | 
| LCD1602 IIC | 
| 积木按键 | 
| 小面包板 | 
| 木板若干 | 
| 杜邦若干 | 
    
    
    2、软件
   
| Arduino IDE | 
|---|
| 微信语音合成小程序 | 
    
    
    3、接线
   
RGB三色彩灯接线
| 灯 | UNO | 
|---|---|
| R | 4 | 
| G | 5 | 
| B | 6 | 
超声波传感器接线
| 超声波传感器 | UNO | 
|---|---|
| Tr | 2 | 
| Ech | 3 | 
JQ8400语音模块
| JQ8400 | UNO | 
|---|---|
| 7 | ONE LINE | 
按键接线
| 按键 | UNO | 
|---|---|
| OUT | A0 | 
LCD1602接线
| LCD1602 | UNO | 
|---|---|
| SDA | SDA | 
| SCL | SCL | 
    
    
    二、Arduino部分
   
    
    
    1、库函数引入
   
     
   
    
    
    2、选择开发板
   
     
   
    
    
    3、代码
   
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); //0x27
#define TrigPin 2
#define EchoPin 3
float distance;
int alarmvalue = 6; //设置初始警报值为15cm
#define BUF_LEN 10
char Dis_Buf[BUF_LEN];
//LED灯
#define rled  4 //红灯io
#define yled  5 //黄灯io
#define gled  6 //绿灯io
//语音模块
#define SDAPin 7 //语音模块onewrite io
#define RLED_Read digitalRead(rled)
#define YLED_Read digitalRead(yled)
#define GLED_Read digitalRead(gled)
//按键变量
int adc_key_val[5] = {30, 80, 165,  350, 500 };
int NUM_KEYS = 5;
int adc_key_in;
int key = -1;
int oldkey = -1;
boolean Page;
#define Home    true
#define Setting false
boolean Dis_Sta;
#define Safe    true
#define Alarm   false
#define S1 0
#define S2 1
#define S3 2
#define S4 3
#define S5 4
unsigned long Timing = 0; //用于定时读取
void setup()
{ // 初始化串口通信及连接SR04的引脚
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(TrigPin, OUTPUT);
  // 要检测引脚上输入的脉冲宽度,需要先设置为输入状态
  pinMode(EchoPin, INPUT);
  pinMode(SDAPin, OUTPUT);
  pinMode(rled, OUTPUT);
  pinMode(yled, OUTPUT);
  pinMode(gled, OUTPUT);
  digitalWrite(rled, LOW);
  digitalWrite(yled, LOW);
  digitalWrite(gled, LOW);
  Dis_Sta = Safe;
  Page = Home;
  Home_Page();
}
void loop()
{
  switch (Page) {
    case Home:
      if (millis() - Timing >= 300) {
        Timing = millis();
        Get_Distance();     //测距
        if (distance < alarmvalue) {  //小于安全距离
          if (Dis_Sta == Safe) {      //上一次为安全距离,这次为过近距离
            Dis_Sta = Alarm;
            //警告界面,语音播报
            lcd.clear();
            Alarm_Page();
            //语音播报
            say_control();
            LED_ON();
          } else {                    //上一次依然是过近距离
            LED_REVERSE();            //反转LED状态,达到闪烁的效果
          }
        } else {                      //安全距离
          if (Dis_Sta == Alarm) {
            LED_OFF();
            Dis_Sta = Safe;
            lcd.clear();
            Home_Page();
          }
          uint8_t integer = distance * 100 / 100;
          uint8_t decimal = int(distance * 100) % 100;
          snprintf(Dis_Buf, BUF_LEN, "%02d.%02d", integer, decimal);
          lcd.setCursor(4, 1);
          lcd.print(Dis_Buf);
          Serial.print("Dis_Buf: ");
          Serial.println(Dis_Buf);
        }
      }
      Home_Ctrl();    //Home_Page下的按键控制
      break;
    case Setting:
      Setting_Ctrl(); //Setting_Page下的按键控制
      break;
  }
}
/*
    0123456789012345
   "    Distance:   "
   "    xx.xx cm    "
*/
void Home_Page() {
  lcd.setCursor(4, 0);
  lcd.print("Distance:");
  lcd.setCursor(10, 1);
  lcd.print("cm");
}
void Home_Ctrl() {
  adc_key_in = analogRead(0);   // 读取模拟A0数值
  key = get_key(adc_key_in);    // 转化成按键
  if (key != oldkey) {
    delay(50);                  //等待反弹时间
    adc_key_in = analogRead(0); // 读取模拟口A0数值
    key = get_key(adc_key_in);  // 转换成按键
    if (key != oldkey) {
      oldkey = key;
      if (key > 0) {
        if (key = S5) {         //5#按键进入设置界面
          Serial.println("S5 OK");
          Page = Setting;
          lcd.clear();
          Setting_Page();
        }
      }
    }
  }
}
/*
    0123456789012345
   "The setting page"
   "  alarm: xx cm  "
*/
void Setting_Page() {
  lcd.setCursor(0, 0);
  lcd.print("The setting page");//
  lcd.setCursor(2, 1);
  lcd.print("alarm:");
  LCD_Setting_Num();
  lcd.setCursor(12, 1);
  lcd.print("cm");
}
void Setting_Ctrl() {
  adc_key_in = analogRead(0);   //读取模拟A0数值
  key = get_key(adc_key_in);    //转化成按键
  if (key != oldkey) {
    delay(50);                  //等待反弹时间
    adc_key_in = analogRead(0); //读取模拟口A0数值
    key = get_key(adc_key_in);  //转换成按键
    if (key != oldkey) {
      oldkey = key;
      if (key > 0) {
        switch (key) {
          case S1:
            alarmvalue += 1;
            LCD_Setting_Num();
            break;
          case S2:
            alarmvalue += 1;
            LCD_Setting_Num();
            break;
          case S3:
            alarmvalue -= 1;
            LCD_Setting_Num();
            break;
          case S4:
            alarmvalue -= 1;
            LCD_Setting_Num();
            break;
          case S5:
            Page = Home;
            lcd.clear();
            Home_Page();
            break;
        }
      }
    }
  }
}
void LCD_Setting_Num() {
  snprintf(Dis_Buf, BUF_LEN, "%02d", alarmvalue);
  lcd.setCursor(9, 1);
  lcd.print(Dis_Buf);
}
/*
    0123456789012345
   "The distance is "
   "   too short    "
*/
void Alarm_Page() {
  lcd.setCursor(0, 0);
  lcd.print("The distance is");
  lcd.setCursor(3, 1);
  lcd.print("too short");
}
void Get_Distance() {
  // 产生一个10us的高脉冲去触发TrigPin
  digitalWrite(TrigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(TrigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(TrigPin, LOW);
  // 检测脉冲宽度,并计算出距离
  distance = pulseIn(EchoPin, HIGH) / 58.00;
  Serial.print(distance);
  Serial.println("cm");
}
// 将ADC值转换为键数
int get_key(unsigned int input) {
  int k;
  for (k = 0; k < NUM_KEYS; k++) {
    if (input < adc_key_val[k]) {
      return k;
    }
  }
  if (k >= NUM_KEYS)k = -1;  // 没有按下有效的键
  return k;
}
//发送语音模块串口指令函数
void S(bool s) {
  digitalWrite(SDAPin, s);
}
void sendData(unsigned char addr) {
  S(1);
  delayMicroseconds(1000);
  S(0);
  delayMicroseconds(3200);
  for (int i = 0; i < 8; i++) {
    S(1);
    if (addr & 0x01) {
      delayMicroseconds(600);
      S(0);
      delayMicroseconds(200);
    } else {
      delayMicroseconds(200);
      S(0);
      delayMicroseconds(600);
    }
    addr >>= 1;
  }
  S(1);
}
//语音指令
void say_control() {
  sendData(0x11);//发送语音指令
  delay(2200);//语音时长
}
void LED_ON() {
  digitalWrite(rled, HIGH);
  digitalWrite(yled, HIGH);
  digitalWrite(gled, HIGH);
}
void LED_OFF() {
  digitalWrite(rled, LOW);
  digitalWrite(yled, LOW);
  digitalWrite(gled, LOW);
}
void LED_REVERSE() {
  digitalWrite(rled, !RLED_Read);
  digitalWrite(yled, !YLED_Read);
  digitalWrite(gled, !GLED_Read);
}
    
    
    4、演示视频
   
    
    
    三、使用说明
   
    
    
    1、项目方案
   
    功能1:(坐姿提醒)
    
    使用超声波模块检测距离,当坐姿不端时,测距小于标准距离,语音提示,有灯光提示,屏幕文字显示提醒
    
    功能2:(调节报警距离)
    
    屏幕实时显示当前的测距,按键可以调节报警距离
   
    功能3:
    
    自定义语音提示
   
    
    
    2、页面切换
   
    各页面切换实现方法:
    
    主界面:
    
    显示距离
    
    距离过小进入报警界面
    
    S5进入设置界面
   
    设置界面:
    
    按键控制±,
    
    S5返回主界面
   
    报警界面:
    
    怎么返回
    
    测距大于安全距离则返回
   
    小于安全距离且原本为安全时
    
    警告界面
    
    小于安全距离且原本时警告界面时
    
    不作反应
    
    大于安全距离且原本为安全时
    
    不做反应
    
    大于安全距离且原本为警告界面时
    
    返回主页
   
    
    
    页面设计
   
功能介绍:
- 
LCD共有三个界面 
 
  主界面(Home_Page)
 
  警报界面(Alarm_Page)
 
  设置界面(Setting_Page)
- 
主界面 
 
  在主界面之下,时刻显示着当前超声波传感器的读值
 
  Distance:
 
  xx.xx cm
- 
“设置界面” 
 
  在设置界面时刻显示着当前的警报设置距离
 
  The setting page
 
  alarm:xx cm
- 
“警报界面” 
 
  The distance is
 
  Too short
    按键使用:
    
     S1 :可进入设置状态
    
     S2 :可增加设置距离
    
     S3 :可减少设置距离
    
     S4 :可减少设置距离
    
     S5 :可自由切换两个页面
   
 

