项目简介
采用北斗授时模块、国产开源语音识别模块,为学生提供欺凌预警功能。在校园偏僻角落,提供LED屏字幕提醒,可语音识别“老师救命”呼救,并从北斗授时模块获取精准时间,通过物联网发送至值班老师手机上,老师及时赶到事发地点,制止欺凌事件并处理。
目的与问题
解决校园偏僻角落如厕所无法安装监控的地点,提供预防欺凌警示,语音识别呼救报警的问题,并通过物联网与手机APP关联,及时通知老师到现场处理欺凌事件。
项目背景:校园欺凌是指发生在学生之间蓄意或恶意通过肢体、语言及网络等手段,实施欺负、侮辱造成伤害的行为。不同于校园暴力以伤人和毁物为目的的打砸抢,校园欺凌更多是欺负和侮辱。根据外部行为表现可以分为身体欺凌、言语欺凌、关系欺凌、网络欺凌等形式。身体欺凌是指通过身体或工具对受欺凌者实施的欺凌行为,如殴打、强迫、勒索等。言语欺凌是指通过语言对受欺凌者实施的欺凌行为,如辱骂、起外号、恐吓等,言语欺凌在所有欺凌类型中发生率最高。校园欺凌不仅对受欺凌者的身体产生伤害,还会容易形成心理障碍。受到校园欺凌的学生很容易产生焦虑、紧张、恐惧,甚至抑郁的心理,从而出现厌学和逃学的情况。当受欺凌者长期将情绪隐藏,不敢宣之于口时,可能会产生自伤、自残甚至自杀等行为。被欺凌的女生更多地表现出自我攻击性,而男生更多表现出攻击他人性。对于欺凌者而言,实施欺凌会进一步强化欺凌的想法,增加个体的暴力行为。在少年时,如果不对欺凌者的行为加以规范,成年后容易失控,甚至引发暴力犯罪。而那些校园欺凌的旁观者,在目睹了校园欺凌后或置身事外、满不在乎,或参与其中、学习效仿,或自我保护、自责矛盾,内心变得极度不平衡。阻止校园欺凌,义不容辞且刻不容缓。作为学生首先不能充当欺凌者,更不能在被欺凌时助长欺凌者的气焰。面对校园欺凌时,尽量冷静下来,保持镇定,不要激怒欺凌者。学会迂回,当下向一切能提供帮助的人求助,并第一时间寻求父母和老师的帮助,勇敢地反映。如果发现身边的同学被欺凌,不做冷漠的旁观者。
基于以上背景,我们设计了一个为“欺凌预警北斗授时牌”。
项目设计:
本“欺凌预警北斗授时牌”,采用北斗授时模块(北斗授时&定位模块集成了卫星定位功能以及RTC实时时钟,该模块会按照预设的校时周期自动获取卫星时间,并对板载RTC芯片进行精准校时,可有效消除RTC芯片因长时间工作或意外情况而产生的时间误差。)1.8位数码管显示模块实时显示精准时间,日常做为时间显示,当欺凌发生时,可供学生记忆欺凌发生的准确时间,并在呼救后,欺凌发生时间会通过物联网发送到值班教师手机APP上。2.使用两块32*16显示屏,前后各一块,滚动显示“教师!救命”提醒呼救词,当有人呼救后,显示屏全屏“红色”闪烁,起警示作用。3.使用“语音识别模块”,录制自定义唤醒词“老师救命”,当有学生呼喊“老师救命”时,语音识别并做出反应。4.使用物联网模块,连接校园内无线WIFI,语音识别模块识别到“老师救命”唤醒词后,通过物联网模块向物联网Easy IOT发送时间信息。4.使用Mit Appinventor2自制手机app,app连接物联网,当接收到信息后,屏幕显示欺凌发生地点、发生时间,并播放警报音加震动提醒值班老师接收信息并及时到现场处理。
项目硬件:
北斗授时模块,Arduino及扩展板2个,8位数码管显示模块,32x16RGB点阵屏2个,物联网模块,语音合成模块,3.7V18650锂电池2节、充电宝。
项目成品:
增加了太阳能板,为LED灯供电,双显示屏前后显示提示信息。
2. 效果图1
3. 效果图2
语音、联网、时间显示屏程序
#include "DFRobot_GNSSAndRTC.h"
#include "DFRobot_LedDisplayModule.h"
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobot_DF2301Q.h"
//DFRobot_DF2301Q_I2C asr;
DFRobot_DF2301Q_I2C asr;
static unsigned long currentTime = 0;
DFRobot_LedDisplayModule LED(&Wire, 0xE0);
#define I2C_COMMUNICATION //use I2C for communication, but use the serial port for communication if the line of codes were masked
#ifdef I2C_COMMUNICATION
DFRobot_GNSSAndRTC_I2C rtc(&Wire, MODULE_I2C_ADDRESS);
#endif
#define WIFISSID "sxs"
#define WIFIPWD "smj080823"
#define SERVER "iot.dfrobot.com.cn"
#define PORT 1883
#define IOTID "rGltuGbIR"
#define IOTPWD "9GlpXGbIRz"
const String separator = "|";
bool pingOn = true;
bool obloqConnectMqtt = false;
static unsigned long pingInterval = 2000;
static unsigned long sendMessageInterval = 10000;
unsigned long previousPingTime = 0;
unsigned long previousSendMessageTime = 0;
String receiveStringIndex[10] = {};
bool subscribeMqttTopic = false;
bool subscribeSuccess = false;
//wifi异常断开检测变量
bool wifiConnect = false;
bool wifiAbnormalDisconnect = false;
//mqtt因为网络断开后重新连接标志
bool mqttReconnectFlag = false;
SoftwareSerial softSerial(10,11);
enum state{
WAIT,
PINGOK,
WIFIOK,
MQTTCONNECTOK
}obloqState;
/********************************************************************************************
Function : sendMessage
Description : 通过串口向OBLOQ发送一次消息
Params : 无
Return : 无
********************************************************************************************/
void sendMessage(String message)
{
softSerial.print(message+"\r");
}
/********************************************************************************************
Function : ping
Description : 通过串口向OBLOQ发送一次字符串"|1|1|",尝试与OBLOQ取得连接
Params : 无
Return : 无
********************************************************************************************/
void ping()
{
String pingMessage = "|1|1|";
sendMessage(pingMessage);
}
/********************************************************************************************
Function : connectWifi
Description : 连接wifi
Params : ssid 连接wifi的ssid;pwd 连接wifi的password
Return : 无
********************************************************************************************/
void connectWifi(String ssid,String pwd)
{
String wifiMessage = "|2|1|" + ssid + "," + pwd + separator;
sendMessage(wifiMessage);
}
/********************************************************************************************
Function : connectMqtt
Description : 连接DF-IoT
Params : server 物联网网址;port 端口;iotid 物联网登录后分配的iotid;iotpwd 物联网登录后分配的iotpwd
Return : 无
********************************************************************************************/
void connectMqtt(String server, String port, String iotid, String iotpwd)
{
String mqttConnectMessage = "|4|1|1|" + server + separator + port + separator + iotid + separator + iotpwd + separator;
sendMessage(mqttConnectMessage);
}
/********************************************************************************************
Function : reconnectMqtt
Description : 重新连接DF-IoT
Params : 无
Return : 无
********************************************************************************************/
void reconnectMqtt()
{
String mqttReconnectMessage = "|4|1|5|";
sendMessage(mqttReconnectMessage);
}
/********************************************************************************************
Function : publish
Description : 向DF-IoT物联网设备发送信息
Params : topic DF-IoT物联网设备编号;message 发送的消息内容
Return : 无
********************************************************************************************/
void publish(String topic,String message)
{
String publishMessage = "|4|1|3|" + topic + separator + message + separator;
sendMessage(publishMessage);
}
/********************************************************************************************
Function : handleUart
Description : 处理串口传回的数据
Params : 无
Return : 无
********************************************************************************************/
void handleUart()
{
while(softSerial.available() > 0)
{
String receivedata = softSerial.readStringUntil('\r');
const char* obloqMessage = receivedata.c_str();
// Serial.print("receivedata = ");
// Serial.println(receivedata);
if (strcmp(obloqMessage, "|1|1|") == 0)
{
Serial.println("Pong");
pingOn = false;
obloqState = PINGOK;
}
if(strcmp(obloqMessage, "|2|1|") == 0)
{
if(wifiConnect)
{
wifiConnect = false;
wifiAbnormalDisconnect = true;
}
}
else if (strstr(obloqMessage,"|2|3|") != NULL && strlen(obloqMessage) != 9)
{
Serial.println("Wifi ready");
wifiConnect = true;
if(wifiAbnormalDisconnect)
{
wifiAbnormalDisconnect = false;
return;
}
obloqState = WIFIOK;
}
else if (strcmp(obloqMessage, "|4|1|1|1|") == 0)
{
Serial.println("Mqtt ready");
obloqConnectMqtt = true;
if(mqttReconnectFlag)
{
mqttReconnectFlag = false;
return;
}
obloqState = MQTTCONNECTOK;
}
else if (strcmp(obloqMessage, "|4|1|2|1|") == 0)
{
Serial.println("subscribe successed");
//subscribeMqttTopic = false;
}
//DF-IoT接收到消息,topic和message传入receiveMessageCallbak函数
else if (strstr(obloqMessage, "|4|1|5|") != NULL)
{
splitString(receiveStringIndex,receivedata,"|");
receiveMessageCallbak(receiveStringIndex[3],receiveStringIndex[4]);
}
}
}
/********************************************************************************************
Function : subscribeSingleTopic
Description : 监听单个DF-IoT物联网设备
Params : topic DF-IoT物联网设备编号
Return : 无
********************************************************************************************/
void subscribeSingleTopic(String topic)
{
if(!subscribeMqttTopic && obloqConnectMqtt)
{
subscribeMqttTopic = true;
subscribe(topic);
}
}
/********************************************************************************************
Function : receiveMessageCallbak
Description : 接收消息的回调函数
Params : topic 发出消息的DF-IoT物联网设备编号;message 收到的消息内容
Return : 无
********************************************************************************************/
void receiveMessageCallbak(String topic,String message)
{
//Serial.println("Message from: " + topic);
//Serial.println("Message content: " + message);
if(message=="R")
{
Serial.print("1");
}
if(message=="C")
{
Serial.print("2");
}
}
/********************************************************************************************
Function : splitString
Description : 剔除分隔符,逐一提取字符串
Params : data[] 提取的字符串的目标储存地址;str 源字符串;delimiters 分隔符
Return : 共提取的字符串的个数
********************************************************************************************/
int splitString(String data[],String str,const char* delimiters)
{
char *s = (char *)(str.c_str());
int count = 0;
data[count] = strtok(s, delimiters);
while(data[count]){
data[++count] = strtok(NULL, delimiters);
}
return count;
}
/********************************************************************************************
Function : sendPing
Description : 每隔pingInterval一段时间,通过串口向OBLOQ ping一次
Params : 无
Return : 无
********************************************************************************************/
void sendPing()
{
if(pingOn && millis() - previousPingTime > pingInterval)
{
previousPingTime = millis();
ping();
}
}
/********************************************************************************************
Function : execute
Description : 根据OBLOQ的状态,进行下一步相应操作。
Params : 无
Return : 无
********************************************************************************************/
void execute()
{
switch(obloqState)
{
case PINGOK: connectWifi(WIFISSID,WIFIPWD); obloqState = WAIT; break;
case WIFIOK: connectMqtt(SERVER,String(PORT),IOTID,IOTPWD);obloqState = WAIT; break;
case MQTTCONNECTOK : obloqState = WAIT; break;
default: break;
}
}
/********************************************************************************************
Function : checkWifiState
Description : 检查wifi状态,如果检测到wifi热点断开会间隔1分钟去重新连接wifi
Params : 无
Return : 无
********************************************************************************************/
void checkWifiState()
{
static unsigned long previousTime = 0;
static bool reconnectWifi = false;
if(wifiAbnormalDisconnect && millis() - previousTime > 60000)
{
previousTime = millis();
//Serial.println("Wifi abnormal disconnect");
reconnectWifi = true;
obloqConnectMqtt = false;
connectWifi(WIFISSID,WIFIPWD);
}
if(!wifiAbnormalDisconnect && reconnectWifi)
{
reconnectWifi = false;
mqttReconnectFlag = true;
//Serial.println("Reconnect mqtt");
reconnectMqtt();
}
}
/********************************************************************************************
Function : subscribe
Description : 订阅DF-IoT物联网设备
Params : topic DF-IoT物联网设备编号
Return : 无
********************************************************************************************/
void subscribe(String topic)
{
String subscribeMessage = "|4|1|2|" + topic + separator;
sendMessage(subscribeMessage);
}
void setup()
{
Serial.begin(9600);
softSerial.begin(9600);
//显示初始化
while(LED.begin(LED.e8Bit) != 0)
{
//Serial.println("Failed to initialize the chip , please confirm the chip connection!");
delay(1000);
}
LED.setDisplayArea(1,2,3,4,5,6,7,8);
LED.print("8","8","8","8","8","8","8","8");
delay(3000);
LED.print("","","","","","","","");
while (!(asr.begin())) {
//Serial.println("Communication with device failed, please check connection");
delay(3000);
}
LED.print("0","0","0","0","0","0","0","0");
delay(3000);
LED.print("","","","","","","","");
//语音初始化
asr.setVolume(4);
asr.setMuteMode(0);
asr.setWakeTime(20);
asr.playByCMDID(23); // 普通词ID
//北斗授时初始化
while (!rtc.begin()) {
//Serial.println("Failed to init chip, please check if the chip connection is fine. ");
delay(10000);
}
rtc.setHourSystem(rtc.e24hours);//Set display format
rtc.calibRTC(1);
//
}
uint8_t underCalibCount = 0;
char hour1[2];
char hour2[2];
char second1[2];
char second2[2];
char minute1[2];
char minute2[2];
char xian[2];
int currentTime1=0;
int currentTime2=0;
DFRobot_GNSSAndRTC::sTimeData_t sTime;
void show22()
{
LED.print("1","8","-","2","7","-","1","9");
}
void show()
{
if(millis() - currentTime1 > 900){
currentTime1 = millis();
uint8_t status = rtc.calibStatus();
if (DFRobot_GNSSAndRTC::eCalibComplete == status) {
underCalibCount = 0;
//Serial.println("Calibration success!");
} else if (DFRobot_GNSSAndRTC::eUnderCalib == status) {
underCalibCount += 1;
if (60 <= underCalibCount) { // If the calibration fails for a long time, manually terminate the calibration
rtc.calibStatus(false);
underCalibCount = 0;
//Serial.println("Calibration failed!");
//Serial.println("It may be due to weak satellite signals.");
//Serial.println("Please proceed to an open outdoor area for time synchronization.");
}
}
sTime = rtc.getRTCTime();
xian[0]='-';
String hour = String(sTime.hour+8);
if(sTime.hour>9){
hour1[0]=hour.charAt(0);
hour2[0]=hour.charAt(1);
}
else{
hour1[0]='0';
hour2[0]=hour.charAt(0);
}
String minute = String(sTime.minute);
if(sTime.minute>9){
minute1[0]=minute.charAt(0);
minute2[0]=minute.charAt(1);
}
else{
minute1[0]='0';
minute2[0]=minute.charAt(0);
}
String second = String(sTime.second);
if(sTime.second>9){
second1[0]=second.charAt(0);
second2[0]=second.charAt(1);
}
else{
second1[0]='0';
second2[0]=second.charAt(0);
}
LED.print(hour1,hour2,xian,minute1,minute2,xian,second1,second2);
}
}
void voice()
{
if(millis() - currentTime2 > 300)
{
currentTime2 = millis();
uint8_t CMDID = asr.getCMDID();
switch (CMDID) {
case 1:
if(obloqConnectMqtt){
publish("1ugQpffSR", String(sTime.hour)+":"+String(sTime.minute)+":"+String(sTime.second));
Serial.print("R");
}
break;
default:
if (CMDID != 0) {
//Serial.print("CMDID = "); //打印命令ID
//Serial.println(CMDID);
}
}
}
}
void loop()
{ //物联网
sendPing();
execute();
handleUart();
subscribeSingleTopic("9VnL8sfIg");
checkWifiState();
//时间显示
show();
//语音求救
voice();
}
求救显示屏
#include <Adafruit_GFX.h>
#include <RGBmatrixPanel.h>
#define CLK 8
#define LAT A3
#define OE 9
#define A A0
#define B A1
#define C A2
RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);
//RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);
//可用Mind+通过扩展添加oled,显示汉字,在“自动生成”处看此汉字字模
int num=5;
int str[5][32]={
{0x02,0x00,0x02,0x08,0x3f,0xd0,0x02,0x20,0x02,0x40,0xff,0xfe,0x01,0x00,0x02,0x00,0x0c,0x10,0x18,0xe0,0x2f,0x00,0x48,0x08,0x88,0x08,0x08,0x08,0x07,0xf8,0x00,0x00},
{0x08,0x00,0x0b,0xfe,0x48,0x20,0x48,0x20,0x48,0x20,0x49,0xfc,0x49,0x24,0x49,0x24,0x49,0x24,0x49,0x24,0x49,0x24,0x09,0x34,0x11,0x28,0x10,0x20,0x20,0x20,0x40,0x20},
{0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00},
{0x10,0x40,0x14,0x40,0x12,0x40,0x10,0x80,0xfe,0xfe,0x11,0x08,0x10,0x88,0x92,0x88,0x54,0x88,0x10,0x50,0x38,0x50,0x54,0x20,0x92,0x50,0x10,0x88,0x51,0x04,0x22,0x02},
{0x01,0x00,0x01,0x00,0x02,0x80,0x04,0x40,0x08,0x20,0x37,0xd8,0xc0,0x06,0x00,0x00,0x3e,0xf8,0x22,0x88,0x22,0x88,0x22,0x88,0x3e,0xa8,0x22,0x90,0x00,0x80,0x00,0x80}
};
//int ma[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
int ma[8]={128,64,32,16,8,4,2,1};
int bs=0;
void setup() {
Serial.begin(9600);
matrix.begin();
}
uint8_t underCalibCount = 0;
void loop() {
//物联网
if(Serial.available()>0)
{
char c=char(Serial.read());
Serial.print(c);
if(c=='R')
{
bs=1;
}
else if(c=='1')
{
bs=2;
}
else{
bs=0;
}
}
if(bs==0){
matrix.fillScreen(matrix.Color333(0, 0, 0));
ts();
delay(1000);
}
else if(bs==1){
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(1000);
matrix.fillScreen(matrix.Color333(7, 0, 0));
delay(1000);
}
else{
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(1000);
matrix.fillScreen(matrix.Color333(0, 7, 0));
delay(1000);
}
}
void ts(){
int i,j,a,n,m,k;
n=0;
for(m=31;m>=-num*16;m--){
for(i=0;i<32;i++)
{
for(j=0;j<8;j++){
for(k=0;k<num;k++){
a=(str[k][i]&ma[j])>>(7-j);//16进制数,按位取,如0x45(十六进制)对应01000101(二进制),一位一位取
if(a==1&&n+m+k*16>=0&&n+m+k*16<=31){//对应位为1,点亮
matrix.drawPixel(n+m+k*16,i/2, matrix.Color333(7, 7, 0)); //n为列号,因为这里一个十六进制数为8位,两个为一行,所以行号为i/2
}
}
n=n+1;
if(n==16){//16*16每过16列,换行,列号从0开始
n=0;
}
}
}
delay(20);
matrix.fillScreen(matrix.Color333(0, 0, 0));
}
}
2. 手机App界面设计及图形化编程代码
预期效果
本项目使用北斗授时模块,可通过北斗获取精准时间,语音识别“老师救命”,通过物联网将信息发送至手机App。主要用于学生在校园偏僻角落欺凌时报警求救使用,也可用于公共场所紧急情况下报警呼救使用。
创意点
在学校无法安装摄像头的场所,为遭受欺凌的学生提供呼救使用。使用北斗授时,提供精准时间。使用语音识别,实现即时呼救。使用物联网,即时通知老师。
总结与展望
遇到的难点:因在学校角落或学校卫生间内,无线网络信号弱,信息传送可能出现不稳定。因设备原因,未实现语音对讲功能,对于学生的呼救,老师无法即时响应。
研究计划:建议学校在这些无线信号弱的地方,增加无线信号强度,来满足信息传送需求,增加语音广播设备,当学生呼救后,即时开启对讲功能,及时进行确认并制止欺凌行为继续发展。
成员及分工
孟繁芃 八年级2班 负责项目创意、程序编写、文档制作、海报制作。
赵雪睿 八年级4班 负责项目视频录制、剪辑。
刘楚涵 八年级4班 负责项目硬件组装、调试。
DeadWalking2024.05.25
666 学习了!
_深蓝_2024.05.13
我再想,这是不是老师的学生做的呢,格式都没有呀
_深蓝_2024.05.13
好想复现云天老师的作品,效果满满,电路一块都是难点哦