# iot-fish **Repository Path**: nwu_zjq/iot-fish ## Basic Information - **Project Name**: iot-fish - **Description**: No description available - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-07-13 - **Last Updated**: 2025-07-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 具体详设见 https://blog.csdn.net/qq_32460819/article/details/149312881 # 硬件 ![image-20230806215454614](readme.assets/image-20230806215454614.png) ![image-20230806215502997](readme.assets/image-20230806215502997.png) # 配置环境 ## vscode安装插件 ![image-20230806220141529](readme.assets/image-20230806220141529.png) # 1. oled显示网络时间, wifi链接网络 ```c // ntp_get_date.h #include "time.h" String week[8] = { "Sun", "Mon", "Tues", "Wednes", "Thur", "Fri", "Sat" }; void printLocalTime(Adafruit_SSD1306 &display) { //打印本地时间函数 struct tm timeinfo; if(!getLocalTime( & timeinfo)) { Serial.println("Failed to obtain time"); return; } Serial.println(&timeinfo, "%A, %Y-%m-%d %H:%M:%S"); char display_str[30] = {0}; sprintf(display_str,"%04d-%02d-%02d %02d:%02d:%02d %01d", timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, timeinfo.tm_wday); display.clearDisplay(); display.setCursor(0, 0); display.println(display_str); display.display(); } const char * ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 28800; const int daylightOffset_sec = 0; void ntp_get_date_init() { // 将请求数据包传输到NTP服务器,并将接收到的时间戳数据包解析为可读格式 configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); } ``` ```c // main.cpp #include #include #include #include "ntp_get_date.h" Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire); const char* id="Wifi账号"; //定义两个字符串指针常量 const char* psw="wifi密码"; void setup() { // 串口初始化 Serial.begin(115200); // oled初始化 if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.display(); delay(500); display.setTextSize(1); display.setTextColor(WHITE); display.setRotation(0); display.clearDisplay(); // wifi初始化 WiFi.begin(id,psw); while(WiFi.status()!=WL_CONNECTED){ //未连接上 delay(500); Serial.println("connection..."); } display.setCursor(0, 0); display.println("wifi connect to Wifi007 success"); display.display(); Serial.println("wifi connect to Wifi007 success"); ntp_get_date_init(); } void loop() { printLocalTime(display); delay(500); } ``` ![在这里插入图片描述](readme.assets/7b5a61605b60403baaa73ed257ae273f.png) # 2. mqtt订阅和发布信息 ## 服务端 docker安装emqx ```shell # http://t.csdn.cn/PqPEo docker run -dit --name emqx -p 18083:18083 -p 1883:1883 -p 8083:8083 -p 8084:8084 emqx/emqx:latest 浏览器登录: http://192.168.3.12:18083/#/dashboard/overview admin 密码是public 18083 管理控制端口 1883 mqtt协议端口 8083 MQTTwebSocket端口 # 认证方式 https://blog.csdn.net/weixin_43869518/article/details/127558282 ``` ## 客户端 esp32c3 xiao ```c #include #include #include const char* id="wifi账号"; //定义两个字符串指针常量 const char* psw="wifi密码"; const char* mqtt_server = "192.168.3.12"; // 服务IP const uint16_t mqtt_port = 1883; // 服务端口 WiFiClient espClient; // WiFi客户端节点 PubSubClient mqttClient(espClient); // void callback(char* topic, byte* payload, unsigned int length); void reconnect(); void setup() { // 串口初始化 Serial.begin(115200); // wifi初始化 WiFi.mode(WIFI_STA); WiFi.begin(id, psw); while(WiFi.status()!=WL_CONNECTED){ //未连接上 delay(500); Serial.println("connection..."); } Serial.println("wifi connect to Wifi007 success"); mqttClient.setServer(mqtt_server, mqtt_port); mqttClient.setCallback(callback); randomSeed(micros()); } #define MSG_BUFFER_SIZE (50) char msg[MSG_BUFFER_SIZE]; int value = 0; unsigned long lastMsg = 0; void loop() { if (!mqttClient.connected()) { reconnect(); } mqttClient.loop(); /* 下面是给订阅的消息队列发送随机数, 也就是mqtt的发布 */ unsigned long now = millis(); if (now - lastMsg > 2000) { lastMsg = now; value = random(1,50);//生成一个随机数 snprintf (msg, MSG_BUFFER_SIZE, "%ld", value);//将随机数转变成为字符串 Serial.print("Publish message: "); Serial.println(msg); mqttClient.publish("test/topic2", msg);//发布这个随机数 } } /** * 下面是订阅获取数据 */ void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); } void reconnect() { while (!mqttClient.connected()) { Serial.print("Attempting MQTT connection..."); if (mqttClient.connect("ESP32Client")) { Serial.println("connected"); // 一旦连接上了,就发布一个主题,这个主题是自定义的,您可以在自己的MQTT服务器进行查看 mqttClient.publish("test/topic2", "hello world"); // subscribe是订阅的意思,MQTT通信的核心就是“订阅+发布” mqttClient.subscribe("test/topic1"); mqttClient.subscribe("test/topic2"); } else { Serial.print("failed, rc="); Serial.print(mqttClient.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } ``` ## 客户端加密码校验 1. 服务端通过访问数据库来校验账号密码 2. 客户端通过账号密码进行connect ### 服务端配置 第一步创建数据库, 并且根据emqx的创建表提示创建表 第二步在表中添加数据 第三步在emqx的客户端认证中增加账号密码 ![在这里插入图片描述](readme.assets/03e6bac17ff243c2a3b189f5d6231c2d.png) ### 客户端通过账号密码登录 ```c void reconnect() { while (!mqttClient.connected()) { Serial.print("Attempting MQTT connection..."); if (mqttClient.connect(CLIENT_ID, mqtt_username, mqtt_password)) { Serial.println("connected"); // 一旦连接上了,就发布一个主题,这个主题是自定义的,您可以在自己的MQTT服务器进行查看 mqttClient.publish("test/topic2", "hello world"); // subscribe是订阅的意思,MQTT通信的核心就是“订阅+发布” mqttClient.subscribe("test/topic1"); mqttClient.subscribe("test/topic2"); } else { Serial.print("failed, rc="); Serial.print(mqttClient.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } ``` ### 最后的效果图, 通过web客户端发送主题消息, topic2, 客户端能够接收到 ![在这里插入图片描述](readme.assets/9d3d7f306dd147f5bb064bfb63d0c34d.png) ![在这里插入图片描述](readme.assets/4aa84dd0c86848a891a3b38807765a84.png) # 2023年8月6日 增加json解析mqtt信息, 通过oled显示出来 ![image-20230806215243413](readme.assets/image-20230806215243413.png) # 2025年6月7日 手势识别 注意: 不生效的原因是因为没选对模块, 所以编译的时候的IIC接口就编译不对 ## 手势识别代码 ```c #include #include "oled.h" #include "ntp_get_date.h" #include "mqtt.h" #include "mywifi.h" #include "flasher.h" #include "json_string.h" #include "mpu6050.h" #include "paj7620.h" void setup() { // 串口初始化 Serial.begin(115200); oled_init(); // wifi_init(); // ntp_get_date_init(); // mqtt_init(); // randomSeed(micros()); // print_vulnerability_init(); // string2jsonTest(); // mpu6050_init(); while(paj7620Init()) { delay(1000); } } int cycle = 0; void run900msTasks() { oled.clearDisplay(); // printLocalTime(); // send_random_to(); // mqtt_reconnect(); // send_random_to(); // handle_6050change(); // mpu6050_dispaly(); // if(cycle>10) { // cycle = 0; // } cycle++; // playSnowing(); paj7620Test(); oled.display(); // 这放到最后 } Flasher_task run900msTaskss(run900msTasks, 1000); void loop() { run900msTaskss.Update(); } ``` # bitmap图片显示 ```c #include #include "oled.h" #include "ntp_get_date.h" #include "mqtt.h" #include "mywifi.h" #include "flasher.h" #include "json_string.h" #include "mpu6050.h" #include "paj7620.h" #include "PubSubClient.h" #define OLED_RESET 4 Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); //用字模工具取模显示,取模方式:C51格式,横向取模 //取16X16汉字字模 逐行式 顺向高位在前 static const unsigned char PROGMEM str1[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x73,0xE0,0x00,0x00,0x00, 0x00,0x00,0x01,0x8E,0x18,0x00,0x00,0x00,0x00,0x00,0x0E,0x30,0x6E,0x00,0x00,0x00, 0x00,0x00,0x11,0x22,0x81,0x00,0x00,0x00,0x00,0x00,0x6C,0x04,0x49,0x80,0x00,0x00, 0x00,0x00,0x80,0x02,0x26,0x40,0x00,0x00,0x00,0x00,0x90,0x01,0xC6,0x20,0x00,0x00, 0x00,0x01,0xA0,0x00,0x5E,0xA0,0x00,0x00,0x00,0x02,0x66,0x19,0x17,0x10,0x00,0x00, 0x00,0x02,0x59,0x86,0x2D,0x30,0x00,0x00,0x00,0x04,0xC5,0xFF,0xFE,0xD0,0x00,0x00, 0x00,0x09,0x23,0x9C,0xFF,0xB0,0x00,0x00,0x00,0x06,0xDC,0x00,0x1D,0x70,0x00,0x00, 0x00,0x03,0x26,0x00,0x0F,0xE0,0x00,0x00,0x00,0x07,0x40,0xC0,0x03,0xC0,0x00,0x00, 0x00,0x0D,0x31,0x3D,0x4F,0x80,0x00,0x00,0x00,0x0F,0x8C,0x0F,0x3F,0x00,0x00,0x00, 0x00,0x0B,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x28,0x00,0x80,0x00,0x00,0x00, 0x00,0x03,0xF8,0x00,0xC0,0x00,0x00,0x00,0x00,0x01,0xD8,0x00,0xC0,0x00,0x00,0x00, 0x00,0x01,0x74,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0xA8,0x00,0x41,0x00,0x00,0x00, 0x00,0x00,0xB0,0x02,0x62,0x00,0x00,0x00,0x00,0x00,0x58,0x01,0xC2,0x00,0x00,0x00, 0x00,0x00,0xB4,0x00,0xE6,0x00,0x00,0x00,0x00,0x00,0xB8,0x00,0x66,0x00,0x00,0x00, 0x00,0x00,0x5C,0x05,0xE4,0x00,0x00,0x00,0x00,0x00,0x2A,0x00,0x64,0x00,0x00,0x00, 0x00,0x00,0x1F,0x00,0xCC,0x00,0x00,0x00,0x00,0x00,0x0B,0x80,0x0C,0x00,0x00,0x00, 0x00,0x00,0x09,0xE0,0x18,0x00,0x00,0x00,0x00,0x00,0x05,0xBC,0x38,0x00,0x00,0x00, 0x00,0x00,0x04,0x8F,0xF8,0x00,0x00,0x00,0x00,0x00,0x04,0x83,0xEC,0x00,0x00,0x00, 0x00,0x00,0x04,0x80,0x44,0x00,0x00,0x00,0x00,0x00,0x04,0x80,0x06,0x00,0x00,0x00, 0x00,0x00,0x02,0x40,0x07,0x80,0x00,0x00,0x00,0x00,0x02,0x40,0x04,0x78,0x00,0x00, 0x00,0x00,0x02,0x68,0x04,0x86,0x00,0x00,0x00,0x00,0x01,0x26,0x08,0x81,0x00,0x00, 0x00,0x00,0x03,0x32,0x09,0x01,0x00,0x00,0x00,0x00,0x0F,0x12,0x09,0x02,0x00,0x00, 0x00,0x02,0x32,0x94,0x10,0x0C,0x00,0x00,0x00,0x00,0x42,0x84,0x10,0x10,0x00,0x00, 0x00,0x00,0x44,0x84,0x22,0x20,0x00,0x00,0x00,0x00,0x40,0x84,0x02,0x40,0x00,0x00, 0x00,0x00,0x20,0x04,0x01,0x40,0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x80,0x00,0x00, 0x00,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x06,0x00,0x00,0x00, 0x00,0x00,0x0C,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0xE0,0x00,0x00,0x00, 0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.setTextColor(WHITE);//开像素点发光 display.clearDisplay();//清屏 display.setTextSize(1); //设置字体大小 display.setCursor(0, 0);//设置显示位置 //第一个参数控制距离左边界的位数,第二个参数控制距离上边界的位数,第三个参数是所要显示的字符, //第四个参数和第五个参数规定了占用范围, //对于汉字来说16X16的点阵才能完整显示一个汉字,一个汉字2个字节16个位,第五个参数是字体的大小 display.drawBitmap(0, 0, str1, 64, 64, 1); //画出字符对应点阵数据 display.display();//开显示 } ``` # 手势控制继电器 ![image-20250711225210513](D:\esp32c3xiao\platform-io\PAJ7620\esp32-c3-xiao-platform-io-ide-vscode\readme.assets\image-20250711225210513.png) ## HC595 点亮 ```c #include // 引脚定义 #define PIN_DS A1 // 数据 #define PIN_SHCP A3 // 移位时钟 #define PIN_STCP A2 // 锁存时钟 // 发送 1 字节到 74HC595 void shiftOutByte(uint8_t data) { digitalWrite(PIN_STCP, LOW); // 准备锁存 for (uint8_t i = 0; i < 8; i++) { digitalWrite(PIN_DS, (data & (0x80 >> i)) ? HIGH : LOW); digitalWrite(PIN_SHCP, LOW); digitalWrite(PIN_SHCP, HIGH); // 上升沿移位 } digitalWrite(PIN_STCP, HIGH); // 上升沿锁存 } void setup() { pinMode(PIN_DS, OUTPUT); pinMode(PIN_SHCP, OUTPUT); pinMode(PIN_STCP, OUTPUT); digitalWrite(PIN_STCP, LOW); } void loop() { shiftOutByte(0x00); // 0000 1111 delay(1000); shiftOutByte(0xFF); // 0000 1111 delay(1000); } ``` ## 模式设计 按键D8选择模式 ### 点按选择模式 其中S表示开关, =1表示打开, =0表示关闭 1. [S0, S1] 1. 手势左控制S0=1, S1=0; 右控制S0=0, S1=1 2. 手势上控制全开 S0=1, S1=1, 下控制全灭 S0=0, S1=0 2. [S2, S3] 1. 手势控制同上 3. [S4, S5] 1. 手势控制同上 4. [S6, S7] 1. 手势控制同上 5. [S0, S1, S2, S3] 1. 手势左控制 6. [S4, S5, S6, S7] 7. [S0, S1, S2, S3, S4, S5, S6, S7] 8. ### 长按选择模式 ### 双击选择模式 ### 手势控制 手势控制只设计了上下左右 1. 当控制两个开关时, 左右控制左右两个灯的打开, 比如往左为左亮右灭, 往右时为右亮左灭