实训6——语音播报

实验九:语音播报

一、实验目的

ESP32通过一线串口通讯,通过DATA线给WTN6系列语音芯片,发送相应的数据,播放第几段的语音。结合相应的锁操作,从而播放相应的语音内容,例如,蓝牙开门操作后,播放已开门。

二、实验内容

1.ESP连接语音模块
2.将程序上传到ESP32
3.语音模块接上喇叭,实施相应操作后,播放相应的语音

三、实验设备

WTN6系列语音模块
ESP32开发板
8R/0.5W喇叭

四、实验步骤

1)连接引脚
语音模块:

语音模块的引脚:PA1接ESP32的32脚,PWM+和PWM-接喇叭
语音模块

连接图:

语音模块连接图

2)上传程序
/*Author :王滨伟
 * Time :2020.6.11
*/
#include <dummy.h>
#include <Servo.h>
#include <BLEDevice.h>
#include <BLE2902.h>
#include <String.h>
#include <Keypad.h>
#include <SPI.h>
#include <MFRC522.h> 
#include <Adafruit_Fingerprint.h>
#include <WiFi.h>
/*
 * ESP32 引脚17(TX)和16(RX)上的Serial2
 * 指纹模块   连 接    ESP32
   RX黄      ---      17  
   TX绿      ---      16 
 */
#define mySerial Serial2
#define Horn_Pin 32 //定义语音模块脚位
#define SS_PIN 21  //定义RC522的SDA引脚的接线位置
#define RST_PIN 22  //定义RC522的RST引脚的接线位置
#define SERVO_PIN 15 //舵机的脚位
#define NormolClose 13
#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"   
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define openInterval  3000
#define fpInterval 5000
BLECharacteristic *pCharacteristic;
bool deviceConnected = false; 
char* SSID = "TP-LINK_9C1F";        //wifi名称
char* PassWord = "cyykj2018";      //wifi密码
/*
 * 按键初始量
*/
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {33, 27, 14, 12}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {4, 0, 2}; //connect to the column pinouts of the keypad
/*
 * RFID标签结构体
 */
struct RFIDTag {  //Tag标签结构体
  uint8_t uid[4];
  char *name;
};
MFRC522 rfid(SS_PIN, RST_PIN); //实例化类 
struct RFIDTag tags[20] = {  // 初始化结构资料,请自行修改RFID识别码
  {{219, 25, 26, 28}, "Mini_Tag"},
  {{0, 0, 0, 0}, "Mini_Tag1"}, 
  {{1, 1, 1, 1}, "Mini_Tag2"}, 
};
/*wifi IP和端口配置*/
const IPAddress serverIP(192,168,0,113); //欲访问的地址
uint16_t serverPort = 53123; //服务器端口号,修改自己服务器的端口号
WiFiClient client; //声明一个客户端对象,用于与服务器进行连接
byte totalTags = sizeof(tags) / sizeof(RFIDTag);  //计算结构资料的数量
bool door_open_flag; //开门动作标志位
bool alarm_flag;//报警标志位
static bool lock_back_flag=false; //回锁标志位
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
byte ble_rx_buffer[20]; //接收蓝牙指令
byte verfi_code[6] = {0x31,0x32,0x33,0x34,0x35,0x36};//初始密码
byte init_pw[6] = {1,2,3,4,5,6};//初始按键密码
Servo myservo;
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
bool FP_busy=false;
short active_fp = -1; //激活指纹模块, -1休眠,1激活,2指纹模块正在激活时间
int pos=0;
bool toggle; //开锁判断位
byte message_type;//上传信息的类型
void locker(bool toggle) { //开锁
  myservo.attach(SERVO_PIN); 
  if (toggle) {
    myservo.write(90); // 开锁
  } else {
    myservo.write(0); // 关锁
  }
  delay(500); // 等马达转到定位
  myservo.detach();
}
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };
    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};
