├── LICENSE ├── awtirx └── esp8266-lattice-clock-awtirx │ ├── BlinkerSupport.h │ ├── ConfigWifiHtml.h │ ├── DateTimes.cpp │ ├── DateTimes.h │ ├── EEPROMTool.h │ ├── Functions.cpp │ ├── Functions.h │ ├── HttpTool.cpp │ ├── HttpTool.h │ ├── Lattice.cpp │ ├── Lattice.h │ ├── LatticePlus.cpp │ ├── LatticePlus.h │ ├── Main.h │ ├── Ota.cpp │ ├── Otas.h │ ├── PilotLight.cpp │ ├── PilotLight.h │ ├── System.cpp │ ├── System.h │ ├── Udps.cpp │ ├── Udps.h │ ├── esp8266-lattice-clock-awtirx.ino │ ├── wifis.cpp │ └── wifis.h ├── blinker ├── esp8266-lattice-clock │ ├── DateTimes.cpp │ ├── DateTimes.h │ ├── EEPROMTool.h │ ├── HttpTool.cpp │ ├── HttpTool.h │ ├── Lattice.cpp │ ├── Lattice.h │ ├── Main.h │ ├── Otas.h │ ├── PilotLight.cpp │ ├── PilotLight.h │ ├── Touch.h │ ├── Udps.cpp │ ├── Udps.h │ ├── esp8266-lattice-clock.ino │ ├── wifis.cpp │ └── wifis.h ├── image-20220325164645545.png ├── image-20220325164702280.png ├── image-20220325164712839.png ├── image-20220325164719116.png ├── image-20220325164854457.png ├── image-20220325165221276.png ├── image-20220325165226894.png ├── image-20220325165233514.png └── readme.md ├── code └── esp8266-lattice-clock │ ├── BlinkerSupport.h │ ├── ConfigWifiHtml.h │ ├── DateTimes.cpp │ ├── DateTimes.h │ ├── EEPROMTool.h │ ├── Functions.cpp │ ├── Functions.h │ ├── HttpTool.cpp │ ├── HttpTool.h │ ├── Lattice.cpp │ ├── Lattice.h │ ├── LatticePlus.cpp │ ├── LatticePlus.h │ ├── Main.h │ ├── Ota.cpp │ ├── Otas.h │ ├── PilotLight.cpp │ ├── PilotLight.h │ ├── System.cpp │ ├── System.h │ ├── Udps.cpp │ ├── Udps.h │ ├── bin │ ├── 3.bin │ ├── 4.bin │ ├── 5.bin │ ├── 6.bin │ ├── 7.bin │ ├── 8.bin │ ├── 9-lattice-clock-for-homekit.bin │ └── 9.bin │ ├── esp8266-lattice-clock.ino │ ├── wifis.cpp │ └── wifis.h ├── file ├── CH34x_Install_Windows_v3_4.zip └── CP210x_Universal_Windows_Driver.zip ├── img ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── esp8266-light.png ├── latticeshow.gif ├── latticeshow2.gif ├── qqgroup.png ├── taobao-1.png ├── taobao-2.png ├── taobao-3.png ├── taobao-4.png ├── taobao-5.png ├── wx-program-1.jpg └── wx-small-program-qr.jpg └── readme.md /awtirx/esp8266-lattice-clock-awtirx/BlinkerSupport.h: -------------------------------------------------------------------------------- 1 | #ifndef BLINKER_SUPPORT_H 2 | #define BLINKER_SUPPORT_H 3 | 4 | #define BLINKER_WIFI 5 | #define BLINKER_MIOT_LIGHT 6 | 7 | #include "Main.h" 8 | #include 9 | 10 | char auth[] = ""; // 这里设置你自己的点灯秘钥 11 | 12 | // 新建组件对象 13 | BlinkerButton powerBtn("btn-power"); // 开关按键 14 | BlinkerButton directBtn("btn-direction"); // 方向按键 15 | BlinkerNumber temperNum("num-temperature"); // 温度回调 16 | BlinkerSlider brightSlider("slider_bright"); //亮度滑动条 17 | BlinkerSlider powerSlider("slider_power"); //功能滑动条 18 | 19 | /** 20 | * @brief 设置方向回调 21 | * 22 | * @param state 23 | */ 24 | void directionCallback(const String &state) 25 | { 26 | Serial.print("设置时钟方向:"); 27 | Serial.println(state); 28 | uint8_t direction = 0; 29 | if (state == BLINKER_CMD_ON) 30 | { 31 | direction = 0; 32 | directBtn.print("on"); // 反馈开关状态 33 | } 34 | else if (state == BLINKER_CMD_OFF) 35 | { 36 | direction = 1; 37 | directBtn.print("off"); // 反馈开关状态 38 | } 39 | lattice.setDirection(direction); // 是否启用点阵屏幕 40 | } 41 | 42 | /** 43 | * @brief 设置亮度的回调 44 | * 45 | * @param value 46 | */ 47 | void brightCallback(int32_t value) 48 | { 49 | lattice.setBrightness(value, true); // 设置亮度 50 | brightSlider.print(value); // 将数据回调给组件 51 | } 52 | 53 | /** 54 | * @brief 功能切换的回调 55 | * 56 | * @param value 57 | */ 58 | void powerCallback(int32_t value) 59 | { 60 | functions.setPowerAndMode(value, 0); // 切换功能 61 | initStatus(); // 重置状态 62 | powerSlider.print(value); // 将数据回调给组件 63 | } 64 | 65 | /** 66 | 小爱同学接入方法 - 开关功能回调 67 | */ 68 | void miotPowerState(const String &state) 69 | { 70 | Serial.print("小爱语音设置的功能状态为:"); 71 | Serial.println(state); 72 | uint8_t power = 0; 73 | if (state == BLINKER_CMD_ON) 74 | { 75 | power = 0; 76 | BlinkerMIOT.powerState("on"); 77 | powerBtn.print("on"); // 反馈开关状态 78 | BlinkerMIOT.print(); 79 | } 80 | else if (state == BLINKER_CMD_OFF) 81 | { 82 | power = 1; 83 | BlinkerMIOT.powerState("off"); 84 | powerBtn.print("off"); // 反馈开关状态 85 | BlinkerMIOT.print(); 86 | } 87 | lattice.shutdown(power); // 是否启用点阵屏幕 88 | } 89 | 90 | /** 91 | 小爱同学接入方法 - 设置亮度 92 | */ 93 | void miotBright(const String &bright) 94 | { 95 | Serial.print("小爱语音设置的亮度为:"); 96 | Serial.println(bright.toInt()); 97 | lattice.setBrightness(bright.toInt() / 100 * 16, true); // 设置亮度 98 | BlinkerMIOT.brightness(bright.toInt()); 99 | brightSlider.print((int)bright.toInt() / 100 * 16); // 将数据回调给组件 100 | BlinkerMIOT.print(); 101 | } 102 | 103 | /** 104 | 小爱同学接入方法 - 查询状态 105 | */ 106 | void miotQuery(int32_t queryCode) 107 | { 108 | Serial.print("小爱语音查询状态代码:"); 109 | Serial.println(queryCode); 110 | } 111 | 112 | /** 113 | * 初始化点灯科技信息 114 | * 115 | */ 116 | void initBlinker() 117 | { 118 | Blinker.begin(auth, WiFi.SSID().c_str(), WiFi.psk().c_str()); // 初始化Blinker 这里只有在连接wifi成功的情况下才能用 119 | powerBtn.attach(miotPowerState); // 注册开关按键 120 | brightSlider.attach(brightCallback); // 注册亮度调节 121 | directBtn.attach(directionCallback); // 注册方向按钮回到 122 | powerSlider.attach(powerCallback); // 注册功能切换 123 | BlinkerMIOT.attachPowerState(miotPowerState); // 注册屏幕开关的回调 124 | BlinkerMIOT.attachBrightness(miotBright); // 注册亮度控制的回调 125 | BlinkerMIOT.attachQuery(miotQuery); // 注册小爱同学语音状态查询 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/ConfigWifiHtml.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_WIFI_HTML_H 2 | #define CONFIG_WIFI_HTML_H 3 | /** 4 | * @brief 配网HTML代码 本段代码改编自 https://blog.csdn.net/weixin_44220583/article/details/111562423 5 | * 这里做一简单的说明吧 6 | * 其实呢一开始呢是并不想开源这段网页配网的功能代码的,因为会触及到他人的利益,之前有网友迫切的跟我说不要搞开源留一手,不希望这个市场泛滥,归根到底还是会触及他人的利益。 7 | * 在这里也希望那位仁兄看到之后切勿介意,我个人始终认为开源是促进进步的一种方式,倘若大家都封闭自己的想法和思路怎么能得到本质上的进步呢,所以这里我也不对代码做任何的混淆处理, 8 | * 只是对内容进行压缩,不然太长影响整个项目。后续也会整理出相关的文档来对这里过程做详细的说明。 9 | */ 10 | const char *page_html = "点阵时钟配置WIFI
欢迎使用点阵时钟
SSID:
密码:
wifi列表刷新
"; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/DateTimes.cpp: -------------------------------------------------------------------------------- 1 | #include "DateTimes.h" 2 | 3 | DateTimes::DateTimes() 4 | { 5 | Wire.begin(D3, D1); 6 | sensor.begin(); 7 | } 8 | 9 | /** 10 | * @brief 获取时间信息 11 | * 12 | * @return Times 13 | */ 14 | Times DateTimes::getTimes() 15 | { 16 | datetime = DateTime(currtimestamp); 17 | times.h = datetime.hour(); 18 | times.m = datetime.minute(); 19 | times.s = datetime.second(); 20 | return times; 21 | } 22 | 23 | /** 24 | * @brief 获取日期信息 25 | * 26 | * @return Dates 27 | */ 28 | Dates DateTimes::getDates() 29 | { 30 | datetime = DateTime(currtimestamp); 31 | dates.y = datetime.year(); 32 | dates.m = datetime.month(); 33 | dates.d = datetime.day(); 34 | return dates; 35 | } 36 | 37 | /** 38 | * @brief 获取温度信息 39 | * 40 | * @return int 41 | */ 42 | int DateTimes::getTemperature() 43 | { 44 | if (sensor.measure()) 45 | { 46 | float temperature = sensor.getTemperature(); 47 | float humidity = sensor.getHumidity(); 48 | Serial.print("获取到的温度为 (°C): "); 49 | Serial.println(temperature); 50 | Serial.print("获取到的湿度为 (%RH): "); 51 | Serial.println(humidity); 52 | return (int)temperature * 100; 53 | } 54 | else 55 | { 56 | return 88 * 100; 57 | } 58 | } 59 | 60 | /** 61 | * @brief 给时钟芯片和系统时间设置时间信息 62 | * 63 | * @param timestamp 64 | */ 65 | void DateTimes::setDateTimes(long timestamp) 66 | { 67 | currtimestamp = timestamp; 68 | datetime = DateTime(timestamp); 69 | } 70 | 71 | /** 72 | * @brief 获取时间戳信息 73 | * 74 | * @return long 75 | */ 76 | long DateTimes::getTimestamp() { return currtimestamp; } 77 | 78 | /** 79 | * @brief 系统时间戳++ 80 | * 81 | */ 82 | void DateTimes::timestampAdd() { currtimestamp++; } 83 | 84 | /** 85 | * @brief 保存倒计时时间戳信息 86 | * 87 | * @param timestamp 88 | */ 89 | void DateTimes::saveCountdownTimestamp(long timestamp) 90 | { 91 | uint8_t arr[5]; 92 | for (int i = 0; i < 5; i++) 93 | { 94 | arr[i] = (timestamp & 0xff); 95 | timestamp >>= 8; 96 | } 97 | EEPROMTool.saveData(arr, COUNTDOWN_TIME, 5); 98 | } 99 | 100 | /** 101 | * @brief 获取倒计时时间戳信息 102 | * 103 | * @return long 104 | */ 105 | long DateTimes::getCountdownTimestamp() 106 | { 107 | long timestamp = 0; 108 | uint8_t *temp = EEPROMTool.loadData(COUNTDOWN_TIME, 5); 109 | for (int i = 0; i < 5; i++) 110 | { 111 | timestamp += temp[i] << (i * 8); 112 | } 113 | // 用完以后删除内存 114 | free(temp); 115 | return timestamp; 116 | } 117 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/DateTimes.h: -------------------------------------------------------------------------------- 1 | #ifndef DATETIMES_H 2 | #define DATETIMES_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "System.h" 6 | #include 7 | #include 8 | #include 9 | 10 | struct Times 11 | { 12 | uint8_t h; 13 | uint8_t m; 14 | uint8_t s; 15 | }; 16 | 17 | struct Dates 18 | { 19 | uint16_t y; 20 | uint8_t m; 21 | uint8_t d; 22 | }; 23 | 24 | /** 25 | * 当前时间戳,冗余做法,即时钟芯片没有或者不生效时,使用系统的时间戳来反馈时间 26 | */ 27 | static long currtimestamp = 0; 28 | 29 | class DateTimes 30 | { 31 | private: 32 | bool h12Flag, pmFlag, century = false; 33 | 34 | /** 35 | * @brief 初始化DS3231 36 | * 37 | */ 38 | DS3231 ds3231; 39 | /** 40 | * @brief 41 | * 42 | */ 43 | DateTime datetime; 44 | 45 | /** 46 | * @brief 温湿度传感器 47 | * 48 | */ 49 | HTU21D sensor; 50 | 51 | /** 52 | * @brief 时间结构体 53 | * 54 | */ 55 | Times times; 56 | 57 | /** 58 | * @brief 日期结构体 59 | * 60 | */ 61 | Dates dates; 62 | 63 | public: 64 | /** 65 | * @brief 温度 66 | * 67 | */ 68 | byte temperature = 0; 69 | /** 70 | * @brief 湿度 71 | * 72 | */ 73 | byte humidity = 0; 74 | /** 75 | * 构造函数 76 | */ 77 | DateTimes(); 78 | 79 | /** 80 | * @brief 初始化 81 | * 82 | */ 83 | void initDateTimes(); 84 | 85 | /** 86 | * 获取时间 87 | */ 88 | Times getTimes(); 89 | 90 | /** 91 | * 获取日期 92 | */ 93 | Dates getDates(); 94 | 95 | /** 96 | * 获取温度信息 97 | */ 98 | int getTemperature(); 99 | 100 | /** 101 | * 根据datetime设置时间 102 | */ 103 | void setDateTimes(long timestamp); 104 | 105 | /** 106 | * 获取时间戳信息 107 | */ 108 | long getTimestamp(); 109 | 110 | /** 111 | * 获取倒计时的时间戳 112 | */ 113 | long getCountdownTimestamp(); 114 | 115 | /** 116 | * 保存倒计时的时间戳 117 | */ 118 | void saveCountdownTimestamp(long timestamp); 119 | 120 | /** 121 | * @brief 时间戳++ 122 | * 123 | */ 124 | static void timestampAdd(); 125 | }; 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/EEPROMTool.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_TOOL_H 2 | #define EEPROM_TOOL_H 3 | 4 | #include 5 | 6 | #define EEPROM_BEGIN 1536 7 | #define EEPROM_LENGTH 50 8 | 9 | /** 10 | * @brief todo 这里可以优化,直接改成结构体 11 | * 12 | */ 13 | enum EEPROM_ADDRESS_ENUM 14 | { 15 | 16 | REMEMBER_WIFI = EEPROM_BEGIN + 1, // 记住wifi内存地址 17 | WIFI_MODE = EEPROM_BEGIN + 2, // wifi模式内存地址 18 | DIRECTION = EEPROM_BEGIN + 3, // 屏幕显示方向内存地址 19 | BRIGHTNESS = EEPROM_BEGIN + 4, // 屏幕亮度内存地址 20 | SLEEP_TIME = EEPROM_BEGIN + 5, // 从第5位到第9位来存睡眠时间相关 21 | BILIBILI_UID = EEPROM_BEGIN + 10, // 从10到14来存储bilibilii的UID 22 | COUNTDOWN_TIME = EEPROM_BEGIN + 15, // 从15到19来存储倒计时时间戳 23 | H12_ADDR = EEPROM_BEGIN + 20, // 12位存放H12标记 \\\ 这里占位,暂无任何意义 24 | ANNI_DAY = EEPROM_BEGIN + 21, // 10,11存放纪念日 月和 日 \\\ 这里占位,暂无任何意义 25 | LIGHT_VAL = EEPROM_BEGIN + 22 // 存放亮度的 26 | }; 27 | 28 | struct EEPROMTOOLS 29 | { 30 | /** 31 | * @brief 保存数据 32 | */ 33 | void saveData(uint8_t *data, int offset, int length) 34 | { 35 | for (int i = 0; i < length; i++) 36 | { 37 | EEPROM.write(offset + i, data[i] & 0xff); 38 | } 39 | EEPROM.commit(); 40 | } 41 | /** 42 | * @brief 只保存一位数据 43 | * 44 | * @param data 45 | * @param offset 46 | */ 47 | void saveDataOne(uint8_t data, int offset) 48 | { 49 | EEPROM.write(offset, data & 0xff); 50 | EEPROM.commit(); 51 | } 52 | 53 | /** 54 | * @brief 获取数据 55 | * 56 | * @param offset 57 | * @param length 58 | * @return uint8_t* 59 | */ 60 | uint8_t *loadData(int offset, int length) 61 | { 62 | unsigned char *arr = new uint8_t[length]; 63 | for (int i = 0; i < length; i++) 64 | { 65 | arr[i] = EEPROM.read(offset + i); 66 | } 67 | return arr; 68 | } 69 | 70 | /** 71 | * @brief 获取一位数据 72 | */ 73 | uint8_t loadDataOne(int offset) 74 | { 75 | unsigned char arr = EEPROM.read(offset); 76 | return arr; 77 | } 78 | 79 | /** 80 | * @brief 删除数据 81 | */ 82 | void clearData(int offset, int length) 83 | { 84 | for (int i = 0; i < length; i++) 85 | { 86 | EEPROM.write(offset + i, 0x0); 87 | } 88 | EEPROM.commit(); 89 | } 90 | 91 | /** 92 | * @brief 删除全部数据 93 | */ 94 | void clearAll() 95 | { 96 | EEPROM.begin(4096); 97 | for (int i = 0; i < 4096; i++) 98 | { 99 | EEPROM.write(i, 0x0); 100 | } 101 | EEPROM.end(); 102 | } 103 | 104 | /** 105 | * @brief 备份自用数据到内存 106 | */ 107 | uint8_t *backupAll() 108 | { 109 | uint8_t *arr = new uint8_t[EEPROM_LENGTH]; 110 | EEPROM.begin(2048); 111 | for (int i = 0; i < EEPROM_LENGTH; i++) 112 | { 113 | arr[i] = EEPROM.read(EEPROM_BEGIN + i); 114 | } 115 | EEPROM.end(); 116 | return arr; 117 | } 118 | 119 | /** 120 | * @brief 恢复内存数据到EEPROM 121 | */ 122 | void restoreAll(uint8_t *arr) 123 | { 124 | EEPROM.begin(2048); 125 | for (int i = 0; i < EEPROM_LENGTH; i++) 126 | { 127 | EEPROM.write(EEPROM_BEGIN + i, arr[i]); 128 | } 129 | EEPROM.end(); 130 | } 131 | }; 132 | 133 | static EEPROMTOOLS EEPROMTool; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Functions.cpp: -------------------------------------------------------------------------------- 1 | #include "Functions.h" 2 | 3 | Functions::Functions() 4 | { 5 | } 6 | 7 | Functions::Functions(PilotLight *pilotLightobj) 8 | { 9 | pilotLight = pilotLightobj; 10 | } 11 | 12 | /** 13 | * @brief 重置功能和模式 14 | * 15 | */ 16 | void Functions::reset() 17 | { 18 | currPower = 0; 19 | currMode[currPower] = 0; 20 | } 21 | 22 | /** 23 | * @brief 设置功能和模式 24 | * 25 | * @param power 26 | * @param mode 27 | */ 28 | void Functions::setPowerAndMode(unsigned char power, unsigned char mode) 29 | { 30 | currPower = power; 31 | currMode[currPower] = mode; 32 | } 33 | 34 | /** 35 | * @brief 设置功能 36 | * 37 | * @param power 38 | */ 39 | void Functions::setPower(unsigned char power) 40 | { 41 | currPower = power; 42 | } 43 | 44 | /** 45 | * @brief 设置当前模式 46 | * 47 | * @param mode 48 | */ 49 | void Functions::setMode(unsigned char mode) 50 | { 51 | currMode[currPower] = mode; 52 | } 53 | 54 | /** 55 | * @brief 功能加 目前基本都只用到了功能加 56 | * 57 | */ 58 | void Functions::powerUp() 59 | { 60 | // 当前功能加到超过最大值时,就重置为默认的功能 61 | if (currPower == POWER_SIZE - 1) 62 | { 63 | currPower = 0; 64 | } 65 | else 66 | { 67 | ++currPower; // 功能加加 68 | } 69 | pilotLight->flashing(50); // 功能处理完闪烁一下状态指示灯 70 | } 71 | 72 | /** 73 | * @brief 功能减 74 | * 75 | */ 76 | void Functions::powerDown() 77 | { 78 | // todo 这个函数先不写 79 | } 80 | 81 | /** 82 | * @brief 模式加 目前基本都只用到了模式加 83 | * 84 | * @param power 85 | */ 86 | void Functions::modeUp() 87 | { 88 | // 如果说当前模式超过最大模式,则设置为初始模式 89 | if (currMode[currPower] == (modePower[currPower] - 1)) 90 | { 91 | currMode[currPower] = 0; 92 | } 93 | else 94 | { 95 | ++currMode[currPower]; 96 | } 97 | pilotLight->flashing(50); // 功能处理完闪烁一下状态指示灯 98 | } 99 | 100 | /** 101 | * @brief 模式减 102 | * 103 | * @param power 104 | */ 105 | void Functions::modeDown() 106 | { 107 | // todo 这个函数先不写 108 | } 109 | 110 | /** 111 | * @brief 获取当前功能 112 | * 113 | * @return unsigned char 114 | */ 115 | unsigned char Functions::getCurrPower() 116 | { 117 | return currPower; 118 | } 119 | 120 | /** 121 | * @brief 获取当前模式 122 | * 123 | * @return unsigned char 124 | */ 125 | unsigned char Functions::getCurrMode() 126 | { 127 | return currMode[currPower]; 128 | } -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Functions.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_H 2 | #define FUNCTIONS_H 3 | #include "PilotLight.h" 4 | 5 | enum ModeEnum 6 | { 7 | // 功能0: 显示时间 8 | SHOW_TIME = 0, 9 | // 功能1: 显示日期 10 | SHOW_DATE = 1, 11 | // 功能2: 显示日期 12 | POWER2 = 2, 13 | // 显示bilibili粉丝数量 14 | BILIFANS = 3, 15 | // 显示用户自定义数据 16 | CUSTOM = 4, 17 | // 显示倒计时 18 | COUNTDOWN = 5, 19 | // NTP更新时间 20 | RESETTIME = 98, 21 | // 系统重置 22 | RESET = 99 23 | }; 24 | 25 | #define POWER_SIZE 6 26 | 27 | const int modePower[POWER_SIZE] = {3, 3, 1, 1, 5, 1}; // 每个功能对应多少种模式 28 | 29 | class Functions 30 | { 31 | private: 32 | /** 33 | * @brief 状态指示灯对象 34 | * 35 | */ 36 | PilotLight *pilotLight; 37 | 38 | /** 39 | * @brief 当前功能对应的当前模式,因为用很多不同的功能,每个功能又有不同的模式,所以这里就要列举不同功能对应的不同模式 40 | * 41 | */ 42 | unsigned char currMode[POWER_SIZE] = {0, 0, 0, 0, 0, 1}; 43 | 44 | /** 45 | * @brief 当前所处在的功能 46 | * 47 | */ 48 | unsigned char currPower = 0; 49 | 50 | public: 51 | Functions(); 52 | 53 | /** 54 | * @brief 带参数的构造函数 55 | * 56 | * @param pilotLightobj 57 | */ 58 | Functions(PilotLight *pilotLightobj); 59 | 60 | /** 61 | * @brief 重置功能以及模式 62 | * 63 | */ 64 | void reset(); 65 | 66 | /** 67 | * @brief 设置功能和模式函数 68 | * 69 | * @param power 70 | * @param mode 71 | */ 72 | void setPowerAndMode(unsigned char power, unsigned char mode); 73 | 74 | /** 75 | * @brief 设置功能 76 | * 77 | * @param power 78 | */ 79 | void setPower(unsigned char power); 80 | 81 | /** 82 | * @brief 设置当前模式 83 | * 84 | * @param mode 85 | */ 86 | void setMode(unsigned char mode); 87 | 88 | /** 89 | * @brief 功能加 目前基本都只用到了功能加 90 | * 91 | */ 92 | void powerUp(); 93 | 94 | /** 95 | * @brief 功能减 96 | * 97 | */ 98 | void powerDown(); 99 | 100 | /** 101 | * @brief 模式加 目前基本都只用到了模式加 102 | * 103 | * @param power 104 | */ 105 | void modeUp(); 106 | 107 | /** 108 | * @brief 模式减 109 | * 110 | * @param power 111 | */ 112 | void modeDown(); 113 | 114 | /** 115 | * @brief 获取当前功能 116 | * 117 | * @return unsigned char 118 | */ 119 | unsigned char getCurrPower(); 120 | 121 | /** 122 | * @brief 获取当前模式 123 | * 124 | * @return unsigned char 125 | */ 126 | unsigned char getCurrMode(); 127 | }; 128 | 129 | #endif -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/HttpTool.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpTool.h" 2 | 3 | HttpTool::HttpTool() {} 4 | 5 | void HttpTool::initHttptool() 6 | { 7 | } 8 | 9 | void HttpTool::bilibiliFans() 10 | { 11 | if (!is_need_update_bilibili) 12 | { 13 | return; 14 | } 15 | fans = 0; // 每次都先重置粉丝数量,避免出现问题 16 | biliUid = loadBuid(); // 每次都重新加载bilibili用户ID 17 | Serial.println(biliUid); 18 | 19 | if (WiFi.status() != WL_CONNECTED) // 确保wifi网络是可用的,不可用则忽略 20 | { 21 | Serial.println("no wifi"); 22 | is_need_update_bilibili = false; 23 | return; 24 | } 25 | std::unique_ptr client(new BearSSL::WiFiClientSecure); 26 | client->setFingerprint(fingerprint); 27 | client->setInsecure(); 28 | // 直接调用B站接口获取粉丝数量 29 | espClient.begin(*client, bilibiliFansApi + biliUid); 30 | int httpCode = espClient.GET(); 31 | Serial.println(httpCode); 32 | if (httpCode > 0) 33 | { 34 | if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) 35 | { 36 | String json = espClient.getString(); 37 | Serial.println(json); 38 | DynamicJsonDocument doc(2048); 39 | deserializeJson(doc, json); 40 | // 获取bilibili粉丝数量 41 | fans = doc["data"]["follower"]; 42 | Serial.println(fans); 43 | is_need_update_bilibili = false; 44 | } 45 | } 46 | else 47 | { 48 | Serial.println("调用BIlibili的接口失败!"); 49 | } 50 | espClient.end(); 51 | } 52 | /** 53 | * @brief 保存bilibili的UID到EEPROM 54 | * 55 | * @param uid 56 | */ 57 | void HttpTool::saveBuid(long uid) 58 | { 59 | uint8_t arr[5]; 60 | for (int i = 0; i < 5; i++) 61 | { 62 | arr[i] = (uid & 0xff); 63 | uid >>= 8; 64 | } 65 | EEPROMTool.saveData(arr, BILIBILI_UID, 5); 66 | } 67 | 68 | /** 69 | * @brief 从EEPROM中加载bilibili的UID 70 | * 71 | * @return long 72 | */ 73 | long HttpTool::loadBuid() 74 | { 75 | long uid = 0; 76 | uint8_t *temp = EEPROMTool.loadData(BILIBILI_UID, 5); // 这里的97处理的不得当,后续优化,但是不影响实际功能 77 | for (int i = 0; i < 5; i++) 78 | { 79 | uid += temp[i] << (i * 8); 80 | } 81 | // 用完以后删除内存 82 | free(temp); 83 | return uid; 84 | } 85 | 86 | void HttpTool::updateBilibiliFlag() 87 | { 88 | is_need_update_bilibili = true; 89 | } -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/HttpTool.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_TOOL_H 2 | #define HTTP_TOOL_H 3 | 4 | #include "EEPROMTool.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static bool is_need_update_bilibili = false; // 判断是否需要更新bilibili粉丝数flag 12 | 13 | const uint8_t fingerprint[20] = {0x40, 0xaf, 0x00, 0x6b, 0xec, 0x90, 0x22, 0x41, 0x8e, 0xa3, 0xad, 0xfa, 0x1a, 0xe8, 0x25, 0x41, 0x1d, 0x1a, 0x54, 0xb3}; 14 | 15 | class HttpTool 16 | { 17 | private: 18 | /** 19 | * http客户端 20 | */ 21 | HTTPClient espClient; 22 | /** 23 | * WiFiMulti 24 | * 25 | */ 26 | ESP8266WiFiMulti WiFiMulti; 27 | 28 | /** 29 | * 加载BiliBili_Uid 30 | */ 31 | long loadBuid(); 32 | 33 | /** 34 | * bilibili用户ID 35 | */ 36 | long biliUid = 0; 37 | 38 | /** 39 | * 获取bilibili粉丝数量的接口 (此处欠妥,先这用着,后续优化) 40 | */ 41 | const String bilibiliFansApi = "https://api.bilibili.com/x/relation/stat?vmid="; 42 | 43 | public: 44 | HttpTool(); 45 | 46 | /** 47 | * bilibili粉丝数 48 | */ 49 | long fans = 0; 50 | 51 | /** 52 | * 初始化 53 | */ 54 | void initHttptool(); 55 | 56 | /** 57 | * 获取bilibili粉丝数量 58 | */ 59 | void bilibiliFans(); 60 | 61 | /** 62 | * 保存BiliBili_Uid 63 | */ 64 | void saveBuid(long uid); 65 | 66 | /** 67 | * @brief 时间戳++ 68 | * 69 | */ 70 | static void updateBilibiliFlag(); 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Lattice.h: -------------------------------------------------------------------------------- 1 | #ifndef LATTICE_H 2 | #define LATTICE_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "System.h" 6 | #include // 此示例程序需要使用FastLED库 7 | 8 | const uint8_t rowLength = 8; 9 | const uint8_t columnLength = 4; 10 | const int WSPIN = D2; // WS2812对应的IO口 11 | #define LED_TYPE WS2812 // LED灯带型号 12 | #define COLOR_ORDER GRB // RGB灯珠中红色、绿色、蓝色LED的排列顺序 13 | 14 | const unsigned char number_font_small[11][rowLength] = { 15 | {0x0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0x0, 0x0}, // 0 16 | {0x0, 0x40, 0xC0, 0x40, 0x40, 0xE0, 0x0, 0x0}, // 1 17 | {0x0, 0xE0, 0x20, 0xE0, 0x80, 0xE0, 0x0, 0x0}, // 2 18 | {0x0, 0xE0, 0x20, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 3 19 | {0x0, 0xA0, 0xA0, 0xE0, 0x20, 0x20, 0x0, 0x0}, // 4 20 | {0x0, 0xE0, 0x80, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 5 21 | {0x0, 0xE0, 0x80, 0xE0, 0xA0, 0xE0, 0x0, 0x0}, // 6 22 | {0x0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0}, // 7 23 | {0x0, 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, 0x0, 0x0}, // 8 24 | {0x0, 0xE0, 0xA0, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 9 25 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} // null 26 | }; 27 | 28 | const unsigned char number_font_middle[10][rowLength] = { 29 | {0x0, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0}, // 0 30 | {0x0, 0x40, 0xc0, 0x40, 0x40, 0x40, 0x40, 0xe0}, // 1 31 | {0x0, 0xf0, 0x10, 0x10, 0xf0, 0x80, 0x80, 0xf0}, // 2 32 | {0x0, 0xf0, 0x10, 0x10, 0xf0, 0x10, 0x10, 0xf0}, // 3 33 | {0x0, 0x90, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x10}, // 4 34 | {0x0, 0xf0, 0x80, 0x80, 0xf0, 0x10, 0x10, 0xf0}, // 5 35 | {0x0, 0xf0, 0x80, 0x80, 0xf0, 0x90, 0x90, 0xf0}, // 6 36 | {0x0, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, // 7 37 | {0x0, 0xf0, 0x90, 0x90, 0xf0, 0x90, 0x90, 0xf0}, // 8 38 | {0x0, 0xf0, 0x90, 0x90, 0xf0, 0x10, 0x10, 0xf0} // 9 39 | }; 40 | 41 | const unsigned char number_font_animations[10][16] = { 42 | {0x40, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 43 | 0x0, 0x0, 0x0, 0x0}, 44 | {0x20, 0x11, 0x21, 0x31, 0x41, 0x50, 0x51, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 45 | 0x0, 0x0, 0x0}, 46 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x31, 0x30, 0x40, 0x50, 0x51, 0x52, 0x0, 0x0, 47 | 0x0, 0x0, 0x0}, 48 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x31, 0x30, 0x42, 0x50, 0x51, 0x52, 0x0, 0x0, 49 | 0x0, 0x0, 0x0}, 50 | {0x10, 0x20, 0x30, 0x31, 0x12, 0x22, 0x32, 0x42, 0x52, 0x0, 0x0, 0x0, 0x0, 51 | 0x0, 0x0, 0x0}, 52 | {0x12, 0x11, 0x10, 0x20, 0x30, 0x31, 0x32, 0x42, 0x52, 0x51, 0x50, 0x0, 0x0, 53 | 0x0, 0x0, 0x0}, 54 | {0x12, 0x11, 0x10, 0x20, 0x30, 0x40, 0x50, 0x51, 0x52, 0x42, 0x32, 0x31, 55 | 0x0, 0x0, 0x0, 0x0}, 56 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 57 | 0x0, 0x0, 0x0}, 58 | {0x31, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 59 | 0x40, 0x0, 0x0, 0x0}, 60 | {0x31, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 61 | 0x0, 0x0, 0x0, 0x0}}; 62 | 63 | /** 64 | * WiFi连接图标 65 | */ 66 | const unsigned char wifiicon[columnLength][rowLength] = { 67 | {0x0, 0xba, 0x20, 0xba, 0xa2, 0xa2, 0x0, 0x0}, 68 | {0x0, 0x22, 0x22, 0x2a, 0x2e, 0x14, 0x0, 0x0}, 69 | {0x0, 0x48, 0x68, 0x79, 0x58, 0x48, 0x0, 0x0}, 70 | {0x0, 0x37, 0x45, 0x45, 0x45, 0x37, 0x0, 0x0}}; 71 | 72 | /** 73 | * 没有wifi的图标 74 | */ 75 | const unsigned char nowifiicon[columnLength][rowLength] = { 76 | {0x0, 0x74, 0x40, 0x74, 0x44, 0x44, 0x0, 0x0}, 77 | {0x0, 0x45, 0x44, 0x55, 0x55, 0x29, 0x0, 0x0}, 78 | {0x0, 0xc0, 0x40, 0x4e, 0x40, 0xc0, 0x0, 0x0}, 79 | {0x0, 0x25, 0x35, 0x3d, 0x2d, 0x25, 0x0, 0x0}}; 80 | 81 | /** 82 | * 更新数据字符 83 | */ 84 | const unsigned char updateClock[columnLength][rowLength] = { 85 | {0x0, 0x0, 0x0, 0xa8, 0x0, 0x0, 0x0, 0x0}, 86 | {0x0, 0xd4, 0x18, 0x10, 0x18, 0xd4, 0x0, 0x0}, 87 | {0x0, 0x1d, 0x15, 0x15, 0x15, 0xdd, 0x0, 0x0}, 88 | {0x0, 0x1d, 0x11, 0x11, 0x11, 0x1d, 0x0, 0x0}}; 89 | 90 | /** 91 | * ota 更新图标 92 | */ 93 | const unsigned char otaicon[columnLength][rowLength] = { 94 | {0x0, 0x24, 0x8, 0x10, 0x20, 0x48, 0x0, 0x0}, 95 | {0x0, 0xee, 0xaa, 0xaa, 0xaa, 0xee, 0x0, 0x0}, 96 | {0x0, 0x90, 0x2a, 0x28, 0x3a, 0x28, 0x0, 0x0}, 97 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 98 | 99 | /** 100 | * 更新完成 101 | */ 102 | const unsigned char otaokicon[columnLength][rowLength] = { 103 | {0x0, 0x80, 0x0, 0x2a, 0x0, 0x80, 0x0, 0x0}, 104 | {0x0, 0x3a, 0x2b, 0x2a, 0x2b, 0x3a, 0x0, 0x0}, 105 | {0x0, 0x90, 0x29, 0x28, 0x39, 0x28, 0x0, 0x0}, 106 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 107 | 108 | /** 109 | * 更新失败 110 | */ 111 | const unsigned char otafailicon[columnLength][rowLength] = { 112 | {0x0, 0x28, 0x88, 0xa8, 0xa8, 0xae, 0x0, 0x0}, 113 | {0x0, 0x39, 0x22, 0x3b, 0x22, 0x22, 0x0, 0x0}, 114 | {0x0, 0x90, 0x29, 0x28, 0x39, 0x28, 0x0, 0x0}, 115 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 116 | 117 | /** 118 | * 没有设置任何数据 119 | */ 120 | const unsigned char noseticon[32] = { 121 | 0x0, 0xbe, 0x8, 0x8, 0x88, 0x8, 0x8, 0x88, 0x0, 0xef, 0x8, 122 | 0x8, 0xef, 0x28, 0x28, 0xef, 0x0, 0xe3, 0x22, 0x22, 0x23, 0x20, 123 | 0x20, 0xe3, 0x0, 0x45, 0x45, 0x65, 0x75, 0x5d, 0x4d, 0x45}; 124 | 125 | /** 126 | * 图标集合 127 | */ 128 | const unsigned char icons[4][rowLength] = { 129 | {0x3C, 0x42, 0x85, 0x89, 0x81, 0x81, 0x42, 0x3C}, // 时钟图标 130 | {0x0, 0xCE, 0xD1, 0x10, 0x10, 0x11, 0xE, 0x0}, // 温度图标 131 | {0x42, 0x24, 0xFF, 0x81, 0xA5, 0x99, 0xFF, 0x42}, // Bilibili图标 132 | {0x3C, 0x42, 0x81, 0xA5, 0x81, 0x99, 0x42, 0x3C} // 微笑 133 | }; 134 | 135 | struct LatticeSetting 136 | { 137 | /** 138 | * 点阵屏幕是否显示 139 | */ 140 | bool isShutdown = false; 141 | 142 | /** 143 | * 屏幕亮度 144 | */ 145 | uint8_t brightness = 0x0; 146 | 147 | /** 148 | * 显示方向, true: 正方向,false: 反方向 149 | */ 150 | bool direction = false; 151 | 152 | /** 153 | * 用户自定义数据 154 | */ 155 | uint8_t userData[32]; 156 | 157 | /** 158 | * 速度 159 | */ 160 | uint8_t speed = 1; 161 | 162 | /** 163 | * @brief 光敏值(光敏电阻的值) (0 - 255) 164 | * 165 | */ 166 | uint8_t light = 20; 167 | }; 168 | 169 | class Lattice 170 | { 171 | protected: 172 | /** 173 | * 建立光带leds 174 | */ 175 | CRGB leds[256]; 176 | 177 | /** 178 | * 点阵显示屏个数 179 | */ 180 | int latticeNums; 181 | 182 | /** 183 | * @brief 184 | * 185 | */ 186 | int tempindex = 3; 187 | 188 | /** 189 | * 是否重置 190 | */ 191 | bool isReset = true; 192 | 193 | /** 194 | * 实际显示的数组 195 | */ 196 | unsigned char data[4][rowLength]; 197 | 198 | /** 199 | * 缓冲数组 200 | */ 201 | unsigned char buff[4][rowLength]; 202 | 203 | /** 204 | * @brief system对象 205 | * 206 | */ 207 | System *systemObj; 208 | 209 | /** 210 | * 初始化点阵 211 | */ 212 | void initData(); 213 | 214 | /** 215 | * 刷新显示 216 | */ 217 | void refreshLed(); 218 | 219 | /** 220 | * 显示数据内容 221 | */ 222 | void showDataLed(const unsigned char (*arr)[rowLength]); 223 | 224 | /** 225 | * 获取需要显示数字的数据 226 | */ 227 | unsigned char *getNumData(uint8_t nums, bool showzero); 228 | 229 | /** 230 | * 获取大号的数字 231 | */ 232 | unsigned char *getBigNumData(uint8_t number, uint8_t offset); 233 | 234 | /** 235 | * 偏移缓存数组 236 | */ 237 | void offsetBuff(uint8_t index, int8_t offset); 238 | 239 | /** 240 | * 偏移显示数组 241 | */ 242 | void offsetData(uint8_t index, int8_t offset); 243 | 244 | /** 245 | * 左移显示内容 246 | */ 247 | void rightMoveData(bool isright); 248 | 249 | /** 250 | * 左移显示缓冲数据 251 | */ 252 | void rightMoveBuff(); 253 | 254 | /** 255 | * 向下移动数据 256 | */ 257 | void downMoveData(bool isdown); 258 | 259 | /** 260 | * 向下移动缓冲数据 261 | */ 262 | void downMoveBuff(); 263 | 264 | /** 265 | * 向下移动缓冲数据 266 | */ 267 | void downMoveBuff(uint8_t *arr); 268 | 269 | /** 270 | * 测试功能 271 | */ 272 | void progressBar(uint8_t offset); 273 | 274 | /** 275 | * 向上或者向下移动内容 276 | */ 277 | void upOrDownMove(uint8_t index, uint8_t region, bool direction); 278 | 279 | public: 280 | LatticeSetting latticeSetting; 281 | 282 | /** 283 | * @brief 显示颜色 284 | * 285 | */ 286 | CRGB showColor = CRGB::White; 287 | 288 | /** 289 | * 构造函数 290 | */ 291 | Lattice(); 292 | /** 293 | * 初始化 294 | */ 295 | void init(); 296 | 297 | /** 298 | * 重置 299 | */ 300 | void reset(); 301 | 302 | /** 303 | * 启用/停用点阵 304 | */ 305 | void shutdown(bool down); 306 | 307 | /** 308 | * 设置显示亮度 309 | */ 310 | void setBrightness(uint8_t bright, bool save); 311 | 312 | /** 313 | * 设置显示方向 314 | */ 315 | void setDirection(bool direct); 316 | 317 | /** 318 | * 显示wifi图案 319 | */ 320 | void showLongIcon(uint8_t index); 321 | 322 | /** 323 | * 显示数字 324 | */ 325 | void showNum(uint8_t index, int nums); 326 | 327 | /** 328 | * 动态显示时间 329 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 330 | * ps: 这里为啥还要搞一个回调函数,既然要有好看的动画就难免需要有点牺牲在里面,所以这里的回调就是处理我们小程序的UDP请求或者是MQTT之类的 331 | */ 332 | void showTime(uint8_t *arr); 333 | 334 | /** 335 | * 显示时间模式2 336 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 337 | * ps: 这里为啥还要搞一个回调函数,既然要有好看的动画就难免需要有点牺牲在里面,所以这里的回调就是处理我们小程序的UDP请求或者是MQTT之类的 338 | */ 339 | void showTime2(uint8_t *arr); 340 | 341 | /** 342 | * 显示时间模式2 343 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 344 | * ps: 这里为啥还要搞一个回调函数,既然要有好看的动画就难免需要有点牺牲在里面,所以这里的回调就是处理我们小程序的UDP请求或者是MQTT之类的 345 | */ 346 | void showTime3(uint8_t *arr); 347 | 348 | /** 349 | * 显示倒计时 350 | */ 351 | void showCountDownTime(long remain, uint8_t *arr, bool showmode, bool minutechange); 352 | 353 | /** 354 | * 显示长的数字 355 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 356 | */ 357 | void showLongNumber(uint8_t *arr); 358 | 359 | /** 360 | * 显示数字和图标 361 | */ 362 | void showNumAndIcon(uint8_t no, uint8_t *arr); 363 | 364 | /** 365 | * 显示日期2 366 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 367 | */ 368 | void showDate2(uint8_t *arr); 369 | 370 | /** 371 | * 显示日期2 372 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 373 | */ 374 | void showDate3(uint8_t *arr); 375 | 376 | /** 377 | * 显示温度 378 | */ 379 | void showTemperature(uint8_t *arr); 380 | 381 | /** 382 | * 显示用户自定义数据 383 | */ 384 | void showUserData(uint8_t mode); 385 | 386 | /** 387 | * 清空显示 388 | */ 389 | void clearShow(); 390 | 391 | /** 392 | * 全量显示 393 | */ 394 | void showFull(); 395 | 396 | /** 397 | * 左右翻转 398 | */ 399 | void reversalLR(uint8_t index); 400 | 401 | /** 402 | * 上下翻转 403 | */ 404 | void reversalUD(uint8_t index); 405 | 406 | /** 407 | * 闪 408 | */ 409 | void lightning(uint8_t index); 410 | 411 | /** 412 | * OTA 更新 413 | */ 414 | void showOtaUpdate(uint8_t num); 415 | }; 416 | 417 | #endif 418 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/LatticePlus.cpp: -------------------------------------------------------------------------------- 1 | #include "LatticePlus.h" 2 | 3 | LatticePlus::LatticePlus() 4 | { 5 | init(); 6 | } 7 | 8 | LatticePlus::LatticePlus(System *systems) 9 | { 10 | systemObj = systems; 11 | init(); 12 | } 13 | 14 | /** 15 | @brief 单个点阵纵向移动函数 16 | 17 | @param tempdata 取模数据 18 | @param dl 移动长度 19 | @return uint8_t 20 | */ 21 | uint8_t *LatticePlus::single_down_move(const unsigned char *tempdata, int dl) 22 | { 23 | uint8_t *arr = new uint8_t[8]; 24 | memcpy(arr, tempdata, 8); 25 | if (dl > 8 || dl % 8 == 0) 26 | { 27 | return arr; 28 | } 29 | uint8_t tmp = tempdata[7]; 30 | for (int k = 7; k > 0; k--) 31 | { 32 | arr[k] = tempdata[k - dl]; 33 | } 34 | arr[0] = tmp; 35 | return arr; 36 | } 37 | 38 | /** 39 | @brief 单个点阵横向移动函数1 40 | 41 | @param tempdata 取模数据 42 | @param ll 移动长度 43 | @return uint8_t 44 | */ 45 | uint8_t *LatticePlus::single_left_move1(uint8_t *tempdata, int ll) 46 | { 47 | uint8_t *arr = new uint8_t[8]; 48 | // 如果横向位移小于最大位移,则使用最简单的模式 49 | for (int i = 0; i < 8; i++) 50 | { 51 | arr[i] = tempdata[i] >> ll; 52 | } 53 | return arr; 54 | } 55 | 56 | /** 57 | @brief 单个点阵横向移动函数2 58 | 59 | @param tempdata 取模数据 60 | @param ll 移动长度 61 | @return uint8_t 62 | */ 63 | uint8_t *LatticePlus::single_left_move2(uint8_t *tempdata, int ll) 64 | { 65 | uint8_t *arr = new uint8_t[8]; 66 | // 2. 根据长度再得到第二个数组 67 | int rightMove = 8 - ll; 68 | for (int i = 0; i < 8; i++) 69 | { 70 | arr[i] = 0xff & (tempdata[i] << rightMove); 71 | } 72 | return arr; 73 | } 74 | 75 | void LatticePlus::absolute_position_show(const unsigned char *tempdata, int width, int x, int y) 76 | { 77 | int t = x / 8; // 记录哪个点阵数据需要被改变 78 | uint8_t *ydata = single_down_move(tempdata, y); // 获取内容向下移动过后的数据 79 | int xl = (t > 0) ? (x % 8) : x; // 记录当前x坐标,y坐标 80 | uint8_t *xdata = single_left_move1(ydata, xl); 81 | for (int i = 0; i < 8; i++) 82 | { 83 | data[3 - t][i] = (data[3 - t][i] ^ xdata[i]); 84 | } 85 | free(xdata); 86 | if (xl > (8 - width) && t < 3) // 判断如果内容需要向下一个点阵移动的话,就渲染下一个点阵屏幕的内容 87 | { 88 | uint8_t *xdata1 = single_left_move2(ydata, xl); 89 | for (int i = 0; i < 8; i++) 90 | { 91 | data[3 - t - 1][i] = data[3 - t - 1][i] ^ xdata1[i]; 92 | } 93 | free(xdata1); 94 | } 95 | free(ydata); 96 | } 97 | 98 | void LatticePlus::boot_animation() 99 | { 100 | int x = 0; 101 | for (int i = 0; i < 8; i++) 102 | { 103 | absolute_position_show(mogu[0], 8, x, 0); // 显示蘑菇 104 | refreshLed(); 105 | delay(200); 106 | initData(); 107 | x++; 108 | absolute_position_show(mogu[1], 8, x, 0); // 显示蘑菇 109 | refreshLed(); 110 | delay(200); 111 | initData(); 112 | x++; 113 | absolute_position_show(mogu[2], 8, x, 0); // 显示蘑菇 114 | refreshLed(); 115 | delay(200); 116 | initData(); 117 | x++; 118 | } 119 | delay(500); 120 | } -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/LatticePlus.h: -------------------------------------------------------------------------------- 1 | #ifndef LATTICE_PLUS_H 2 | #define LATTICE_PLUS_H 3 | 4 | #include "Lattice.h" 5 | 6 | const unsigned char mogu[3][8] = { 7 | {0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x5A, 0xA5}, //蘑菇1 8 | {0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x5A, 0x5A}, //蘑菇2 9 | {0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x24, 0x5A} //蘑菇3 10 | }; 11 | 12 | /** 13 | * @brief 继承Lattice来写代码,Lattice写的东西实在是太多了,换一个新的写法 14 | * 15 | */ 16 | class LatticePlus : public Lattice 17 | { 18 | private: 19 | /** 20 | @brief 单个点阵纵向移动函数 21 | 22 | @param tempdata 取模数据 23 | @param dl 移动长度 24 | @return uint8_t 25 | */ 26 | uint8_t *single_down_move(const unsigned char *tempdata, int dl); 27 | 28 | /** 29 | @brief 单个点阵横向移动函数1 30 | 31 | @param tempdata 取模数据 32 | @param ll 移动长度 33 | @return uint8_t 34 | */ 35 | uint8_t *single_left_move1(uint8_t *tempdata, int ll); 36 | 37 | /** 38 | @brief 单个点阵横向移动函数2 39 | 40 | @param tempdata 取模数据 41 | @param ll 移动长度 42 | @return uint8_t 43 | */ 44 | uint8_t *single_left_move2(uint8_t *tempdata, int ll); 45 | 46 | public: 47 | LatticePlus(); 48 | 49 | /** 50 | * 构造函数 51 | */ 52 | LatticePlus(System *systems); 53 | 54 | /** 55 | * @brief 根据绝对坐标位置显示8x8点阵内容 56 | * 57 | * @param tempdata 8x8点阵取模数据 58 | * @param width 数据位宽 59 | * @param x x轴坐标 60 | * @param y y坐标 61 | */ 62 | void absolute_position_show(const unsigned char *tempdata, int width, int x, int y); 63 | 64 | /** 65 | * @brief 开机动画 66 | * 67 | */ 68 | void boot_animation(); 69 | }; 70 | #endif -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Ota.cpp: -------------------------------------------------------------------------------- 1 | #include "Otas.h" 2 | Otas *Otas::s_otas; 3 | 4 | Otas::Otas() { s_otas = this; } 5 | 6 | Otas::Otas(LatticePlus *latticeobj, PilotLight *pilotLightobj) 7 | { 8 | lattice = latticeobj; 9 | pilotLight = pilotLightobj; 10 | s_otas = this; 11 | } 12 | 13 | void Otas::flashing() 14 | { 15 | pilotLight->flashing(20); 16 | } 17 | 18 | void Otas::showLongIcon(int type) 19 | { 20 | lattice->showLongIcon(type); 21 | } 22 | 23 | void Otas::showOtaUpdate(int process) 24 | { 25 | lattice->showOtaUpdate(process); 26 | } 27 | 28 | void Otas::reset() 29 | { 30 | lattice->reset(); 31 | } 32 | 33 | void Otas::update_started() 34 | { 35 | Serial.println("callback: ota is start!"); // 打印日志 36 | s_otas->reset(); // 重置显示内容 37 | Serial.println("callback: ota is start1!"); // 打印日志 38 | } 39 | 40 | void Otas::update_finished() 41 | { 42 | Serial.println("callback: ota is finish!"); // 打印日志 43 | s_otas->reset(); // 重置显示内容 44 | s_otas->showLongIcon(3); // 显示OTA更新成功图案 45 | } 46 | 47 | void Otas::update_progress(long cur, long total) 48 | { 49 | int process = (int)(((cur * 0.1) / (total * 0.1)) * 100); // 计算更新进度 50 | Serial.printf("callback: updateing %d of %d bytes...\n", cur, total); // 打印日志 51 | s_otas->flashing(); // 固件升级的时候LED闪 52 | s_otas->showOtaUpdate(process); // OTA显示当前进度图案 53 | } 54 | 55 | void Otas::update_error(int err) 56 | { 57 | Serial.printf("callback: ota is error: %d\n", err); // 打印日志 58 | s_otas->reset(); // 重置显示内容 59 | s_otas->showLongIcon(4); // 显示OTA更新失败图案 60 | } 61 | 62 | void Otas::updateOta(int version) 63 | { 64 | WiFiClient client; 65 | if (WiFi.status() == WL_CONNECTED) // 确保有网络 66 | { 67 | ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); 68 | ESPhttpUpdate.onStart(update_started); // 添加可选的回调通知程序 69 | ESPhttpUpdate.onEnd(update_finished); // 添加更新完成回调方法 70 | ESPhttpUpdate.onProgress(update_progress); // 添加更新中回调方法 71 | ESPhttpUpdate.onError(update_error); // 添加更新失败回调方法 72 | char *updateUrl = new char[50]; // 临时放url 73 | sprintf(updateUrl, "%s%d%s%d", "http://oss.lengff.com/iot/lattice/", version, ".bin?t=", millis() % 100); // 后面的对100取余就是为了解除文件CDN缓存 74 | Serial.println(updateUrl); 75 | t_httpUpdate_return ret = ESPhttpUpdate.update(client, updateUrl); 76 | free(updateUrl); 77 | switch (ret) 78 | { 79 | case HTTP_UPDATE_FAILED: 80 | Serial.printf("ote is fail, err code is:(%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); // 打印日志 81 | s_otas->reset(); // 重置显示内容 82 | s_otas->showLongIcon(4); // 显示OTA更新失败图案 83 | break; 84 | case HTTP_UPDATE_NO_UPDATES: 85 | Serial.println("not ota update!"); // 打印日志 86 | s_otas->showLongIcon(3); // 显示OTA更新成功图案 87 | break; 88 | case HTTP_UPDATE_OK: 89 | s_otas->reset(); // 打印日志 90 | s_otas->showLongIcon(3); // 显示OTA更新成功图案 91 | break; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Otas.h: -------------------------------------------------------------------------------- 1 | #ifndef OTAS_H 2 | #define OTAS_H 3 | #include "LatticePlus.h" 4 | #include "PilotLight.h" 5 | #include 6 | #include 7 | #include 8 | 9 | class Otas 10 | { 11 | private: 12 | /** 13 | * @brief 显示内容对象 14 | * 15 | */ 16 | LatticePlus *lattice; 17 | /** 18 | * @brief 状态指示灯对象 19 | * 20 | */ 21 | PilotLight *pilotLight; 22 | 23 | /** 24 | * @brief 搞个单例 25 | * 26 | */ 27 | static Otas *s_otas; 28 | 29 | /** 30 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 31 | * 32 | */ 33 | void flashing(); 34 | 35 | /** 36 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 37 | * 38 | */ 39 | void showLongIcon(int type); 40 | 41 | /** 42 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 43 | * 44 | */ 45 | void reset(); 46 | 47 | /** 48 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 49 | * 50 | */ 51 | void showOtaUpdate(int process); 52 | 53 | public: 54 | Otas(); 55 | 56 | Otas(LatticePlus *latticeobj, PilotLight *pilotLightobj); 57 | 58 | /** 59 | * @brief 60 | * 61 | * @param version 62 | */ 63 | static void updateOta(int version); 64 | 65 | protected: 66 | /** 67 | * @brief 更新开始回调函数 68 | * 69 | */ 70 | static void update_started(); 71 | 72 | /** 73 | * @brief 更新完成回调函数 74 | * 75 | */ 76 | static void update_finished(); 77 | /** 78 | * @brief 更新中回调函数 79 | * 80 | */ 81 | static void update_progress(long cur, long total); 82 | 83 | /** 84 | * @brief 更新错误回调函数 85 | * 86 | */ 87 | static void update_error(int err); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/PilotLight.cpp: -------------------------------------------------------------------------------- 1 | #include "PilotLight.h" 2 | 3 | PilotLight::PilotLight() 4 | { 5 | pinMode(LED_PIN, OUTPUT); 6 | } 7 | 8 | void PilotLight::flashing() 9 | { 10 | status = status == HIGH ? LOW : HIGH; 11 | digitalWrite(LED_PIN, status); 12 | } 13 | 14 | void PilotLight::flashing(int ms) 15 | { 16 | bright(); 17 | delay(ms); 18 | dim(); 19 | } 20 | 21 | void PilotLight::bright(){ 22 | digitalWrite(LED_PIN, LOW); 23 | } 24 | 25 | void PilotLight::dim(){ 26 | digitalWrite(LED_PIN, HIGH); 27 | } -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/PilotLight.h: -------------------------------------------------------------------------------- 1 | #ifndef PILOT_LIGHT 2 | #define PILOT_LIGHT 3 | 4 | #include 5 | 6 | #define LED_PIN D0 // LED引脚 7 | 8 | class PilotLight 9 | { 10 | private: 11 | int status = HIGH; 12 | 13 | public: 14 | PilotLight(); 15 | 16 | /** 17 | * LED 闪烁 18 | */ 19 | void flashing(); 20 | 21 | /** 22 | * @brief LED 闪烁 23 | * 24 | * @param ms 25 | */ 26 | void flashing(int ms); 27 | 28 | /** 29 | * @brief LED亮 30 | * 31 | */ 32 | void bright(); 33 | 34 | /** 35 | * @brief LED灭 36 | * 37 | */ 38 | void dim(); 39 | }; 40 | #endif -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/System.cpp: -------------------------------------------------------------------------------- 1 | #include "System.h" 2 | 3 | System::System() 4 | { 5 | } 6 | 7 | void System::init_callback(void (*callback)()) 8 | { 9 | callbackMethod = callback; 10 | } 11 | 12 | void System::reset_system() 13 | { 14 | EEPROMTool.clearAll(); // 删除EEPRON信息 15 | ESP.restart(); // 重启系统 16 | } 17 | 18 | /** 19 | * @brief uint8_t数组转long 20 | * 21 | * @param data 22 | * @return long 23 | */ 24 | long System::uint8t_to_long(uint8_t *data, int length) 25 | { 26 | long temp = 0; 27 | for (int i = 0; i < length; i++) 28 | { 29 | temp += data[i] << (i * 8); 30 | } 31 | return temp; 32 | } 33 | 34 | /** 35 | * @brief 重新定义延迟函数 36 | * 37 | * @param ms 38 | * @param callback 39 | */ 40 | void System::delay_time(int ms) 41 | { 42 | int timeFlag = millis(); 43 | while (true) 44 | { 45 | if (millis() - timeFlag >= ms) 46 | { 47 | break; 48 | } 49 | if (callbackMethod != NULL) 50 | { 51 | callbackMethod(); 52 | } 53 | } 54 | } 55 | 56 | bool System::is_overtime(int time) 57 | { 58 | if (millis() - tmp_value < time) 59 | { 60 | return false; 61 | } 62 | tmp_value = millis(); 63 | return true; 64 | } 65 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/System.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSTEM_H 2 | #define SYSTEM_H 3 | #include "EEPROMTool.h" 4 | #include 5 | 6 | static long tmp_value = 1l; 7 | 8 | class System 9 | { 10 | private: 11 | void (*callbackMethod)(); 12 | 13 | public: 14 | System(); 15 | 16 | void init_callback(void (*callback)()); 17 | 18 | /** 19 | * @brief 重启系统 20 | * 21 | */ 22 | static void reset_system(); 23 | 24 | /** 25 | * @brief uint8_t数组转long 26 | * 27 | * @param data 28 | * @return long 29 | */ 30 | static long uint8t_to_long(uint8_t *data, int length); 31 | 32 | /** 33 | * @brief 时间延迟函数 34 | * 35 | * @param ms 延迟毫秒数 36 | */ 37 | void delay_time(int ms); 38 | 39 | /** 40 | * @brief 判断是否超时 41 | * 42 | * @param time 43 | * @return true 44 | * @return false 45 | */ 46 | static bool is_overtime(int time); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Udps.cpp: -------------------------------------------------------------------------------- 1 | #include "Udps.h" 2 | 3 | Udps::Udps() 4 | { 5 | // todo 6 | } 7 | 8 | Udps::Udps(DateTimes *datetimesobj, LatticePlus *latticeobj, PilotLight *pilotLightobj) 9 | { 10 | datetimes = datetimesobj; 11 | lattice = latticeobj; 12 | pilotLight = pilotLightobj; 13 | } 14 | 15 | void Udps::initudp() 16 | { 17 | // todo 这里需要判断网络状态 18 | WiFi.hostByName(ntpServerName, timeServerIP); //将域名转换成IP地址 19 | udp.begin(localPort); // 启动监听本地端口 20 | } 21 | 22 | /** 23 | * @brief 发送NTP请求 24 | * 25 | */ 26 | void Udps::sendNTPpacket() 27 | { 28 | memset(packetBuffer, 0, NTP_PACKET_SIZE); // 将字节数组的数据全部设置为0 29 | packetBuffer[0] = 0b11100011; // 请求部分其实是有很多数据的,具体的请看参考请求报文说明,这里我们就只设置一个请求头部分即可 30 | udp.beginPacket(timeServerIP, remoteNtpPort); // 配置远端ip地址和端口 31 | udp.write(packetBuffer, NTP_PACKET_SIZE); // 把数据写入发送缓冲区 32 | udp.endPacket(); // 发送数据 33 | Serial.println("send ntp data"); // 34 | } 35 | 36 | long Udps::getNtpTimestamp() 37 | { 38 | int packetSize = udp.parsePacket(); //解析Udp数据包 39 | if (!packetSize) //解析包为空 40 | { 41 | return 0; 42 | } 43 | else //解析包不为空 44 | { 45 | udp.read(packetBuffer, NTP_PACKET_SIZE); // 解析UDP数据包中的数据 46 | // todo这里获取到的时间其实不是真实的时间,实际上还包含了网络延时的,但是为了方便,这里我们忽略这个因素的存在 47 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); // 取出t2时间的高位和低位数据拼凑成以秒为单位的时间戳 48 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 49 | unsigned long secsSince1900 = highWord << 16 | lowWord; // 拼凑成以秒为单位的时间戳(时间戳的记录以秒的形式从 1900-01-01 00:00:00算起) 50 | const unsigned long seventyYears = 2208988800UL; 51 | unsigned long timestamp = secsSince1900 - seventyYears; // 前面的32bit是时间戳的秒数(是用1900-01-01 00:00:00开始的秒数,但是我们的是1970年,所以需要减掉2208988800秒) 52 | timestamp = timestamp + 8 * 60 * 60; // 这里加8 是因为时区的问题,如果不加8,得到的结果就会是其他时区的时间 53 | return timestamp; 54 | } 55 | } 56 | 57 | Udpdata Udps::userLatticeLoop(uint8_t power, uint8_t mode, uint8_t version) 58 | { 59 | Udpdata udpdata; 60 | udpdata.lh = 0; 61 | int packetSize = udp.parsePacket(); //解析Udp数据包 62 | if (packetSize) //解析包不为空 63 | { 64 | pilotLight->flashing(100); // 每次接收到UDP数据的时候都闪烁一下LED灯 65 | memset(packetBuffer, 0, NTP_PACKET_SIZE); //每次都先清空掉原有的数据包 66 | udp.read(packetBuffer, NTP_PACKET_SIZE); // 读取UDP数据 67 | // Udp.remoteIP().toString().c_str()用于将获取的远端IP地址转化为字符串 68 | // Serial.printf("收到来自远程IP:%s(远程端口:%d)的数据包字节数:%d %X\n", udp.remoteIP().toString().c_str(), udp.remotePort(), packetSize, packetBuffer[8]); 69 | uint8_t vn = packetBuffer[0] & 0x3; // 数据包版本 70 | if (vn != 0x1) // 如果数据包版本不为1,则直接结束 71 | { 72 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 73 | replyPacket[0] = {0x01}; // 74 | udp.write(replyPacket); //把数据写入发送缓冲区 75 | udp.endPacket(); //发送数据 76 | return udpdata; 77 | } 78 | udpdata.rt = packetBuffer[0] >> 6; // 数据包接收成功返回值 79 | if (udpdata.rt == 0x0) // 如果返回类型值为0的时候,则上报状态信息值 80 | { 81 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 todo 这里的做法是错误的,我们自定义协议,返回的也应该是按协议来返回的,但是我就是想偷个懒 82 | replyPacket[0] = lattice->latticeSetting.isShutdown << 6; // 是否显示 83 | replyPacket[0] += lattice->latticeSetting.brightness << 2; // 显示亮度 84 | replyPacket[0] += lattice->latticeSetting.direction; // 显示方向 85 | replyPacket[1] = power; // 功能 86 | replyPacket[2] = mode; // 功能模式 87 | replyPacket[3] = lattice->latticeSetting.speed; // 动画移动速度 88 | replyPacket[4] = version; // 系统版本 89 | udp.write(replyPacket, 5); //把数据写入发送缓冲区 90 | udp.endPacket(); //发送数据 91 | return udpdata; 92 | } 93 | udpdata.te = (packetBuffer[0] & 0x3f) >> 2; // 功能模式 94 | udpdata.lh = packetBuffer[1]; // 数据长度 95 | Serial.print("UDP接收到的数据信息为: "); 96 | Serial.print(udpdata.rt); 97 | Serial.print(udpdata.te); 98 | Serial.println(udpdata.lh); 99 | // 数据包 100 | for (int i = 0; i < udpdata.lh; i++) 101 | { 102 | udpdata.data[i] = packetBuffer[i + 2]; 103 | } 104 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 105 | replyPacket[0] = {0x01}; // 106 | udp.write(replyPacket, 3); //把数据写入发送缓冲区 107 | udp.endPacket(); //发送数据 108 | return udpdata; 109 | } 110 | return udpdata; 111 | } 112 | 113 | /** 114 | * @brief 更新系统时间 115 | * 116 | */ 117 | void Udps::updateTime() 118 | { 119 | int count = 30; // 一共尝试五次 120 | sendNTPpacket(); // 向NTP服务器发请求,先发送更新时间的操作 121 | int lastSendTime = millis(); // 记录上一次发送更新时间的时间戳 122 | while (true) // 这里用一个死循环来搞定,先发送更新时间的操作 123 | { 124 | long timeStamp = getNtpTimestamp(); // 获取当前时间戳 125 | if (timeStamp != 0) // 说明已经获取到了时间信息,这里直接做更新操作 126 | { 127 | datetimes->setDateTimes(timeStamp); // 给时钟设置时间 128 | break; // 跳出死循环 129 | } 130 | else 131 | { 132 | if (millis() - lastSendTime >= 1000) // 如果说发送时间超出上次发送时间1秒钟,则重新发送NTP请求 133 | { 134 | sendNTPpacket(); // 向NTP服务器发请求,先发送更新时间的操作 135 | lastSendTime = millis(); 136 | count--; 137 | } 138 | } 139 | lattice->showLongIcon(2); // 这里延迟两秒是因为过程太快了,交互体验不好 140 | pilotLight->flashing(); // 校准时间LED闪烁 141 | delay(100); // 等待100ms 142 | if (count < 0) // 如果说所有机会都用完了 143 | { 144 | break; // 跳出死循环 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/Udps.h: -------------------------------------------------------------------------------- 1 | #ifndef UDPS_H 2 | #define UDPS_H 3 | 4 | #include "DateTimes.h" 5 | #include "LatticePlus.h" 6 | #include "PilotLight.h" 7 | #include 8 | #include 9 | #include 10 | 11 | #define localPort 1234 // 监听本地UDP数据包端口 12 | #define remoteNtpPort 123 // 远程服务端口 13 | #define NTP_PACKET_SIZE 48 // NTP数据包数据长度 14 | 15 | struct Udpdata 16 | { 17 | uint8_t rt; // 返回类型 18 | uint8_t te; // 数据类型 19 | uint8_t lh; // 数据长度 20 | uint8_t data[64]; // 数据包 21 | }; 22 | 23 | class Udps 24 | { 25 | private: 26 | /** 27 | * @brief 显示对象 28 | * 29 | */ 30 | LatticePlus *lattice; 31 | 32 | /** 33 | * @brief 状态指示灯对象 34 | * 35 | */ 36 | PilotLight *pilotLight; 37 | /** 38 | * @brief 操作时间的对象 39 | * 40 | */ 41 | DateTimes *datetimes; 42 | /** 43 | * @brief NTP服务器网址 44 | */ 45 | const char *ntpServerName = "cn.ntp.org.cn"; 46 | 47 | /** 48 | * @brief 服务器IP 49 | */ 50 | IPAddress timeServerIP; 51 | 52 | /** 53 | * @brief 接收到的ntp数据包 54 | */ 55 | byte packetBuffer[NTP_PACKET_SIZE]; 56 | 57 | /** 58 | * @brief 返回UDP数据包 59 | */ 60 | char replyPacket[4]; 61 | 62 | /** 63 | * @brief udp对象 64 | */ 65 | WiFiUDP udp; 66 | 67 | /** 68 | * @brief 发送NTP数据包 69 | */ 70 | void sendNTPpacket(); 71 | 72 | public: 73 | /** 74 | * @brief构造函数 75 | */ 76 | Udps(); 77 | 78 | /** 79 | * @brief 构造函数 80 | * 81 | * @param datetimes 82 | */ 83 | Udps(DateTimes *datetimesobj, LatticePlus *latticeobj, PilotLight *pilotLightobj); 84 | 85 | /** 86 | * @brief 初始化UDP信息 87 | * 88 | */ 89 | void initudp(); 90 | 91 | /** 92 | * @brief设置系统时间 93 | */ 94 | long getNtpTimestamp(); 95 | 96 | /** 97 | * @brief 更新时间 98 | * 99 | */ 100 | void updateTime(); 101 | 102 | /** 103 | * @brief接收自定义UPD协议的数据 104 | */ 105 | Udpdata userLatticeLoop(uint8_t power, uint8_t mode, uint8_t version); 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/esp8266-lattice-clock-awtirx.ino: -------------------------------------------------------------------------------- 1 | #include "Main.h" 2 | // 这里加了一个点灯科技的代码支持,假如说你想用点灯科技,则可以取消注释掉下面这段代码 3 | // #include "BlinkerSupport.h" 4 | // 然后在step函数中取消注释掉那段initBlinker();和Blinker.run();代码即可 5 | // 关于这段的说明请参考:https://gitee.com/lengff/esp8266-lattice-clock-open/tree/master/blinker 6 | 7 | #define LATTICE_CLOCK_VERSION 9 // 点阵时钟代码版本号码 8 | 9 | /** 10 | * 处理接受到的UDP数据 11 | */ 12 | void handleUdpData() 13 | { 14 | Udpdata udpdata = udps.userLatticeLoop(functions.getCurrPower(), functions.getCurrMode(), LATTICE_CLOCK_VERSION); // 15 | if (udpdata.lh < 1) // 数据长度小于1则表示没有接收到任何数据 16 | { 17 | // 没有收到任何UDP数据 18 | return; 19 | } 20 | switch (udpdata.te) // 判断UDP数据类型 21 | { 22 | case 0: 23 | resetTime(udpdata.data); // 重置时间 24 | break; 25 | case 1: 26 | lattice.setBrightness(udpdata.data[0], true); // 设置亮度 27 | break; 28 | case 2: 29 | functions.setPowerAndMode(udpdata.data[0], 0); // 切换功能 30 | initStatus(); 31 | break; 32 | case 3: 33 | functions.setMode(udpdata.data[0]); // 切换功能模式 34 | initStatus(); 35 | break; 36 | case 4: 37 | httptool.updateBilibiliFlag(); // 更新bilibili粉丝数量前,需要重置一下flag 38 | subBili(udpdata.data); // 订阅BIlibiliUID 39 | initStatus(); 40 | break; 41 | case 5: 42 | lattice.shutdown(udpdata.data[0]); // 是否启用点阵屏幕 43 | break; 44 | case 6: 45 | lattice.setDirection(udpdata.data[0]); // 切换显示方向 46 | break; 47 | case 7: 48 | setUserData(udpdata.data); // 设置用户数据 49 | break; 50 | case 8: 51 | lattice.latticeSetting.speed = udpdata.data[0]; // 设置动画速度 52 | functions.setPower(CUSTOM); 53 | break; 54 | case 9: 55 | otas.updateOta(udpdata.data[0]); // OTA 升级 56 | break; 57 | case 10: 58 | setCountdown(udpdata.data); // 设置倒计时 59 | initStatus(); 60 | break; 61 | case 11: 62 | setSleepTime(udpdata.data); // 设置睡眠时间 63 | break; 64 | default: 65 | break; 66 | } 67 | } 68 | 69 | /** 70 | * @brief 显示时间回调处理 71 | * 72 | */ 73 | void showTimeCallback() 74 | { 75 | handleUdpData(); 76 | touchLoop(); 77 | if (WiFi.status() == WL_CONNECTED) // 确保wifi网络是可用的,不可用则忽略 78 | { 79 | // Blinker.run(); 80 | } 81 | } 82 | 83 | /** 84 | * @brief 显示时间 85 | * 86 | * @param showmode 87 | */ 88 | void showTime(uint8_t showmode) 89 | { 90 | Times times = datetimes.getTimes(); 91 | if (times.s == powerFlag) 92 | { 93 | return; // 如果秒钟数没有改变,则不执行方法 94 | } 95 | displayData[0] = times.s; 96 | displayData[1] = times.m; 97 | displayData[2] = times.h; 98 | if (showmode == 0) 99 | { 100 | lattice.showTime3(displayData); 101 | } 102 | else if (showmode == 1) 103 | { 104 | lattice.showTime(displayData); 105 | } 106 | else 107 | { 108 | if (times.s == 0 || powerFlag2 == -1) 109 | { 110 | powerFlag2 = 0; 111 | displayData[0] = times.m % 10; 112 | displayData[1] = times.m / 10; 113 | displayData[2] = times.h % 10; 114 | displayData[3] = times.h / 10; 115 | lattice.showTime2(displayData); 116 | } 117 | if (times.s % 2 == 0) 118 | { 119 | lattice.reversalLR(3); 120 | } 121 | else 122 | { 123 | lattice.reversalUD(3); 124 | } 125 | } 126 | powerFlag = times.s; 127 | } 128 | 129 | /** 130 | * 功能处理 131 | */ 132 | void handlePower() 133 | { 134 | switch (functions.getCurrPower()) // 显示数据模式 135 | { 136 | case SHOW_TIME: 137 | showTime(functions.getCurrMode()); // 显示时间 138 | break; 139 | case SHOW_DATE: 140 | showDate(functions.getCurrMode()); // 显示日期 141 | break; 142 | case POWER2: 143 | showTemperature(); // 显示温度 144 | break; 145 | case BILIFANS: 146 | showBiliFans(); // 显示bilibili粉丝数量 147 | break; 148 | case CUSTOM: 149 | showUserData(functions.getCurrMode()); // 显示用户自定义的数据 150 | break; 151 | case COUNTDOWN: 152 | showCountDown(); // 显示倒计时 153 | break; 154 | case RESET: 155 | System::reset_system(); // 重置系统 156 | break; 157 | case RESETTIME: 158 | resetTime(NULL); // 重置时间,这里是随便传的一个参数,不想重新声明参数 159 | break; 160 | default: 161 | break; // 默认不做任何处理 162 | } 163 | } 164 | 165 | void setup() 166 | { 167 | lattice.boot_animation(); // 显示开机动画 168 | Serial.begin(115200); // 初始化串口波特率 169 | EEPROM.begin(4096); // 170 | WiFi.hostname("lattice-clock"); // 设置ESP8266设备名 171 | initTouch(); // 初始化按键信息 172 | wifis.connWifi(); // 连接wifi 173 | udps.initudp(); // 初始化UDP客户端 174 | httptoolticker.attach(60, httptool.updateBilibiliFlag); // 每五分分钟更新一次更新bilibili粉丝flag 175 | timestampticker.attach(1, DateTimes::timestampAdd); // 每一秒叠加一次秒数 176 | if (!wifis.isApMode()) // 如果wifi模式为连接wifi的模式则联网矫正时间 177 | { // 178 | resetTime(NULL); // 每次初始化的时候都校准一下时间,这里是随便传的一个参数,不想重新声明参数 179 | httptool.updateBilibiliFlag(); // 更新bilibili粉丝数量前,需要重置一下flag 180 | httptool.bilibiliFans(); // 刷新bilibili粉丝数量 181 | // initBlinker(); 182 | } 183 | initSleepTime(); // 初始化休眠时间 184 | systems.init_callback(showTimeCallback); 185 | } 186 | 187 | void loop() 188 | { 189 | wifis.wifiloop(); 190 | handleUdpData(); 191 | touchLoop(); 192 | handlePower(); 193 | sleepTimeLoop(); 194 | lightLoop(); 195 | resetTimeLoop(); 196 | if (WiFi.status() == WL_CONNECTED) // 确保wifi网络是可用的,不可用则忽略 197 | { 198 | // Blinker.run(); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/wifis.cpp: -------------------------------------------------------------------------------- 1 | #include "Wifis.h" 2 | #include "ConfigWifiHtml.h" 3 | 4 | Wifis::Wifis() 5 | { 6 | } 7 | 8 | Wifis::Wifis(LatticePlus *latticeobj, PilotLight *pilotLightobj) 9 | { 10 | lattice = latticeobj; 11 | pilotLight = pilotLightobj; 12 | } 13 | 14 | void Wifis::wifiloop() 15 | { 16 | if (isApMode()) // 这里只有在热点模式下才进行此操作 17 | { 18 | server.handleClient(); 19 | dnsServer.processNextRequest(); 20 | } 21 | } 22 | 23 | void Wifis::initWifi() 24 | { 25 | if (EEPROMTool.loadDataOne(WIFI_MODE) == 0x01) // 如果取到的数据为1时表示启动了热点模式 26 | { 27 | EEPROMTool.saveDataOne(0x00, WIFI_MODE); // 处理完热点模式以后随机将默认模式改为wifi模式 28 | wifiMode = 0x01; // 标记当前wifi模式 29 | initWebServer(); // 初始化web服务 30 | } 31 | else 32 | { 33 | WiFi.mode(WIFI_STA); 34 | wifiMode = 0x00; 35 | } 36 | } 37 | 38 | /** 39 | * @brief 处理主页请求 40 | * 41 | */ 42 | void Wifis::handleIndex() 43 | { 44 | server.send(200, "text/html", page_html); 45 | } 46 | 47 | /** 48 | * @brief 处理配网请求 49 | * 50 | */ 51 | void Wifis::handleConfigWifi() 52 | { 53 | WiFi.persistent(true); //首次在flash中保存WiFi配置-可选 54 | WiFi.begin(server.arg("ssid").c_str(), server.arg("pwd").c_str()); // 使用配网获取的wifi信息 55 | WiFi.setAutoConnect(true); // 设置自动连接 56 | EEPROMTool.saveDataOne(0XFE, REMEMBER_WIFI); // 记住wifi密码 57 | int count = 0; 58 | while (WiFi.status() != WL_CONNECTED) 59 | { 60 | delay(500); 61 | count++; 62 | if (count > 40) //如果20秒内没有连上,就开启Web配网 可适当调整这个时间 63 | { 64 | server.send(200, "text/html", "wifi连接失败,请检查密码后重试。"); //返回保存成功页面 65 | break; //跳出 防止无限初始化 66 | } 67 | Serial.println("."); 68 | } 69 | if (WiFi.status() == WL_CONNECTED) //如果连接上 就输出IP信息 70 | { 71 | Serial.print("WIFI Connected:"); //打印esp8266的IP地址 72 | Serial.println(WiFi.localIP()); //打印esp8266的IP地址 73 | server.send(200, "text/html", "wifi连接成功,即将重启设备。"); //返回保存成功页面 74 | delay(3000); // 等待三秒 75 | ESP.reset(); // 重启设备 76 | } 77 | } 78 | 79 | /** 80 | * @brief 处理扫描wifi请求 81 | * 82 | */ 83 | void Wifis::handleWifiList() 84 | { 85 | int n = WiFi.scanNetworks(); //开始同步扫描,将返回值存放在变量n中 86 | if (n > 0) // 只有有数据的时候才处理 87 | { 88 | char wifilist[640] = {0}; // 返回给网页的数据 89 | Serial.println("sacn wifi."); 90 | for (int i = 0; i < 20; ++i) //开始逐个打印扫描到的 91 | { 92 | sprintf(wifilist, "%s%s%s", wifilist, WiFi.SSID(i).c_str(), ","); // 组装信息返回给接口 93 | } 94 | Serial.print(wifilist); // 打印一下日志 95 | server.send(200, "text/html", wifilist); //返回保存成功页面 96 | return; // 结束这里的操作 97 | } 98 | Serial.println("no any wifi."); // 打印没有任何wifi日志 99 | server.send(200, "text/html", ".nodata"); //返回保存成功页面 100 | } 101 | 102 | void Wifis::initWebServer() 103 | { 104 | WiFi.mode(WIFI_AP_STA); // 设置模式为wifi热点模式 105 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); //初始化AP模式 106 | WiFi.softAP(AP_NAME, NULL, 1, 0, 4); //初始化AP模式 107 | server.on("/", HTTP_GET, handleIndex); //设置主页回调函数 108 | server.on("/configwifi", HTTP_GET, handleConfigWifi); //设置Post请求回调函数 109 | server.on("/wifilist", HTTP_GET, handleWifiList); // 设置获取wifi列表回调函数 110 | server.onNotFound(handleIndex); //设置无法响应的http请求的回调函数 111 | server.begin(); //启动WebServer 112 | Serial.println("WebServer started!"); 113 | dnsServer.start(53, "*", apIP); 114 | } 115 | 116 | void Wifis::connWifi() 117 | { 118 | initWifi(); // 初始化wifi信息 119 | int timer = 0; // 清零计数器 120 | if (isApMode()) // 模式为热点模式就不走连接wifi和wifi配网了 121 | { 122 | return; // 如果wifi模式为热点模式,则不进wifi连接和配网 123 | } 124 | if (EEPROMTool.loadDataOne(REMEMBER_WIFI) == 0XFE) // 如果记住wifi值不为0xfe表示存在WiFi账号密码等信息 125 | { 126 | Serial.println("start connect wifi "); 127 | Serial.println(WiFi.SSID().c_str()); 128 | Serial.println(WiFi.psk().c_str()); 129 | WiFi.begin(WiFi.SSID().c_str(), WiFi.psk().c_str()); // 使用配网获取的wifi信息 130 | // WiFi.begin("你的wifiSSID", "你的wifi密码"); // 使用固定的wifi信息 131 | while (WiFi.status() != WL_CONNECTED) 132 | { 133 | timer++; 134 | pilotLight->flashing(); // 闪烁LED灯 135 | lattice->showLongIcon(0); // 显示连接wifi图案 136 | delay(100); 137 | if (timer >= 300) // 如果计数器大于60次,表示超过一分钟,则说明一分钟都没有连接上wifi,就不连了 138 | { 139 | timer = 0; // 清零计数器 140 | enableApMode(); // 联网失败进入到热点模式 141 | break; // 防止出问题还是break一下 142 | } 143 | } 144 | WiFi.setAutoConnect(true); // 设置自动连接 145 | } 146 | else 147 | { 148 | enableApMode(); 149 | // WiFi.beginSmartConfig(); // 没有账号密码则进入到配网模式 150 | // Serial.println("smart config wifi "); 151 | // while (1) 152 | // { 153 | // timer++; 154 | // lattice.showLongIcon(1); // 显示配网中图案信息 155 | // pilotLight.flashing(); // 闪烁LED灯 156 | // delay(500); // 等待半秒钟 157 | // if (timer >= 20) // 如果配网次数超过20此,则重启系统,重新配网 158 | // { 159 | // ESP.restart(); // 重启系统 160 | // return; 161 | // } 162 | // if (WiFi.smartConfigDone()) // 配网成功 163 | // { 164 | // WiFi.setAutoConnect(true); // 设置自动连接 165 | // EEPROMTool.saveDataOne(true, REMEMBER_WIFI); // 记住wifi密码 166 | // break; 167 | // } 168 | // wifiloop(); 169 | // } 170 | } 171 | Serial.println("conn wifi successful"); // 记录一下日志,避免一点都不知道有没有连上wifi 172 | delay(500); // 等几秒再进入系统 173 | } 174 | 175 | void Wifis::enableApMode() 176 | { 177 | EEPROMTool.saveDataOne(0x01, WIFI_MODE); // 修改wifi模式,随后重启ESP 178 | ESP.restart(); // 重启系统 179 | } 180 | 181 | /** 182 | * @brief 获取当前wifi状态,判断是否是热点模式 183 | * 184 | * @return true 185 | * @return false 186 | */ 187 | bool Wifis::isApMode() 188 | { 189 | // 只有wifi模式 == 0x01时,表示wifi为热点模式 190 | return wifiMode == 0x01; 191 | } 192 | -------------------------------------------------------------------------------- /awtirx/esp8266-lattice-clock-awtirx/wifis.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFIS_H 2 | #define WIFIS_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "LatticePlus.h" 6 | #include "PilotLight.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef APSSID 13 | #define APSSID "lattice-clock-ap" // wifi热点的账号密码 14 | #endif 15 | 16 | static ESP8266WebServer server(80); // 创建dnsServer实例 17 | static DNSServer dnsServer; // dnsServer 18 | static IPAddress apIP(192, 168, 4, 1); // esp8266-AP-IP地址 19 | 20 | class Wifis 21 | { 22 | private: 23 | /** 24 | * @brief 显示对象 25 | * 26 | */ 27 | LatticePlus *lattice; 28 | 29 | /** 30 | * @brief 状态指示灯对象 31 | * 32 | */ 33 | PilotLight *pilotLight; 34 | 35 | /** 36 | * @brief wifi名字 37 | * 38 | */ 39 | const char *AP_NAME = APSSID; 40 | 41 | /** 42 | * @brief 是否记住wifi密码 43 | * 44 | */ 45 | bool rememberWifiPwd = 0X00; 46 | 47 | /** 48 | * @brief 初始化wifi 49 | * 50 | */ 51 | void initWifi(); 52 | 53 | /** 54 | * @brief 初始化web服务 55 | * 56 | */ 57 | void initWebServer(); 58 | 59 | /** 60 | * @brief wifi模式 0x00: 连接wifi模式(STA) 0x01: wifi热点模式(AP) 61 | * 62 | */ 63 | uint8_t wifiMode = 0x00; 64 | 65 | public: 66 | /** 67 | * @brief 构造函数 68 | * 69 | */ 70 | Wifis(); 71 | 72 | /** 73 | * @brief 构造函数 74 | * 75 | * @param lattice 76 | * @param pilotLight 77 | */ 78 | Wifis(LatticePlus *latticeobj, PilotLight *pilotLightobj); 79 | 80 | /** 81 | * @brief 连接wifi 82 | * 83 | */ 84 | void connWifi(); 85 | 86 | /** 87 | * @brief 启动热点模式 88 | * 89 | */ 90 | void enableApMode(); 91 | 92 | /** 93 | * @brief 获取当前wifi状态,判断是否是热点模式 94 | * 95 | * @return true 96 | * @return false 97 | */ 98 | bool isApMode(); 99 | 100 | /** 101 | * @brief wifi轮循方法 102 | * 103 | */ 104 | void wifiloop(); 105 | 106 | /** 107 | * @brief 处理主页请求 108 | * 109 | */ 110 | static void handleIndex(); 111 | 112 | /** 113 | * @brief 处理配网请求 114 | * 115 | */ 116 | static void handleConfigWifi(); 117 | 118 | /** 119 | * @brief 处理扫描wifi请求 120 | * 121 | */ 122 | static void handleWifiList(); 123 | }; 124 | #endif 125 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/DateTimes.cpp: -------------------------------------------------------------------------------- 1 | #include "DateTimes.h" 2 | 3 | DateTimes::DateTimes() { Wire.begin(); } 4 | 5 | Times DateTimes::getTimes() 6 | { 7 | times.s = ds3231.getSecond(); 8 | times.m = ds3231.getMinute(); 9 | times.h = ds3231.getHour(h12Flag, pmFlag); 10 | return times; 11 | } 12 | 13 | Dates DateTimes::getDates() 14 | { 15 | dates.y = 2000 + ds3231.getYear(); 16 | dates.m = ds3231.getMonth(century); 17 | dates.d = ds3231.getDate(); 18 | return dates; 19 | } 20 | 21 | int DateTimes::getTemperature() { return ds3231.getTemperature() * 100; } 22 | 23 | void DateTimes::setDateTimes(long timestamp) 24 | { 25 | datetime = DateTime(timestamp); 26 | ds3231.setYear(datetime.year() % 100); 27 | ds3231.setMonth(datetime.month()); 28 | ds3231.setDate(datetime.day()); 29 | ds3231.setHour(datetime.hour()); 30 | ds3231.setMinute(datetime.minute()); 31 | ds3231.setSecond(datetime.second()); 32 | } 33 | 34 | long DateTimes::getTimestamp() 35 | { 36 | return RTClib::now().unixtime(); 37 | } 38 | 39 | void DateTimes::saveCountdownTimestamp(long timestamp) 40 | { 41 | uint8_t arr[5]; 42 | for (int i = 0; i < 5; i++) 43 | { 44 | arr[i] = (timestamp & 0xff); 45 | timestamp >>= 8; 46 | } 47 | EEPROMTool.saveData(arr, 103, 5); 48 | } 49 | 50 | long DateTimes::getCountdownTimestamp() 51 | { 52 | long timestamp = 0; 53 | uint8_t *temp = EEPROMTool.loadData(103, 5); // 这里的103处理的不得当,后续优化,但是不影响实际功能 54 | for (int i = 0; i < 5; i++) 55 | { 56 | timestamp += temp[i] << (i * 8); 57 | } 58 | // 用完以后删除内存 59 | free(temp); 60 | return timestamp; 61 | } 62 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/DateTimes.h: -------------------------------------------------------------------------------- 1 | #ifndef DATETIMES_H 2 | #define DATETIMES_H 3 | 4 | #include 5 | #include 6 | #include "EEPROMTool.h" 7 | 8 | struct Times 9 | { 10 | uint8_t h; 11 | uint8_t m; 12 | uint8_t s; 13 | }; 14 | 15 | struct Dates 16 | { 17 | uint16_t y; 18 | uint8_t m; 19 | uint8_t d; 20 | }; 21 | 22 | class DateTimes 23 | { 24 | private: 25 | bool h12Flag, pmFlag, century = false; 26 | DS3231 ds3231; 27 | 28 | DateTime datetime; 29 | 30 | /** 31 | * @brief 时间结构体 32 | * 33 | */ 34 | Times times; 35 | 36 | /** 37 | * @brief 日期结构体 38 | * 39 | */ 40 | Dates dates; 41 | 42 | public: 43 | /** 44 | * 构造函数 45 | */ 46 | DateTimes(); 47 | 48 | void initDateTimes(); 49 | 50 | /** 51 | * 获取时间 52 | */ 53 | Times getTimes(); 54 | 55 | /** 56 | * 获取日期 57 | */ 58 | Dates getDates(); 59 | 60 | /** 61 | * 获取温度信息 62 | */ 63 | int getTemperature(); 64 | 65 | /** 66 | * 根据datetime设置时间 67 | */ 68 | void setDateTimes(long timestamp); 69 | 70 | /** 71 | * 获取时间戳信息 72 | */ 73 | long getTimestamp(); 74 | 75 | /** 76 | * 获取倒计时的时间戳 77 | */ 78 | long getCountdownTimestamp(); 79 | 80 | /** 81 | * 保存倒计时的时间戳 82 | */ 83 | void saveCountdownTimestamp(long timestamp); 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/EEPROMTool.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_TOOL_H 2 | #define EEPROM_TOOL_H 3 | 4 | #include 5 | 6 | #define EEPROM_BEGIN 1024 7 | 8 | enum EEPROM_ADDRESS_ENUM 9 | { 10 | 11 | REMEMBER_WIFI = 1, // 记住wifi内存地址 12 | WIFI_MODE = 2, // wifi模式内存地址 13 | DIRECTION = 3, // 屏幕显示方向内存地址 14 | BRIGHTNESS = 4, // 屏幕亮度内存地址 15 | SLEEP_TIME = 5 // 从第5位到第9位来存睡眠时间 16 | }; 17 | 18 | struct EEPROMTOOLS 19 | { 20 | /** 21 | * @brief 保存数据 22 | */ 23 | void saveData(uint8_t *data, int offset, int length) 24 | { 25 | EEPROM.begin(EEPROM_BEGIN); 26 | for (int i = 0; i < length; i++) 27 | { 28 | EEPROM.write(offset + i, data[i] & 0xff); 29 | } 30 | EEPROM.commit(); 31 | } 32 | /** 33 | * @brief 只保存一位数据 34 | * 35 | * @param data 36 | * @param offset 37 | */ 38 | void saveDataOne(uint8_t data, int offset) 39 | { 40 | EEPROM.begin(EEPROM_BEGIN); 41 | EEPROM.write(offset, data & 0xff); 42 | EEPROM.commit(); 43 | } 44 | 45 | /** 46 | * @brief 获取数据 47 | * 48 | * @param offset 49 | * @param length 50 | * @return uint8_t* 51 | */ 52 | uint8_t *loadData(int offset, int length) 53 | { 54 | unsigned char *arr = new uint8_t[length]; 55 | EEPROM.begin(EEPROM_BEGIN); 56 | for (int i = 0; i < length; i++) 57 | { 58 | arr[i] = EEPROM.read(offset + i); 59 | } 60 | return arr; 61 | } 62 | 63 | /** 64 | * @brief 获取一位数据 65 | */ 66 | uint8_t loadDataOne(int offset) 67 | { 68 | unsigned char *arr = new uint8_t[1]; 69 | EEPROM.begin(EEPROM_BEGIN); 70 | return EEPROM.read(offset); 71 | } 72 | 73 | /** 74 | * @brief 删除数据 75 | */ 76 | void clearData(int offset, int length) 77 | { 78 | EEPROM.begin(EEPROM_BEGIN); 79 | for (int i = 0; i < length; i++) 80 | { 81 | EEPROM.write(offset + i, 0x0); 82 | } 83 | EEPROM.commit(); 84 | } 85 | 86 | /** 87 | * @brief 删除全部数据 88 | */ 89 | void clearAll() 90 | { 91 | EEPROM.begin(EEPROM_BEGIN); 92 | for (int i = 0; i < EEPROM_BEGIN + 300; i++) 93 | { 94 | EEPROM.write(i, 0x0); 95 | } 96 | EEPROM.commit(); 97 | } 98 | }; 99 | 100 | static EEPROMTOOLS EEPROMTool; 101 | 102 | #endif -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/HttpTool.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpTool.h" 2 | 3 | HttpTool::HttpTool() {} 4 | 5 | void HttpTool::initHttptool() {} 6 | 7 | void HttpTool::bilibiliFans() 8 | { 9 | fans = 0; // 每次都先重置粉丝数量,避免出现问题 10 | biliUid = loadBuid(); // 每次都重新加载bilibili用户ID 11 | Serial.println(biliUid); 12 | 13 | if (WiFi.status() != WL_CONNECTED) // 确保wifi网络是可用的,不可用则忽略 14 | { 15 | Serial.println("no wifi"); 16 | return; 17 | } 18 | espClient.begin(wifiClient, bilibiliFansApi + biliUid); // 这里做法欠妥,我用自己服务器做了一层代理,直接解析了UP的粉丝数 19 | int httpCode = espClient.GET(); 20 | Serial.println(httpCode); 21 | if (httpCode > 0) 22 | { 23 | if (httpCode == HTTP_CODE_OK) 24 | { 25 | String nums = espClient.getString(); 26 | fans = strtol(nums.c_str(), NULL, 10); 27 | Serial.println(fans); 28 | } 29 | } 30 | espClient.end(); 31 | } 32 | 33 | void HttpTool::saveBuid(long uid) 34 | { 35 | uint8_t arr[5]; 36 | for (int i = 0; i < 5; i++) 37 | { 38 | arr[i] = (uid & 0xff); 39 | uid >>= 8; 40 | } 41 | EEPROMTool.saveData(arr, 97, 5); 42 | } 43 | 44 | long HttpTool::loadBuid() 45 | { 46 | long uid = 0; 47 | uint8_t *temp = EEPROMTool.loadData(97, 5); // 这里的97处理的不得当,后续优化,但是不影响实际功能 48 | for (int i = 0; i < 5; i++) 49 | { 50 | uid += temp[i] << (i * 8); 51 | } 52 | // 用完以后删除内存 53 | free(temp); 54 | return uid; 55 | } -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/HttpTool.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_TOOL_H 2 | #define HTTP_TOOL_H 3 | 4 | #include "EEPROMTool.h" 5 | #include 6 | #include 7 | 8 | class HttpTool 9 | { 10 | private: 11 | WiFiClient wifiClient; 12 | /** 13 | * http客户端 14 | */ 15 | HTTPClient espClient; 16 | 17 | /** 18 | * 加载BiliBili_Uid 19 | */ 20 | long loadBuid(); 21 | 22 | /** 23 | * bilibili用户ID 24 | */ 25 | long biliUid = 0; 26 | 27 | /** 28 | * 获取bilibili粉丝数量的接口 (此处欠妥,先这用着,后续优化) 29 | */ 30 | const String bilibiliFansApi = "http://api.lengff.com/openapi/bili/f/"; 31 | 32 | public: 33 | HttpTool(); 34 | 35 | /** 36 | * bilibili粉丝数 37 | */ 38 | long fans = 0; 39 | 40 | /** 41 | * 初始化 42 | */ 43 | void initHttptool(); 44 | 45 | /** 46 | * 获取bilibili粉丝数量 47 | */ 48 | void bilibiliFans(); 49 | 50 | /** 51 | * 保存BiliBili_Uid 52 | */ 53 | void saveBuid(long uid); 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/Lattice.h: -------------------------------------------------------------------------------- 1 | #ifndef LATTICE_H 2 | #define LATTICE_H 3 | 4 | #include 5 | #include "EEPROMTool.h" 6 | 7 | const uint8_t rowLength = 8; 8 | const uint8_t columnLength = 4; 9 | 10 | const int DIN = D7; 11 | const int CS = D6; 12 | const int CLK = D5; 13 | 14 | const unsigned char number_font_small[11][rowLength] = { 15 | {0x0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0x0, 0x0}, // 0 16 | {0x0, 0x40, 0xC0, 0x40, 0x40, 0xE0, 0x0, 0x0}, // 1 17 | {0x0, 0xE0, 0x20, 0xE0, 0x80, 0xE0, 0x0, 0x0}, // 2 18 | {0x0, 0xE0, 0x20, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 3 19 | {0x0, 0xA0, 0xA0, 0xE0, 0x20, 0x20, 0x0, 0x0}, // 4 20 | {0x0, 0xE0, 0x80, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 5 21 | {0x0, 0xE0, 0x80, 0xE0, 0xA0, 0xE0, 0x0, 0x0}, // 6 22 | {0x0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0}, // 7 23 | {0x0, 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, 0x0, 0x0}, // 8 24 | {0x0, 0xE0, 0xA0, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 9 25 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} // null 26 | }; 27 | 28 | const unsigned char number_font_middle[10][rowLength] = { 29 | {0x0, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0}, // 0 30 | {0x0, 0x40, 0xc0, 0x40, 0x40, 0x40, 0x40, 0xe0}, // 1 31 | {0x0, 0xf0, 0x10, 0x10, 0xf0, 0x80, 0x80, 0xf0}, // 2 32 | {0x0, 0xf0, 0x10, 0x10, 0xf0, 0x10, 0x10, 0xf0}, // 3 33 | {0x0, 0x90, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x10}, // 4 34 | {0x0, 0xf0, 0x80, 0x80, 0xf0, 0x10, 0x10, 0xf0}, // 5 35 | {0x0, 0xf0, 0x80, 0x80, 0xf0, 0x90, 0x90, 0xf0}, // 6 36 | {0x0, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, // 7 37 | {0x0, 0xf0, 0x90, 0x90, 0xf0, 0x90, 0x90, 0xf0}, // 8 38 | {0x0, 0xf0, 0x90, 0x90, 0xf0, 0x10, 0x10, 0xf0} // 9 39 | }; 40 | 41 | const unsigned char number_font_animations[10][16] = { 42 | {0x40, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 43 | 0x0, 0x0, 0x0, 0x0}, 44 | {0x20, 0x11, 0x21, 0x31, 0x41, 0x50, 0x51, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 45 | 0x0, 0x0, 0x0}, 46 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x31, 0x30, 0x40, 0x50, 0x51, 0x52, 0x0, 0x0, 47 | 0x0, 0x0, 0x0}, 48 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x31, 0x30, 0x42, 0x50, 0x51, 0x52, 0x0, 0x0, 49 | 0x0, 0x0, 0x0}, 50 | {0x10, 0x20, 0x30, 0x31, 0x12, 0x22, 0x32, 0x42, 0x52, 0x0, 0x0, 0x0, 0x0, 51 | 0x0, 0x0, 0x0}, 52 | {0x12, 0x11, 0x10, 0x20, 0x30, 0x31, 0x32, 0x42, 0x52, 0x51, 0x50, 0x0, 0x0, 53 | 0x0, 0x0, 0x0}, 54 | {0x12, 0x11, 0x10, 0x20, 0x30, 0x40, 0x50, 0x51, 0x52, 0x42, 0x32, 0x31, 55 | 0x0, 0x0, 0x0, 0x0}, 56 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 57 | 0x0, 0x0, 0x0}, 58 | {0x31, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 59 | 0x40, 0x0, 0x0, 0x0}, 60 | {0x31, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 61 | 0x0, 0x0, 0x0, 0x0}}; 62 | 63 | /** 64 | * WiFi连接图标 65 | */ 66 | const unsigned char wifiicon[columnLength][rowLength] = { 67 | {0x0, 0xba, 0x20, 0xba, 0xa2, 0xa2, 0x0, 0x0}, 68 | {0x0, 0x22, 0x22, 0x2a, 0x2e, 0x14, 0x0, 0x0}, 69 | {0x0, 0x48, 0x68, 0x79, 0x58, 0x48, 0x0, 0x0}, 70 | {0x0, 0x37, 0x45, 0x45, 0x45, 0x37, 0x0, 0x0}}; 71 | 72 | /** 73 | * 没有wifi的图标 74 | */ 75 | const unsigned char nowifiicon[columnLength][rowLength] = { 76 | {0x0, 0x74, 0x40, 0x74, 0x44, 0x44, 0x0, 0x0}, 77 | {0x0, 0x45, 0x44, 0x55, 0x55, 0x29, 0x0, 0x0}, 78 | {0x0, 0xc0, 0x40, 0x4e, 0x40, 0xc0, 0x0, 0x0}, 79 | {0x0, 0x25, 0x35, 0x3d, 0x2d, 0x25, 0x0, 0x0}}; 80 | 81 | /** 82 | * 更新数据字符 83 | */ 84 | const unsigned char updateClock[columnLength][rowLength] = { 85 | {0x0, 0x0, 0x0, 0xa8, 0x0, 0x0, 0x0, 0x0}, 86 | {0x0, 0xd4, 0x18, 0x10, 0x18, 0xd4, 0x0, 0x0}, 87 | {0x0, 0x1d, 0x15, 0x15, 0x15, 0xdd, 0x0, 0x0}, 88 | {0x0, 0x1d, 0x11, 0x11, 0x11, 0x1d, 0x0, 0x0}}; 89 | 90 | /** 91 | * ota 更新图标 92 | */ 93 | const unsigned char otaicon[columnLength][rowLength] = { 94 | {0x0, 0x24, 0x8, 0x10, 0x20, 0x48, 0x0, 0x0}, 95 | {0x0, 0xee, 0xaa, 0xaa, 0xaa, 0xee, 0x0, 0x0}, 96 | {0x0, 0x90, 0x2a, 0x28, 0x3a, 0x28, 0x0, 0x0}, 97 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 98 | 99 | /** 100 | * 更新完成 101 | */ 102 | const unsigned char otaokicon[columnLength][rowLength] = { 103 | {0x0, 0x80, 0x0, 0x2a, 0x0, 0x80, 0x0, 0x0}, 104 | {0x0, 0x3a, 0x2b, 0x2a, 0x2b, 0x3a, 0x0, 0x0}, 105 | {0x0, 0x90, 0x29, 0x28, 0x39, 0x28, 0x0, 0x0}, 106 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 107 | 108 | /** 109 | * 更新失败 110 | */ 111 | const unsigned char otafailicon[columnLength][rowLength] = { 112 | {0x0, 0x28, 0x88, 0xa8, 0xa8, 0xae, 0x0, 0x0}, 113 | {0x0, 0x39, 0x22, 0x3b, 0x22, 0x22, 0x0, 0x0}, 114 | {0x0, 0x90, 0x29, 0x28, 0x39, 0x28, 0x0, 0x0}, 115 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 116 | 117 | /** 118 | * 没有设置任何数据 119 | */ 120 | const unsigned char noseticon[32] = { 121 | 0x0, 0xbe, 0x8, 0x8, 0x88, 0x8, 0x8, 0x88, 0x0, 0xef, 0x8, 122 | 0x8, 0xef, 0x28, 0x28, 0xef, 0x0, 0xe3, 0x22, 0x22, 0x23, 0x20, 123 | 0x20, 0xe3, 0x0, 0x45, 0x45, 0x65, 0x75, 0x5d, 0x4d, 0x45}; 124 | 125 | /** 126 | * 图标集合 127 | */ 128 | const unsigned char icons[4][rowLength] = { 129 | {0x3C, 0x42, 0x85, 0x89, 0x81, 0x81, 0x42, 0x3C}, // 时钟图标 130 | {0x0, 0xCE, 0xD1, 0x10, 0x10, 0x11, 0xE, 0x0}, // 温度图标 131 | {0x42, 0x24, 0xFF, 0x81, 0xA5, 0x99, 0xFF, 0x42}, // Bilibili图标 132 | {0x3C, 0x42, 0x81, 0xA5, 0x81, 0x99, 0x42, 0x3C} // 微笑 133 | }; 134 | 135 | struct LatticeSetting 136 | { 137 | /** 138 | * 点阵屏幕是否显示 139 | */ 140 | bool isShutdown = false; 141 | 142 | /** 143 | * 屏幕亮度 144 | */ 145 | uint8_t brightness = 0x0; 146 | 147 | /** 148 | * 显示方向, true: 正方向,false: 反方向 149 | */ 150 | bool direction = false; 151 | 152 | /** 153 | * 用户自定义数据 154 | */ 155 | uint8_t userData[32]; 156 | 157 | /** 158 | * 速度 159 | */ 160 | uint8_t speed = 1; 161 | }; 162 | 163 | class Lattice 164 | { 165 | private: 166 | /** 167 | * lc对象 168 | */ 169 | LedControl lc = LedControl(DIN, CLK, CS, columnLength); 170 | 171 | /** 172 | * 点阵显示屏个数 173 | */ 174 | int latticeNums; 175 | 176 | int tempindex = 3; 177 | 178 | /** 179 | * 是否重置 180 | */ 181 | bool isReset = true; 182 | 183 | /** 184 | * 实际显示的数组 185 | */ 186 | unsigned char data[4][rowLength]; 187 | 188 | /** 189 | * 缓冲数组 190 | */ 191 | unsigned char buff[4][rowLength]; 192 | /** 193 | * 初始化点阵 194 | */ 195 | void initLattice(); 196 | 197 | /** 198 | * 刷新显示 199 | */ 200 | void refreshLed(); 201 | 202 | /** 203 | * 显示数据内容 204 | */ 205 | void showDataLed(const unsigned char (*arr)[rowLength]); 206 | 207 | /** 208 | * 获取需要显示数字的数据 209 | */ 210 | unsigned char *getNumData(uint8_t nums, bool showzero); 211 | 212 | /** 213 | * 获取大号的数字 214 | */ 215 | unsigned char *getBigNumData(uint8_t number, uint8_t offset); 216 | 217 | /** 218 | * 偏移缓存数组 219 | */ 220 | void offsetBuff(uint8_t index, int8_t offset); 221 | 222 | /** 223 | * 偏移显示数组 224 | */ 225 | void offsetData(uint8_t index, int8_t offset); 226 | 227 | /** 228 | * 左移显示内容 229 | */ 230 | void rightMoveData(bool isright); 231 | 232 | /** 233 | * 左移显示缓冲数据 234 | */ 235 | void rightMoveBuff(); 236 | 237 | /** 238 | * 向下移动数据 239 | */ 240 | void downMoveData(bool isdown); 241 | 242 | /** 243 | * 向下移动缓冲数据 244 | */ 245 | void downMoveBuff(); 246 | 247 | /** 248 | * 向下移动缓冲数据 249 | */ 250 | void downMoveBuff(uint8_t *arr); 251 | 252 | /** 253 | * 测试功能 254 | */ 255 | void progressBar(uint8_t offset); 256 | 257 | /** 258 | * 向上或者向下移动内容 259 | */ 260 | void upOrDownMove(uint8_t index, uint8_t region, bool direction); 261 | 262 | public: 263 | LatticeSetting latticeSetting; 264 | /** 265 | * 构造函数 266 | */ 267 | Lattice(); 268 | 269 | /** 270 | * 初始化 271 | */ 272 | void init(); 273 | 274 | /** 275 | * 重置 276 | */ 277 | void reset(); 278 | 279 | /** 280 | * 启用/停用点阵 281 | */ 282 | void shutdown(bool down); 283 | 284 | /** 285 | * 设置显示亮度 286 | */ 287 | void setBrightness(uint8_t bright,bool save); 288 | 289 | /** 290 | * 设置显示方向 291 | */ 292 | void setDirection(bool direct); 293 | 294 | /** 295 | * 显示wifi图案 296 | */ 297 | void showLongIcon(uint8_t index); 298 | 299 | /** 300 | * 显示数字 301 | */ 302 | void showNum(uint8_t index, int nums); 303 | 304 | /** 305 | * 动态显示时间 306 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 307 | */ 308 | void showTime(uint8_t *arr); 309 | 310 | /** 311 | * 显示时间模式2 312 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 313 | */ 314 | void showTime2(uint8_t *arr); 315 | 316 | /** 317 | *显示时间模式2 318 | *这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 319 | */ 320 | void showTime3(uint8_t *arr); 321 | 322 | /** 323 | * 显示倒计时 324 | */ 325 | void showCountDownTime(long remain, uint8_t *arr, bool showmode, bool minutechange); 326 | 327 | /** 328 | * 显示长的数字 329 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 330 | */ 331 | void showLongNumber(uint8_t *arr); 332 | 333 | /** 334 | * 显示数字和图标 335 | */ 336 | void showNumAndIcon(uint8_t no, uint8_t *arr); 337 | 338 | /** 339 | * 显示日期2 340 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 341 | */ 342 | void showDate2(uint8_t *arr); 343 | 344 | /** 345 | * 显示日期2 346 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 347 | */ 348 | void showDate3(uint8_t *arr); 349 | 350 | /** 351 | * 显示温度 352 | */ 353 | void showTemperature(uint8_t *arr); 354 | 355 | /** 356 | * 显示用户自定义数据 357 | */ 358 | void showUserData(uint8_t mode); 359 | 360 | /** 361 | * 清空显示 362 | */ 363 | void clearShow(); 364 | 365 | /** 366 | * 全量显示 367 | */ 368 | void showFull(); 369 | 370 | /** 371 | * 左右翻转 372 | */ 373 | void reversalLR(uint8_t index); 374 | 375 | /** 376 | * 上下翻转 377 | */ 378 | void reversalUD(uint8_t index); 379 | 380 | /** 381 | * 闪 382 | */ 383 | void lightning(uint8_t index); 384 | 385 | /** 386 | * OTA 更新 387 | */ 388 | void showOtaUpdate(uint8_t num); 389 | }; 390 | 391 | #endif 392 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/Otas.h: -------------------------------------------------------------------------------- 1 | #ifndef OTAS_H 2 | #define OTAS_H 3 | #include "Touch.h" 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * OTA 固件版本号 10 | */ 11 | const uint8_t version = 8; 12 | int process = 0; 13 | 14 | void update_started() 15 | { 16 | Serial.println("callback: ota is start!"); 17 | lattice.reset(); // 重置显示内容 18 | } 19 | 20 | void update_finished() 21 | { 22 | Serial.println("callback: ota is finish!"); 23 | lattice.reset(); // 重置显示内容 24 | lattice.showLongIcon(3); // 显示OTA更新成功图案 25 | } 26 | 27 | void update_progress(long cur, long total) 28 | { 29 | process = (int)(((cur * 0.1) / (total * 0.1)) * 100); 30 | Serial.printf("callback: updateing %d of %d bytes...\n", cur, total); 31 | pilotLight.flashing(20); // 固件升级的时候LED闪 32 | lattice.showOtaUpdate(process); // OTA显示当前进度图案 33 | } 34 | 35 | void update_error(int err) 36 | { 37 | Serial.printf("callback: ota is error: %d\n", err); 38 | lattice.reset(); // 重置显示内容 39 | lattice.showLongIcon(4); // 显示OTA更新失败图案 40 | } 41 | 42 | void updateOta(int version) 43 | { 44 | WiFiClient client; 45 | 46 | if (WiFi.status() == WL_CONNECTED) // 确保有网络 47 | { 48 | ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); 49 | 50 | ESPhttpUpdate.onStart(update_started); // 添加可选的回调通知程序 51 | ESPhttpUpdate.onEnd(update_finished); // 添加更新完成回调方法 52 | ESPhttpUpdate.onProgress(update_progress); // 添加更新中回调方法 53 | ESPhttpUpdate.onError(update_error); // 添加更新失败回调方法 54 | char *updateUrl = new char[50]; 55 | sprintf(updateUrl, "%s%d%s%d", "http://oss.lengff.com/iot/lattice/", version, ".bin?t=", millis() % 100); // 后面的对100取余就是为了解除文件CDN缓存 56 | Serial.println(updateUrl); 57 | t_httpUpdate_return ret = ESPhttpUpdate.update(client, updateUrl); 58 | free(updateUrl); 59 | switch (ret) 60 | { 61 | case HTTP_UPDATE_FAILED: 62 | Serial.printf("ote is fail, err code is:(%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 63 | lattice.reset(); // 重置显示内容 64 | lattice.showLongIcon(4); // 显示OTA更新失败图案 65 | break; 66 | case HTTP_UPDATE_NO_UPDATES: 67 | Serial.println("not ota update!"); 68 | lattice.showLongIcon(3); // 显示OTA更新成功图案 69 | break; 70 | case HTTP_UPDATE_OK: 71 | lattice.reset(); 72 | lattice.showLongIcon(3); // 显示OTA更新成功图案 73 | break; 74 | } 75 | } 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/PilotLight.cpp: -------------------------------------------------------------------------------- 1 | #include "PilotLight.h" 2 | 3 | PilotLight::PilotLight() 4 | { 5 | pinMode(LED_PIN, OUTPUT); 6 | } 7 | 8 | void PilotLight::flashing() 9 | { 10 | status = status == HIGH ? LOW : HIGH; 11 | digitalWrite(LED_PIN, status); 12 | } 13 | 14 | void PilotLight::flashing(int ms) 15 | { 16 | bright(); 17 | delay(ms); 18 | dim(); 19 | } 20 | 21 | void PilotLight::bright(){ 22 | digitalWrite(LED_PIN, LOW); 23 | } 24 | 25 | void PilotLight::dim(){ 26 | digitalWrite(LED_PIN, HIGH); 27 | } -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/PilotLight.h: -------------------------------------------------------------------------------- 1 | #ifndef PILOT_LIGHT 2 | #define PILOT_LIGHT 3 | 4 | #include 5 | 6 | #define LED_PIN D0 // LED引脚 7 | 8 | class PilotLight 9 | { 10 | private: 11 | int status = HIGH; 12 | 13 | public: 14 | PilotLight(); 15 | 16 | /** 17 | * LED 闪烁 18 | */ 19 | void flashing(); 20 | 21 | /** 22 | * @brief LED 闪烁 23 | * 24 | * @param ms 25 | */ 26 | void flashing(int ms); 27 | 28 | /** 29 | * @brief LED亮 30 | * 31 | */ 32 | void bright(); 33 | 34 | /** 35 | * @brief LED灭 36 | * 37 | */ 38 | void dim(); 39 | }; 40 | #endif -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/Touch.h: -------------------------------------------------------------------------------- 1 | #ifndef TOUCH_H 2 | #define TOUCH_H 3 | #include "EEPROMTool.h" 4 | #include "HttpTool.h" 5 | #include "PilotLight.h" 6 | #include "Udps.h" 7 | #include "Wifis.h" 8 | #include 9 | #include 10 | 11 | enum ModeEnum 12 | { 13 | // 功能0: 显示时间 14 | POWER0 = 0, 15 | // 功能1: 显示日期 16 | POWER1 = 1, 17 | // 功能2: 显示日期 18 | POWER2 = 2, 19 | // 显示bilibili粉丝数量 20 | BILIFANS = 3, 21 | // 显示用户自定义数据 22 | CUSTOM = 4, 23 | // 显示倒计时 24 | COUNTDOWN = 5, 25 | // NTP更新时间 26 | RESETTIME = 98, 27 | // 系统重置 28 | RESET = 99 29 | }; 30 | 31 | Udps udps; // UDP数据传输对象 32 | Wifis wifis = Wifis(); // wifi对象 33 | OneButton btnA = OneButton(D8, false, false); // 按钮对象 34 | PilotLight pilotLight = PilotLight(); // 控制LED亮灭对象 35 | Lattice lattice = Lattice(); // 点阵显示对象 36 | unsigned char displayData[4] = {0x00, 0x00, 0x00, 0xff}; // 点阵显示数,每个点阵应该显示那些数据 37 | const int powerLength = 5; // 有多少种功能 38 | uint8_t modePower[6] = {3, 3, 1, 1, 5, 1}; // 每个功能对应多少种模式 39 | uint8_t powers[6] = {0, 0, 0, 0, 0, 1}; // 默认的功能模式 40 | uint8_t power = 0; // 0:显示时间,1:显示日日期,2:显示温度,3:显示倒计时,4:显示自定义内容 41 | uint32_t clicktime = 0; // 记录按键按下时间 42 | long powerFlag = 0, powerFlag2 = 0; // 功能flag 43 | uint8_t sleepTime[5] = {0, 0, 8, 0, 1}; // 这里表示设备休眠时间,默认是凌晨0点到早上八点,所以表示为0:0 ~ 8:0,最后一位是亮度(0:表示息屏,15表示最亮了) 44 | bool isSleepMode = false; // 标记当前是否处于睡眠模式 45 | 46 | /** 47 | * @brief 初始化状态 48 | * 49 | */ 50 | void initStatus() 51 | { 52 | if (power == CUSTOM && powers[power] != 0) 53 | { 54 | // 如果显示模式是自定义内容的话,就不重置屏幕显示 55 | powerFlag = -1; 56 | return; 57 | } 58 | else if (power == COUNTDOWN) 59 | { 60 | 61 | displayData[0] = 0; 62 | displayData[1] = 1; 63 | displayData[2] = 2; 64 | powerFlag2 = 1; // 这里一定是到大于0的,后续会根据这个值是否为0来判断逻辑 65 | } 66 | 67 | powerFlag = -1; 68 | lattice.reset(); 69 | } 70 | 71 | /** 72 | * @brief 处理单击 73 | * 74 | */ 75 | void singleAClick() 76 | { 77 | Serial.println("A按键单击"); 78 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 79 | { 80 | lattice.shutdown(false); // 让点阵屏重新显示 81 | return; 82 | } 83 | // 如果wifi未连接,且当前wifi模式为wifi直连模式,单击则修改wifi模式为热点模式 84 | if (WiFi.status() != WL_CONNECTED && wifis.wifiMode == 0x0) 85 | { 86 | // wifi没连接上的情况 87 | wifis.enableApMode(); 88 | } 89 | else 90 | { 91 | pilotLight.flashing(100); // 按键单击时先闪一下LED 92 | power = power == powerLength ? 0 : ++power; 93 | initStatus(); 94 | } 95 | } 96 | 97 | /** 98 | * @brief 处理双击 99 | * 100 | */ 101 | void doubleAClick() 102 | { 103 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 104 | { 105 | lattice.shutdown(false); // 让点阵屏重新显示 106 | return; 107 | } 108 | pilotLight.flashing(100); // 按键单击时先闪一下LED 109 | Serial.println("A按键双击"); 110 | powers[power] = powers[power] == (modePower[power] - 1) ? 0 : ++powers[power]; 111 | initStatus(); 112 | } 113 | 114 | /** 115 | * @brief 按键长按开始做的事情 116 | * 117 | */ 118 | void longAClickStart() 119 | { 120 | Serial.println("A按键长按开始"); 121 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 122 | { 123 | lattice.shutdown(false); // 让点阵屏重新显示 124 | return; 125 | } 126 | pilotLight.bright(); // 按下的时候LED亮起来 127 | clicktime = millis(); 128 | } 129 | 130 | /** 131 | * @brief 处理按键长按 132 | * 133 | */ 134 | void longAClick() 135 | { 136 | Serial.println("A按键长按结束"); 137 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 138 | { 139 | lattice.shutdown(false); // 让点阵屏重新显示 140 | return; 141 | } 142 | pilotLight.dim(); // 按下的时候LED熄灭 143 | clicktime = millis() - clicktime + 1000; 144 | if (clicktime > 2000 && clicktime <= 5000) // 如果长按时间大于3秒,小于六秒则表示重置时间 145 | { 146 | power = RESETTIME; 147 | } 148 | else if (clicktime >= 5000) // 如果时间大于六秒则重置系统 149 | { 150 | power = RESET; // 切换功能模式 151 | } 152 | clicktime = 0; // 重置按下时间 153 | } 154 | 155 | ICACHE_RAM_ATTR void tickloop() 156 | { 157 | btnA.tick(); 158 | } 159 | 160 | /** 161 | * @brief 按键初始化 162 | * 163 | */ 164 | void initTouch(); 165 | 166 | /** 167 | * @brief 按键循环 168 | * 169 | */ 170 | void touchLoop(); 171 | 172 | void initTouch() 173 | { 174 | attachInterrupt(digitalPinToInterrupt(D8), tickloop, CHANGE); 175 | btnA.attachClick(singleAClick); // 添加单击事件函数 176 | btnA.attachDoubleClick(doubleAClick); // 添加双击事件函数 177 | btnA.attachLongPressStop(longAClick); // 添加长按事件函数 178 | btnA.attachLongPressStart(longAClickStart); // 添加按下事件函数 179 | } 180 | 181 | void touchLoop() 182 | { 183 | btnA.tick(); 184 | } 185 | #endif 186 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/Udps.cpp: -------------------------------------------------------------------------------- 1 | #include "Udps.h" 2 | 3 | Udps::Udps() 4 | { 5 | // todo 6 | } 7 | 8 | void Udps::initudp() 9 | { 10 | WiFi.hostByName(ntpServerName, timeServerIP); 11 | // todo 这里需要判断网络状态 12 | udp.begin(localPort); // 启动监听本地端口 13 | isSetTime = false; 14 | } 15 | 16 | void Udps::sendNTPpacket() 17 | { 18 | memset(packetBuffer, 0, NTP_PACKET_SIZE); // 将字节数组的数据全部设置为0 19 | packetBuffer[0] = 0b11100011; // 请求部分其实是有很多数据的,具体的请看参考请求报文说明,这里我们就只设置一个请求头部分即可 20 | udp.beginPacket(timeServerIP, remoteNtpPort); // 配置远端ip地址和端口 21 | udp.write(packetBuffer, NTP_PACKET_SIZE); // 把数据写入发送缓冲区 22 | udp.endPacket(); // 发送数据 23 | Serial.println("send ntp data"); 24 | } 25 | 26 | long Udps::getNtpTimestamp() 27 | { 28 | if (millis() - timeFlag >= 1000) // 每过一秒发一次ntp请求 29 | { 30 | sendNTPpacket(); // 向NTP服务器发请求 31 | timeFlag = millis(); // 重置时间戳 32 | } 33 | int packetSize = udp.parsePacket(); //解析Udp数据包 34 | if (!packetSize) //解析包为空 35 | { 36 | return 0; 37 | } 38 | else //解析包不为空 39 | { 40 | udp.read(packetBuffer, NTP_PACKET_SIZE); // 解析UDP数据包中的数据 41 | // todo这里获取到的时间其实不是真实的时间,实际上还包含了网络延时的,但是为了方便,这里我们忽略这个因素的存在 42 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); // 取出t2时间的高位和低位数据拼凑成以秒为单位的时间戳 43 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 44 | unsigned long secsSince1900 = highWord << 16 | lowWord; // 拼凑成以秒为单位的时间戳(时间戳的记录以秒的形式从 1900-01-01 00:00:00算起) 45 | const unsigned long seventyYears = 2208988800UL; 46 | unsigned long timestamp = secsSince1900 - seventyYears; // 前面的32bit是时间戳的秒数(是用1900-01-01 00:00:00开始的秒数,但是我们的是1970年,所以需要减掉2208988800秒) 47 | timestamp = timestamp + 8 * 60 * 60; // 这里加8 是因为时区的问题,如果不加8,得到的结果就会是其他时区的时间 48 | return timestamp; 49 | } 50 | } 51 | 52 | Udpdata Udps::userLatticeLoop(LatticeSetting latticeSetting, uint8_t power, uint8_t mode, uint8_t version) 53 | { 54 | Udpdata udpdata; 55 | udpdata.lh = 0; 56 | int packetSize = udp.parsePacket(); //解析Udp数据包 57 | if (packetSize) //解析包不为空 58 | { 59 | memset(packetBuffer, 0, NTP_PACKET_SIZE); //每次都先清空掉原有的数据包 60 | udp.read(packetBuffer, NTP_PACKET_SIZE); 61 | // Udp.remoteIP().toString().c_str()用于将获取的远端IP地址转化为字符串 62 | // Serial.printf("收到来自远程IP:%s(远程端口:%d)的数据包字节数:%d %X\n", udp.remoteIP().toString().c_str(), udp.remotePort(), packetSize, packetBuffer[8]); 63 | uint8_t vn = packetBuffer[0] & 0x3; // 数据包版本 64 | if (vn != 0x1) // 如果数据包版本不为1,则直接结束 65 | { 66 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 67 | replyPacket[0] = {0x01}; 68 | udp.write(replyPacket); //把数据写入发送缓冲区 69 | udp.endPacket(); //发送数据 70 | return udpdata; 71 | } 72 | udpdata.rt = packetBuffer[0] >> 6; // 数据包接收成功返回值 73 | if (udpdata.rt == 0x0) 74 | { 75 | 76 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 todo 这里的做法是错误的,我们自定义协议,返回的也应该是按协议来返回的,但是我就是想偷个懒 77 | // 如果返回类型值为0的时候,则上报状态信息值 78 | replyPacket[0] = latticeSetting.isShutdown << 6; // 是否显示 79 | replyPacket[0] += latticeSetting.brightness << 2; // 显示亮度 80 | replyPacket[0] += latticeSetting.direction; // 显示方向 81 | replyPacket[1] = power; // 功能 82 | replyPacket[2] = mode; // 功能模式 83 | replyPacket[3] = latticeSetting.speed; // 动画移动速度 84 | replyPacket[4] = version; // 系统版本 85 | udp.write(replyPacket, 5); //把数据写入发送缓冲区 86 | udp.endPacket(); //发送数据 87 | return udpdata; 88 | } 89 | udpdata.te = (packetBuffer[0] & 0x3f) >> 2; // 功能模式 90 | udpdata.lh = packetBuffer[1]; // 数据长度 91 | Serial.println("UDP接收到的数据信息为: "); 92 | Serial.println(udpdata.rt); 93 | Serial.println(udpdata.te); 94 | Serial.println(udpdata.lh); 95 | // 数据包 96 | for (int i = 0; i < udpdata.lh; i++) 97 | { 98 | udpdata.data[i] = packetBuffer[i + 2]; 99 | } 100 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 101 | replyPacket[0] = {0x01}; 102 | udp.write(replyPacket, 3); //把数据写入发送缓冲区 103 | udp.endPacket(); //发送数据 104 | return udpdata; 105 | } 106 | return udpdata; 107 | } 108 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/Udps.h: -------------------------------------------------------------------------------- 1 | #ifndef UDPS_H 2 | #define UDPS_H 3 | 4 | #include "DateTimes.h" 5 | #include "Lattice.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #define localPort 1234 // 监听本地UDP数据包端口 11 | #define remoteNtpPort 123 // 远程服务端口 12 | #define NTP_PACKET_SIZE 48 // NTP数据包数据长度 13 | 14 | struct Udpdata 15 | { 16 | uint8_t rt; // 返回类型 17 | uint8_t te; // 数据类型 18 | uint8_t lh; // 数据长度 19 | uint8_t data[64]; // 数据包 20 | }; 21 | 22 | class Udps 23 | { 24 | private: 25 | long timeFlag = 0; 26 | 27 | /** 28 | * @brief NTP服务器网址 29 | */ 30 | const char *ntpServerName = "cn.ntp.org.cn"; 31 | 32 | /** 33 | * @brief 服务器IP 34 | */ 35 | IPAddress timeServerIP; 36 | 37 | /** 38 | * @brief 接收到的ntp数据包 39 | */ 40 | byte packetBuffer[NTP_PACKET_SIZE]; 41 | 42 | /** 43 | * @brief 返回UDP数据包 44 | */ 45 | char replyPacket[4]; 46 | 47 | /** 48 | * @brief udp对象 49 | */ 50 | WiFiUDP udp; 51 | 52 | /** 53 | * @brief 发送NTP数据包 54 | */ 55 | void sendNTPpacket(); 56 | 57 | public: 58 | /** 59 | * @brief 是否 60 | */ 61 | bool isSetTime; 62 | /** 63 | * @brief构造函数 64 | */ 65 | Udps(); 66 | 67 | /** 68 | * @brief 初始化UDP信息 69 | * 70 | */ 71 | void initudp(); 72 | 73 | /** 74 | * @brief设置系统时间 75 | */ 76 | long getNtpTimestamp(); 77 | 78 | /** 79 | * @brief接收自定义UPD协议的数据 80 | */ 81 | Udpdata userLatticeLoop(LatticeSetting latticeSetting, uint8_t power, uint8_t mode, uint8_t version); 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/esp8266-lattice-clock.ino: -------------------------------------------------------------------------------- 1 | #include "Main.h" 2 | #define BLINKER_WIFI 3 | #define BLINKER_MIOT_LIGHT 4 | #include 5 | 6 | char auth[] = "0b6921f916bd"; 7 | 8 | // 新建组件对象 9 | BlinkerButton powerBtn("btn-power"); // 开关按键 10 | BlinkerButton directBtn("btn-direction"); // 方向按键 11 | BlinkerNumber temperNum("num-temperature"); // 温度回调 12 | BlinkerSlider brightSlider("slider_bright"); //亮度滑动条 13 | BlinkerSlider powerSlider("slider_power"); //功能滑动条 14 | 15 | /** 16 | * @brief 设置方向回调 17 | * 18 | * @param state 19 | */ 20 | void directionCallback(const String &state) 21 | { 22 | Serial.print("设置时钟方向:"); 23 | Serial.println(state); 24 | uint8_t direction = 0; 25 | if (state == BLINKER_CMD_ON) 26 | { 27 | direction = 0; 28 | directBtn.print("on"); // 反馈开关状态 29 | } 30 | else if (state == BLINKER_CMD_OFF) 31 | { 32 | direction = 1; 33 | directBtn.print("off"); // 反馈开关状态 34 | } 35 | lattice.setDirection(direction); // 是否启用点阵屏幕 36 | } 37 | 38 | /** 39 | * @brief 设置亮度的回调 40 | * 41 | * @param value 42 | */ 43 | void brightCallback(int32_t value) 44 | { 45 | lattice.setBrightness(value, true); // 设置亮度 46 | brightSlider.print(value); // 将数据回调给组件 47 | } 48 | 49 | /** 50 | * @brief 功能切换的回调 51 | * 52 | * @param value 53 | */ 54 | void powerCallback(int32_t value) 55 | { 56 | power = value; // 切换功能 57 | initStatus(); 58 | powerSlider.print(value); // 将数据回调给组件 59 | } 60 | 61 | /** 62 | 小爱同学接入方法 - 开关功能回调 63 | */ 64 | void miotPowerState(const String &state) 65 | { 66 | Serial.print("小爱语音设置的功能状态为:"); 67 | Serial.println(state); 68 | uint8_t power = 0; 69 | if (state == BLINKER_CMD_ON) 70 | { 71 | power = 0; 72 | BlinkerMIOT.powerState("on"); 73 | powerBtn.print("on"); // 反馈开关状态 74 | BlinkerMIOT.print(); 75 | } 76 | else if (state == BLINKER_CMD_OFF) 77 | { 78 | power = 1; 79 | BlinkerMIOT.powerState("off"); 80 | powerBtn.print("off"); // 反馈开关状态 81 | BlinkerMIOT.print(); 82 | } 83 | lattice.shutdown(power); // 是否启用点阵屏幕 84 | } 85 | 86 | /** 87 | 小爱同学接入方法 - 设置亮度 88 | */ 89 | void miotBright(const String &bright) 90 | { 91 | Serial.print("小爱语音设置的亮度为:"); 92 | Serial.println(bright.toInt()); 93 | lattice.setBrightness(bright.toInt() / 100 * 16, true); // 设置亮度 94 | BlinkerMIOT.brightness(bright.toInt()); 95 | brightSlider.print((int)bright.toInt() / 100 * 16); // 将数据回调给组件 96 | BlinkerMIOT.print(); 97 | } 98 | 99 | /** 100 | 小爱同学接入方法 - 查询状态 101 | */ 102 | void miotQuery(int32_t queryCode) 103 | { 104 | Serial.print("小爱语音查询状态代码:"); 105 | Serial.println(queryCode); 106 | } 107 | 108 | void setup() 109 | { 110 | Serial.begin(115200); // 初始化串口波特率 111 | initTouch(); // 初始化按键信息 112 | wifis.connWifi(lattice, pilotLight); // 连接wifi 113 | udps.initudp(); // 初始化UDP客户端 114 | pilotLight.dim(); //正常进操作就熄灭指示灯 115 | httptoolticker.attach(5, updateBiliFstatus); // 每分钟更新一次bilibili粉丝数量 116 | if (wifis.wifiMode == 0x00) // 如果wifi模式为连接wifi的模式则联网矫正时间 117 | { 118 | Blinker.begin(auth, WiFi.SSID().c_str(), WiFi.psk().c_str()); // 初始化Blinker 这里只有在连接wifi成功的情况下才能用 119 | powerBtn.attach(miotPowerState); // 注册开关按键 120 | brightSlider.attach(brightCallback); // 注册亮度调节 121 | directBtn.attach(directionCallback); // 注册方向按钮回到 122 | powerSlider.attach(powerCallback); // 注册功能切换 123 | BlinkerMIOT.attachPowerState(miotPowerState); // 注册屏幕开关的回调 124 | BlinkerMIOT.attachBrightness(miotBright); // 注册亮度控制的回调 125 | BlinkerMIOT.attachQuery(miotQuery); // 注册小爱同学语音状态查询 126 | resetTime(displayData); // 每次初始化的时候都校准一下时间,这里是随便传的一个参数,不想重新声明参数 127 | httptool.bilibiliFans(); // 刷新bilibili粉丝数量 128 | } 129 | else 130 | { 131 | pilotLight.bright(); // 如果是热点模式的话,指示的LED灯常亮 132 | } 133 | initSleepTime(); // 初始化休眠时间 134 | } 135 | 136 | void loop() 137 | { 138 | handleUdpData(); 139 | touchLoop(); 140 | handlePower(); 141 | sleepTimeLoop(); 142 | // temperNum.print(datetimes.getTemperature()); 143 | Blinker.run(); 144 | } 145 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/wifis.cpp: -------------------------------------------------------------------------------- 1 | #include "Wifis.h" 2 | 3 | // 连接您的WIFI SSID和密码 (调试模式可使用此方式连接wifi) 4 | #define WIFI_SSID "wifiname" 5 | #define WIFI_PASSWD "password" 6 | 7 | bool usePass = false; // 如果需要使用上面的密码,请将值 `false` 改成 `true` 8 | 9 | Wifis::Wifis() 10 | { 11 | } 12 | 13 | void Wifis::initWifi() 14 | { 15 | if (EEPROMTool.loadDataOne(WIFI_MODE) == 0x01) // 如果取到的数据为1时表示启动了热点模式 16 | { 17 | WiFi.mode(WIFI_AP); // 设置wifi模式为热点模式 18 | wifiMode = 0x01; // 标记当前wifi模式 19 | WiFi.softAP(ssid, password); // 设置wifi热点账号密码 20 | EEPROMTool.saveDataOne(0x00, WIFI_MODE); // 处理完热点模式以后随机将默认模式改为wifi模式 21 | } 22 | else 23 | { 24 | WiFi.mode(WIFI_STA); 25 | wifiMode = 0x00; 26 | } 27 | } 28 | 29 | void Wifis::connWifi(Lattice lattice, PilotLight pilotLight) 30 | { 31 | initWifi(); // 初始化wifi信息 32 | timer = 0; // 清零计数器 33 | if (wifiMode == 0x01) // 模式为热点模式就不走连接wifi和wifi配网了 34 | { 35 | // 如果wifi模式为热点模式,则不进wifi连接和配网 36 | return; 37 | } 38 | if (EEPROMTool.loadDataOne(REMEMBER_WIFI) || usePass) // 如果记住wifi值不为0xfe表示存在WiFi账号密码等信息 39 | { 40 | Serial.println("start connect wifi "); 41 | if (usePass) // 判断是否使用代码中的wifi信息 42 | { 43 | WiFi.begin(WIFI_SSID, WIFI_PASSWD); // 使用固定的wifi信息 44 | } 45 | else 46 | { 47 | WiFi.begin(WiFi.SSID().c_str(), WiFi.psk().c_str()); // 使用配网获取的wifi信息 48 | } 49 | while (WiFi.status() != WL_CONNECTED) 50 | { 51 | timer++; 52 | pilotLight.flashing(); // 闪烁LED灯 53 | lattice.showLongIcon(0); // 显示连接wifi图案 54 | delay(100); 55 | if (timer >= 600) // 如果计数器大于60次,表示超过一分钟,则说明一分钟都没有连接上wifi,就不连了 56 | { 57 | timer = 0; // 清零计数器 58 | enableApMode(); // 联网失败进入到热点模式 59 | break; // 防止出问题还是break一下 60 | } 61 | } 62 | WiFi.setAutoConnect(true); // 设置自动连接 63 | } 64 | else 65 | { 66 | WiFi.beginSmartConfig(); // 没有账号密码则进入到配网模式 67 | Serial.println("smart config wifi "); 68 | while (1) 69 | { 70 | timer++; 71 | lattice.showLongIcon(1); // 显示配网中图案信息 72 | pilotLight.flashing(); // 闪烁LED灯 73 | delay(500); // 等待半秒钟 74 | if (timer >= 20) // 如果配网次数超过20此,则重启系统,重新配网 75 | { 76 | ESP.restart(); // 重启系统 77 | return; 78 | } 79 | if (WiFi.smartConfigDone()) // 配网成功 80 | { 81 | WiFi.setAutoConnect(true); // 设置自动连接 82 | EEPROMTool.saveDataOne(true, REMEMBER_WIFI); // 记住wifi密码 83 | break; 84 | } 85 | } 86 | } 87 | Serial.println("conn wifi successful"); // 记录一下日志,避免一点都不知道有没有连上wifi 88 | delay(500); // 等几秒再进入系统 89 | timer = 0; // 清零计数器 90 | } 91 | 92 | void Wifis::enableApMode() 93 | { 94 | EEPROMTool.saveDataOne(0x01, WIFI_MODE); // 修改wifi模式,随后重启ESP 95 | ESP.restart(); // 重启系统 96 | } 97 | -------------------------------------------------------------------------------- /blinker/esp8266-lattice-clock/wifis.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFIS_H 2 | #define WIFIS_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "Lattice.h" 6 | #include "PilotLight.h" 7 | #include 8 | #include 9 | 10 | // wifi热点的账号密码 11 | #ifndef APSSID 12 | #define APSSID "lattice-clock" 13 | #define APPSK "12345678" 14 | #endif 15 | 16 | class Wifis 17 | { 18 | private: 19 | /** 20 | * @brief 是否记住wifi密码 21 | * 22 | */ 23 | bool rememberWifiPwd = true; 24 | 25 | /** 26 | * @brief 固定设备热点的SSID 27 | * 28 | */ 29 | const char *ssid = APSSID; 30 | 31 | /** 32 | * @brief 固定设备热点的密码 33 | * 34 | */ 35 | const char *password = APPSK; 36 | 37 | /** 38 | * @brief 初始化wifi 39 | * 40 | */ 41 | void initWifi(); 42 | 43 | /** 44 | * @brief 计数器 45 | * 46 | */ 47 | int timer; 48 | 49 | public: 50 | /** 51 | * @brief 构造函数 52 | * 53 | */ 54 | Wifis(); 55 | 56 | /** 57 | * @brief wifi模式 0x00: 连接wifi模式(STA) 0x01: wifi热点模式(AP) 58 | * 59 | */ 60 | uint8_t wifiMode = 0x00; 61 | 62 | /** 63 | * @brief 连接wifi 64 | * 65 | * @param lattice 点阵显示对象 66 | * @param pilotLight LED显示对象 67 | */ 68 | void connWifi(Lattice lattice, PilotLight pilotLight); 69 | 70 | /** 71 | * @brief 启动热点模式 72 | * 73 | */ 74 | void enableApMode(); 75 | }; 76 | #endif 77 | -------------------------------------------------------------------------------- /blinker/image-20220325164645545.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325164645545.png -------------------------------------------------------------------------------- /blinker/image-20220325164702280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325164702280.png -------------------------------------------------------------------------------- /blinker/image-20220325164712839.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325164712839.png -------------------------------------------------------------------------------- /blinker/image-20220325164719116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325164719116.png -------------------------------------------------------------------------------- /blinker/image-20220325164854457.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325164854457.png -------------------------------------------------------------------------------- /blinker/image-20220325165221276.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325165221276.png -------------------------------------------------------------------------------- /blinker/image-20220325165226894.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325165226894.png -------------------------------------------------------------------------------- /blinker/image-20220325165233514.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/blinker/image-20220325165233514.png -------------------------------------------------------------------------------- /blinker/readme.md: -------------------------------------------------------------------------------- 1 | # 点阵时钟 - 接入点灯版本说明 2 | 3 | ## 序 4 | 5 | 此版本为体验版本,后续不做过多更新操作,有兴趣的欢饮自己手动开发,这里只做基本的说明 6 | 7 | ## 使用说明 8 | 9 | ### 1. 注册点灯程序获取 Secret key 10 | 11 | 12 | 13 | ![image-20220325164645545](image-20220325164645545.png) 14 | 15 | ![image-20220325164702280](image-20220325164702280.png) 16 | 17 | ![image-20220325164712839](image-20220325164712839.png) 18 | 19 | ![image-20220325164719116](image-20220325164719116.png) 20 | 21 | ### 2. 修改代码 22 | 23 | 将第一步获取的点灯``Secret key`` 放在下图代码处 24 | 25 | ![image-20220325164854457](image-20220325164854457.png) 26 | 27 | ### 3. 写入程序 28 | 29 | 这个应该都懂,不懂请参考 [https://gitee.com/lengff/esp8266-lattice-clock-open](https://gitee.com/lengff/esp8266-lattice-clock-open) 30 | 31 | 32 | 33 | ### 4. 导入点灯的操作界面 34 | 35 | 界面代码如下: 36 | 37 | ``` 38 | {¨version¨¨2.0.0¨¨config¨{¨headerColor¨¨transparent¨¨headerStyle¨¨dark¨¨background¨{¨img¨¨assets/img/headerbg.jpg¨¨isFull¨«}}¨dashboard¨|{¨type¨¨btn¨¨ico¨¨fal fa-power-off¨¨mode¨Ê¨t0¨¨熄屏¨¨t1¨¨文本2¨¨bg¨É¨cols¨Ë¨rows¨Ë¨key¨¨btn-power¨´x´É´y´Ë¨lstyle¨Ê}{ßCßDßEßFßGÊßH¨显示方向¨ßJßKßLÉßMËßNËßO¨btn-direction¨´x´É´y´ÍßQÊ}{ßC¨ran¨ßH¨功能切换¨¨clr¨¨#389BEE¨¨max¨Î¨min¨ÉßLÉßMÏßNËßO¨slider_power¨´x´Ë´y´ÍßQͨrt¨»}{ßCßTßH¨设置亮度¨ßVßWßX¤FßYÉßLÉßMÏßNËßO¨slider_bright¨´x´Ë´y´ËßQÍßa»}÷¨actions¨|÷¨triggers¨|÷ßa|ßZßc÷} 39 | ``` 40 | 41 | 操作步骤如下: 42 | 43 | ![image-20220325165221276](image-20220325165221276.png) 44 | 45 | ![image-20220325165226894](image-20220325165226894.png) 46 | 47 | ![image-20220325165233514](image-20220325165233514.png) 48 | 49 | ### 5. 结束 50 | 51 | 52 | 53 | ## 绑定小爱同学 54 | 55 | ### 1.打开米家App。通过 **我的>其他平台设备>点击添加>点灯科技>绑定账号** ,绑定blinker账号 56 | ### 2.绑定成功后,支持小爱控制的blinker设备会出现在 **我的>其他平台设备>点灯科技** 设备列表中 57 | ### 3.现在可以使用小爱控制该设备了 58 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/BlinkerSupport.h: -------------------------------------------------------------------------------- 1 | #ifndef BLINKER_SUPPORT_H 2 | #define BLINKER_SUPPORT_H 3 | 4 | #define BLINKER_WIFI 5 | #define BLINKER_MIOT_LIGHT 6 | 7 | #include "Main.h" 8 | #include 9 | 10 | char auth[] = ""; // 这里设置你自己的点灯秘钥 11 | 12 | // 新建组件对象 13 | BlinkerButton powerBtn("btn-power"); // 开关按键 14 | BlinkerButton directBtn("btn-direction"); // 方向按键 15 | BlinkerNumber temperNum("num-temperature"); // 温度回调 16 | BlinkerSlider brightSlider("slider_bright"); //亮度滑动条 17 | BlinkerSlider powerSlider("slider_power"); //功能滑动条 18 | 19 | /** 20 | * @brief 设置方向回调 21 | * 22 | * @param state 23 | */ 24 | void directionCallback(const String &state) 25 | { 26 | Serial.print("设置时钟方向:"); 27 | Serial.println(state); 28 | uint8_t direction = 0; 29 | if (state == BLINKER_CMD_ON) 30 | { 31 | direction = 0; 32 | directBtn.print("on"); // 反馈开关状态 33 | } 34 | else if (state == BLINKER_CMD_OFF) 35 | { 36 | direction = 1; 37 | directBtn.print("off"); // 反馈开关状态 38 | } 39 | lattice.setDirection(direction); // 是否启用点阵屏幕 40 | } 41 | 42 | /** 43 | * @brief 设置亮度的回调 44 | * 45 | * @param value 46 | */ 47 | void brightCallback(int32_t value) 48 | { 49 | lattice.setBrightness(value, true); // 设置亮度 50 | brightSlider.print(value); // 将数据回调给组件 51 | } 52 | 53 | /** 54 | * @brief 功能切换的回调 55 | * 56 | * @param value 57 | */ 58 | void powerCallback(int32_t value) 59 | { 60 | functions.setPowerAndMode(value, 0); // 切换功能 61 | initStatus(); // 重置状态 62 | powerSlider.print(value); // 将数据回调给组件 63 | } 64 | 65 | /** 66 | 小爱同学接入方法 - 开关功能回调 67 | */ 68 | void miotPowerState(const String &state) 69 | { 70 | Serial.print("小爱语音设置的功能状态为:"); 71 | Serial.println(state); 72 | uint8_t power = 0; 73 | if (state == BLINKER_CMD_ON) 74 | { 75 | power = 0; 76 | BlinkerMIOT.powerState("on"); 77 | powerBtn.print("on"); // 反馈开关状态 78 | BlinkerMIOT.print(); 79 | } 80 | else if (state == BLINKER_CMD_OFF) 81 | { 82 | power = 1; 83 | BlinkerMIOT.powerState("off"); 84 | powerBtn.print("off"); // 反馈开关状态 85 | BlinkerMIOT.print(); 86 | } 87 | lattice.shutdown(power); // 是否启用点阵屏幕 88 | } 89 | 90 | /** 91 | 小爱同学接入方法 - 设置亮度 92 | */ 93 | void miotBright(const String &bright) 94 | { 95 | Serial.print("小爱语音设置的亮度为:"); 96 | Serial.println(bright.toInt()); 97 | lattice.setBrightness(bright.toInt() / 100 * 16, true); // 设置亮度 98 | BlinkerMIOT.brightness(bright.toInt()); 99 | brightSlider.print((int)bright.toInt() / 100 * 16); // 将数据回调给组件 100 | BlinkerMIOT.print(); 101 | } 102 | 103 | /** 104 | 小爱同学接入方法 - 查询状态 105 | */ 106 | void miotQuery(int32_t queryCode) 107 | { 108 | Serial.print("小爱语音查询状态代码:"); 109 | Serial.println(queryCode); 110 | } 111 | 112 | /** 113 | * 初始化点灯科技信息 114 | * 115 | */ 116 | void initBlinker() 117 | { 118 | Blinker.begin(auth, WiFi.SSID().c_str(), WiFi.psk().c_str()); // 初始化Blinker 这里只有在连接wifi成功的情况下才能用 119 | powerBtn.attach(miotPowerState); // 注册开关按键 120 | brightSlider.attach(brightCallback); // 注册亮度调节 121 | directBtn.attach(directionCallback); // 注册方向按钮回到 122 | powerSlider.attach(powerCallback); // 注册功能切换 123 | BlinkerMIOT.attachPowerState(miotPowerState); // 注册屏幕开关的回调 124 | BlinkerMIOT.attachBrightness(miotBright); // 注册亮度控制的回调 125 | BlinkerMIOT.attachQuery(miotQuery); // 注册小爱同学语音状态查询 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/ConfigWifiHtml.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_WIFI_HTML_H 2 | #define CONFIG_WIFI_HTML_H 3 | /** 4 | * @brief 配网HTML代码 本段代码改编自 https://blog.csdn.net/weixin_44220583/article/details/111562423 5 | * 这里做一简单的说明吧 6 | * 其实呢一开始呢是并不想开源这段网页配网的功能代码的,因为会触及到他人的利益,之前有网友迫切的跟我说不要搞开源留一手,不希望这个市场泛滥,归根到底还是会触及他人的利益。 7 | * 在这里也希望那位仁兄看到之后切勿介意,我个人始终认为开源是促进进步的一种方式,倘若大家都封闭自己的想法和思路怎么能得到本质上的进步呢,所以这里我也不对代码做任何的混淆处理, 8 | * 只是对内容进行压缩,不然太长影响整个项目。后续也会整理出相关的文档来对这里过程做详细的说明。 9 | */ 10 | const char *page_html = "点阵时钟配置WIFI
欢迎使用点阵时钟
SSID:
密码:
wifi列表刷新
"; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/DateTimes.cpp: -------------------------------------------------------------------------------- 1 | #include "DateTimes.h" 2 | 3 | DateTimes::DateTimes() 4 | { 5 | Wire.begin(D1,D2); 6 | // dht11 = SimpleDHT11(16); 7 | } 8 | 9 | /** 10 | * @brief 获取时间信息 11 | * 12 | * @return Times 13 | */ 14 | Times DateTimes::getTimes() 15 | { 16 | times.s = ds3231.getSecond(); 17 | times.m = ds3231.getMinute(); 18 | times.h = ds3231.getHour(h12Flag, pmFlag); 19 | // 如果从芯片取到的小时数据为25的时候,表示这个数据不是从芯片里面取到的,所以就需要换成我们手动的 20 | if (times.h == 25) 21 | { 22 | datetime = DateTime(currtimestamp); 23 | times.h = datetime.hour(); 24 | times.m = datetime.minute(); 25 | times.s = datetime.second(); 26 | } 27 | return times; 28 | } 29 | 30 | /** 31 | * @brief 获取日期信息 32 | * 33 | * @return Dates 34 | */ 35 | Dates DateTimes::getDates() 36 | { 37 | dates.y = 2000 + ds3231.getYear(); 38 | dates.m = ds3231.getMonth(century); 39 | dates.d = ds3231.getDate(); 40 | // 如果从芯片取到的年数据为85的时候,表示这个数据不是从芯片里面取到的,所以就需要换成我们手动的 41 | if (dates.m == 85) 42 | { 43 | datetime = DateTime(currtimestamp); 44 | dates.y = datetime.year(); 45 | dates.m = datetime.month(); 46 | dates.d = datetime.day(); 47 | } 48 | return dates; 49 | } 50 | 51 | /** 52 | * @brief 获取温度信息 53 | * 54 | * @return int 55 | */ 56 | int DateTimes::getTemperature() 57 | { 58 | // 如果从芯片取到的年数据为85的时候,表示这个数据不是从芯片里面取到的,所以就需要换成我们手动的 59 | if (ds3231.getMonth(century) == 85) 60 | { 61 | if (System::is_overtime(3000)) // 这里三秒获取一次温度信息 62 | { 63 | if (dht11.read(&temperature, &humidity, NULL) != SimpleDHTErrSuccess) 64 | { 65 | // 这里表示获取温度失败,暂时不做任何处理 66 | } 67 | } 68 | return temperature * 100; 69 | } 70 | return ds3231.getTemperature() * 100; 71 | } 72 | 73 | /** 74 | * @brief 给时钟芯片和系统时间设置时间信息 75 | * 76 | * @param timestamp 77 | */ 78 | void DateTimes::setDateTimes(long timestamp) 79 | { 80 | currtimestamp = timestamp; 81 | datetime = DateTime(timestamp); 82 | ds3231.setYear(datetime.year() % 100); 83 | ds3231.setMonth(datetime.month()); 84 | ds3231.setDate(datetime.day()); 85 | ds3231.setHour(datetime.hour()); 86 | ds3231.setMinute(datetime.minute()); 87 | ds3231.setSecond(datetime.second()); 88 | } 89 | 90 | /** 91 | * @brief 获取时间戳信息 92 | * 93 | * @return long 94 | */ 95 | long DateTimes::getTimestamp() 96 | { 97 | // 如果从芯片取到的年数据为85的时候,表示这个数据不是从芯片里面取到的,所以就需要换成我们手动的 98 | if (ds3231.getMonth(century) == 85) 99 | { 100 | return currtimestamp; 101 | } 102 | return RTClib::now().unixtime(); 103 | } 104 | 105 | /** 106 | * @brief 系统时间戳++ 107 | * 108 | */ 109 | void DateTimes::timestampAdd() 110 | { 111 | currtimestamp++; 112 | } 113 | 114 | /** 115 | * @brief 保存倒计时时间戳信息 116 | * 117 | * @param timestamp 118 | */ 119 | void DateTimes::saveCountdownTimestamp(long timestamp) 120 | { 121 | uint8_t arr[5]; 122 | for (int i = 0; i < 5; i++) 123 | { 124 | arr[i] = (timestamp & 0xff); 125 | timestamp >>= 8; 126 | } 127 | EEPROMTool.saveData(arr, COUNTDOWN_TIME, 5); 128 | } 129 | 130 | /** 131 | * @brief 获取倒计时时间戳信息 132 | * 133 | * @return long 134 | */ 135 | long DateTimes::getCountdownTimestamp() 136 | { 137 | long timestamp = 0; 138 | uint8_t *temp = EEPROMTool.loadData(COUNTDOWN_TIME, 5); 139 | for (int i = 0; i < 5; i++) 140 | { 141 | timestamp += temp[i] << (i * 8); 142 | } 143 | // 用完以后删除内存 144 | free(temp); 145 | return timestamp; 146 | } 147 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/DateTimes.h: -------------------------------------------------------------------------------- 1 | #ifndef DATETIMES_H 2 | #define DATETIMES_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "System.h" 6 | #include 7 | #include 8 | #include 9 | 10 | struct Times 11 | { 12 | uint8_t h; 13 | uint8_t m; 14 | uint8_t s; 15 | }; 16 | 17 | struct Dates 18 | { 19 | uint16_t y; 20 | uint8_t m; 21 | uint8_t d; 22 | }; 23 | 24 | /** 25 | * 当前时间戳,冗余做法,即时钟芯片没有或者不生效时,使用系统的时间戳来反馈时间 26 | */ 27 | static long currtimestamp = 0; 28 | 29 | class DateTimes 30 | { 31 | private: 32 | bool h12Flag, pmFlag, century = false; 33 | 34 | /** 35 | * @brief 初始化DS3231 36 | * 37 | */ 38 | DS3231 ds3231; 39 | /** 40 | * @brief 41 | * 42 | */ 43 | DateTime datetime; 44 | 45 | /** 46 | * @brief Dht11 温度传感器对象 47 | * 48 | */ 49 | SimpleDHT11 dht11; 50 | 51 | /** 52 | * @brief 时间结构体 53 | * 54 | */ 55 | Times times; 56 | 57 | /** 58 | * @brief 日期结构体 59 | * 60 | */ 61 | Dates dates; 62 | 63 | public: 64 | /** 65 | * @brief 温度 66 | * 67 | */ 68 | byte temperature = 0; 69 | /** 70 | * @brief 湿度 71 | * 72 | */ 73 | byte humidity = 0; 74 | /** 75 | * 构造函数 76 | */ 77 | DateTimes(); 78 | 79 | /** 80 | * @brief 初始化 81 | * 82 | */ 83 | void initDateTimes(); 84 | 85 | /** 86 | * 获取时间 87 | */ 88 | Times getTimes(); 89 | 90 | /** 91 | * 获取日期 92 | */ 93 | Dates getDates(); 94 | 95 | /** 96 | * 获取温度信息 97 | */ 98 | int getTemperature(); 99 | 100 | /** 101 | * 根据datetime设置时间 102 | */ 103 | void setDateTimes(long timestamp); 104 | 105 | /** 106 | * 获取时间戳信息 107 | */ 108 | long getTimestamp(); 109 | 110 | /** 111 | * 获取倒计时的时间戳 112 | */ 113 | long getCountdownTimestamp(); 114 | 115 | /** 116 | * 保存倒计时的时间戳 117 | */ 118 | void saveCountdownTimestamp(long timestamp); 119 | 120 | /** 121 | * @brief 时间戳++ 122 | * 123 | */ 124 | static void timestampAdd(); 125 | }; 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/EEPROMTool.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_TOOL_H 2 | #define EEPROM_TOOL_H 3 | 4 | #include 5 | 6 | #define EEPROM_BEGIN 1536 7 | #define EEPROM_LENGTH 50 8 | 9 | /** 10 | * @brief todo 这里可以优化,直接改成结构体 11 | * 12 | */ 13 | enum EEPROM_ADDRESS_ENUM 14 | { 15 | 16 | REMEMBER_WIFI = EEPROM_BEGIN + 1, // 记住wifi内存地址 17 | WIFI_MODE = EEPROM_BEGIN + 2, // wifi模式内存地址 18 | DIRECTION = EEPROM_BEGIN + 3, // 屏幕显示方向内存地址 19 | BRIGHTNESS = EEPROM_BEGIN + 4, // 屏幕亮度内存地址 20 | SLEEP_TIME = EEPROM_BEGIN + 5, // 从第5位到第9位来存睡眠时间相关 21 | BILIBILI_UID = EEPROM_BEGIN + 10, // 从10到14来存储bilibilii的UID 22 | COUNTDOWN_TIME = EEPROM_BEGIN + 15, // 从15到19来存储倒计时时间戳 23 | H12_ADDR = EEPROM_BEGIN + 20, // 12位存放H12标记 \\\ 这里占位,暂无任何意义 24 | ANNI_DAY = EEPROM_BEGIN + 21, // 10,11存放纪念日 月和 日 \\\ 这里占位,暂无任何意义 25 | LIGHT_VAL = EEPROM_BEGIN + 22 // 存放亮度的 26 | }; 27 | 28 | struct EEPROMTOOLS 29 | { 30 | /** 31 | * @brief 保存数据 32 | */ 33 | void saveData(uint8_t *data, int offset, int length) 34 | { 35 | for (int i = 0; i < length; i++) 36 | { 37 | EEPROM.write(offset + i, data[i] & 0xff); 38 | } 39 | EEPROM.commit(); 40 | } 41 | /** 42 | * @brief 只保存一位数据 43 | * 44 | * @param data 45 | * @param offset 46 | */ 47 | void saveDataOne(uint8_t data, int offset) 48 | { 49 | EEPROM.write(offset, data & 0xff); 50 | EEPROM.commit(); 51 | } 52 | 53 | /** 54 | * @brief 获取数据 55 | * 56 | * @param offset 57 | * @param length 58 | * @return uint8_t* 59 | */ 60 | uint8_t *loadData(int offset, int length) 61 | { 62 | unsigned char *arr = new uint8_t[length]; 63 | for (int i = 0; i < length; i++) 64 | { 65 | arr[i] = EEPROM.read(offset + i); 66 | } 67 | return arr; 68 | } 69 | 70 | /** 71 | * @brief 获取一位数据 72 | */ 73 | uint8_t loadDataOne(int offset) 74 | { 75 | unsigned char arr = EEPROM.read(offset); 76 | return arr; 77 | } 78 | 79 | /** 80 | * @brief 删除数据 81 | */ 82 | void clearData(int offset, int length) 83 | { 84 | for (int i = 0; i < length; i++) 85 | { 86 | EEPROM.write(offset + i, 0x0); 87 | } 88 | EEPROM.commit(); 89 | } 90 | 91 | /** 92 | * @brief 删除全部数据 93 | */ 94 | void clearAll() 95 | { 96 | EEPROM.begin(4096); 97 | for (int i = 0; i < 4096; i++) 98 | { 99 | EEPROM.write(i, 0x0); 100 | } 101 | EEPROM.end(); 102 | } 103 | 104 | /** 105 | * @brief 备份自用数据到内存 106 | */ 107 | uint8_t *backupAll() 108 | { 109 | uint8_t *arr = new uint8_t[EEPROM_LENGTH]; 110 | EEPROM.begin(2048); 111 | for (int i = 0; i < EEPROM_LENGTH; i++) 112 | { 113 | arr[i] = EEPROM.read(EEPROM_BEGIN + i); 114 | } 115 | EEPROM.end(); 116 | return arr; 117 | } 118 | 119 | /** 120 | * @brief 恢复内存数据到EEPROM 121 | */ 122 | void restoreAll(uint8_t *arr) 123 | { 124 | EEPROM.begin(2048); 125 | for (int i = 0; i < EEPROM_LENGTH; i++) 126 | { 127 | EEPROM.write(EEPROM_BEGIN + i, arr[i]); 128 | } 129 | EEPROM.end(); 130 | } 131 | }; 132 | 133 | static EEPROMTOOLS EEPROMTool; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Functions.cpp: -------------------------------------------------------------------------------- 1 | #include "Functions.h" 2 | 3 | Functions::Functions() 4 | { 5 | } 6 | 7 | Functions::Functions(PilotLight *pilotLightobj) 8 | { 9 | pilotLight = pilotLightobj; 10 | } 11 | 12 | /** 13 | * @brief 重置功能和模式 14 | * 15 | */ 16 | void Functions::reset() 17 | { 18 | currPower = 0; 19 | currMode[currPower] = 0; 20 | } 21 | 22 | /** 23 | * @brief 设置功能和模式 24 | * 25 | * @param power 26 | * @param mode 27 | */ 28 | void Functions::setPowerAndMode(unsigned char power, unsigned char mode) 29 | { 30 | currPower = power; 31 | currMode[currPower] = mode; 32 | } 33 | 34 | /** 35 | * @brief 设置功能 36 | * 37 | * @param power 38 | */ 39 | void Functions::setPower(unsigned char power) 40 | { 41 | currPower = power; 42 | } 43 | 44 | /** 45 | * @brief 设置当前模式 46 | * 47 | * @param mode 48 | */ 49 | void Functions::setMode(unsigned char mode) 50 | { 51 | currMode[currPower] = mode; 52 | } 53 | 54 | /** 55 | * @brief 功能加 目前基本都只用到了功能加 56 | * 57 | */ 58 | void Functions::powerUp() 59 | { 60 | // 当前功能加到超过最大值时,就重置为默认的功能 61 | if (currPower == POWER_SIZE - 1) 62 | { 63 | currPower = 0; 64 | } 65 | else 66 | { 67 | ++currPower; // 功能加加 68 | } 69 | pilotLight->flashing(50); // 功能处理完闪烁一下状态指示灯 70 | } 71 | 72 | /** 73 | * @brief 功能减 74 | * 75 | */ 76 | void Functions::powerDown() 77 | { 78 | // todo 这个函数先不写 79 | } 80 | 81 | /** 82 | * @brief 模式加 目前基本都只用到了模式加 83 | * 84 | * @param power 85 | */ 86 | void Functions::modeUp() 87 | { 88 | // 如果说当前模式超过最大模式,则设置为初始模式 89 | if (currMode[currPower] == (modePower[currPower] - 1)) 90 | { 91 | currMode[currPower] = 0; 92 | } 93 | else 94 | { 95 | ++currMode[currPower]; 96 | } 97 | pilotLight->flashing(50); // 功能处理完闪烁一下状态指示灯 98 | } 99 | 100 | /** 101 | * @brief 模式减 102 | * 103 | * @param power 104 | */ 105 | void Functions::modeDown() 106 | { 107 | // todo 这个函数先不写 108 | } 109 | 110 | /** 111 | * @brief 获取当前功能 112 | * 113 | * @return unsigned char 114 | */ 115 | unsigned char Functions::getCurrPower() 116 | { 117 | return currPower; 118 | } 119 | 120 | /** 121 | * @brief 获取当前模式 122 | * 123 | * @return unsigned char 124 | */ 125 | unsigned char Functions::getCurrMode() 126 | { 127 | return currMode[currPower]; 128 | } -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Functions.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_H 2 | #define FUNCTIONS_H 3 | #include "PilotLight.h" 4 | 5 | enum ModeEnum 6 | { 7 | // 功能0: 显示时间 8 | SHOW_TIME = 0, 9 | // 功能1: 显示日期 10 | SHOW_DATE = 1, 11 | // 功能2: 显示日期 12 | POWER2 = 2, 13 | // 显示bilibili粉丝数量 14 | BILIFANS = 3, 15 | // 显示用户自定义数据 16 | CUSTOM = 4, 17 | // 显示倒计时 18 | COUNTDOWN = 5, 19 | // NTP更新时间 20 | RESETTIME = 98, 21 | // 系统重置 22 | RESET = 99 23 | }; 24 | 25 | #define POWER_SIZE 6 26 | 27 | const int modePower[POWER_SIZE] = {3, 3, 1, 1, 5, 1}; // 每个功能对应多少种模式 28 | 29 | class Functions 30 | { 31 | private: 32 | /** 33 | * @brief 状态指示灯对象 34 | * 35 | */ 36 | PilotLight *pilotLight; 37 | 38 | /** 39 | * @brief 当前功能对应的当前模式,因为用很多不同的功能,每个功能又有不同的模式,所以这里就要列举不同功能对应的不同模式 40 | * 41 | */ 42 | unsigned char currMode[POWER_SIZE] = {0, 0, 0, 0, 0, 1}; 43 | 44 | /** 45 | * @brief 当前所处在的功能 46 | * 47 | */ 48 | unsigned char currPower = 0; 49 | 50 | public: 51 | Functions(); 52 | 53 | /** 54 | * @brief 带参数的构造函数 55 | * 56 | * @param pilotLightobj 57 | */ 58 | Functions(PilotLight *pilotLightobj); 59 | 60 | /** 61 | * @brief 重置功能以及模式 62 | * 63 | */ 64 | void reset(); 65 | 66 | /** 67 | * @brief 设置功能和模式函数 68 | * 69 | * @param power 70 | * @param mode 71 | */ 72 | void setPowerAndMode(unsigned char power, unsigned char mode); 73 | 74 | /** 75 | * @brief 设置功能 76 | * 77 | * @param power 78 | */ 79 | void setPower(unsigned char power); 80 | 81 | /** 82 | * @brief 设置当前模式 83 | * 84 | * @param mode 85 | */ 86 | void setMode(unsigned char mode); 87 | 88 | /** 89 | * @brief 功能加 目前基本都只用到了功能加 90 | * 91 | */ 92 | void powerUp(); 93 | 94 | /** 95 | * @brief 功能减 96 | * 97 | */ 98 | void powerDown(); 99 | 100 | /** 101 | * @brief 模式加 目前基本都只用到了模式加 102 | * 103 | * @param power 104 | */ 105 | void modeUp(); 106 | 107 | /** 108 | * @brief 模式减 109 | * 110 | * @param power 111 | */ 112 | void modeDown(); 113 | 114 | /** 115 | * @brief 获取当前功能 116 | * 117 | * @return unsigned char 118 | */ 119 | unsigned char getCurrPower(); 120 | 121 | /** 122 | * @brief 获取当前模式 123 | * 124 | * @return unsigned char 125 | */ 126 | unsigned char getCurrMode(); 127 | }; 128 | 129 | #endif -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/HttpTool.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpTool.h" 2 | 3 | HttpTool::HttpTool() {} 4 | 5 | void HttpTool::initHttptool() {} 6 | 7 | void HttpTool::bilibiliFans() 8 | { 9 | if (!is_need_update_bilibili) 10 | { 11 | return; 12 | } 13 | fans = 0; // 每次都先重置粉丝数量,避免出现问题 14 | biliUid = loadBuid(); // 每次都重新加载bilibili用户ID 15 | Serial.println(biliUid); 16 | 17 | if (WiFi.status() != WL_CONNECTED) // 确保wifi网络是可用的,不可用则忽略 18 | { 19 | Serial.println("no wifi"); 20 | is_need_update_bilibili = false; 21 | return; 22 | } 23 | std::unique_ptr client(new BearSSL::WiFiClientSecure); 24 | client->setFingerprint(fingerprint); 25 | client->setInsecure(); 26 | // 直接调用B站接口获取粉丝数量 27 | espClient.begin(*client, bilibiliFansApi + biliUid); 28 | int httpCode = espClient.GET(); 29 | Serial.println(httpCode); 30 | if (httpCode > 0) 31 | { 32 | if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) 33 | { 34 | String json = espClient.getString(); 35 | Serial.println(json); 36 | DynamicJsonDocument doc(2048); 37 | deserializeJson(doc, json); 38 | // 获取bilibili粉丝数量 39 | fans = doc["data"]["follower"]; 40 | Serial.println(fans); 41 | is_need_update_bilibili = false; 42 | } 43 | } 44 | else 45 | { 46 | Serial.println("调用BIlibili的接口失败!"); 47 | } 48 | espClient.end(); 49 | } 50 | /** 51 | * @brief 保存bilibili的UID到EEPROM 52 | * 53 | * @param uid 54 | */ 55 | void HttpTool::saveBuid(long uid) 56 | { 57 | uint8_t arr[5]; 58 | for (int i = 0; i < 5; i++) 59 | { 60 | arr[i] = (uid & 0xff); 61 | uid >>= 8; 62 | } 63 | EEPROMTool.saveData(arr, BILIBILI_UID, 5); 64 | } 65 | 66 | /** 67 | * @brief 从EEPROM中加载bilibili的UID 68 | * 69 | * @return long 70 | */ 71 | long HttpTool::loadBuid() 72 | { 73 | long uid = 0; 74 | uint8_t *temp = EEPROMTool.loadData(BILIBILI_UID, 5); // 这里的97处理的不得当,后续优化,但是不影响实际功能 75 | for (int i = 0; i < 5; i++) 76 | { 77 | uid += temp[i] << (i * 8); 78 | } 79 | // 用完以后删除内存 80 | free(temp); 81 | return uid; 82 | } 83 | 84 | void HttpTool::updateBilibiliFlag() 85 | { 86 | is_need_update_bilibili = true; 87 | } -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/HttpTool.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_TOOL_H 2 | #define HTTP_TOOL_H 3 | 4 | #include "EEPROMTool.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static bool is_need_update_bilibili = false; // 判断是否需要更新bilibili粉丝数flag 12 | 13 | const uint8_t fingerprint[20] = {0x40, 0xaf, 0x00, 0x6b, 0xec, 0x90, 0x22, 0x41, 0x8e, 0xa3, 0xad, 0xfa, 0x1a, 0xe8, 0x25, 0x41, 0x1d, 0x1a, 0x54, 0xb3}; 14 | 15 | 16 | class HttpTool 17 | { 18 | private: 19 | /** 20 | * WiFiMulti 21 | * 22 | */ 23 | ESP8266WiFiMulti WiFiMulti; 24 | /** 25 | * http客户端 26 | */ 27 | HTTPClient espClient; 28 | 29 | /** 30 | * 加载BiliBili_Uid 31 | */ 32 | long loadBuid(); 33 | 34 | /** 35 | * bilibili用户ID 36 | */ 37 | long biliUid = 0; 38 | 39 | /** 40 | * 获取bilibili粉丝数量的接口 (此处欠妥,先这用着,后续优化) 41 | */ 42 | const String bilibiliFansApi = "https://api.bilibili.com/x/relation/stat?vmid="; 43 | 44 | public: 45 | HttpTool(); 46 | 47 | /** 48 | * bilibili粉丝数 49 | */ 50 | long fans = 0; 51 | 52 | /** 53 | * 初始化 54 | */ 55 | void initHttptool(); 56 | 57 | /** 58 | * 获取bilibili粉丝数量 59 | */ 60 | void bilibiliFans(); 61 | 62 | /** 63 | * 保存BiliBili_Uid 64 | */ 65 | void saveBuid(long uid); 66 | 67 | /** 68 | * @brief 时间戳++ 69 | * 70 | */ 71 | static void updateBilibiliFlag(); 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Lattice.h: -------------------------------------------------------------------------------- 1 | #ifndef LATTICE_H 2 | #define LATTICE_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "System.h" 6 | #include 7 | 8 | const uint8_t rowLength = 8; 9 | const uint8_t columnLength = 4; 10 | 11 | const int DIN = D7; 12 | const int CS = D6; 13 | const int CLK = D5; 14 | 15 | const unsigned char number_font_small[11][rowLength] = { 16 | {0x0, 0xE0, 0xA0, 0xA0, 0xA0, 0xE0, 0x0, 0x0}, // 0 17 | {0x0, 0x40, 0xC0, 0x40, 0x40, 0xE0, 0x0, 0x0}, // 1 18 | {0x0, 0xE0, 0x20, 0xE0, 0x80, 0xE0, 0x0, 0x0}, // 2 19 | {0x0, 0xE0, 0x20, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 3 20 | {0x0, 0xA0, 0xA0, 0xE0, 0x20, 0x20, 0x0, 0x0}, // 4 21 | {0x0, 0xE0, 0x80, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 5 22 | {0x0, 0xE0, 0x80, 0xE0, 0xA0, 0xE0, 0x0, 0x0}, // 6 23 | {0x0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0}, // 7 24 | {0x0, 0xE0, 0xA0, 0xE0, 0xA0, 0xE0, 0x0, 0x0}, // 8 25 | {0x0, 0xE0, 0xA0, 0xE0, 0x20, 0xE0, 0x0, 0x0}, // 9 26 | {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} // null 27 | }; 28 | 29 | const unsigned char number_font_middle[10][rowLength] = { 30 | {0x0, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0}, // 0 31 | {0x0, 0x40, 0xc0, 0x40, 0x40, 0x40, 0x40, 0xe0}, // 1 32 | {0x0, 0xf0, 0x10, 0x10, 0xf0, 0x80, 0x80, 0xf0}, // 2 33 | {0x0, 0xf0, 0x10, 0x10, 0xf0, 0x10, 0x10, 0xf0}, // 3 34 | {0x0, 0x90, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x10}, // 4 35 | {0x0, 0xf0, 0x80, 0x80, 0xf0, 0x10, 0x10, 0xf0}, // 5 36 | {0x0, 0xf0, 0x80, 0x80, 0xf0, 0x90, 0x90, 0xf0}, // 6 37 | {0x0, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, // 7 38 | {0x0, 0xf0, 0x90, 0x90, 0xf0, 0x90, 0x90, 0xf0}, // 8 39 | {0x0, 0xf0, 0x90, 0x90, 0xf0, 0x10, 0x10, 0xf0} // 9 40 | }; 41 | 42 | const unsigned char number_font_animations[10][16] = { 43 | {0x40, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 44 | 0x0, 0x0, 0x0, 0x0}, 45 | {0x20, 0x11, 0x21, 0x31, 0x41, 0x50, 0x51, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 46 | 0x0, 0x0, 0x0}, 47 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x31, 0x30, 0x40, 0x50, 0x51, 0x52, 0x0, 0x0, 48 | 0x0, 0x0, 0x0}, 49 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x31, 0x30, 0x42, 0x50, 0x51, 0x52, 0x0, 0x0, 50 | 0x0, 0x0, 0x0}, 51 | {0x10, 0x20, 0x30, 0x31, 0x12, 0x22, 0x32, 0x42, 0x52, 0x0, 0x0, 0x0, 0x0, 52 | 0x0, 0x0, 0x0}, 53 | {0x12, 0x11, 0x10, 0x20, 0x30, 0x31, 0x32, 0x42, 0x52, 0x51, 0x50, 0x0, 0x0, 54 | 0x0, 0x0, 0x0}, 55 | {0x12, 0x11, 0x10, 0x20, 0x30, 0x40, 0x50, 0x51, 0x52, 0x42, 0x32, 0x31, 56 | 0x0, 0x0, 0x0, 0x0}, 57 | {0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 58 | 0x0, 0x0, 0x0}, 59 | {0x31, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 60 | 0x40, 0x0, 0x0, 0x0}, 61 | {0x31, 0x30, 0x20, 0x10, 0x11, 0x12, 0x22, 0x32, 0x42, 0x52, 0x51, 0x50, 62 | 0x0, 0x0, 0x0, 0x0}}; 63 | 64 | /** 65 | * WiFi连接图标 66 | */ 67 | const unsigned char wifiicon[columnLength][rowLength] = { 68 | {0x0, 0xba, 0x20, 0xba, 0xa2, 0xa2, 0x0, 0x0}, 69 | {0x0, 0x22, 0x22, 0x2a, 0x2e, 0x14, 0x0, 0x0}, 70 | {0x0, 0x48, 0x68, 0x79, 0x58, 0x48, 0x0, 0x0}, 71 | {0x0, 0x37, 0x45, 0x45, 0x45, 0x37, 0x0, 0x0}}; 72 | 73 | /** 74 | * 没有wifi的图标 75 | */ 76 | const unsigned char nowifiicon[columnLength][rowLength] = { 77 | {0x0, 0x74, 0x40, 0x74, 0x44, 0x44, 0x0, 0x0}, 78 | {0x0, 0x45, 0x44, 0x55, 0x55, 0x29, 0x0, 0x0}, 79 | {0x0, 0xc0, 0x40, 0x4e, 0x40, 0xc0, 0x0, 0x0}, 80 | {0x0, 0x25, 0x35, 0x3d, 0x2d, 0x25, 0x0, 0x0}}; 81 | 82 | /** 83 | * 更新数据字符 84 | */ 85 | const unsigned char updateClock[columnLength][rowLength] = { 86 | {0x0, 0x0, 0x0, 0xa8, 0x0, 0x0, 0x0, 0x0}, 87 | {0x0, 0xd4, 0x18, 0x10, 0x18, 0xd4, 0x0, 0x0}, 88 | {0x0, 0x1d, 0x15, 0x15, 0x15, 0xdd, 0x0, 0x0}, 89 | {0x0, 0x1d, 0x11, 0x11, 0x11, 0x1d, 0x0, 0x0}}; 90 | 91 | /** 92 | * ota 更新图标 93 | */ 94 | const unsigned char otaicon[columnLength][rowLength] = { 95 | {0x0, 0x24, 0x8, 0x10, 0x20, 0x48, 0x0, 0x0}, 96 | {0x0, 0xee, 0xaa, 0xaa, 0xaa, 0xee, 0x0, 0x0}, 97 | {0x0, 0x90, 0x2a, 0x28, 0x3a, 0x28, 0x0, 0x0}, 98 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 99 | 100 | /** 101 | * 更新完成 102 | */ 103 | const unsigned char otaokicon[columnLength][rowLength] = { 104 | {0x0, 0x80, 0x0, 0x2a, 0x0, 0x80, 0x0, 0x0}, 105 | {0x0, 0x3a, 0x2b, 0x2a, 0x2b, 0x3a, 0x0, 0x0}, 106 | {0x0, 0x90, 0x29, 0x28, 0x39, 0x28, 0x0, 0x0}, 107 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 108 | 109 | /** 110 | * 更新失败 111 | */ 112 | const unsigned char otafailicon[columnLength][rowLength] = { 113 | {0x0, 0x28, 0x88, 0xa8, 0xa8, 0xae, 0x0, 0x0}, 114 | {0x0, 0x39, 0x22, 0x3b, 0x22, 0x22, 0x0, 0x0}, 115 | {0x0, 0x90, 0x29, 0x28, 0x39, 0x28, 0x0, 0x0}, 116 | {0x0, 0x3b, 0x29, 0x29, 0x29, 0x39, 0x0, 0x0}}; 117 | 118 | /** 119 | * 没有设置任何数据 120 | */ 121 | const unsigned char noseticon[32] = { 122 | 0x0, 0xbe, 0x8, 0x8, 0x88, 0x8, 0x8, 0x88, 0x0, 0xef, 0x8, 123 | 0x8, 0xef, 0x28, 0x28, 0xef, 0x0, 0xe3, 0x22, 0x22, 0x23, 0x20, 124 | 0x20, 0xe3, 0x0, 0x45, 0x45, 0x65, 0x75, 0x5d, 0x4d, 0x45}; 125 | 126 | /** 127 | * 图标集合 128 | */ 129 | const unsigned char icons[4][rowLength] = { 130 | {0x3C, 0x42, 0x85, 0x89, 0x81, 0x81, 0x42, 0x3C}, // 时钟图标 131 | {0x0, 0xCE, 0xD1, 0x10, 0x10, 0x11, 0xE, 0x0}, // 温度图标 132 | {0x42, 0x24, 0xFF, 0x81, 0xA5, 0x99, 0xFF, 0x42}, // Bilibili图标 133 | {0x3C, 0x42, 0x81, 0xA5, 0x81, 0x99, 0x42, 0x3C} // 微笑 134 | }; 135 | 136 | struct LatticeSetting 137 | { 138 | /** 139 | * 点阵屏幕是否显示 140 | */ 141 | bool isShutdown = false; 142 | 143 | /** 144 | * 屏幕亮度 145 | */ 146 | uint8_t brightness = 0x0; 147 | 148 | /** 149 | * 显示方向, true: 正方向,false: 反方向 150 | */ 151 | bool direction = false; 152 | 153 | /** 154 | * 用户自定义数据 155 | */ 156 | uint8_t userData[32]; 157 | 158 | /** 159 | * 速度 160 | */ 161 | uint8_t speed = 1; 162 | 163 | /** 164 | * @brief 光敏值(光敏电阻的值) (0 - 255) 165 | * 166 | */ 167 | uint8_t light = 20; 168 | }; 169 | 170 | class Lattice 171 | { 172 | protected: 173 | /** 174 | * lc对象 175 | */ 176 | LedControl lc = LedControl(DIN, CLK, CS, columnLength); 177 | 178 | /** 179 | * 点阵显示屏个数 180 | */ 181 | int latticeNums; 182 | 183 | /** 184 | * @brief 185 | * 186 | */ 187 | int tempindex = 3; 188 | 189 | /** 190 | * 是否重置 191 | */ 192 | bool isReset = true; 193 | 194 | /** 195 | * 实际显示的数组 196 | */ 197 | unsigned char data[4][rowLength]; 198 | 199 | /** 200 | * 缓冲数组 201 | */ 202 | unsigned char buff[4][rowLength]; 203 | 204 | /** 205 | * @brief system对象 206 | * 207 | */ 208 | System *systemObj; 209 | 210 | /** 211 | * 初始化点阵 212 | */ 213 | void initData(); 214 | 215 | /** 216 | * 刷新显示 217 | */ 218 | void refreshLed(); 219 | 220 | /** 221 | * 显示数据内容 222 | */ 223 | void showDataLed(const unsigned char (*arr)[rowLength]); 224 | 225 | /** 226 | * 获取需要显示数字的数据 227 | */ 228 | unsigned char *getNumData(uint8_t nums, bool showzero); 229 | 230 | /** 231 | * 获取大号的数字 232 | */ 233 | unsigned char *getBigNumData(uint8_t number, uint8_t offset); 234 | 235 | /** 236 | * 偏移缓存数组 237 | */ 238 | void offsetBuff(uint8_t index, int8_t offset); 239 | 240 | /** 241 | * 偏移显示数组 242 | */ 243 | void offsetData(uint8_t index, int8_t offset); 244 | 245 | /** 246 | * 左移显示内容 247 | */ 248 | void rightMoveData(bool isright); 249 | 250 | /** 251 | * 左移显示缓冲数据 252 | */ 253 | void rightMoveBuff(); 254 | 255 | /** 256 | * 向下移动数据 257 | */ 258 | void downMoveData(bool isdown); 259 | 260 | /** 261 | * 向下移动缓冲数据 262 | */ 263 | void downMoveBuff(); 264 | 265 | /** 266 | * 向下移动缓冲数据 267 | */ 268 | void downMoveBuff(uint8_t *arr); 269 | 270 | /** 271 | * 测试功能 272 | */ 273 | void progressBar(uint8_t offset); 274 | 275 | /** 276 | * 向上或者向下移动内容 277 | */ 278 | void upOrDownMove(uint8_t index, uint8_t region, bool direction); 279 | 280 | public: 281 | LatticeSetting latticeSetting; 282 | /** 283 | * 构造函数 284 | */ 285 | Lattice(); 286 | 287 | /** 288 | * 初始化 289 | */ 290 | void init(); 291 | 292 | /** 293 | * 重置 294 | */ 295 | void reset(); 296 | 297 | /** 298 | * 启用/停用点阵 299 | */ 300 | void shutdown(bool down); 301 | 302 | /** 303 | * 设置显示亮度 304 | */ 305 | void setBrightness(uint8_t bright, bool save); 306 | 307 | /** 308 | * 设置显示方向 309 | */ 310 | void setDirection(bool direct); 311 | 312 | /** 313 | * 显示wifi图案 314 | */ 315 | void showLongIcon(uint8_t index); 316 | 317 | /** 318 | * 显示数字 319 | */ 320 | void showNum(uint8_t index, int nums); 321 | 322 | /** 323 | * 动态显示时间 324 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 325 | * ps: 这里为啥还要搞一个回调函数,既然要有好看的动画就难免需要有点牺牲在里面,所以这里的回调就是处理我们小程序的UDP请求或者是MQTT之类的 326 | */ 327 | void showTime(uint8_t *arr); 328 | 329 | /** 330 | * 显示时间模式2 331 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 332 | * ps: 这里为啥还要搞一个回调函数,既然要有好看的动画就难免需要有点牺牲在里面,所以这里的回调就是处理我们小程序的UDP请求或者是MQTT之类的 333 | */ 334 | void showTime2(uint8_t *arr); 335 | 336 | /** 337 | * 显示时间模式2 338 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 339 | * ps: 这里为啥还要搞一个回调函数,既然要有好看的动画就难免需要有点牺牲在里面,所以这里的回调就是处理我们小程序的UDP请求或者是MQTT之类的 340 | */ 341 | void showTime3(uint8_t *arr); 342 | 343 | /** 344 | * 显示倒计时 345 | */ 346 | void showCountDownTime(long remain, uint8_t *arr, bool showmode, bool minutechange); 347 | 348 | /** 349 | * 显示长的数字 350 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 351 | */ 352 | void showLongNumber(uint8_t *arr); 353 | 354 | /** 355 | * 显示数字和图标 356 | */ 357 | void showNumAndIcon(uint8_t no, uint8_t *arr); 358 | 359 | /** 360 | * 显示日期2 361 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 362 | */ 363 | void showDate2(uint8_t *arr); 364 | 365 | /** 366 | * 显示日期2 367 | * 这里目前还没有好的解决方法, 所以就使用了一些简单粗暴的方式来做 368 | */ 369 | void showDate3(uint8_t *arr); 370 | 371 | /** 372 | * 显示温度 373 | */ 374 | void showTemperature(uint8_t *arr); 375 | 376 | /** 377 | * 显示用户自定义数据 378 | */ 379 | void showUserData(uint8_t mode); 380 | 381 | /** 382 | * 清空显示 383 | */ 384 | void clearShow(); 385 | 386 | /** 387 | * 全量显示 388 | */ 389 | void showFull(); 390 | 391 | /** 392 | * 左右翻转 393 | */ 394 | void reversalLR(uint8_t index); 395 | 396 | /** 397 | * 上下翻转 398 | */ 399 | void reversalUD(uint8_t index); 400 | 401 | /** 402 | * 闪 403 | */ 404 | void lightning(uint8_t index); 405 | 406 | /** 407 | * OTA 更新 408 | */ 409 | void showOtaUpdate(uint8_t num); 410 | }; 411 | 412 | #endif 413 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/LatticePlus.cpp: -------------------------------------------------------------------------------- 1 | #include "LatticePlus.h" 2 | 3 | LatticePlus::LatticePlus() 4 | { 5 | init(); 6 | } 7 | 8 | LatticePlus::LatticePlus(System *systems) 9 | { 10 | systemObj = systems; 11 | init(); 12 | } 13 | 14 | /** 15 | @brief 单个点阵纵向移动函数 16 | 17 | @param tempdata 取模数据 18 | @param dl 移动长度 19 | @return uint8_t 20 | */ 21 | uint8_t *LatticePlus::single_down_move(const unsigned char *tempdata, int dl) 22 | { 23 | uint8_t *arr = new uint8_t[8]; 24 | memcpy(arr, tempdata, 8); 25 | if (dl > 8 || dl % 8 == 0) 26 | { 27 | return arr; 28 | } 29 | uint8_t tmp = tempdata[7]; 30 | for (int k = 7; k > 0; k--) 31 | { 32 | arr[k] = tempdata[k - dl]; 33 | } 34 | arr[0] = tmp; 35 | return arr; 36 | } 37 | 38 | /** 39 | @brief 单个点阵横向移动函数1 40 | 41 | @param tempdata 取模数据 42 | @param ll 移动长度 43 | @return uint8_t 44 | */ 45 | uint8_t *LatticePlus::single_left_move1(uint8_t *tempdata, int ll) 46 | { 47 | uint8_t *arr = new uint8_t[8]; 48 | // 如果横向位移小于最大位移,则使用最简单的模式 49 | for (int i = 0; i < 8; i++) 50 | { 51 | arr[i] = tempdata[i] >> ll; 52 | } 53 | return arr; 54 | } 55 | 56 | /** 57 | @brief 单个点阵横向移动函数2 58 | 59 | @param tempdata 取模数据 60 | @param ll 移动长度 61 | @return uint8_t 62 | */ 63 | uint8_t *LatticePlus::single_left_move2(uint8_t *tempdata, int ll) 64 | { 65 | uint8_t *arr = new uint8_t[8]; 66 | // 2. 根据长度再得到第二个数组 67 | int rightMove = 8 - ll; 68 | for (int i = 0; i < 8; i++) 69 | { 70 | arr[i] = 0xff & (tempdata[i] << rightMove); 71 | } 72 | return arr; 73 | } 74 | 75 | void LatticePlus::absolute_position_show(const unsigned char *tempdata, int width, int x, int y) 76 | { 77 | int t = x / 8; // 记录哪个点阵数据需要被改变 78 | uint8_t *ydata = single_down_move(tempdata, y); // 获取内容向下移动过后的数据 79 | int xl = (t > 0) ? (x % 8) : x; // 记录当前x坐标,y坐标 80 | uint8_t *xdata = single_left_move1(ydata, xl); 81 | for (int i = 0; i < 8; i++) 82 | { 83 | data[3 - t][i] = (data[3 - t][i] ^ xdata[i]); 84 | } 85 | free(xdata); 86 | if (xl > (8 - width) && t < 3) // 判断如果内容需要向下一个点阵移动的话,就渲染下一个点阵屏幕的内容 87 | { 88 | uint8_t *xdata1 = single_left_move2(ydata, xl); 89 | for (int i = 0; i < 8; i++) 90 | { 91 | data[3 - t - 1][i] = data[3 - t - 1][i] ^ xdata1[i]; 92 | } 93 | free(xdata1); 94 | } 95 | free(ydata); 96 | } 97 | 98 | void LatticePlus::boot_animation() 99 | { 100 | int x = 0; 101 | for (int i = 0; i < 8; i++) 102 | { 103 | absolute_position_show(mogu[0], 8, x, 0); // 显示蘑菇 104 | refreshLed(); 105 | delay(200); 106 | initData(); 107 | x++; 108 | absolute_position_show(mogu[1], 8, x, 0); // 显示蘑菇 109 | refreshLed(); 110 | delay(200); 111 | initData(); 112 | x++; 113 | absolute_position_show(mogu[2], 8, x, 0); // 显示蘑菇 114 | refreshLed(); 115 | delay(200); 116 | initData(); 117 | x++; 118 | } 119 | delay(500); 120 | } -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/LatticePlus.h: -------------------------------------------------------------------------------- 1 | #ifndef LATTICE_PLUS_H 2 | #define LATTICE_PLUS_H 3 | 4 | #include "Lattice.h" 5 | 6 | const unsigned char mogu[3][8] = { 7 | {0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x5A, 0xA5}, //蘑菇1 8 | {0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x5A, 0x5A}, //蘑菇2 9 | {0x18, 0x3C, 0x7E, 0xDB, 0xFF, 0x24, 0x24, 0x5A} //蘑菇3 10 | }; 11 | 12 | /** 13 | * @brief 继承Lattice来写代码,Lattice写的东西实在是太多了,换一个新的写法 14 | * 15 | */ 16 | class LatticePlus : public Lattice 17 | { 18 | private: 19 | /** 20 | @brief 单个点阵纵向移动函数 21 | 22 | @param tempdata 取模数据 23 | @param dl 移动长度 24 | @return uint8_t 25 | */ 26 | uint8_t *single_down_move(const unsigned char *tempdata, int dl); 27 | 28 | /** 29 | @brief 单个点阵横向移动函数1 30 | 31 | @param tempdata 取模数据 32 | @param ll 移动长度 33 | @return uint8_t 34 | */ 35 | uint8_t *single_left_move1(uint8_t *tempdata, int ll); 36 | 37 | /** 38 | @brief 单个点阵横向移动函数2 39 | 40 | @param tempdata 取模数据 41 | @param ll 移动长度 42 | @return uint8_t 43 | */ 44 | uint8_t *single_left_move2(uint8_t *tempdata, int ll); 45 | 46 | public: 47 | LatticePlus(); 48 | 49 | /** 50 | * 构造函数 51 | */ 52 | LatticePlus(System *systems); 53 | 54 | /** 55 | * @brief 根据绝对坐标位置显示8x8点阵内容 56 | * 57 | * @param tempdata 8x8点阵取模数据 58 | * @param width 数据位宽 59 | * @param x x轴坐标 60 | * @param y y坐标 61 | */ 62 | void absolute_position_show(const unsigned char *tempdata, int width, int x, int y); 63 | 64 | /** 65 | * @brief 开机动画 66 | * 67 | */ 68 | void boot_animation(); 69 | }; 70 | #endif -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | #include "DateTimes.h" 4 | #include "EEPROMTool.h" 5 | #include "Functions.h" 6 | #include "HttpTool.h" 7 | #include "LatticePlus.h" 8 | #include "Otas.h" 9 | #include "PilotLight.h" 10 | #include "System.h" 11 | #include "Udps.h" 12 | #include "Wifis.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | Ticker timestampticker; // 手动累加时间戳任务 20 | Ticker httptoolticker; // 每五秒钟处理一次http请求标志 21 | System systems; // 系统内置对象 22 | DateTimes datetimes = DateTimes(); // 时间管理对象 23 | HttpTool httptool; // HTTP 请求对象 24 | LatticePlus lattice = LatticePlus(&systems); // 点阵显示对象 25 | OneButton btnA = OneButton(D8, false, false); // 按钮对象 26 | PilotLight pilotLight = PilotLight(); // 控制LED亮灭对象 27 | Wifis wifis = Wifis(&lattice, &pilotLight); // wifi对象 28 | Functions functions = Functions(&pilotLight); // 功能管理对象 29 | Otas otas = Otas(&lattice, &pilotLight); // OTA更新处理对象 30 | Udps udps = Udps(&datetimes, &lattice, &pilotLight); // UDP数据传输对象 31 | unsigned char displayData[4] = {0x00, 0x00, 0x00, 0xff}; // 点阵显示数,每个点阵应该显示那些数据 32 | long powerFlag = 0, powerFlag2 = 0; // 功能flag 33 | uint8_t sleepTime[5] = {0, 0, 8, 0, 1}; // 这里表示设备休眠时间,默认是凌晨0点到早上八点,所以表示为0:0 ~ 8:0,最后一位是亮度(0:表示息屏,15表示最亮了) 34 | bool isSleepMode = false; // 标记当前是否处于睡眠模式 35 | 36 | /** 37 | * @brief 初始化状态 38 | * 39 | */ 40 | void initStatus() 41 | { 42 | powerFlag = -1; 43 | powerFlag2 = -1; 44 | if (functions.getCurrPower() == CUSTOM && functions.getCurrMode() != 0) // 如果是自定义显示功能条件下(且模式为显示模式),就不重置屏幕显示 45 | { 46 | return; 47 | } 48 | else if (functions.getCurrPower() == COUNTDOWN) // 如果是倒计时功能条件下,则进入倒计时相关逻辑操作 49 | { 50 | displayData[0] = 0; 51 | displayData[1] = 1; 52 | displayData[2] = 2; 53 | powerFlag2 = 1; // 这里一定是到大于0的,后续会根据这个值是否为0来判断逻辑 54 | } 55 | lattice.reset(); 56 | } 57 | 58 | /** 59 | * @brief 处理单击 60 | * 61 | */ 62 | void singleAClickHandler() 63 | { 64 | Serial.println("A按键单击"); // 打印日志,便于校验 65 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 66 | { 67 | lattice.shutdown(false); // 让点阵屏重新显示 68 | return; 69 | } 70 | functions.powerUp(); // 功能加一下 71 | initStatus(); // 初始化一些东西 72 | } 73 | 74 | /** 75 | * @brief 处理双击 76 | * 77 | */ 78 | void doubleAClickHandler() 79 | { 80 | Serial.println("A按键双击"); // 打印日志,便于校验 81 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 82 | { 83 | lattice.shutdown(false); // 让点阵屏重新显示 84 | return; 85 | } 86 | pilotLight.flashing(100); // 按键单击时先闪一下LED 87 | functions.modeUp(); // 功能模式加一下 88 | initStatus(); // 初始化一些东西 89 | } 90 | 91 | /** 92 | * @brief 按键长按开始做的事情 93 | * 94 | */ 95 | void longAClickStartHandler() 96 | { 97 | Serial.println("A按键长按开始"); // 打印日志,便于校验 98 | pilotLight.bright(); // 按下的时候LED亮起来 99 | } 100 | 101 | /** 102 | * @brief 处理按键长按 103 | * 104 | */ 105 | void longAClickHandler() 106 | { 107 | Serial.println("A按键长按结束"); // 打印日志,便于校验 108 | if (lattice.latticeSetting.isShutdown) // 如果屏幕是熄灭的话,单击操作则是点亮屏幕不做其他操作 109 | { 110 | lattice.shutdown(false); // 让点阵屏重新显示 111 | return; 112 | } 113 | lattice.shutdown(true); // 否则关闭点阵屏 114 | } 115 | 116 | /** 117 | * @brief 初始化按键信息 118 | * 119 | */ 120 | void initTouch() 121 | { 122 | btnA.attachClick(singleAClickHandler); // 添加单击事件函数 123 | btnA.attachDoubleClick(doubleAClickHandler); // 添加双击事件函数 124 | btnA.attachLongPressStop(longAClickHandler); // 添加长按事件函数 125 | // btnA.attachLongPressStart(longAClickStartHandler); // 添加按下事件函数 126 | } 127 | 128 | /** 129 | * 订阅bilibili用户ID 130 | */ 131 | void subBili(uint8_t *data) 132 | { 133 | long uid = System::uint8t_to_long(data, 5); // 先将uint_8转成 long 134 | httptool.saveBuid(uid); // 在将uid保存到存储器中去 135 | functions.setPowerAndMode(BILIFANS, 0); // 切换显示模式为bilibili显示 136 | } 137 | 138 | /** 139 | * @brief 设置倒计时信息 140 | * 141 | * @param data 142 | */ 143 | void setCountdown(uint8_t *data) 144 | { 145 | long timestamp = System::uint8t_to_long(data, 5); // 先将uint_8转成 long 146 | datetimes.saveCountdownTimestamp(timestamp); // 将倒计时时间戳保存起来 147 | functions.setPowerAndMode(COUNTDOWN, 0); // 切换显示模式为bilibili显示 148 | } 149 | 150 | /** 151 | * @brief 初始化休眠时间 152 | * 153 | */ 154 | void initSleepTime() 155 | { 156 | uint8_t *t = EEPROMTool.loadData(SLEEP_TIME, 5); // 先从内存中加载 157 | memcpy(sleepTime, t, 4); 158 | free(t); 159 | } 160 | 161 | /** 162 | * @brief 设置睡眠时间 163 | * 164 | * @param data 165 | */ 166 | void setSleepTime(uint8_t *data) 167 | { 168 | // 这里的做法目前是比较简单的,data就是一个四位长度的数组,第0和1位表示开始时间的小时和分钟,第2和3位表示结束的小时和分钟 169 | memcpy(sleepTime, data, 4); 170 | EEPROMTool.saveData(data, SLEEP_TIME, 5); // 将数据设置EEPROM中去 171 | // todo 这里为了交互友好,最好还是显示一个config ok 之类的提示 172 | } 173 | 174 | void sleepTimeLoop() 175 | { 176 | Times times = datetimes.getTimes(); 177 | int starttime = sleepTime[0] * 100 + sleepTime[1]; // 开始时间 178 | int endtime = sleepTime[2] * 100 + sleepTime[3]; // 结束时间 179 | if (starttime == endtime) // 如果开始时间和结束时间是一样的话,就什么都不做 180 | { 181 | return; 182 | } 183 | int currtime = times.h * 100 + times.m; // 当前时间 184 | if (starttime < endtime) // 如果开始时间小于结束时间,则只需要判断当前时间是否在开始时间和结束时间的区间范围内 185 | { 186 | if (currtime >= starttime && currtime < endtime) // 如果时间在休眠时间范围内则休眠 187 | { 188 | if (!isSleepMode) 189 | { 190 | isSleepMode = true; // 标记进入睡眠模式 191 | if (sleepTime[4] == 0) // 判断亮度是否为0,如果亮度为0的话,则熄灭屏幕 192 | { 193 | lattice.shutdown(true); // 休眠操作(目前就是把屏幕熄灭) 194 | } 195 | else 196 | { 197 | lattice.setBrightness(sleepTime[4], false); // 亮度不为0则将设置屏幕亮度为指定的屏幕亮度 198 | } 199 | } 200 | } 201 | else 202 | { 203 | if (isSleepMode) 204 | { 205 | // 这里避免出现误操作,每次都将屏幕点亮,将屏幕亮度设置到预设亮度 206 | isSleepMode = false; // 标记退出睡眠模式 207 | lattice.shutdown(false); // 退出休眠操作(目前就是把屏幕点亮) 208 | lattice.setBrightness(lattice.latticeSetting.brightness, false); // 亮度不为0则将设置屏幕亮度为指定的屏幕亮度 209 | } 210 | } 211 | } 212 | else // 如果开始时间大于结束时间,表示表示当前时间在反向的范围内则不需要休眠 213 | { 214 | if (currtime >= endtime && currtime < starttime) // 如果时间在休眠时间范围内则休眠 215 | { 216 | if (isSleepMode) 217 | { 218 | // 这里避免出现误操作,每次都将屏幕点亮,将屏幕亮度设置到预设亮度 219 | isSleepMode = false; // 标记退出睡眠模式 220 | lattice.shutdown(false); // 退出休眠操作(目前就是把屏幕点亮) 221 | lattice.setBrightness(lattice.latticeSetting.brightness, false); // 亮度不为0则将设置屏幕亮度为指定的屏幕亮度 222 | } 223 | } 224 | else 225 | { 226 | if (!isSleepMode) 227 | { 228 | isSleepMode = true; // 标记进入睡眠模式 229 | if (sleepTime[4] == 0) // 判断亮度是否为0,如果亮度为0的话,则熄灭屏幕 230 | { 231 | lattice.shutdown(true); // 休眠操作(目前就是把屏幕熄灭) 232 | } 233 | else 234 | { 235 | lattice.setBrightness(sleepTime[4], false); // 亮度不为0则将设置屏幕亮度为指定的屏幕亮度 236 | } 237 | } 238 | } 239 | } 240 | } 241 | 242 | /** 243 | * @brief 初始化光敏电阻配置的值 244 | * 245 | */ 246 | void initLight() 247 | { 248 | uint8_t t = EEPROMTool.loadDataOne(LIGHT_VAL); // 先从内存中加载 249 | lattice.latticeSetting.light = t; 250 | } 251 | 252 | /** 253 | * @brief 设置光敏电阻配置的值 254 | * 255 | * @param data 256 | */ 257 | void setLight(uint8_t data) 258 | { 259 | EEPROMTool.saveDataOne(data, LIGHT_VAL); // 将数据设置EEPROM中去 260 | } 261 | 262 | void lightLoop() 263 | { 264 | if (analogRead(A0) < lattice.latticeSetting.light) 265 | { 266 | lattice.shutdown(true); // 休眠操作(目前就是把屏幕熄灭) 267 | } 268 | else 269 | { 270 | lattice.shutdown(false); // 退出休眠操作(目前就是把屏幕点亮) 271 | lattice.setBrightness(lattice.latticeSetting.brightness, false); // 亮度不为0则将设置屏幕亮度为指定的屏幕亮度 272 | } 273 | } 274 | 275 | /** 276 | * 每天早上八点重置时间 277 | * 278 | */ 279 | void resetTimeLoop() 280 | { 281 | // 确定是连接wifi的状态,如果不是连接wifi的状态则不进行时间重置 282 | if (WiFi.status() == WL_CONNECTED) 283 | { 284 | Times time = datetimes.getTimes(); 285 | if (time.h == 8 && time.m == 0 && time.s == 0) 286 | { 287 | // 直接重启时钟用于校准时间 288 | Serial.println("校准时间!"); 289 | ESP.restart(); // 重启系统 290 | } 291 | } 292 | } 293 | 294 | /** 295 | * @brief 重置时间 296 | * 重置时间这里有两种方式,一种就是用NTP校准时间,还有一种就是设备没有连接wifi,直接用手机发来的时间戳进行校准时间 297 | * @param data 298 | */ 299 | void resetTime(uint8_t *data) 300 | { 301 | if (data != NULL) // 函数中参数如果不为空则使用参数值来作为时间来历 302 | { 303 | long timestamp = System::uint8t_to_long(data, 5); // 先将uint_8转成 long 304 | datetimes.setDateTimes(timestamp + 8 * 60 * 60); // 将时间戳同步到系统时间中去 305 | } 306 | else 307 | { 308 | udps.updateTime(); // 校准时间 309 | } 310 | functions.reset(); // 重置功能 311 | initStatus(); // 重置状态 312 | } 313 | 314 | /** 315 | * 显示用户自定义的数据 316 | */ 317 | void setUserData(uint8_t *data) 318 | { 319 | memcpy(lattice.latticeSetting.userData, data, 32); // 切换用户自定义 320 | functions.setPowerAndMode(CUSTOM, 0); // 重置功能 321 | initStatus(); // 重置状态 322 | } 323 | 324 | /** 325 | * @brief 显示倒计时 326 | * 327 | */ 328 | void showCountDown() 329 | { 330 | bool showmode = true, minutechange = false; 331 | long countdown = datetimes.getCountdownTimestamp(); // 根据倒计时时间戳获取截止日期 332 | long timestamp = datetimes.getTimestamp() - 8 * 3600; // 获取当前日期 333 | if (countdown - timestamp == powerFlag2 || powerFlag2 <= 0) 334 | { 335 | return; // 时间没有发生改变,则跳过 336 | } 337 | if ((countdown - timestamp) < (24 * 3600)) // 倒计时时间戳 - 当前时间戳时间小于一天则 按 时分秒 来进行倒计时 338 | { 339 | showmode = false; 340 | minutechange = true; 341 | if (((countdown - timestamp) / 3600) != (powerFlag2 / 3600)) // 倒计时小于一天,则使用时分秒的显示模式 342 | { 343 | lattice.reset(); // 倒计时时钟发生改变 344 | displayData[0] = 0; 345 | displayData[1] = 1; 346 | displayData[2] = 2; 347 | } 348 | } 349 | else 350 | { 351 | showmode = true; 352 | if (((countdown - timestamp) / 3600 / 24) != (powerFlag2 / 3600 / 24)) // 这里判断天数是否发生改变,如果天数发生改变则需要重置一下显示 353 | { 354 | lattice.reset(); // 倒计时日发生改变 355 | displayData[0] = 0; 356 | displayData[1] = 1; 357 | displayData[2] = 2; 358 | } 359 | if (((countdown - timestamp) / 60) != (powerFlag2 / 60)) // 这里判断分钟数是否发生改变,如果分钟数发生改变,则需要刷新显示 360 | { 361 | minutechange = true; // 这里表示分钟数值发生改变 362 | } 363 | } 364 | powerFlag2 = (countdown - timestamp) < 1 ? 0 : (countdown - timestamp); 365 | lattice.showCountDownTime(powerFlag2, displayData, showmode, minutechange); 366 | for (int i = 0; i < 3; i++) 367 | { 368 | displayData[i] = displayData[i] == 6 ? 1 : ++displayData[i]; 369 | } 370 | } 371 | 372 | /** 373 | * @brief 显示日期 374 | * 375 | * @param showmode 376 | */ 377 | void showDate(uint8_t showmode) 378 | { 379 | Dates dates = datetimes.getDates(); 380 | if (dates.d == powerFlag) 381 | { 382 | return; // 如果天数没有发生改变,则不更新时间显示 383 | } 384 | powerFlag = dates.d; 385 | displayData[3] = dates.y / 100; 386 | displayData[2] = dates.y % 100; 387 | displayData[1] = dates.m; 388 | displayData[0] = dates.d; 389 | if (showmode == 1) 390 | { 391 | lattice.showLongNumber(displayData); 392 | } 393 | else if (showmode == 0) 394 | { 395 | lattice.showDate3(displayData); 396 | } 397 | else 398 | { 399 | displayData[3] = dates.m / 10; 400 | displayData[2] = dates.m % 10; 401 | displayData[1] = dates.d / 10; 402 | displayData[0] = dates.d % 10; 403 | lattice.showDate2(displayData); 404 | } 405 | } 406 | 407 | /** 408 | * @brief 显示温度(由于这个温度显示不是很准确,所以我也就没有花很多心思来搞这个,就简单弄一个显示就完事了) 409 | * 410 | */ 411 | void showTemperature() 412 | { 413 | int t = datetimes.getTemperature(); 414 | if (t == powerFlag) 415 | { 416 | return; // 温度没有发生改变则忽略 417 | } 418 | powerFlag = t; 419 | lattice.reset(); 420 | displayData[3] = 0; 421 | displayData[2] = 0x00; 422 | displayData[1] = t / 100; 423 | displayData[0] = t % 100; 424 | lattice.showTemperature(displayData); 425 | } 426 | 427 | /** 428 | * 显示bilibili粉丝数量 429 | */ 430 | void showBiliFans() 431 | { 432 | httptool.bilibiliFans(); // 每次进来判断是否需要更新bilibili粉丝信息,这里是五分钟更新一次 433 | Times times = datetimes.getTimes(); // 获取系统时间 434 | if (displayData[0] != times.s && powerFlag < 99999) 435 | { 436 | displayData[0] = times.s; // 这里由于没有缓存数组了,所以就用这个缓存数组 437 | lattice.lightning(3); 438 | } 439 | long fans = httptool.fans; 440 | if (fans == powerFlag) 441 | { 442 | return; 443 | } 444 | lattice.reset(); 445 | powerFlag = fans; 446 | for (int i = 0; i < 4; i++) // 将千万的数字分解成四个数组 447 | { 448 | displayData[i] = fans % 100; 449 | fans = fans / 100; 450 | } 451 | if (powerFlag >= 99999) 452 | { 453 | lattice.showLongNumber(displayData); // 如果粉丝数量大于这个数量时,就显示全部的粉丝数量 454 | } 455 | else 456 | { 457 | lattice.showNumAndIcon(2, displayData); // 如果粉丝数量不大于那个数量时,就显示图标加数量 458 | } 459 | } 460 | 461 | /** 462 | * 显示用户自定数据 463 | */ 464 | void showUserData(uint8_t showmode) 465 | { 466 | if (showmode == 0) 467 | { 468 | lattice.showUserData(showmode); // 优先模式 469 | return; 470 | } 471 | if (System::is_overtime(100 + (lattice.latticeSetting.speed * 10))) 472 | { 473 | lattice.showUserData(showmode); // 刷新显示内容 474 | } 475 | } 476 | 477 | /** 478 | * @brief 按键中断处理 479 | * 480 | */ 481 | void touchLoop() 482 | { 483 | btnA.tick(); 484 | } 485 | #endif 486 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Ota.cpp: -------------------------------------------------------------------------------- 1 | #include "Otas.h" 2 | Otas *Otas::s_otas; 3 | 4 | Otas::Otas() { s_otas = this; } 5 | 6 | Otas::Otas(LatticePlus *latticeobj, PilotLight *pilotLightobj) 7 | { 8 | lattice = latticeobj; 9 | pilotLight = pilotLightobj; 10 | s_otas = this; 11 | } 12 | 13 | void Otas::flashing() 14 | { 15 | pilotLight->flashing(20); 16 | } 17 | 18 | void Otas::showLongIcon(int type) 19 | { 20 | lattice->showLongIcon(type); 21 | } 22 | 23 | void Otas::showOtaUpdate(int process) 24 | { 25 | lattice->showOtaUpdate(process); 26 | } 27 | 28 | void Otas::reset() 29 | { 30 | lattice->reset(); 31 | } 32 | 33 | void Otas::update_started() 34 | { 35 | Serial.println("callback: ota is start!"); // 打印日志 36 | s_otas->reset(); // 重置显示内容 37 | Serial.println("callback: ota is start1!"); // 打印日志 38 | } 39 | 40 | void Otas::update_finished() 41 | { 42 | Serial.println("callback: ota is finish!"); // 打印日志 43 | s_otas->reset(); // 重置显示内容 44 | s_otas->showLongIcon(3); // 显示OTA更新成功图案 45 | } 46 | 47 | void Otas::update_progress(long cur, long total) 48 | { 49 | int process = (int)(((cur * 0.1) / (total * 0.1)) * 100); // 计算更新进度 50 | Serial.printf("callback: updateing %d of %d bytes...\n", cur, total); // 打印日志 51 | s_otas->flashing(); // 固件升级的时候LED闪 52 | s_otas->showOtaUpdate(process); // OTA显示当前进度图案 53 | } 54 | 55 | void Otas::update_error(int err) 56 | { 57 | Serial.printf("callback: ota is error: %d\n", err); // 打印日志 58 | s_otas->reset(); // 重置显示内容 59 | s_otas->showLongIcon(4); // 显示OTA更新失败图案 60 | } 61 | 62 | void Otas::updateOta(int version) 63 | { 64 | WiFiClient client; 65 | if (WiFi.status() == WL_CONNECTED) // 确保有网络 66 | { 67 | ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW); 68 | ESPhttpUpdate.onStart(update_started); // 添加可选的回调通知程序 69 | ESPhttpUpdate.onEnd(update_finished); // 添加更新完成回调方法 70 | ESPhttpUpdate.onProgress(update_progress); // 添加更新中回调方法 71 | ESPhttpUpdate.onError(update_error); // 添加更新失败回调方法 72 | char *updateUrl = new char[50]; // 临时放url 73 | sprintf(updateUrl, "%s%d%s%d", "http://oss.lengff.com/iot/lattice/", version, ".bin?t=", millis() % 100); // 后面的对100取余就是为了解除文件CDN缓存 74 | Serial.println(updateUrl); 75 | t_httpUpdate_return ret = ESPhttpUpdate.update(client, updateUrl); 76 | free(updateUrl); 77 | switch (ret) 78 | { 79 | case HTTP_UPDATE_FAILED: 80 | Serial.printf("ote is fail, err code is:(%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); // 打印日志 81 | s_otas->reset(); // 重置显示内容 82 | s_otas->showLongIcon(4); // 显示OTA更新失败图案 83 | break; 84 | case HTTP_UPDATE_NO_UPDATES: 85 | Serial.println("not ota update!"); // 打印日志 86 | s_otas->showLongIcon(3); // 显示OTA更新成功图案 87 | break; 88 | case HTTP_UPDATE_OK: 89 | s_otas->reset(); // 打印日志 90 | s_otas->showLongIcon(3); // 显示OTA更新成功图案 91 | break; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Otas.h: -------------------------------------------------------------------------------- 1 | #ifndef OTAS_H 2 | #define OTAS_H 3 | #include "LatticePlus.h" 4 | #include "PilotLight.h" 5 | #include 6 | #include 7 | #include 8 | 9 | class Otas 10 | { 11 | private: 12 | /** 13 | * @brief 显示内容对象 14 | * 15 | */ 16 | LatticePlus *lattice; 17 | /** 18 | * @brief 状态指示灯对象 19 | * 20 | */ 21 | PilotLight *pilotLight; 22 | 23 | /** 24 | * @brief 搞个单例 25 | * 26 | */ 27 | static Otas *s_otas; 28 | 29 | /** 30 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 31 | * 32 | */ 33 | void flashing(); 34 | 35 | /** 36 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 37 | * 38 | */ 39 | void showLongIcon(int type); 40 | 41 | /** 42 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 43 | * 44 | */ 45 | void reset(); 46 | 47 | /** 48 | * @brief 因为回调方法都是静态方法,所以这里就只能搞一个折中的了 49 | * 50 | */ 51 | void showOtaUpdate(int process); 52 | 53 | public: 54 | Otas(); 55 | 56 | Otas(LatticePlus *latticeobj, PilotLight *pilotLightobj); 57 | 58 | /** 59 | * @brief 60 | * 61 | * @param version 62 | */ 63 | static void updateOta(int version); 64 | 65 | protected: 66 | /** 67 | * @brief 更新开始回调函数 68 | * 69 | */ 70 | static void update_started(); 71 | 72 | /** 73 | * @brief 更新完成回调函数 74 | * 75 | */ 76 | static void update_finished(); 77 | /** 78 | * @brief 更新中回调函数 79 | * 80 | */ 81 | static void update_progress(long cur, long total); 82 | 83 | /** 84 | * @brief 更新错误回调函数 85 | * 86 | */ 87 | static void update_error(int err); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/PilotLight.cpp: -------------------------------------------------------------------------------- 1 | #include "PilotLight.h" 2 | 3 | PilotLight::PilotLight() 4 | { 5 | pinMode(LED_PIN, OUTPUT); 6 | } 7 | 8 | void PilotLight::flashing() 9 | { 10 | status = status == HIGH ? LOW : HIGH; 11 | digitalWrite(LED_PIN, status); 12 | } 13 | 14 | void PilotLight::flashing(int ms) 15 | { 16 | bright(); 17 | delay(ms); 18 | dim(); 19 | } 20 | 21 | void PilotLight::bright(){ 22 | digitalWrite(LED_PIN, LOW); 23 | } 24 | 25 | void PilotLight::dim(){ 26 | digitalWrite(LED_PIN, HIGH); 27 | } -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/PilotLight.h: -------------------------------------------------------------------------------- 1 | #ifndef PILOT_LIGHT 2 | #define PILOT_LIGHT 3 | 4 | #include 5 | 6 | #define LED_PIN D0 // LED引脚 7 | 8 | class PilotLight 9 | { 10 | private: 11 | int status = HIGH; 12 | 13 | public: 14 | PilotLight(); 15 | 16 | /** 17 | * LED 闪烁 18 | */ 19 | void flashing(); 20 | 21 | /** 22 | * @brief LED 闪烁 23 | * 24 | * @param ms 25 | */ 26 | void flashing(int ms); 27 | 28 | /** 29 | * @brief LED亮 30 | * 31 | */ 32 | void bright(); 33 | 34 | /** 35 | * @brief LED灭 36 | * 37 | */ 38 | void dim(); 39 | }; 40 | #endif -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/System.cpp: -------------------------------------------------------------------------------- 1 | #include "System.h" 2 | 3 | System::System() 4 | { 5 | } 6 | 7 | void System::init_callback(void (*callback)()) 8 | { 9 | callbackMethod = callback; 10 | } 11 | 12 | void System::reset_system() 13 | { 14 | EEPROMTool.clearAll(); // 删除EEPRON信息 15 | ESP.restart(); // 重启系统 16 | } 17 | 18 | /** 19 | * @brief uint8_t数组转long 20 | * 21 | * @param data 22 | * @return long 23 | */ 24 | long System::uint8t_to_long(uint8_t *data, int length) 25 | { 26 | long temp = 0; 27 | for (int i = 0; i < length; i++) 28 | { 29 | temp += data[i] << (i * 8); 30 | } 31 | return temp; 32 | } 33 | 34 | /** 35 | * @brief 重新定义延迟函数 36 | * 37 | * @param ms 38 | * @param callback 39 | */ 40 | void System::delay_time(int ms) 41 | { 42 | int timeFlag = millis(); 43 | while (true) 44 | { 45 | if (millis() - timeFlag >= ms) 46 | { 47 | break; 48 | } 49 | if (callbackMethod != NULL) 50 | { 51 | callbackMethod(); 52 | } 53 | } 54 | } 55 | 56 | bool System::is_overtime(int time) 57 | { 58 | if (millis() - tmp_value < time) 59 | { 60 | return false; 61 | } 62 | tmp_value = millis(); 63 | return true; 64 | } -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/System.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSTEM_H 2 | #define SYSTEM_H 3 | #include "EEPROMTool.h" 4 | #include 5 | 6 | static long tmp_value = 1l; 7 | 8 | class System 9 | { 10 | private: 11 | void (*callbackMethod)(); 12 | 13 | public: 14 | System(); 15 | 16 | void init_callback(void (*callback)()); 17 | 18 | /** 19 | * @brief 重启系统 20 | * 21 | */ 22 | static void reset_system(); 23 | 24 | /** 25 | * @brief uint8_t数组转long 26 | * 27 | * @param data 28 | * @return long 29 | */ 30 | static long uint8t_to_long(uint8_t *data, int length); 31 | 32 | /** 33 | * @brief 时间延迟函数 34 | * 35 | * @param ms 延迟毫秒数 36 | */ 37 | void delay_time(int ms); 38 | 39 | /** 40 | * @brief 判断是否超时 41 | * 42 | * @param time 43 | * @return true 44 | * @return false 45 | */ 46 | static bool is_overtime(int time); 47 | }; 48 | 49 | #endif -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Udps.cpp: -------------------------------------------------------------------------------- 1 | #include "Udps.h" 2 | 3 | Udps::Udps() 4 | { 5 | // todo 6 | } 7 | 8 | Udps::Udps(DateTimes *datetimesobj, LatticePlus *latticeobj, PilotLight *pilotLightobj) 9 | { 10 | datetimes = datetimesobj; 11 | lattice = latticeobj; 12 | pilotLight = pilotLightobj; 13 | } 14 | 15 | void Udps::initudp() 16 | { 17 | // todo 这里需要判断网络状态 18 | WiFi.hostByName(ntpServerName, timeServerIP); //将域名转换成IP地址 19 | udp.begin(localPort); // 启动监听本地端口 20 | } 21 | 22 | /** 23 | * @brief 发送NTP请求 24 | * 25 | */ 26 | void Udps::sendNTPpacket() 27 | { 28 | memset(packetBuffer, 0, NTP_PACKET_SIZE); // 将字节数组的数据全部设置为0 29 | packetBuffer[0] = 0b11100011; // 请求部分其实是有很多数据的,具体的请看参考请求报文说明,这里我们就只设置一个请求头部分即可 30 | udp.beginPacket(timeServerIP, remoteNtpPort); // 配置远端ip地址和端口 31 | udp.write(packetBuffer, NTP_PACKET_SIZE); // 把数据写入发送缓冲区 32 | udp.endPacket(); // 发送数据 33 | Serial.println("send ntp data"); // 34 | } 35 | 36 | long Udps::getNtpTimestamp() 37 | { 38 | int packetSize = udp.parsePacket(); //解析Udp数据包 39 | if (!packetSize) //解析包为空 40 | { 41 | return 0; 42 | } 43 | else //解析包不为空 44 | { 45 | udp.read(packetBuffer, NTP_PACKET_SIZE); // 解析UDP数据包中的数据 46 | // todo这里获取到的时间其实不是真实的时间,实际上还包含了网络延时的,但是为了方便,这里我们忽略这个因素的存在 47 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); // 取出t2时间的高位和低位数据拼凑成以秒为单位的时间戳 48 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // 49 | unsigned long secsSince1900 = highWord << 16 | lowWord; // 拼凑成以秒为单位的时间戳(时间戳的记录以秒的形式从 1900-01-01 00:00:00算起) 50 | const unsigned long seventyYears = 2208988800UL; // 51 | unsigned long timestamp = secsSince1900 - seventyYears; // 前面的32bit是时间戳的秒数(是用1900-01-01 00:00:00开始的秒数,但是我们的是1970年,所以需要减掉2208988800秒) 52 | timestamp = timestamp + 8 * 60 * 60; // 这里加8 是因为时区的问题,如果不加8,得到的结果就会是其他时区的时间 53 | return timestamp; 54 | } 55 | } 56 | 57 | Udpdata Udps::userLatticeLoop(uint8_t power, uint8_t mode, uint8_t version) 58 | { 59 | Udpdata udpdata; 60 | udpdata.lh = 0; 61 | int packetSize = udp.parsePacket(); //解析Udp数据包 62 | if (packetSize) //解析包不为空 63 | { // 64 | pilotLight->flashing(100); // 每次接收到UDP数据的时候都闪烁一下LED灯 65 | memset(packetBuffer, 0, NTP_PACKET_SIZE); //每次都先清空掉原有的数据包 66 | udp.read(packetBuffer, NTP_PACKET_SIZE); // 读取UDP数据 67 | // Udp.remoteIP().toString().c_str()用于将获取的远端IP地址转化为字符串 68 | // Serial.printf("收到来自远程IP:%s(远程端口:%d)的数据包字节数:%d %X\n", udp.remoteIP().toString().c_str(), udp.remotePort(), packetSize, packetBuffer[8]); 69 | uint8_t vn = packetBuffer[0] & 0x3; // 数据包版本 70 | if (vn != 0x1) // 如果数据包版本不为1,则直接结束 71 | { 72 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 73 | replyPacket[0] = {0x01}; // 74 | udp.write(replyPacket); //把数据写入发送缓冲区 75 | udp.endPacket(); //发送数据 76 | return udpdata; 77 | } 78 | udpdata.rt = packetBuffer[0] >> 6; // 数据包接收成功返回值 79 | if (udpdata.rt == 0x0) // 如果返回类型值为0的时候,则上报状态信息值 80 | { 81 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 todo 这里的做法是错误的,我们自定义协议,返回的也应该是按协议来返回的,但是我就是想偷个懒 82 | replyPacket[0] = lattice->latticeSetting.isShutdown << 6; // 是否显示 83 | replyPacket[0] += lattice->latticeSetting.brightness << 2; // 显示亮度 84 | replyPacket[0] += lattice->latticeSetting.direction; // 显示方向 85 | replyPacket[1] = power; // 功能 86 | replyPacket[2] = mode; // 功能模式 87 | replyPacket[3] = lattice->latticeSetting.speed; // 动画移动速度 88 | replyPacket[4] = version; // 系统版本 89 | udp.write(replyPacket, 5); //把数据写入发送缓冲区 90 | udp.endPacket(); //发送数据 91 | return udpdata; 92 | } 93 | udpdata.te = (packetBuffer[0] & 0x3f) >> 2; // 功能模式 94 | udpdata.lh = packetBuffer[1]; // 数据长度 95 | Serial.print("UDP接收到的数据信息为: "); 96 | Serial.print(udpdata.rt); 97 | Serial.print(udpdata.te); 98 | Serial.println(udpdata.lh); 99 | // 数据包 100 | for (int i = 0; i < udpdata.lh; i++) 101 | { 102 | udpdata.data[i] = packetBuffer[i + 2]; 103 | } 104 | udp.beginPacket(udp.remoteIP(), udp.remotePort()); //向udp工具发送消息 105 | replyPacket[0] = {0x01}; // 106 | udp.write(replyPacket, 3); //把数据写入发送缓冲区 107 | udp.endPacket(); //发送数据 108 | return udpdata; 109 | } 110 | return udpdata; 111 | } 112 | 113 | /** 114 | * @brief 更新系统时间 115 | * 116 | */ 117 | void Udps::updateTime() 118 | { 119 | int count = 30; // 一共尝试五次 120 | sendNTPpacket(); // 向NTP服务器发请求,先发送更新时间的操作 121 | int lastSendTime = millis(); // 记录上一次发送更新时间的时间戳 122 | while (true) // 这里用一个死循环来搞定,先发送更新时间的操作 123 | { 124 | long timeStamp = getNtpTimestamp(); // 获取当前时间戳 125 | if (timeStamp != 0) // 说明已经获取到了时间信息,这里直接做更新操作 126 | { 127 | datetimes->setDateTimes(timeStamp); // 给时钟设置时间 128 | break; // 跳出死循环 129 | } 130 | else 131 | { 132 | if (millis() - lastSendTime >= 1000) // 如果说发送时间超出上次发送时间1秒钟,则重新发送NTP请求 133 | { 134 | sendNTPpacket(); // 向NTP服务器发请求,先发送更新时间的操作 135 | lastSendTime = millis(); 136 | count--; 137 | } 138 | } 139 | lattice->showLongIcon(2); // 这里延迟两秒是因为过程太快了,交互体验不好 140 | pilotLight->flashing(); // 校准时间LED闪烁 141 | delay(100); // 等待100ms 142 | if (count < 0) // 如果说所有机会都用完了 143 | { 144 | break; // 跳出死循环 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/Udps.h: -------------------------------------------------------------------------------- 1 | #ifndef UDPS_H 2 | #define UDPS_H 3 | 4 | #include "DateTimes.h" 5 | #include "LatticePlus.h" 6 | #include "PilotLight.h" 7 | #include 8 | #include 9 | #include 10 | 11 | #define localPort 1234 // 监听本地UDP数据包端口 12 | #define remoteNtpPort 123 // 远程服务端口 13 | #define NTP_PACKET_SIZE 48 // NTP数据包数据长度 14 | 15 | struct Udpdata 16 | { 17 | uint8_t rt; // 返回类型 18 | uint8_t te; // 数据类型 19 | uint8_t lh; // 数据长度 20 | uint8_t data[64]; // 数据包 21 | }; 22 | 23 | class Udps 24 | { 25 | private: 26 | /** 27 | * @brief 显示对象 28 | * 29 | */ 30 | LatticePlus *lattice; 31 | 32 | /** 33 | * @brief 状态指示灯对象 34 | * 35 | */ 36 | PilotLight *pilotLight; 37 | /** 38 | * @brief 操作时间的对象 39 | * 40 | */ 41 | DateTimes *datetimes; 42 | /** 43 | * @brief NTP服务器网址 44 | */ 45 | const char *ntpServerName = "cn.ntp.org.cn"; 46 | 47 | /** 48 | * @brief 服务器IP 49 | */ 50 | IPAddress timeServerIP; 51 | 52 | /** 53 | * @brief 接收到的ntp数据包 54 | */ 55 | byte packetBuffer[NTP_PACKET_SIZE]; 56 | 57 | /** 58 | * @brief 返回UDP数据包 59 | */ 60 | char replyPacket[4]; 61 | 62 | /** 63 | * @brief udp对象 64 | */ 65 | WiFiUDP udp; 66 | 67 | /** 68 | * @brief 发送NTP数据包 69 | */ 70 | void sendNTPpacket(); 71 | 72 | public: 73 | /** 74 | * @brief构造函数 75 | */ 76 | Udps(); 77 | 78 | /** 79 | * @brief 构造函数 80 | * 81 | * @param datetimes 82 | */ 83 | Udps(DateTimes *datetimesobj, LatticePlus *latticeobj, PilotLight *pilotLightobj); 84 | 85 | /** 86 | * @brief 初始化UDP信息 87 | * 88 | */ 89 | void initudp(); 90 | 91 | /** 92 | * @brief设置系统时间 93 | */ 94 | long getNtpTimestamp(); 95 | 96 | /** 97 | * @brief 更新时间 98 | * 99 | */ 100 | void updateTime(); 101 | 102 | /** 103 | * @brief接收自定义UPD协议的数据 104 | */ 105 | Udpdata userLatticeLoop(uint8_t power, uint8_t mode, uint8_t version); 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/3.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/4.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/4.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/5.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/5.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/6.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/6.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/7.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/7.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/8.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/8.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/9-lattice-clock-for-homekit.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/9-lattice-clock-for-homekit.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/bin/9.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/code/esp8266-lattice-clock/bin/9.bin -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/esp8266-lattice-clock.ino: -------------------------------------------------------------------------------- 1 | #include "Main.h" 2 | 3 | // 如果你要开启 光敏电阻调节亮度,则只需要把 0 改成 1 就可以了 4 | #define OPEN_LIGHT 0 5 | // 如果你要启用点灯科技的代码,则只需要将这里的 0 改成 1 就可以了 6 | #define USE_BLINKER 0 7 | 8 | #define LATTICE_CLOCK_VERSION 9 // 点阵时钟代码版本号码 9 | 10 | #if USE_BLINKER 11 | // 这里加了一个点灯科技的代码支持,假如说你想用点灯科技,则可以取消注释掉下面这段代码 12 | // 关于这段的说明请参考:https : // gitee.com/lengff/esp8266-lattice-clock-open/tree/master/blinker 13 | #include "BlinkerSupport.h" 14 | #endif 15 | 16 | void handleUdpData() 17 | { 18 | Udpdata udpdata = udps.userLatticeLoop(functions.getCurrPower(), functions.getCurrMode(), LATTICE_CLOCK_VERSION); // 19 | if (udpdata.lh < 1) // 数据长度小于1则表示没有接收到任何数据 20 | { 21 | // 没有收到任何UDP数据 22 | return; 23 | } 24 | switch (udpdata.te) // 判断UDP数据类型 25 | { 26 | case 0: 27 | resetTime(udpdata.data); // 重置时间 28 | break; 29 | case 1: 30 | lattice.setBrightness(udpdata.data[0], true); // 设置亮度 31 | break; 32 | case 2: 33 | functions.setPowerAndMode(udpdata.data[0], 0); // 切换功能 34 | initStatus(); 35 | break; 36 | case 3: 37 | functions.setMode(udpdata.data[0]); // 切换功能模式 38 | initStatus(); 39 | break; 40 | case 4: 41 | httptool.updateBilibiliFlag(); // 更新bilibili粉丝数量前,需要重置一下flag 42 | subBili(udpdata.data); // 订阅BIlibiliUID 43 | initStatus(); 44 | break; 45 | case 5: 46 | lattice.shutdown(udpdata.data[0]); // 是否启用点阵屏幕 47 | break; 48 | case 6: 49 | lattice.setDirection(udpdata.data[0]); // 切换显示方向 50 | break; 51 | case 7: 52 | setUserData(udpdata.data); // 设置用户数据 53 | break; 54 | case 8: 55 | lattice.latticeSetting.speed = udpdata.data[0]; // 设置动画速度 56 | functions.setPower(CUSTOM); 57 | break; 58 | case 9: 59 | otas.updateOta(udpdata.data[0]); // OTA 升级 60 | break; 61 | case 10: 62 | setCountdown(udpdata.data); // 设置倒计时 63 | initStatus(); 64 | break; 65 | case 11: 66 | setSleepTime(udpdata.data); // 设置睡眠时间 67 | break; 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | /** 74 | @brief 显示时间回调处理 75 | 76 | */ 77 | void showTimeCallback() 78 | { 79 | handleUdpData(); 80 | touchLoop(); 81 | 82 | #if USE_BLINKER 83 | if (WiFi.status() == WL_CONNECTED) // 确保wifi网络是可用的,不可用则忽略 84 | { 85 | Blinker.run(); 86 | } 87 | #endif 88 | } 89 | 90 | /** 91 | @brief 显示时间 92 | 93 | @param showmode 94 | */ 95 | void showTime(uint8_t showmode) 96 | { 97 | Times times = datetimes.getTimes(); 98 | if (times.s == powerFlag) 99 | { 100 | return; // 如果秒钟数没有改变,则不执行方法 101 | } 102 | displayData[0] = times.s; 103 | displayData[1] = times.m; 104 | displayData[2] = times.h; 105 | if (showmode == 0) 106 | { 107 | lattice.showTime3(displayData); 108 | } 109 | else if (showmode == 1) 110 | { 111 | lattice.showTime(displayData); 112 | } 113 | else 114 | { 115 | if (times.s == 0 || powerFlag2 == -1) 116 | { 117 | powerFlag2 = 0; 118 | displayData[0] = times.m % 10; 119 | displayData[1] = times.m / 10; 120 | displayData[2] = times.h % 10; 121 | displayData[3] = times.h / 10; 122 | lattice.showTime2(displayData); 123 | } 124 | if (times.s % 2 == 0) 125 | { 126 | lattice.reversalLR(3); 127 | } 128 | else 129 | { 130 | lattice.reversalUD(3); 131 | } 132 | } 133 | powerFlag = times.s; 134 | } 135 | 136 | /** 137 | 功能处理 138 | */ 139 | void handlePower() 140 | { 141 | switch (functions.getCurrPower()) // 显示数据模式 142 | { 143 | case SHOW_TIME: 144 | showTime(functions.getCurrMode()); // 显示时间 145 | break; 146 | case SHOW_DATE: 147 | showDate(functions.getCurrMode()); // 显示日期 148 | break; 149 | case POWER2: 150 | showTemperature(); // 显示温度 151 | break; 152 | case BILIFANS: 153 | showBiliFans(); // 显示bilibili粉丝数量 154 | break; 155 | case CUSTOM: 156 | showUserData(functions.getCurrMode()); // 显示用户自定义的数据 157 | break; 158 | case COUNTDOWN: 159 | showCountDown(); // 显示倒计时 160 | break; 161 | case RESET: 162 | System::reset_system(); // 重置系统 163 | break; 164 | case RESETTIME: 165 | resetTime(NULL); // 重置时间,这里是随便传的一个参数,不想重新声明参数 166 | break; 167 | default: 168 | break; // 默认不做任何处理 169 | } 170 | } 171 | 172 | void setup() 173 | { 174 | lattice.boot_animation(); // 显示开机动画 175 | Serial.begin(115200); // 初始化串口波特率 176 | EEPROM.begin(4096); // 177 | WiFi.hostname("lattice-clock"); // 设置ESP8266设备名 178 | initTouch(); // 初始化按键信息 179 | wifis.connWifi(); // 连接wifi 180 | udps.initudp(); // 初始化UDP客户端 181 | httptoolticker.attach(5 * 60, httptool.updateBilibiliFlag); // 每五分分钟更新一次更新bilibili粉丝flag 182 | timestampticker.attach(1, DateTimes::timestampAdd); // 每一秒叠加一次秒数 183 | if (!wifis.isApMode()) // 如果wifi模式为连接wifi的模式则联网矫正时间 184 | { // 185 | resetTime(NULL); // 每次初始化的时候都校准一下时间,这里是随便传的一个参数,不想重新声明参数 186 | httptool.updateBilibiliFlag(); // 更新bilibili粉丝数量前,需要重置一下flag 187 | httptool.bilibiliFans(); // 刷新bilibili粉丝数量 188 | } 189 | initSleepTime(); // 初始化休眠时间 190 | systems.init_callback(showTimeCallback); 191 | 192 | #if USE_BLINKER 193 | initBlinker(); 194 | #endif 195 | } 196 | 197 | void loop() 198 | { 199 | wifis.wifiloop(); 200 | handleUdpData(); 201 | touchLoop(); 202 | handlePower(); 203 | sleepTimeLoop(); 204 | resetTimeLoop(); 205 | 206 | #if OPEN_LIGHT 207 | lightLoop(); 208 | #endif 209 | 210 | #if USE_BLINKER 211 | // 确保wifi网络是可用的,不可用则忽略 212 | if (WiFi.status() == WL_CONNECTED) 213 | { 214 | Blinker.run(); 215 | } 216 | #endif 217 | } 218 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/wifis.cpp: -------------------------------------------------------------------------------- 1 | #include "Wifis.h" 2 | #include "ConfigWifiHtml.h" 3 | 4 | Wifis::Wifis() 5 | { 6 | } 7 | 8 | Wifis::Wifis(LatticePlus *latticeobj, PilotLight *pilotLightobj) 9 | { 10 | lattice = latticeobj; 11 | pilotLight = pilotLightobj; 12 | } 13 | 14 | void Wifis::wifiloop() 15 | { 16 | if (isApMode()) // 这里只有在热点模式下才进行此操作 17 | { 18 | server.handleClient(); 19 | dnsServer.processNextRequest(); 20 | } 21 | } 22 | 23 | void Wifis::initWifi() 24 | { 25 | if (EEPROMTool.loadDataOne(WIFI_MODE) == 0x01) // 如果取到的数据为1时表示启动了热点模式 26 | { 27 | EEPROMTool.saveDataOne(0x00, WIFI_MODE); // 处理完热点模式以后随机将默认模式改为wifi模式 28 | wifiMode = 0x01; // 标记当前wifi模式 29 | initWebServer(); // 初始化web服务 30 | } 31 | else 32 | { 33 | WiFi.mode(WIFI_STA); 34 | wifiMode = 0x00; 35 | } 36 | } 37 | 38 | /** 39 | * @brief 处理主页请求 40 | * 41 | */ 42 | void Wifis::handleIndex() 43 | { 44 | server.send(200, "text/html", page_html); 45 | } 46 | 47 | /** 48 | * @brief 处理配网请求 49 | * 50 | */ 51 | void Wifis::handleConfigWifi() 52 | { 53 | WiFi.persistent(true); //首次在flash中保存WiFi配置-可选 54 | WiFi.begin(server.arg("ssid").c_str(), server.arg("pwd").c_str()); // 使用配网获取的wifi信息 55 | WiFi.setAutoConnect(true); // 设置自动连接 56 | EEPROMTool.saveDataOne(0XFE, REMEMBER_WIFI); // 记住wifi密码 57 | int count = 0; 58 | while (WiFi.status() != WL_CONNECTED) 59 | { 60 | delay(500); 61 | count++; 62 | if (count > 40) //如果20秒内没有连上,就开启Web配网 可适当调整这个时间 63 | { 64 | server.send(200, "text/html", "wifi连接失败,请检查密码后重试。"); //返回保存成功页面 65 | break; //跳出 防止无限初始化 66 | } 67 | Serial.println("."); 68 | } 69 | if (WiFi.status() == WL_CONNECTED) //如果连接上 就输出IP信息 70 | { 71 | Serial.print("WIFI Connected:"); //打印esp8266的IP地址 72 | Serial.println(WiFi.localIP()); //打印esp8266的IP地址 73 | server.send(200, "text/html", "wifi连接成功,即将重启设备。"); //返回保存成功页面 74 | delay(3000); // 等待三秒 75 | ESP.reset(); // 重启设备 76 | } 77 | } 78 | 79 | /** 80 | * @brief 处理扫描wifi请求 81 | * 82 | */ 83 | void Wifis::handleWifiList() 84 | { 85 | int n = WiFi.scanNetworks(); //开始同步扫描,将返回值存放在变量n中 86 | if (n > 0) // 只有有数据的时候才处理 87 | { 88 | char wifilist[640] = {0}; // 返回给网页的数据 89 | Serial.println("sacn wifi."); 90 | for (int i = 0; i < 20; ++i) //开始逐个打印扫描到的 91 | { 92 | sprintf(wifilist, "%s%s%s", wifilist, WiFi.SSID(i).c_str(), ","); // 组装信息返回给接口 93 | } 94 | Serial.print(wifilist); // 打印一下日志 95 | server.send(200, "text/html", wifilist); //返回保存成功页面 96 | return; // 结束这里的操作 97 | } 98 | Serial.println("no any wifi."); // 打印没有任何wifi日志 99 | server.send(200, "text/html", ".nodata"); //返回保存成功页面 100 | } 101 | 102 | void Wifis::initWebServer() 103 | { 104 | WiFi.mode(WIFI_AP_STA); // 设置模式为wifi热点模式 105 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); //初始化AP模式 106 | WiFi.softAP(AP_NAME, NULL, 1, 0, 4); //初始化AP模式 107 | server.on("/", HTTP_GET, handleIndex); //设置主页回调函数 108 | server.on("/configwifi", HTTP_GET, handleConfigWifi); //设置Post请求回调函数 109 | server.on("/wifilist", HTTP_GET, handleWifiList); // 设置获取wifi列表回调函数 110 | server.onNotFound(handleIndex); //设置无法响应的http请求的回调函数 111 | server.begin(); //启动WebServer 112 | Serial.println("WebServer started!"); 113 | dnsServer.start(53, "*", apIP); 114 | Serial.println("判断是否是初始化热点出现的bug"); 115 | } 116 | 117 | void Wifis::connWifi() 118 | { 119 | initWifi(); // 初始化wifi信息 120 | int timer = 0; // 清零计数器 121 | if (isApMode()) // 模式为热点模式就不走连接wifi和wifi配网了 122 | { 123 | return; // 如果wifi模式为热点模式,则不进wifi连接和配网 124 | } 125 | if (EEPROMTool.loadDataOne(REMEMBER_WIFI) == 0XFE) // 如果记住wifi值不为0xfe表示存在WiFi账号密码等信息 126 | { 127 | Serial.println("start connect wifi "); 128 | Serial.println(WiFi.SSID().c_str()); 129 | Serial.println(WiFi.psk().c_str()); 130 | WiFi.begin(WiFi.SSID().c_str(), WiFi.psk().c_str()); // 使用配网获取的wifi信息 131 | // WiFi.begin("你的wifiSSID", "你的wifi密码"); // 使用固定的wifi信息 132 | while (WiFi.status() != WL_CONNECTED) 133 | { 134 | timer++; 135 | pilotLight->flashing(); // 闪烁LED灯 136 | lattice->showLongIcon(0); // 显示连接wifi图案 137 | delay(100); 138 | if (timer >= 300) // 如果计数器大于60次,表示超过一分钟,则说明一分钟都没有连接上wifi,就不连了 139 | { 140 | timer = 0; // 清零计数器 141 | enableApMode(); // 联网失败进入到热点模式 142 | break; // 防止出问题还是break一下 143 | } 144 | } 145 | WiFi.setAutoConnect(true); // 设置自动连接 146 | } 147 | else 148 | { 149 | enableApMode(); 150 | // WiFi.beginSmartConfig(); // 没有账号密码则进入到配网模式 151 | // Serial.println("smart config wifi "); 152 | // while (1) 153 | // { 154 | // timer++; 155 | // lattice.showLongIcon(1); // 显示配网中图案信息 156 | // pilotLight.flashing(); // 闪烁LED灯 157 | // delay(500); // 等待半秒钟 158 | // if (timer >= 20) // 如果配网次数超过20此,则重启系统,重新配网 159 | // { 160 | // ESP.restart(); // 重启系统 161 | // return; 162 | // } 163 | // if (WiFi.smartConfigDone()) // 配网成功 164 | // { 165 | // WiFi.setAutoConnect(true); // 设置自动连接 166 | // EEPROMTool.saveDataOne(true, REMEMBER_WIFI); // 记住wifi密码 167 | // break; 168 | // } 169 | // wifiloop(); 170 | // } 171 | } 172 | Serial.println("conn wifi successful"); // 记录一下日志,避免一点都不知道有没有连上wifi 173 | delay(500); // 等几秒再进入系统 174 | } 175 | 176 | void Wifis::enableApMode() 177 | { 178 | EEPROMTool.saveDataOne(0x01, WIFI_MODE); // 修改wifi模式,随后重启ESP 179 | ESP.restart(); // 重启系统 180 | } 181 | 182 | /** 183 | * @brief 获取当前wifi状态,判断是否是热点模式 184 | * 185 | * @return true 186 | * @return false 187 | */ 188 | bool Wifis::isApMode() 189 | { 190 | // 只有wifi模式 == 0x01时,表示wifi为热点模式 191 | return wifiMode == 0x01; 192 | } 193 | -------------------------------------------------------------------------------- /code/esp8266-lattice-clock/wifis.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFIS_H 2 | #define WIFIS_H 3 | 4 | #include "EEPROMTool.h" 5 | #include "LatticePlus.h" 6 | #include "PilotLight.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef APSSID 13 | #define APSSID "lattice-clock-ap" // wifi热点的账号密码 14 | #endif 15 | 16 | static ESP8266WebServer server(80); // 创建dnsServer实例 17 | static DNSServer dnsServer; // dnsServer 18 | static IPAddress apIP(192, 168, 4, 1); // esp8266-AP-IP地址 19 | 20 | class Wifis 21 | { 22 | private: 23 | /** 24 | * @brief 显示对象 25 | * 26 | */ 27 | LatticePlus *lattice; 28 | 29 | /** 30 | * @brief 状态指示灯对象 31 | * 32 | */ 33 | PilotLight *pilotLight; 34 | 35 | /** 36 | * @brief wifi名字 37 | * 38 | */ 39 | const char *AP_NAME = APSSID; 40 | 41 | /** 42 | * @brief 是否记住wifi密码 43 | * 44 | */ 45 | bool rememberWifiPwd = 0X00; 46 | 47 | /** 48 | * @brief 初始化wifi 49 | * 50 | */ 51 | void initWifi(); 52 | 53 | /** 54 | * @brief 初始化web服务 55 | * 56 | */ 57 | void initWebServer(); 58 | 59 | /** 60 | * @brief wifi模式 0x00: 连接wifi模式(STA) 0x01: wifi热点模式(AP) 61 | * 62 | */ 63 | uint8_t wifiMode = 0x00; 64 | 65 | public: 66 | /** 67 | * @brief 构造函数 68 | * 69 | */ 70 | Wifis(); 71 | 72 | /** 73 | * @brief 构造函数 74 | * 75 | * @param lattice 76 | * @param pilotLight 77 | */ 78 | Wifis(LatticePlus *latticeobj, PilotLight *pilotLightobj); 79 | 80 | /** 81 | * @brief 连接wifi 82 | * 83 | */ 84 | void connWifi(); 85 | 86 | /** 87 | * @brief 启动热点模式 88 | * 89 | */ 90 | void enableApMode(); 91 | 92 | /** 93 | * @brief 获取当前wifi状态,判断是否是热点模式 94 | * 95 | * @return true 96 | * @return false 97 | */ 98 | bool isApMode(); 99 | 100 | /** 101 | * @brief wifi轮循方法 102 | * 103 | */ 104 | void wifiloop(); 105 | 106 | /** 107 | * @brief 处理主页请求 108 | * 109 | */ 110 | static void handleIndex(); 111 | 112 | /** 113 | * @brief 处理配网请求 114 | * 115 | */ 116 | static void handleConfigWifi(); 117 | 118 | /** 119 | * @brief 处理扫描wifi请求 120 | * 121 | */ 122 | static void handleWifiList(); 123 | }; 124 | #endif -------------------------------------------------------------------------------- /file/CH34x_Install_Windows_v3_4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/file/CH34x_Install_Windows_v3_4.zip -------------------------------------------------------------------------------- /file/CP210x_Universal_Windows_Driver.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/file/CP210x_Universal_Windows_Driver.zip -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/4.png -------------------------------------------------------------------------------- /img/esp8266-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/esp8266-light.png -------------------------------------------------------------------------------- /img/latticeshow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/latticeshow.gif -------------------------------------------------------------------------------- /img/latticeshow2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/latticeshow2.gif -------------------------------------------------------------------------------- /img/qqgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/qqgroup.png -------------------------------------------------------------------------------- /img/taobao-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/taobao-1.png -------------------------------------------------------------------------------- /img/taobao-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/taobao-2.png -------------------------------------------------------------------------------- /img/taobao-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/taobao-3.png -------------------------------------------------------------------------------- /img/taobao-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/taobao-4.png -------------------------------------------------------------------------------- /img/taobao-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/taobao-5.png -------------------------------------------------------------------------------- /img/wx-program-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/wx-program-1.jpg -------------------------------------------------------------------------------- /img/wx-small-program-qr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lengff/esp8266-lattice-clock-open/42eb434e4e2b99be7f8f876a890b4b52c8130da2/img/wx-small-program-qr.jpg -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 原文地址 2 | 3 | [多功能点阵时钟](http://blog.lengff.com/2022/01/17/project-lattice-clock/) 4 | 5 | ## 说明 6 | 7 | 最近一直都有人询问配网,配网已经从之前视频演示的智能配网改成了,新版的热点网页配网。操作方法如下: 8 | 9 | 1. 时钟上电,然后会自己产生一个``lattice-clock-ap``的wifi热点 10 | 2. 用手机连接上述热点,然后就会自动弹出配网页面 11 | 3. 如果没有弹出来的话,那就自己到手机浏览器输入:``192.168.4.1``一样也会有配网页面 12 | 4. 按照页面提示即可完成配网 13 | 14 | ## 更新 15 | 16 | #### 7. 受群友建议,增加光敏电阻,实现亮度低的时候自动熄灭,亮度高的时候自动亮起,相关接线图和代码如下 17 | ##### 接线图 18 | ![接线图](./img/esp8266-light.png) 19 | ##### 相关代码说明: 20 | 配置的代码在 `code`目录中的 `esp8266-lattice-clock`目录中的 `Lattice.c` 文件中 21 | ``` c 22 | /** 23 | * @brief 光敏值(光敏电阻的值) (0 - 255)这个20是我用1k电阻测试出来的,具体的需要自己去判断,这个值的设置后续考虑加到小程序中去 24 | * 25 | */ 26 | uint8_t light = 20; 27 | ``` 28 | 逻辑代码在`code`目录中的 `esp8266-lattice-clock`目录中的 `Main.h` 文件中 29 | ```c 30 | /** 31 | * 这里就是根据光敏的取值来判断是否显示点阵,逻辑代码较为简单,有大佬愿意优化此代码甚好(好人一生平安) 32 | */ 33 | void lightLoop() 34 | { 35 | if (analogRead(A0) < lattice.latticeSetting.light) 36 | { 37 | lattice.shutdown(true); // 休眠操作(目前就是把屏幕熄灭) 38 | } 39 | else 40 | { 41 | lattice.shutdown(false); // 退出休眠操作(目前就是把屏幕点亮) 42 | lattice.setBrightness(lattice.latticeSetting.brightness, false); // 亮度不为0则将设置屏幕亮度为指定的屏幕亮度 43 | } 44 | } 45 | 46 | ``` 47 | 48 | 49 | 50 | #### 6. 增加DHT11温度的支持,使用需要自己到代码的``DHT11.h``文件中,修改支持的引脚。 51 | 52 | #### 5. 新增对awtirx的支持,如果温湿度传感器用的是HTU21D的话可以显示温度,增加几个简单的颜色变化,由于时间比较忙,所以就只简单适配一下代码,如果有兴趣的朋友可以自己修改代码 53 | 54 | #### 4. 更新了配网逻辑,取消了原来的配网,使用新的配网(连接时钟热点,自动弹出配网页面,即可配网) 55 | 56 | #### 3. 最新的代码可以忽略时钟芯片,只要ESP8266 + 点阵即可实现时钟功能 57 | 58 | #### 2. 新增了一个接入点灯科技版本的代码,具体说明请参考这里:[点灯科技版本说明](blinker) 59 | 60 | #### 1. 点阵时钟立创PCB工程已经完成,开源地址为: [ESP8266多功能点阵时钟 - PCB制作分享](https://oshwhub.com/Lengff/lattice-clock) 61 | 62 | 63 | # 简介 64 | 65 | 很早就了解ESP8266了,当时也用这个搞过一些小demo,一直也没有想过要弄些实际的作品出来,也许是因为自己比较菜吧!最近刚好工作上没那么忙, 想着自己重新搭建一个mqtt服务器,做一些和物联网有关的功能。逛论坛的时候有一篇帖子吸引到我了,就是这篇 《 [ESP8266物联网创意点阵时钟,女朋友看了都想要!](https://www.arduino.cn/thread-98790-1-1.html) 》让我萌生了自己搞一个点阵时钟的想法,所以就一点一点的开始了这个制作点阵时钟的制作旅程,过程谈不上很难,但是中途也遇到了很多的问题,中途遇到了很多的问题,有些解决了,有些就只能简单粗暴的解决,好在最终还是能够实现自己所想的功能。 总结经验和教训想分享给有同样兴趣爱好的人,文笔不太好,代码也写的不好,但依旧阻挡不了这颗分享的心。 66 | 67 | # 演示 68 | 69 | ## 图片 70 | 71 | ![动态图](img/latticeshow.gif) 72 | 73 | ![演示图片2](img/latticeshow2.gif) 74 | 75 | ## 视频 76 | 77 | [点击跳转接哔哩哔哩查看](https://www.bilibili.com/video/BV18i4y1R7ft?spm_id_from=333.999.0.0) 78 | 79 | # 理念 80 | 81 | 尽管网上有很多人都搞过或正在`esp8266`的点阵时钟,我重复搞更像是在造轮子,我作为一个开发者也很抵制重复造轮子的行为。 但是作为一个初学者的我更想去体验这个造轮子的过程,而且在这个过程中我希望这个轮子有更多我个人的想法在里面,所以就想着自己从头开始来搞这个东西。至于我个人的理念就是做一个极简的点阵时钟出来, 在拥有更多其他的功能之外,要满足它最最核心的功能(时钟,误差低),其次就是拥有更多的功能,更简单的交互,再者就是成本要低,希望想自己``diy``的人用最低的成本做一个好看又好用的点阵时钟出来。 82 | 83 | - 简单 84 | - 好用 85 | - 功能丰富 86 | - 低成本 87 | 88 | # 硬件选材 89 | 90 | 这里我就只介绍一些必要的硬件,懂得都懂就不需要我这种小白列举了。在这里我也放上我一直逛的一家淘宝店:[[欣薇电子科技](https://xwdzqyd.taobao.com/)](https://xwdzqyd.taobao.com/) 。不是打广告,主要是里面东西比较全面,且价格也差不多,购买东西的时候不需要不同的淘宝店之间换来换去,而且运费也只要3元,一般的这种电子产品店运费都是5-6元的。元器件的具体作用请自行百度,我就不一一赘述。 91 | 92 | ## NodeMcu(Esp8266) 93 | 94 | 这是我们的核心硬件,功能很多,可能是今年芯片涨价的缘故吧,这类东西越来越贵了。 95 | 96 | ![NodeMcu(Esp8266)淘宝图片](img/taobao-3.png) 97 | 98 | ## DS3231 (最新代码可忽略该模块,不买也没关系) 99 | 100 | 买这个模块主要的作用是掉电时间不重置,且时间误差小。 101 | 102 | ![NodeMcu(Esp8266)淘宝图片](img/taobao-1.png) 103 | 104 | ## Max7219 32x8 点阵 105 | 106 | 点阵的驱动芯片好像有蛮多的,`Max7219`有一个优点就是可以级联。 107 | 108 | ![NodeMcu(Esp8266)淘宝图片](img/taobao-2.png) 109 | 110 | ## 杜邦线若干 111 | 112 | 买杜邦线是解决焊接的麻烦,可以快速简单的搞出我们需要的电路出来 113 | 114 | ![NodeMcu(Esp8266)淘宝图片](img/taobao-4.png) 115 | 116 | ## 单路触摸模块 117 | 118 | 原本是搞点动开关的,但是那种成本也不低,体验又不好,所以就买这种触摸模块,用来实现单击,双击,长按等功能,当然这个是非必要的,如果你不想要任何实体交互,只用小程序进行交互的话,就可以忽略此模块。 119 | 120 | ![NodeMcu(Esp8266)淘宝图片](img/taobao-5.png) 121 | 122 | ---- 123 | 124 | # 时钟功能 125 | 126 | ### **硬件功能** 127 | 128 | - NTP校时 129 | - 调节显示方向 130 | - 息屏 131 | - 调节亮度 132 | - 显示时间 133 | - 显示日期 134 | - 显示倒计时 135 | - 显示温度 136 | - 显示B站粉丝数 137 | - 显示自定义内容 138 | - OTA更新 139 | - 热点模式 140 | 141 | ### **微信小程序功能** 142 | 143 | - 同步设备状态 144 | - 设置显示方向 145 | - 设置亮度 146 | - 设置是否显示 147 | - 切换显示内容 148 | - 切换显示模式 149 | - 恢复出厂 150 | - wifi配网 151 | - 热点模式 152 | - 倒计时设置 153 | - OTA更新 154 | - 自定义点阵内容 155 | 156 | # 原理图 157 | 158 | ## 手残原理图 159 | 实在是没有作图的天赋,也没有找到比较好的制图工具,只能借助`windows`自带的画图工具制作一个简单易懂的原理图出来吧,望见谅 160 | 161 | ![原理图](img/4.png) 162 | 163 | ## 立创原理图 164 | 165 | 点阵时钟立创`PCB`工程已经完成,开源地址为: [ESP8266多功能点阵时钟 - PCB制作分享](https://oshwhub.com/Lengff/lattice-clock) 166 | 167 | # 制作过程 168 | 169 | 其实制作过程算是比简单的,主要分以下几步 170 | 171 | ## 1. 购买元器件 172 | 173 | 购买器材可参考上文中提到的一些元器件,淘宝上很多淘宝店都是有卖的。 174 | 175 | ## 2. 安装对应的软件 176 | 177 | 这里我们主要用到的软件是``arduino`` 很多diy爱好者应该都用过,用过的用户可跳过,也可以接着往下看 178 | 179 | ### 安装Arduino 180 | 181 | 通过官网下载``Arduino IDE``,解压后运行``arduino.exe``就表示安装完成了。下载地址如下: 182 | 183 | [arduino-1.8.19-windows.zip](https://downloads.arduino.cc/arduino-1.8.19-windows.zip) 184 | 185 | 如果链接失效,请在 [官网](https://www.arduino.cc/en/software) 自行下载,下载选项选``Windows ZIP file`` 186 | 187 | ### 安装esp8266 SDK 188 | 189 | 这里我也是直接从论坛搬运的,但是为了方便阅读所以在下面列出来,可以直接按我列出来的步骤来,如果有问题请到 [这里](https://www.arduino.cn/thread-76029-1-1.html) 看看有没有其他的解决方法 190 | 191 | 1. 打开`Arduino IDE`菜单 > 文件 >首选项,在 **附加开发板管理器网址** 输入框中,填入以下网址:``https://www.arduino.cn/package_esp8266com_index.json`` 192 | 193 | 2. 下载社区打包的``esp8266``安装包,直接运行并解压即可 194 | 195 | - SDK下载(44M): 196 | 197 | 蓝凑云分享地址:https://wwi.lanzouo.com/ij4GEyjcz7c (推荐使用这个) 198 | 199 | 阿里云盘地址:https://www.aliyundrive.com/s/QhgGreFTjJb (推荐使用这个) 200 | 201 | 3. 双击运行即可解压,解压完成后,再打开``Arduino IDE``,即可在 **`菜单栏 --> 工具 --> 开发板`** 中找到你使用的`esp8266`开发板 202 | 203 | 4. 提示:如果安装过其他版本的esp8266sdk,请先删除,再使用本安装包,删除方法:文件管理器地址栏输入 **%LOCALAPPDATA%/Arduino15/packages**,回车进入,然后**删除**掉其中的**esp8266文件夹** 204 | 205 | ### 安装串口驱动 206 | 207 | NodeMcu有好多种规格,比较常见的是CP2102串口芯片和CH340串口芯片的,所以都需要对应的驱动,下面分享两者的驱动下载地址: 208 | 209 | #### **CP2102驱动** 210 | 211 | 蓝凑云分享地址:https://wwi.lanzouo.com/iU5mcyjdxuj (推荐使用这个) 212 | 213 | 博客文件地址:[CP210x_Universal_Windows_Driver.zip](file/CP210x_Universal_Windows_Driver.zip)(不推荐这个,上面链接失效可以使用) 214 | 215 | #### **CH340驱动** 216 | 217 | 蓝凑云分享地址:https://wwi.lanzouo.com/iyFTayjdxsh (推荐使用这个) 218 | 219 | 博客文件地址:[CH34x_Install_Windows_v3_4.zip](file/CH34x_Install_Windows_v3_4.zip) (不推荐这个,上面链接失效可以使用) 220 | 221 | - 解压对应的驱动压缩包,然后双击运行对应系统的驱动即可。 222 | 223 | - `驱动安装成功以后需要重启电脑` 224 | 225 | ## 3. 连接对应的线材 226 | 227 | 这里就是使用杜邦线进行一个简单的连接,如果有大佬愿意也可以通过我画的电路图进行焊接,不过还是推荐先使用杜邦线连接,调试好程序后再进行焊接处理。 228 | 229 | #### `NodeMcu`和`Max7219点阵`接线 230 | 231 | - VCC → 3.3V (其实这里也可以接5v,如果你想点阵的亮度比较亮的话) 232 | 233 | - GND → GND 234 | 235 | - DIN → D7 236 | 237 | - CS → D6 238 | 239 | - CLK → D5 240 | 241 | #### `NodeMcu`和`DS3231模块`接线 242 | 243 | - VCC → 3.3V (这里不用接5v,低电压更安全) 244 | - GND → GND 245 | - SDA → D2 246 | - SCL → D1 247 | 248 | #### `NodeMcu`和`单路触摸模块`接线 249 | 250 | - VCC → 3.3V (这里不用接5v,低电压更安全) 251 | 252 | - GND → GND 253 | 254 | - SIG → D8 255 | 256 | 257 | 258 | ## 4. 写入程序 259 | 260 | 使用`Arduino`写入程序其实也挺简单的,大致步骤分为如下几步 261 | 262 | ### 检查 263 | 264 | - 简单ESP8266SDK是否安装成功 265 | 266 | - 检查串口驱动是否安装成功 267 | 268 | ### **安装所需要的依赖包** 269 | 270 | - 由于我也是初学者,很多都是使用别人的库文件,自己并没有去深入原理的去重写代码,所以我们需要安装一些库文件 271 | 272 | - 安装步骤如下: 273 | 274 | - `工具 --> 管理库 --> 搜索我们需要安装的库 --> 点击安装即可` 275 | ![安装步骤如下](img/1.png) 276 | 277 | - 需要安装的库文件如下 278 | 279 | - `DS3231` DS3231时钟现成库文件 280 | - `LedControl` 驱动Max7219点阵的库文件 281 | - `OneButton` 实现按键单击,双击,长按功能的库 282 | - `如果有遗漏的库,请看报错提示,按错误提示将其中的库安装一下即可` 283 | 284 | ### **下载源码** 285 | 286 | - `github`下载 https://github.com/Lengff/esp8266-lattice-clock-open 287 | - `gitee`下载 https://gitee.com/lengff/esp8266-lattice-clock-open 288 | 289 | 通过上面的地址下载我们的源码。 290 | 291 | ### **将源码写入Esp8266** 292 | 293 | - 通过`Arduino IDE` 打开我们上一步下载好的代码 294 | 295 | `文件 --> 打开 --> 选择源码所在文件夹中.ino后缀的文件` 296 | 297 | - 选择开发板 298 | 299 | `工具 --> 开发板 --> ESP8266 --> NodeMcu 1.0` 300 | 301 | ![选择开发板](img/2.png) 302 | 303 | - 选择端口 304 | 305 | `工具 --> 端口 --> 选择我们串口设备对应的端口` 306 | 307 | - 写入程序 308 | 309 | `项目 --> 上传` 或 `点左侧的 → 箭头` (波特率选115200,会快一些) 310 | 311 | ## 5. 测试&检验 312 | 313 | 这里列举个人处理过程中遇到比较多的问题: 314 | 315 | 1. 程序编译失败 316 | - 编译失败最多的可能就是库文件不存在,或者esp8266的sdk安装有问题,检查一下编译报错的地方即可 317 | 318 | 2. 程序上传失败 319 | - 上传失败基本就是串口没选对或者开发板没有选对 320 | 321 | 3. 上传成功后不无任何显示 322 | - 这里就需要检查自己的接线是否正确。(此操作请断开电源后进行) 323 | 324 | 4. 一键配网不成功 325 | - 配网需要手机和设备在同一个wifi,且wifi是2.4G的wifi 326 | - 查看点阵显示内容:`no wifi` 表示没有配置过wifi,需要配置wifi;`con wifi` 则表示已经配置过wifi信息了,正在连接wifi,如果需要重新配置wifi的话就需要长按触摸按键6秒重置系统。 327 | - 配网建议先按`nodeMcu`上的reset按键,再点击手机上的配网 328 | 329 | 5. 配网成功后显示的内容是 `23:59:59` 330 | - 这个就表示你的DS3231接线有问题,需要检查接线是否正确。(此操作请断开电源后进行) 331 | - 还是不行的话,请先断开电源,再将DS3231上的电池拆拆下来,重新安装后重试。 332 | 333 | 6. 如果上述的一些都无法帮助到你,烦请添加个人QQ群:`711284300` (有问必答) 334 | 335 | - ![qq群图片](img/qqgroup.png) 336 | 337 | # 交互 338 | 339 | 硬件上我们设置了一个触摸按键用于人机交互,更多的交互逻辑我是在小程序上实现的。 340 | 341 | ## 触摸按键: 342 | 343 | - 单击:单击为切换显示功能,目前是 `时间 - 日期 - 温度 - B站粉丝数 - 自定义显示` 五个循环切换 344 | - 双击:双击是切换功能的显示模式,例如:时间有两种显示模式 1. `时- 分 - 秒` 2. `时 - 分` 可以实现两种不同显示模式 345 | - 长按三秒:长按超过三秒低于六秒会触发NTP自动校准时间 346 | - 长按六秒:长按超过六秒则会重置系统(在系统出现异常的时候,或者更换了wifi的情况下需要重置系统) 347 | 348 | ## 微信小程序: 349 | 350 | - 这里不做过多赘述,微信小程序里面的功能都比较简单明了一看便知,上述按键支持的功能微信小程序全部都支持 351 | 352 | # 小程序 353 | 354 | 为什么会选择小程序来和Esp8266交互呢? 355 | 356 | 1. 小程序开发起来比较快捷,支持的功能也很多,很多功能直接调用api即可 357 | 2. 使用app或者是在访问esp8266里面的页面我觉得交互有点差,我认为最交互应该简单到家,不需要有过多的学习成本 358 | 3. 小程序支持配网,UDP,蓝牙等等诸多功能,后续的其他小作品可能都会用到小程序 359 | 360 | ## 页面展示 361 | 362 | ![小程序页面截图](img/wx-program-1.jpg) 363 | 364 | ## 代码 365 | 366 | 目前小程序不在此次开源分享当中,其实小程序这块难度不大,而且可以直接使用,减少大家的开发工作。 367 | 368 | ## 小程序入口 369 | 370 | 通过手机微信扫描下面的小程序码 或 微信搜索小程序 `Lengff`即可找到我的小程序 371 | 372 | ![小程序页面截图](img/wx-small-program-qr.jpg) 373 | 374 | # 通讯方式 375 | 376 | 目前esp8266和小程序的通讯是使用UDP的通讯方式,使用UDP的通讯方式的好处就是无连接,即开即用,不需要有服务器的存在,只要保证esp8266和手机连接的是同一个局域网即可,而且可以通过广播的方式发现设备,不需要建立长链接,缺点就是不稳定,会丢包,但是我认为是可以接受的,因为手机更多的是发送一些指令,我每次都发送两次指令丢包的概率就会下降一些。 377 | 378 | ## 传输协议 379 | 380 | udp是发送一些hex数据包来实现esp8266和手机之间的数据传输,所以我就自己简单定义了一个自己的UDP传输协议,协议如下: 381 | 382 | ``` 383 | 自定义一个UPD协议用于小程序和esp8266进行UDP通信 384 | 385 | |0 1|2 3 4 5|6 7|0 1 2 3 4 5 6 7| 386 | --------------------------------- 387 | |RT |TE |VN | LH | 388 | --------------------------------- 389 | 报文数据 (64bit) 390 | --------------------------------- 391 | 392 | 自定义UDP协议说明: 393 | 394 | 1. RT: (2bit)成功返回数值 395 | 2. TE: (4bit)type报文类型: 0: 重置时间 ,1: 设置亮度 ,2: 切换功能 ,3: 切换功能显示样式 ,4: 订阅BIlibiliUID ,5: 是否启用点阵屏幕 ,6: 切换显示方向 ,7: 设置用户数据 ,8: 设置动画速度 ,9: OTA 升级 (这里有个缺陷,就是最多只支持16种类型,所以后续会扩展此处) 396 | 3. VN: (2bit)version协议版本,目前固定为1 397 | 4. LH: (8bit)length数据包长度 398 | 5. 报文数据: (64)bit 版本1目前支持的最大数据包长度为64bit 399 | ``` 400 | 401 | ## 优化 ? 402 | 403 | 优化空间自然是有的,改动其实也不大,那就是后续接入到自己的MQTT服务器上去同时兼容UPD通讯方式,但是目前没有搞是因为目前没有稳定可用的服务器,盲目的接入后续升级起来就会影响设备正常的功能使用。 404 | 405 | # 自我评价 406 | 407 | 这个项目(也算是一个小项目吧)我做了一个多月,也不能算是从0开始,一开始到时候熟悉一点东西,只是知识点零零散散的,就像一堆凌乱的拼图,这次就像在拼拼图,虽然拼的不好但是好歹也是拼出来的一个作品,虽说也有很多人搞这种点阵时钟,但是我个人觉得自己真的去实践就会发现没有那么简单,而且别人做的你也未必会满意,自己去做更能去注入自己的灵魂在里面。在此讲讲个人感悟:本人不是专职搞这种硬件开发,这个只是个人的业余爱好,虽然平常也有积累一些相关的知识,但是远不如系统性学习来的高效,所以如果真的要学习这个还是要系统性的看一些视频来学习,其次就是就是实践,虽然我掌握了一些零零散散的技能,但是却不知道如何去使用,所以需要不断的去实践来理解自己所掌握的技能。 408 | 409 | 缺点: 410 | 411 | - 需要连接wifi(且必须是2.4Gwifi)才能用 -(这个也会在后续程序上做出优化) 412 | - 需要持续供电,因为点阵功耗比较大,所以用电池反而体验很差 413 | 414 | 优点 415 | 416 | - 支持OTA可持续更新系统 417 | - 交互简单便捷 418 | - 支持自定义显示内容 419 | 420 | # 结尾 421 | 422 | 这里我提供一个``8x8``和 ``32x8``点阵的在线取模工具,做的比较粗糙,如果有需要改进的地方欢迎在评论区留言。 423 | 424 | [8x8点阵的在线取模工具](http://lengff.com/lengff/lattice2.html) 425 | [32x8点阵的在线取模工具](http://lengff.com/lengff/lattice3.html) 426 | 427 | # 参考文章 428 | 429 | 对于我这种小白而言,能顺利搞出这个来,更多的还是那些愿意分享的人让我不断进步,再次鸣谢那些让我进步文章作者。 430 | 431 | [ESP8266物联网创意点阵时钟,女朋友看了都想要!](https://www.arduino.cn/thread-98790-1-1.html) 432 | [从零开始的ESP8266探索(11)-定时任务调度器Ticker使用演示](https://blog.csdn.net/Naisu_kun/article/details/85165262) 433 | [Arduino 触摸按键:实现单击,双击,长按功能,稳定无抖动。](https://blog.csdn.net/weixin_44291381/article/details/116585220) 434 | [ESP8266-12F 中断](https://www.cnblogs.com/liming19680104/p/11001989.html) 435 | [太极创客 ESP8266 – WiFiUDP库](http://www.taichi-maker.com/homepage/iot-development/iot-dev-reference/esp8266-c-plus-plus-reference/wifiudp/) 436 | [Arduino 学习笔记 | 单片机控制驱动MAX7219 8*8 LED点阵显示模块](https://blog.csdn.net/Naiva/article/details/105330435) 437 | [分享一个基于airkiss协议的配网小程序](https://aithinker.blog.csdn.net/article/details/121536188) 438 | [硬件时钟电路(RTC)及其与Arduino接口](https://zhuanlan.zhihu.com/p/349838613) 439 | [arduino语法](https://www.ncnynl.com/category/arduino-language/) 440 | [【开源】32元打造高精度WiFi时钟,保证每个人都能做出来,全开源](https://www.bilibili.com/video/BV1yz4y1r7XE) 441 | [NTP 协议简单分析](https://blog.srefan.com/2017/07/ntp-protocol/) 442 | [NTP报文解析及对时原理](https://www.cnblogs.com/21summer/p/14819406.html) 443 | --------------------------------------------------------------------------------