一个QQ消息提醒器

一个QQ消息提醒器

黄鹏宇 1,230 2022-01-23

简介

接收QQ消息,与确认收到

  • 正常工作时亮蓝灯
  • 当有重要的人发来消息后,亮红灯
  • 点击LED按钮,则会发送确认收到,然后转亮蓝灯
  • 点击按键开关,会转蓝灯,并不回复消息

效果展示

演示效果

流程图

image.png

实物图

10BB9D4FA5513488672095D559F36AA9.jpg

连线图

image.png

代码

/*
 * 接收QQ消息,与确认收到
 * 正常工作时亮蓝灯
 * 当有重要的人发来消息后,亮红灯
 * 此时点击按钮,则会发送确认收到,然后转亮蓝灯
 */

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
#include <Hash.h>

const char * ssid = "【wifi名字】";
const char * password = "【wifi密码】";
const char* HOST = "【cqhttp的IP】";
const int WsPort = 【WsPort】;
const int HttpPort = 【HttpPort】;
const String AccessToken = "【AccessToken】";

WebSocketsClient webSocket;

unsigned long messageInterval = 5000;
unsigned long lastUpdate = millis();

bool connected = false;
#define DEBUG_SERIAL Serial

int currentState = 0;
int resetState = 0;
int btnState = 0;
int brightness = 255;

String currentQQNum = "";

// 定义记录最近一次抖动的时间变量,并初始化时间为0毫秒。
long lastDebounceTime = 0;

// 定义延迟抖动的时间变量,并初始化为50毫秒。
long debounceDelay = 50;

// 是否为重要的人发来的信息
const String subQQNumArr[] = {"QQ号","QQ号"};

// 解析cqhttp发来的json数据
void parseJson(uint8_t * payload){
  DynamicJsonDocument doc(1024);
  deserializeJson(doc, payload);
  const char* msgTypeChar = doc["message_type"];
  String msgType = msgTypeChar;
  if(msgType.compareTo("private") == 0){
    DEBUG_SERIAL.println("收到一条新消息");
    String qqNum = doc["user_id"].as<String>();;
    DEBUG_SERIAL.println(qqNum);
    if(isLoverMsg(qqNum)){
      currentQQNum = qqNum;
      DEBUG_SERIAL.println("素素发来的");
      lightBlink(true);
     }
    const char* rawMsg = doc["raw_message"];
    DEBUG_SERIAL.printf("消息内容为:%s\n",rawMsg);
    DEBUG_SERIAL.println("=========================");
  }
}

void lightBlink(bool finalRed){
      lightRed();  
      delay(100);
      lightBlue();  
      delay(100);
      lightRed();  
      delay(100);
      lightBlue();  
      delay(100);
      lightRed();  
      delay(100);
      lightBlue();  
      delay(100);
      lightRed();  
      delay(100);
      lightBlue();  
      delay(100);
      lightRed();  
}

/*
 * 是否为重要的人发来的信息
 */
bool isLoverMsg(String qqNum){
  bool flag = false;
  int arrLen = 0;
  if(subQQNumArr[0]!=NULL){
    arrLen = sizeof(subQQNumArr)/sizeof(subQQNumArr[0]);  
  }
  for(int i = 0 ;i < arrLen;i++){
    if(qqNum == subQQNumArr[i]){
      return true;    
    }
  }
  
  return flag;
}

void sendImpMsg(String qqNum){
  bool sendSuccess = esp8266Http(qqNum);
    // 成功的话
    if (sendSuccess) {
      lightBlue();       
    } else {
      // 闪烁一下
      lightBlink(true); 
    }
}