/* C++多态,创建类MyCallbacks,继承类BLECharacteristicCallbacks
 * 类MyCallbacks,下的成员函数onWrite(),接收蓝牙消息处理函数
 * 55aa1000313233343536 蓝牙发送开门指令,hex 16进制发送
 * 55aa2000363534333231 蓝牙修改密码指令
 * 55aa3000000000000000 蓝牙查询指令
 * 55aa3100310000000000 蓝牙注册指纹 注册id号为01
 * 55aa3200330000000000 蓝牙删除指纹 删除id号为03
*/
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string rxValue = pCharacteristic->getValue(); 
      byte op;
      for (int i = 0; i < rxValue.length(); i++){
          ble_rx_buffer[i] = rxValue[i];
          Serial.print(rxValue[i]);
       }
       if(ble_rx_buffer[0] == 0x55 && ble_rx_buffer[1] == 0xaa){
            op=ble_rx_buffer[2];
       switch(op){
        case 0x10:{ //蓝牙开门
          if(memcmp(ble_rx_buffer+4,verfi_code,6) == 0 && lock_back_flag==false){ //匹配成功,切不在回锁状态
             toggle = true;  
             message_type = 6;
             Serial.println("开门成功");     
          }
          else{
            Serial.println("密码错误");
          }
          break;
        }
        case 0x20:{ //设置密码
          static byte temp_pw[6]; //临时密码
          for(byte i=0;i<6;i++){ 
            if(ble_rx_buffer[i+4]<=0x39 && ble_rx_buffer[i+4]>=0x30){ //保证密码在0-9之间 
                 temp_pw[i]=ble_rx_buffer[i+4]-0x30; 
            }
            else{ //无效字符
               break;
            }
            if(i==5){ //密码修改完成
              Serial.println("密码修改完成");
              memcpy(init_pw,temp_pw,6); 
            }
          }
          break;
        }
        case 0x30:{ //查看指纹
          FP_busy = true;
          finger.getTemplateCount(); //查当前指纹模板的数量
          Serial.print("Sensor contains ");
          Serial.print(finger.templateCount);
          Serial.println(" templates");
          FP_busy = false;
          break;
        }
        case 0x31:{//添加指纹
          byte id;
          FP_busy = true;
          id = ble_rx_buffer[4] - 0x30; //注册指纹id
          getFingerprintEnroll(id);
          FP_busy = false; 
          break;
        }
        case 0x32:{ //删除指纹
          byte id;
          FP_busy = true;
          id = ble_rx_buffer[4] - 0x30; //删除指纹id
          Serial.print("Deleting ID #");
          Serial.println(id);
          deleteFingerprint(id);
          FP_busy = false;
          break;
        }
       }
     }
    }
};
/*配置蓝牙
 * 参数:BLEName 蓝牙名字
 * 返回值: 无
*/
void setupBLE(String BLEName){           
  const char *ble_name = BLEName.c_str();       
  BLEDevice::init(ble_name);                    
  BLEServer *pServer = BLEDevice::createServer(); //创建服务
  pServer->setCallbacks(new MyServerCallbacks());
  BLEService *pService = pServer->createService(SERVICE_UUID);    
  pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX,BLECharacteristic::PROPERTY_NOTIFY);  
  pCharacteristic->addDescriptor(new BLE2902());  //添加描述
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX,BLECharacteristic::PROPERTY_WRITE);
  pCharacteristic->setCallbacks(new MyCallbacks());  //receive message callback  
  pService->start();// Start the service
  pServer->getAdvertising()->start();// Start advertising
  Serial.println("Waiting a client connection to notify...");
}
/*配置Wifi函数
 * 输入参数: ssid: wifi名字,password: wifi密码
 * 返回值:无
*/
void configure_Wifi(char* ssid, char* password){
  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false); //关闭STA模式下wifi休眠,提高响应速度
  WiFi.begin(ssid, password);
  if(WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.println("Connecting to WiFi fail..");
  }
  else{
    Serial.println("Connected to the WiFi network");
    Serial.print("IP Address:");
    Serial.println(WiFi.localIP());  
  }
}
/*TCP上传消息函数
 * 输入值:type=value
 * value=1时,表示撬门        
 * value=2时, 表示门未关好
 * value=3时,表示RFID开门
 * value=4时,表示指纹开门
 * value=5时,表示按键开门
 * value=6时,表示蓝牙开门
 * 返回值:true时,表示TCP连接成功
 * false时,表示TCP连接失败
*/
bool SendOnCloud(byte type){
  char value = type+'0';
  if(type>0 && type<=6){ //在消息协议范围内
    if (client.connect(serverIP, serverPort)){ //尝试访问目标地址
      Serial.print("访问成功:");
      Serial.println(value);
      client.println(value);//向服务器发送数据
      message_type =0;
      return 1;
    }
    else{
      Serial.println("访问失败");
      client.stop(); //关闭客户端
      message_type =0;
      return 0;
    }
  }
  else{ //无效协议
    //Serial.println("Invalid Message");
    return 0;
  }
  
}
/*按键输入的匹配函数
 * 参数:key为一次按键输入的值
*/    
bool compare_pw(char key){
  static byte pw[20];
  static byte keys_len = 0;
  bool pw_flag = false; //匹配密码的标志位
  if(key){
    //FP_busy = false;//唤醒指纹模块,可以开始匹配
    Serial.println(key);
    play_voice(4);
    active_fp = 1;
    //Serial.println(active_fp);
    switch(key){
      case '#':{ //确认键
        if(keys_len == 6 && memcmp(pw,init_pw,6)==0){  //匹配成功,开锁
            Serial.println("开门成功");
            pw_flag = true; 
            message_type = 5;
        }
        else{
          Serial.println("密码错误");
        }
        keys_len=0; //清零
        break;
      }
      case '*':{ //回退键
        if(keys_len >0)
          keys_len--;
        break;
      }
      default:{ //默认数字键0-9
        pw[keys_len++]=key-'0';
        if(keys_len>19){
          Serial.println("OverSize");
          keys_len=0;
        }
        break;
      }
    }
  }
  return pw_flag;
}
void printDec(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++){
    //Serial.print(buffer[i] < 0x10 ? " 0" : "");
    Serial.print(buffer[i]);
    Serial.print(" ");
  }
}
/*
 * RFID匹配函数
 * 参数:card_num为卡片资料的总数
*/
bool Match_Card(byte card_num){ 
  bool foundTag = false;    //是否找到记录中的标签,初始是false
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { //寻找新卡
    byte *id = rfid.uid.uidByte;   // 取得卡片的UID
    byte idSize = rfid.uid.size;   // 取得UID的长度
    Serial.print("十进制UID:");
    printDec(rfid.uid.uidByte, rfid.uid.size);
    Serial.println();
    for (byte i = 0; i < card_num; i++) {
    if (memcmp(tags[i].uid, id, idSize) == 0) {  // 比对阵列资料值
          Serial.println(tags[i].name);  //显示标签名字
          foundTag = true;
          message_type=3;
          break;
      }
     if(i==card_num-1){
        Serial.println("验证失败");  
     }
   }
    // 使放置在读卡区的IC卡进入休眠状态,不再重复读卡
    rfid.PICC_HaltA();
    // 停止读卡模块编码
    rfid.PCD_StopCrypto1(); 
  }
  return foundTag;
}
/*门磁中断函数
 * 参数 open_flag:正常开门的标志位
*/
void handleInterrupt() {
  delay(100);
  if (digitalRead(NormolClose)){// 电平高,门开
      detachInterrupt(digitalPinToInterrupt(NormolClose)); //屏蔽中断脚位
      door_open_flag = 1;  
      if(!lock_back_flag){//门没在开锁时间,门磁变化,算撬门报警
        alarm_flag = 1;  
      }    
    Serial.println("door open");
  }
}

/*指纹模块配置函数
 * configure_FP()
*/
void configure_FP(){
    finger.begin(57600);
    if (finger.verifyPassword()) {
        Serial.println("Found fingerprint sensor!");
        finger.getTemplateCount();
        Serial.print("Sensor contains ");
        Serial.print(finger.templateCount);
        Serial.println(" templates");
        Serial.println("Waiting for valid finger...");
    }
    else {
      delay(1000);
      Serial.println("Did not find fingerprint sensor :");
    }
}

/* 匹配指纹函数
 *  传入:searchFinger,表示匹配指纹与否
 * returns -1 if failed, otherwise returns ID
*/
int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;
  // Found a match!
  //searchFinger = 1;
  //idSearch = finger.fingerID;
  Serial.print("Found ID #"); Serial.print(finger.fingerID); 
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  message_type = 4;
  return finger.fingerID; 
}
/*指纹注册函数
 * 输入参数: id为注册指纹的id
 * 返回值: uint8_t p, p=0时录入成功
*/
uint8_t getFingerprintEnroll(byte id) {
  int p = -1;
  Serial.print("Waiting for valid finger to enroll as ");
  Serial.println(id);
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }
//第一次采集结束
//槽位置放置要素模板
  p = finger.image2Tz(1);
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }
  //OK Success
  
  Serial.println("Remove finger");
  delay(2000);
  p = 0;
  
  while (p != FINGERPRINT_NOFINGER) {
    p = finger.getImage();                              //请求传感器采集指纹
  }
  Serial.print("ID "); 
  Serial.println(id);
  
  p = -1;
  Serial.println("Place same finger again");
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.print(".");
      break;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      break;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      break;
    default:
      Serial.println("Unknown error");
      break;
    }
  }