// 发送HTTP请求并且将服务器响应通过串口输出
bool esp8266Http(String qqNum){
  WiFiClient client; //新添加
  const char* host = HOST;
  const int httpPort = HttpPort;              
  int randomeCQCode = random(221);
  // 建立字符串,用于HTTP请求
  String httpRequest =  String("GET /send_private_msg?user_id="+qqNum+"&access_token="+AccessToken+"&message=[CQ:face,id=109][CQ:face,id="+randomeCQCode+"]") + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n" + "\r\n";

  // 通过串口输出连接服务器名称以便查阅连接服务器的网址                      
  DEBUG_SERIAL.print("Connecting to "); 
  DEBUG_SERIAL.print(host); 
  if (client.connect(host, httpPort)){ 
    DEBUG_SERIAL.println(" Success!");        
    client.print(httpRequest);          
    DEBUG_SERIAL.println("Sending request: ");
    DEBUG_SERIAL.println(httpRequest);     
       
    DEBUG_SERIAL.println("Web Server Response:");        
    while (client.connected() || client.available()){ 
      if (client.available()){
        String line = client.readStringUntil('\n');
        DEBUG_SERIAL.println(line);
      }
    }
    client.stop();                     
    DEBUG_SERIAL.print("Disconnected from "); // 并且通过串口输出断开连接信息
    DEBUG_SERIAL.print(host);
    return true;    
  } else{    
    DEBUG_SERIAL.println(" connection failed!");
    client.stop();
    return false;
  }  
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
    switch(type) {
      case WStype_DISCONNECTED:
          DEBUG_SERIAL.printf("[WSc] Disconnected!\n");
          connected = false;
          break;
      case WStype_CONNECTED: {
          DEBUG_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
          connected = true;
          // send message to server when Connected
          DEBUG_SERIAL.println("[WSc] SENT: Connected");
          webSocket.sendTXT("Connected");
      }
          break;
      case WStype_TEXT:
          //  DEBUG_SERIAL.printf("[WSc] RESPONSE: %s\n", payload);
          parseJson(payload);
          break;
      case WStype_BIN:
          DEBUG_SERIAL.printf("[WSc] get binary length: %u\n", length);
          break;
      case WStype_PING:
          // pong will be send automatically
          DEBUG_SERIAL.printf("[WSc] get ping\n");
          break;
      case WStype_PONG:
          // answer to a ping we send
          DEBUG_SERIAL.printf("[WSc] get pong\n");
          break;
      case WStype_ERROR:
      case WStype_FRAGMENT_TEXT_START:
      case WStype_FRAGMENT_BIN_START:
      case WStype_FRAGMENT:
      case WStype_FRAGMENT_FIN:
          break;
    }
}

void setup() {
  DEBUG_SERIAL.begin(115200);
  
  DEBUG_SERIAL.println();
  DEBUG_SERIAL.println();
  DEBUG_SERIAL.println();
  
  pinMode(D1,INPUT);
  pinMode(D2,INPUT_PULLUP);
  
  pinMode(D7,OUTPUT);
  pinMode(D6,OUTPUT);
  for(uint8_t t = 4; t > 0; t--) {
      DEBUG_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
      DEBUG_SERIAL.flush();
      delay(1000);
  }
 
  DEBUG_SERIAL.print("Connecting to ");
  DEBUG_SERIAL.println(ssid);
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    DEBUG_SERIAL.print(".");
  }

  DEBUG_SERIAL.println("");
  DEBUG_SERIAL.println("WiFi connected");
  DEBUG_SERIAL.print("Local IP: "); 
  DEBUG_SERIAL.println(WiFi.localIP());
  webSocket.begin(HOST, WsPort, "/?access_token="+AccessToken,"ws");
  webSocket.onEvent(webSocketEvent);
  lightBlue();
}


void loop() {
    webSocket.loop();
    if (connected && lastUpdate+messageInterval<millis()){
        lastUpdate = millis();
    }

    btnState =  digitalRead(D1);
    resetState = digitalRead(D2);
//    DEBUG_SERIAL.println(resetState);
    if(resetState == 0){
      lightBlue();
    }
    if(btnState != currentState){
      // 1. 按钮被按下,2.并且两次按键时间间隔>抖动时间,则改变
      if(btnState == 0 && (millis() - lastDebounceTime) > debounceDelay){
        btnClick(currentQQNum);
        // 如果按键发生了变化,则重新设置最近一次抖动的时间。
        lastDebounceTime = millis();
      }
      currentState = btnState;
    }
    
    delay(100);
}

void btnClick(String qqNum){
   sendImpMsg(qqNum);
}

void lightNone(){
  analogWrite(D7,0);
  analogWrite(D6,0);
}

void lightRed(){
  analogWrite(D7,255);
  analogWrite(D6,LOW);
}
void lightBlue(){
  analogWrite(D7,LOW);
  analogWrite(D6,50);
}