// OK success!

  p = finger.image2Tz(2);
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }
  
  // OK converted!
  Serial.print("Creating model for =");
  Serial.println(id);
  
  p = finger.createModel();   //要求传感器采用两个打印特征模板并创建模型
  if (p == FINGERPRINT_OK) {
    Serial.println("Print matched");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_ENROLLMISMATCH) {
    Serial.println("Fingerprints did not match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }   
  
  Serial.print("ID: "); 
  Serial.println(id);
  p = finger.storeModel(id);  //要求传感器存储计算模型以进行后续匹配
  if (p == FINGERPRINT_OK) {
    Serial.println("Stored!");
    return 1;
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_BADLOCATION) {
    Serial.println("Could not store in that location");
    return p;
  } else if (p == FINGERPRINT_FLASHERR) {
    Serial.println("Error writing to flash");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }  
}
/*删除指纹函数
 * 输入参数:id为指纹id
 * 返回值:p,p=0时,表示指纹模块删除成功
*/
uint8_t deleteFingerprint(uint8_t id) {
  uint8_t p = -1;
  p = finger.deleteModel(id);             //删除指纹成功返回0x00
  if (p == FINGERPRINT_OK) {
    Serial.println("Deleted!");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_BADLOCATION) {
    Serial.println("Could not delete in that location");
    return p;
  } else if (p == FINGERPRINT_FLASHERR) {
    Serial.println("Error writing to flash");
    return p;
  } else {
    Serial.print("Unknown error: 0x"); 
    Serial.println(p, HEX);
    return p;
  }   
}
/* send_pulse
 * 输入参数:ddata播放相应的地址
 * 返回值:无
*/
void  send_pulse(byte ddata){ 
  byte s_data,j;
  byte b_data;
  s_data = ddata;
  digitalWrite(Horn_Pin,LOW);
  delay(5); //延时5ms
  b_data = s_data&0x01;
  
  for(j=0;j<8;j++){
    if(b_data == 1){
      digitalWrite(Horn_Pin,HIGH);
      delayMicroseconds(600); //延时600us
      digitalWrite(Horn_Pin,LOW);
      delayMicroseconds(200); //延时200us
    }
    else{
      digitalWrite(Horn_Pin,HIGH);
      delayMicroseconds(200); //延时600us
      digitalWrite(Horn_Pin,LOW);
      delayMicroseconds(600); //延时200us
    }
    s_data = s_data>>1;
    b_data = s_data&0x01;
  }
   digitalWrite(Horn_Pin,HIGH);
}
/* 函数play_voice
 * 输入参数:byte m,表示播放第几段语音
 * 返回值:无
*/
void play_voice(byte m){ //播放第m段语音
  send_pulse(0xF3);
  delay(2);
  send_pulse(m);
}
void setup() { 
  Serial.begin(115200);
  setupBLE("ESP32 Device");//设置蓝牙名称
  pinMode(Horn_Pin,OUTPUT); //配置语音模块的脚位
  digitalWrite(Horn_Pin,HIGH);
  myservo.attach(SERVO_PIN); //引脚12为PWM脚  
  SPI.begin(); // 初始化SPI总线
  rfid.PCD_Init(); // 初始化 MFRC522
  configure_FP();//配置指纹模块
  configure_Wifi(SSID, PassWord);//配置WiFi模块
  pinMode(NormolClose, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(NormolClose), handleInterrupt, RISING); 
}
void loop() {
  static unsigned long toggle_time = millis();
  static unsigned long fp_time = millis();
  static bool door_forget_alarm; //门未关好标志位
  static bool door_illegal_alarm;//撬门报警
  short idSearch = -1;       //匹配的指纹id
  if (!digitalRead(NormolClose)){ //电平低,门关
    attachInterrupt(digitalPinToInterrupt(NormolClose), handleInterrupt, RISING); // 设置中断脚位,检测下降沿电压
    door_open_flag = 0; 
  }
  if(!FP_busy && active_fp==2) //指纹模块空闲时进行指纹匹配 && 指纹模块正在激活期间
    idSearch=getFingerprintIDez(); //指纹匹配
  if(compare_pw(keypad.getKey()) || Match_Card(totalTags) || idSearch!=-1 && lock_back_flag==false){ //密码正确 or 刷卡成功 or 指纹开门,且不再回锁阶段
    toggle = true; //舵机启动
    alarm_flag = false; //解除报警
  }
  if(toggle){ //开门
    play_voice(1);//播放语音已开门
    door_forget_alarm=0; //门未关好标志位
    door_illegal_alarm=0;//门撬门标志位
    locker(toggle); //开锁
    toggle = false;
    lock_back_flag = true;
    toggle_time = millis();
  }
  if(millis() - toggle_time > openInterval && lock_back_flag){  //回锁时间5s
    locker(toggle);//回锁
    lock_back_flag = false;
  }
  
  if(active_fp == 1){ //激活指纹模块
    fp_time = millis();
    active_fp=2;
  }
  if(active_fp==2 && millis() - fp_time > fpInterval){ //指纹激活时间5s
    active_fp = -1; //休眠
  }
  //报警信息:1、门未关好 2、有人撬门
  if(!lock_back_flag && door_open_flag){//关锁 and 门的状态是开
    if(alarm_flag){ //非法撬门,一直滴
      if(door_illegal_alarm==0){ //消息报警传送一次
        play_voice(3); //播放语音有人撬门
        Serial.println("非法撬门");
        message_type = 1;
        door_illegal_alarm=1; 
      }
    }
    else{ //门未关好,滴一声
      if(door_forget_alarm==0){
        Serial.println("门未关好");
        play_voice(2);//播放语音门未关好
        door_forget_alarm=1;
        message_type = 2; 
      }
    }
  }
  SendOnCloud(message_type);//上传相应的wifi消息 
}

3)详细说明

该语音模块需要连码发送才能播放一段完整的指令(例如:F3H+00H+F3H+01H)。F3H+01H,表示播放第一段语音内容。上述代码通过play_voice(byte m)函数来播放第m段语音,函数send_pulse(byte ddata)是用来发送相应的地址的脉冲。
具体的语音内容:
第一段内容:已开门
第二段内容:门未关好
第三段内容:有人撬门
第四 段内容:滴

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值