├── BLE.cpp ├── BLE.h ├── DS1302.cpp ├── DS1302.h ├── README.md ├── RTC.cpp ├── RTC.h ├── SmartMatrix.ino ├── buzzer.cpp ├── buzzer.h ├── common.h ├── img ├── animHack.h ├── bell.h ├── cake.h ├── checkingTime.h ├── heart.h ├── heart2.h ├── success.h ├── timeAnim0.h ├── timeAnim1.h ├── timeAnim2.h ├── timeAnim3.h ├── timeAnim4.h ├── timeAnim5.h ├── timeAnim6.h ├── timeAnim7.h └── timeAnim8.h ├── light.cpp ├── light.h ├── net.cpp ├── net.h ├── preferencesUtil.cpp ├── preferencesUtil.h ├── songs.h ├── task.cpp └── task.h /BLE.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common.h" 7 | #include "preferencesUtil.h" 8 | #include "light.h" 9 | #include "RTC.h" 10 | #include "net.h" 11 | #include "task.h" 12 | 13 | 14 | BLEServer *pServer = NULL; //BLEServer指针 pServer 15 | BLEAddress *authorizedDevice = NULL; 16 | 17 | BLECharacteristic *pTxCharacteristic = NULL; //BLECharacteristic指针 pTxCharacteristic 18 | BLECharacteristic *pRxCharacteristic = NULL; 19 | BLECharacteristic *pCharacteristicBrightness = NULL; 20 | BLECharacteristic *pCharacteristicColor = NULL; 21 | BLECharacteristic *pCharacteristicAnniversary = NULL; 22 | BLECharacteristic *pCharacteristicBirthday = NULL; 23 | BLECharacteristic *pCharacteristicWIFI = NULL; 24 | BLECharacteristic *pCharacteristicAnim = NULL; 25 | 26 | bool BLEConnected = false; //蓝牙连接状态 27 | bool advertising = false; //蓝牙广播状态 28 | 29 | // 生成 UUID 网站: https://www.uuidgenerator.net/ 30 | #define SERVICE1_UUID "14d59bde-ba3d-4477-ba91-3a7d6589b164" // UART service UUID 31 | #define BRIGHTNESS_UUID "be8948de-9de8-4736-9da8-8a8169265a0e" 32 | #define COLOR_UUID "515ced63-935b-48f8-8087-4b93e2e64f88" 33 | #define ANNIVERSARY_UUID "e56b3238-0af0-4502-82ad-473dc6f3d9f9" 34 | #define BIRTHDAY_UUID "0ceae7bf-16ed-46f5-b6c3-17dc8439d574" 35 | #define WIFI_UUID "da75e597-b36a-444c-b771-9b058fb1fcd8" 36 | #define ANIM_UUID "8e952b12-dd4e-4c78-8bc6-810dc018c22b" 37 | 38 | 39 | #define SERVICE2_UUID "5738a36e-8e61-48cf-ad73-06fce0d5843d" // UART service UUID 40 | #define CHARACTERISTIC_UUID_RX "836a72f2-c6c7-44c9-b0ea-ce09cc23038f" //通用接收特性 41 | #define CHARACTERISTIC_UUID_TX "a0ea1b56-2f4d-464c-8de8-d523fbb9b284" //通用发送特性 42 | 43 | void startAdvertising(); 44 | 45 | class MyServerCallbacks : public BLEServerCallbacks 46 | { 47 | void onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) 48 | { 49 | BLEConnected = true; 50 | setBLEInfo(); 51 | auto p = param->connect; 52 | BLEAddress address(p.remote_bda); 53 | BLEAddress peerAddress = BLEAddress(p.remote_bda); 54 | uint16_t conn_id = p.conn_id; 55 | Serial.print("已连接"); 56 | Serial.println(peerAddress.toString().c_str()); 57 | currentPage = SETTING; 58 | clearMatrix(); 59 | drawSuccess(6, 21, "BLE"); 60 | }; 61 | 62 | void onDisconnect(BLEServer *pServer) 63 | { 64 | BLEConnected = false; 65 | advertising = false; 66 | Serial.println("BLE断开连接"); 67 | startAdvertising(); 68 | } 69 | }; 70 | 71 | class MyCallbacks : public BLECharacteristicCallbacks 72 | { 73 | void onWrite(BLECharacteristic *pCharacteristic) 74 | { 75 | std::string rValue = pCharacteristic->getValue(); //接收信息 76 | BLEUUID characteristic_uuid = pCharacteristic->getUUID(); 77 | 78 | Serial.println(rValue.c_str()); //输出调试信息 79 | 80 | if (!rValue.empty()){ 81 | if (characteristic_uuid.equals(BLEUUID(BRIGHTNESS_UUID))) { 82 | Serial.println("亮度设置"); 83 | // 处理亮度设置 84 | if (rValue != "AUTO"){ 85 | brightModel = BRIGHT_MODEL_MANUAL; 86 | int rValueInt = std::stoi(rValue); // 将字符串转换为整数 87 | brightness = map(rValueInt, 1, 100, MIN_BRIGHTNESS, MAX_BRIGHTNESS); 88 | recordBrightness(); 89 | } else { 90 | brightModel = BRIGHT_MODEL_AUTO; 91 | clearBrightSampling(); 92 | recordBrightness(); 93 | } 94 | 95 | } else if (characteristic_uuid.equals(BLEUUID(COLOR_UUID))) { 96 | Serial.println("颜色设置"); 97 | // 处理颜色设置 98 | int r, g, b; 99 | sscanf(rValue.c_str(), "%d,%d,%d", &r, &g, &b); 100 | recordClockColor(r, g, b); 101 | } else if (characteristic_uuid.equals(BLEUUID(ANNIVERSARY_UUID))) { 102 | Serial.println("纪念日设置"); 103 | // 处理纪念日设置 104 | size_t pos = rValue.find('|'); 105 | if (pos != std::string::npos) { 106 | anniversaryOpen = rValue.substr(0, pos) == "1"; 107 | size_t pos2 = rValue.find(',', pos + 1); 108 | if (pos2 != std::string::npos) { 109 | anniversaryA = atol(rValue.substr(pos + 1, pos2 - pos - 1).c_str()); 110 | anniversaryB = atol(rValue.substr(pos2 + 1).c_str()); 111 | recordAnniversary(); 112 | } 113 | } 114 | } else if (characteristic_uuid.equals(BLEUUID(BIRTHDAY_UUID))) { 115 | Serial.println("生日设置"); 116 | // 处理生日设置 117 | size_t pos = rValue.find('|'); 118 | if (pos != std::string::npos) { 119 | birthdayOpen = rValue.substr(0, pos) == "1"; 120 | birthday = atol(rValue.substr(pos + 1).c_str()); 121 | recordBirthday(); 122 | } 123 | } else if (characteristic_uuid.equals(BLEUUID(WIFI_UUID))) { 124 | Serial.println("WIFI设置"); 125 | // 处理WIFI设置 126 | size_t pos = rValue.find('|'); 127 | if (pos != std::string::npos) { 128 | ssid = rValue.substr(0, pos).c_str(); 129 | pass = rValue.substr(pos + 1).c_str(); 130 | recordWifiConfig(); 131 | } 132 | } else if(characteristic_uuid.equals(BLEUUID(ANIM_UUID))) { 133 | Serial.println("动画设置"); 134 | // 处理动画设置 135 | if (rValue == "true") { 136 | timeModel = TIME_MODEL_ANIM; 137 | } else if (rValue == "false") { 138 | timeModel = TIME_MODEL_DIRECT; 139 | } 140 | recordAnim(); 141 | } else if (characteristic_uuid.equals(BLEUUID(CHARACTERISTIC_UUID_RX))) { 142 | Serial.println("RX接收到数据"); 143 | if (rValue[0] == 'T') { // 蓝牙对时 144 | try { 145 | std::string numberString = rValue.substr(1); // 获取从第二个字符开始的子字符串 146 | int timestamp = std::stoi(numberString); // 将子字符串转换为整数 147 | setenv("TZ", "CST-8", 1); // 设置时区 148 | tzset(); 149 | Serial.println(timestamp); 150 | struct timeval tv = {.tv_sec = timestamp}; 151 | if (settimeofday(&tv, NULL) == 0) { // 如果 settimeofday 返回非零值,表示设置时间失败 152 | RTCSuccess = true; 153 | } 154 | setRTCtime(timestamp); 155 | } catch (...) { 156 | // 捕获所有其他类型的异常 157 | Serial.println("处理对时数据失败"); 158 | } 159 | } 160 | pTxCharacteristic->setValue(rValue); 161 | pTxCharacteristic->notify(); 162 | // 处理接收到的数据 163 | } 164 | } 165 | } 166 | }; 167 | 168 | void initBLE() { 169 | BLEDevice::init("SmartMatrix"); 170 | BLEDevice::setPower(ESP_PWR_LVL_N24); // 设置蓝牙功耗级别,降低蓝牙功耗 171 | 172 | // 创建一个新的服务 173 | // 创建一个 BLE 服务 174 | pServer = BLEDevice::createServer(); 175 | pServer->setCallbacks(new MyServerCallbacks()); //设置回调 176 | MyCallbacks* pCallbacks = new MyCallbacks(); 177 | 178 | BLEService *pService2 = pServer->createService(SERVICE2_UUID); 179 | 180 | // 创建通用发送特性 181 | pTxCharacteristic = pService2->createCharacteristic( 182 | CHARACTERISTIC_UUID_TX, 183 | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_READ 184 | ); 185 | pTxCharacteristic->addDescriptor(new BLE2902()); 186 | 187 | // 创建通用接收特性 188 | pRxCharacteristic = pService2->createCharacteristic( 189 | CHARACTERISTIC_UUID_RX, 190 | BLECharacteristic::PROPERTY_WRITE 191 | ); 192 | pRxCharacteristic->setCallbacks(pCallbacks); //设置回调 193 | 194 | BLEService *pService = pServer->createService(SERVICE1_UUID); 195 | 196 | // 创建亮度特性 197 | pCharacteristicBrightness = pService->createCharacteristic( 198 | BRIGHTNESS_UUID, 199 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE 200 | ); 201 | pCharacteristicBrightness->setCallbacks(pCallbacks); 202 | 203 | // 创建颜色特性 204 | pCharacteristicColor = pService->createCharacteristic( 205 | COLOR_UUID, 206 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE 207 | ); 208 | pCharacteristicColor->setCallbacks(pCallbacks); 209 | 210 | // 创建纪念日特性 211 | pCharacteristicAnniversary = pService->createCharacteristic( 212 | ANNIVERSARY_UUID, 213 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE 214 | ); 215 | pCharacteristicAnniversary->setCallbacks(pCallbacks); 216 | 217 | // 创建生日特性 218 | pCharacteristicBirthday = pService->createCharacteristic( 219 | BIRTHDAY_UUID, 220 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE 221 | ); 222 | pCharacteristicBirthday->setCallbacks(pCallbacks); 223 | 224 | // 创建WIFI特性 225 | pCharacteristicWIFI = pService->createCharacteristic( 226 | WIFI_UUID, 227 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE 228 | ); 229 | pCharacteristicWIFI->setCallbacks(pCallbacks); 230 | 231 | // 创建动画特性 232 | pCharacteristicAnim = pService->createCharacteristic( 233 | ANIM_UUID, 234 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE 235 | ); 236 | pCharacteristicAnim->setCallbacks(pCallbacks); 237 | 238 | pService2->start(); // 开始服务2 239 | pService->start(); // 开始服务1 240 | Serial.println("BLE初始化完成"); 241 | } 242 | 243 | // 开启蓝牙广播 244 | void startAdvertising() { 245 | if (!advertising) { 246 | pServer->getAdvertising()->start(); 247 | advertising = true; 248 | Serial.println("蓝牙开始广播"); 249 | } 250 | } 251 | 252 | // 停止蓝牙广播 253 | void stopAdvertising() { 254 | if (advertising){ 255 | pServer->getAdvertising()->stop(); 256 | advertising = false; 257 | Serial.println("蓝牙停止广播"); 258 | } 259 | } 260 | 261 | // 断开蓝牙连接 262 | void disconnectBLE() { 263 | if (BLEConnected) { 264 | pServer->disconnect(pServer->getConnId()); 265 | BLEConnected = false; 266 | } 267 | } 268 | 269 | // 发送数据 270 | void sendBLEData(String data) { 271 | if (BLEConnected) { 272 | pTxCharacteristic->setValue(data.c_str()); 273 | pTxCharacteristic->notify(); 274 | } 275 | } 276 | 277 | // 设置亮度特征值 278 | void setBLEBrightness(int brightness) { 279 | if (BLEConnected){ 280 | if (brightModel == BRIGHT_MODEL_MANUAL) { 281 | int rValueInt = map(brightness, MIN_BRIGHTNESS, MAX_BRIGHTNESS, 1, 100); 282 | std::string rValue = std::to_string(rValueInt); 283 | pCharacteristicBrightness->setValue(rValue); 284 | sendBLEData("Brightness"); 285 | }else if (brightModel == BRIGHT_MODEL_AUTO){ 286 | pCharacteristicBrightness->setValue("AUTO"); 287 | sendBLEData("Brightness"); 288 | } 289 | } 290 | } 291 | 292 | // 设置颜色特征值 293 | void setBLEColor(int r, int g, int b) { 294 | if (BLEConnected){ 295 | std::string rValue = std::to_string(r) + "," + std::to_string(g) + "," + std::to_string(b); 296 | pCharacteristicColor->setValue(rValue.c_str()); 297 | sendBLEData("Color"); 298 | } 299 | } 300 | 301 | // 设置纪念日特征值 302 | void setBLEAnniversary(bool open, unsigned long anniversaryA, unsigned long anniversaryB) { 303 | if (BLEConnected){ 304 | std::string rValue = std::to_string(open) + "|" + std::to_string(anniversaryA) + "," + std::to_string(anniversaryB); 305 | pCharacteristicAnniversary->setValue(rValue); 306 | sendBLEData("Anniversary"); 307 | } 308 | } 309 | 310 | // 设置生日特征值 311 | void setBLEBirthday(bool open, unsigned long birthday) { 312 | if (BLEConnected){ 313 | std::string rValue = std::to_string(open) + "|" + std::to_string(birthday); 314 | pCharacteristicBirthday->setValue(rValue); 315 | sendBLEData("Birthday"); 316 | } 317 | } 318 | 319 | // 设置WIFI特征值 320 | void setBLEWIFI(String ssid, String pass) { 321 | if (BLEConnected){ 322 | std::string rValue = std::string(ssid.c_str()) + "|" + std::string(pass.c_str()); 323 | pCharacteristicWIFI->setValue(rValue); 324 | sendBLEData("WIFI"); 325 | } 326 | } 327 | 328 | // 设置时间动画特征值 329 | void setBLEAnim(int mode) { 330 | if (BLEConnected){ 331 | if (mode == TIME_MODEL_ANIM){ 332 | pCharacteristicAnim->setValue("true"); 333 | } else if (mode == TIME_MODEL_DIRECT){ 334 | pCharacteristicAnim->setValue("false"); 335 | } 336 | sendBLEData("Anim"); 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /BLE.h: -------------------------------------------------------------------------------- 1 | #ifndef BLE_H 2 | #define BLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "common.h" 10 | 11 | extern bool BLEConnected; 12 | extern bool advertising; 13 | 14 | void initBLE(); 15 | void startAdvertising(); 16 | void stopAdvertising(); 17 | void disconnectBLE(); 18 | void sendBLEData(String data); 19 | void setBLEBrightness(int brightness); 20 | void setBLEColor(int r, int g, int b); 21 | void setBLEAnniversary(bool open, unsigned long anniversaryA, unsigned long anniversaryB); 22 | void setBLEBirthday(bool open, unsigned long birthday); 23 | void setBLEWIFI(String ssid, String pass); 24 | void setBLEAnim(int anim); 25 | 26 | 27 | #endif // BLE_H -------------------------------------------------------------------------------- /DS1302.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "DS1302.h" 3 | #include 4 | 5 | namespace { 6 | 7 | enum Register { 8 | kSecondReg = 0, 9 | kMinuteReg = 1, 10 | kHourReg = 2, 11 | kDateReg = 3, 12 | kMonthReg = 4, 13 | kDayReg = 5, 14 | kYearReg = 6, 15 | kWriteProtectReg = 7, 16 | 17 | // The RAM register space follows the clock register space. 18 | kRamAddress0 = 32 19 | }; 20 | 21 | enum Command { 22 | kClockBurstRead = 0xBF, 23 | kClockBurstWrite = 0xBE, 24 | kRamBurstRead = 0xFF, 25 | kRamBurstWrite = 0xFE 26 | }; 27 | 28 | // Establishes and terminates a three-wire SPI session. 29 | class SPISession { 30 | public: 31 | SPISession(const int ce_pin, const int io_pin, const int sclk_pin) 32 | : ce_pin_(ce_pin), io_pin_(io_pin), sclk_pin_(sclk_pin) { 33 | digitalWrite(sclk_pin_, LOW); 34 | digitalWrite(ce_pin_, HIGH); 35 | delayMicroseconds(4); // tCC 36 | } 37 | ~SPISession() { 38 | digitalWrite(ce_pin_, LOW); 39 | delayMicroseconds(4); // tCWH 40 | } 41 | 42 | private: 43 | const int ce_pin_; 44 | const int io_pin_; 45 | const int sclk_pin_; 46 | }; 47 | 48 | // Returns the decoded decimal value from a binary-coded decimal (BCD) byte. 49 | // Assumes 'bcd' is coded with 4-bits per digit, with the tens place digit in 50 | // the upper 4 MSBs. 51 | uint8_t bcdToDec(const uint8_t bcd) { 52 | return (10 * ((bcd & 0xF0) >> 4) + (bcd & 0x0F)); 53 | } 54 | 55 | // Returns the binary-coded decimal of 'dec'. Inverse of bcdToDec. 56 | uint8_t decToBcd(const uint8_t dec) { 57 | const uint8_t tens = dec / 10; 58 | const uint8_t ones = dec % 10; 59 | return (tens << 4) | ones; 60 | } 61 | 62 | // Returns the hour in 24-hour format from the hour register value. 63 | uint8_t hourFromRegisterValue(const uint8_t value) { 64 | uint8_t adj; 65 | if (value & 128) // 12-hour mode 66 | adj = 12 * ((value & 32) >> 5); 67 | else // 24-hour mode 68 | adj = 10 * ((value & (32 + 16)) >> 4); 69 | return (value & 15) + adj; 70 | } 71 | 72 | } // namespace 73 | 74 | DS1302::DS1302(const uint8_t ce_pin, const uint8_t io_pin, 75 | const uint8_t sclk_pin) { 76 | ce_pin_ = ce_pin; 77 | io_pin_ = io_pin; 78 | sclk_pin_ = sclk_pin; 79 | 80 | digitalWrite(ce_pin, LOW); 81 | pinMode(ce_pin, OUTPUT); 82 | pinMode(io_pin, INPUT); 83 | digitalWrite(sclk_pin, LOW); 84 | pinMode(sclk_pin, OUTPUT); 85 | } 86 | 87 | void DS1302::writeOut(const uint8_t value, bool readAfter) { 88 | pinMode(io_pin_, OUTPUT); 89 | 90 | for (int i = 0; i < 8; ++i) { 91 | digitalWrite(io_pin_, (value >> i) & 1); 92 | delayMicroseconds(1); 93 | digitalWrite(sclk_pin_, HIGH); 94 | delayMicroseconds(1); 95 | 96 | if (readAfter && i == 7) { 97 | // We're about to read data -- ensure the pin is back in input mode 98 | // before the clock is lowered. 99 | pinMode(io_pin_, INPUT); 100 | } else { 101 | digitalWrite(sclk_pin_, LOW); 102 | delayMicroseconds(1); 103 | } 104 | } 105 | } 106 | 107 | uint8_t DS1302::readIn() { 108 | uint8_t input_value = 0; 109 | uint8_t bit = 0; 110 | pinMode(io_pin_, INPUT); 111 | 112 | // Bits from the DS1302 are output on the falling edge of the clock 113 | // cycle. This is called after readIn (which will leave the clock low) or 114 | // writeOut(..., true) (which will leave it high). 115 | for (int i = 0; i < 8; ++i) { 116 | digitalWrite(sclk_pin_, HIGH); 117 | delayMicroseconds(1); 118 | digitalWrite(sclk_pin_, LOW); 119 | delayMicroseconds(1); 120 | 121 | bit = digitalRead(io_pin_); 122 | input_value |= (bit << i); // Bits are read LSB first. 123 | } 124 | 125 | return input_value; 126 | } 127 | 128 | uint8_t DS1302::readRegister(const uint8_t reg) { 129 | const SPISession s(ce_pin_, io_pin_, sclk_pin_); 130 | 131 | const uint8_t cmd_byte = (0x81 | (reg << 1)); 132 | writeOut(cmd_byte, true); 133 | return readIn(); 134 | } 135 | 136 | void DS1302::writeRegister(const uint8_t reg, const uint8_t value) { 137 | const SPISession s(ce_pin_, io_pin_, sclk_pin_); 138 | 139 | const uint8_t cmd_byte = (0x80 | (reg << 1)); 140 | writeOut(cmd_byte); 141 | writeOut(value); 142 | } 143 | 144 | void DS1302::writeProtect(const bool enable) { 145 | writeRegister(kWriteProtectReg, (enable << 7)); 146 | } 147 | 148 | void DS1302::halt(const bool enable) { 149 | uint8_t sec = readRegister(kSecondReg); 150 | sec &= ~(1 << 7); 151 | sec |= (enable << 7); 152 | writeRegister(kSecondReg, sec); 153 | } 154 | 155 | tm DS1302::time() { 156 | const SPISession s(ce_pin_, io_pin_, sclk_pin_); 157 | 158 | tm t; 159 | writeOut(kClockBurstRead, true); 160 | t.tm_sec = bcdToDec(readIn() & 0x7F); 161 | t.tm_min = bcdToDec(readIn()); 162 | t.tm_hour = hourFromRegisterValue(readIn()); 163 | t.tm_mday = bcdToDec(readIn()); 164 | t.tm_mon = bcdToDec(readIn()) - 1; // tm_mon is 0-11 165 | t.tm_wday = bcdToDec(readIn()) - 1; // tm_wday is 0-6 166 | t.tm_year = 100 + bcdToDec(readIn()); // tm_year is years since 1900 167 | return t; 168 | } 169 | 170 | void DS1302::time(const tm t) { 171 | // We want to maintain the Clock Halt flag if it is set. 172 | const uint8_t ch_value = readRegister(kSecondReg) & 0x80; 173 | 174 | const SPISession s(ce_pin_, io_pin_, sclk_pin_); 175 | 176 | writeOut(kClockBurstWrite); 177 | writeOut(ch_value | decToBcd(t.tm_sec)); 178 | writeOut(decToBcd(t.tm_min)); 179 | writeOut(decToBcd(t.tm_hour)); 180 | writeOut(decToBcd(t.tm_mday)); 181 | writeOut(decToBcd(t.tm_mon + 1)); // tm_mon is 0-11 182 | writeOut(decToBcd(t.tm_wday + 1)); // tm_wday is 0-6 183 | writeOut(decToBcd(t.tm_year - 100)); // tm_year is years since 1900 184 | // All clock registers *and* the WP register have to be written for the time 185 | // to be set. 186 | writeOut(0); // Write protection register. 187 | } 188 | 189 | // Rest of the code remains the same... 190 | 191 | void DS1302::writeRam(const uint8_t address, const uint8_t value) { 192 | if (address >= kRamSize) { 193 | return; 194 | } 195 | 196 | writeRegister(kRamAddress0 + address, value); 197 | } 198 | 199 | uint8_t DS1302::readRam(const uint8_t address) { 200 | if (address >= kRamSize) { 201 | return 0; 202 | } 203 | 204 | return readRegister(kRamAddress0 + address); 205 | } 206 | 207 | void DS1302::writeRamBulk(const uint8_t* const data, int len) { 208 | if (len <= 0) { 209 | return; 210 | } 211 | if (len > kRamSize) { 212 | len = kRamSize; 213 | } 214 | 215 | const SPISession s(ce_pin_, io_pin_, sclk_pin_); 216 | 217 | writeOut(kRamBurstWrite); 218 | for (int i = 0; i < len; ++i) { 219 | writeOut(data[i]); 220 | } 221 | } 222 | 223 | void DS1302::readRamBulk(uint8_t* const data, int len) { 224 | if (len <= 0) { 225 | return; 226 | } 227 | if (len > kRamSize) { 228 | len = kRamSize; 229 | } 230 | 231 | const SPISession s(ce_pin_, io_pin_, sclk_pin_); 232 | 233 | writeOut(kRamBurstRead, true); 234 | for (int i = 0; i < len; ++i) { 235 | data[i] = readIn(); 236 | } 237 | } -------------------------------------------------------------------------------- /DS1302.h: -------------------------------------------------------------------------------- 1 | // Interface for the DS1302 timekeeping chip. 2 | // 3 | // Copyright (c) 2009, Matt Sparks 4 | // All rights reserved. 5 | // 6 | // Distributed under the 2-clause BSD license. 7 | #ifndef DS1302_H_ 8 | #define DS1302_H_ 9 | 10 | #include 11 | 12 | // Class representing a particular time and date. 13 | class DS1302 { 14 | public: 15 | // Size of the DS1302's RAM storage, in bytes. 16 | static const int kRamSize = 31; 17 | 18 | DS1302() = default; 19 | 20 | // Prepares to interface with the chip on the given I/O pins. 21 | // 22 | // Args: 23 | // ce_pin: CE pin number 24 | // io_pin: IO pin number 25 | // sclk_pin: SCLK pin number 26 | DS1302(uint8_t ce_pin, uint8_t io_pin, uint8_t sclk_pin); 27 | 28 | // Enables or disables write protection on the chip. 29 | // 30 | // While write protection is enabled, all attempts to write to the chip (e.g., 31 | // setting the time) will have no effect. 32 | // 33 | // The DS1302 datasheet does not define the initial state of write protection, 34 | // so this method should be called at least once when initializing a device 35 | // for the first time. 36 | // 37 | // Args: 38 | // enable: true to enable write protection. 39 | void writeProtect(bool enable); 40 | 41 | // Sets or clears Clock Halt flag on the chip. 42 | // 43 | // Enabling the Clock Halt flag disables the DS1302's clock oscillator and 44 | // places it into a low-power standby mode. While in this mode, the time does 45 | // not progress. The time can still be read from the chip while it is halted, 46 | // however. 47 | // 48 | // The DS1302 datasheet does not define the initial state of the Clock Halt 49 | // flag, so this method should be called at least once when initializing a 50 | // device for the first time. 51 | // 52 | // Args: 53 | // value: true to set halt flag, false to clear. 54 | void halt(bool value); 55 | 56 | // Returns the current time and date in a struct tm. 57 | // 58 | // Returns: 59 | // Current time as struct tm. 60 | struct tm time(); 61 | 62 | // Sets the time and date to the instant specified in a given struct tm. 63 | // 64 | // The time will not be set if write protection is enabled on the 65 | // chip. Setting the time with this function has no effect on the Clock Halt 66 | // flag. 67 | // 68 | // Args: 69 | // t: Time instant. 70 | void time(struct tm t); 71 | 72 | // Writes a byte to RAM. 73 | // 74 | // The DS1302 has 31 bytes (kRamSize) of static RAM that can store arbitrary 75 | // data as long as the device has power. 76 | // 77 | // Writes to invalid addresses have no effect. 78 | // 79 | // Args: 80 | // address: RAM address in [0, kRamSize). 81 | // value: Byte to write to the RAM address. 82 | void writeRam(uint8_t address, uint8_t value); 83 | 84 | // Reads a byte from RAM. 85 | // 86 | // Reads of invalid addresses return 0. 87 | // 88 | // Args: 89 | // address: RAM address in [0, kRamSize). 90 | // 91 | // Returns: 92 | // Byte from RAM or 0 if the address is invalid. 93 | uint8_t readRam(uint8_t address); 94 | 95 | // Writes 'len' bytes into RAM from '*data', starting at RAM address 0. 96 | // 97 | // Args: 98 | // data: Input data. 99 | // len: Number of bytes of '*data' to read. Must be <= kRamSize. 100 | void writeRamBulk(const uint8_t* data, int len); 101 | 102 | // Reads 'len' bytes from RAM into '*data', starting at RAM address 0. 103 | // 104 | // Args: 105 | // data: Output data. 106 | // len: Number of bytes of RAM to read. Must be <= kRamSize. 107 | void readRamBulk(uint8_t* data, int len); 108 | 109 | // Reads register byte value. 110 | // 111 | // Args: 112 | // reg: register number 113 | // 114 | // Returns: 115 | // register value 116 | uint8_t readRegister(uint8_t reg); 117 | 118 | // Writes byte into register. 119 | // 120 | // Args: 121 | // reg: register number 122 | // value: byte to write 123 | void writeRegister(uint8_t reg, uint8_t value); 124 | 125 | private: 126 | uint8_t ce_pin_; 127 | uint8_t io_pin_; 128 | uint8_t sclk_pin_; 129 | 130 | // Shifts out a value to the IO pin. 131 | // 132 | // Side effects: sets io_pin_ as OUTPUT. 133 | // 134 | // Args: 135 | // value: byte to shift out 136 | // readAfter: whether this will be followed by a read; if so, it 137 | // will leave io_pin_ as INPUT. 138 | void writeOut(uint8_t value, bool readAfter = false); 139 | 140 | // Reads in a byte from the IO pin. 141 | // 142 | // Side effects: sets io_pin_ to INPUT. 143 | // 144 | // Returns: 145 | // byte read in 146 | uint8_t readIn(); 147 | }; 148 | 149 | #endif // DS1302_H_ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmartMatrix Arduino 2 | 3 | 该项目为 SmartMatrix 像素时钟的 Arduino 端,基于B站UP主 @大聪明的二手脑袋 的 EasyMatrix 项目修改而来。 4 | 5 | 相比原版新增功能: 6 | 7 | - 添加了蓝牙配置功能,可使用小程序调整亮度、时钟颜色、动画、对时等; 8 | 9 | - 添加外置 DS1302 时钟模块,断电后也可以保持时间,不用每次启动时重新对时; 10 | 11 | - 删除原版 AP 模式配网相关代码,减少不必要的占用。 12 | 13 | - 另外还有其它有意思的隐藏功能,期待各位的发现。 14 | 15 | 16 | 17 | 该项目配套的小程序源码:[Frspble/SmartMatrix (github.com)](https://github.com/Frspble/SmartMatrix) -------------------------------------------------------------------------------- /RTC.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "DS1302.h" 4 | #include "common.h" 5 | #include "time.h" 6 | 7 | DS1302 rtc; 8 | 9 | tm create_tm(int year, int month, int day, int hour, int minute, int second, int week) { 10 | tm t; 11 | t.tm_year = year - 1900; 12 | t.tm_mon = month - 1; 13 | t.tm_mday = day; 14 | t.tm_hour = hour; 15 | t.tm_min = minute; 16 | t.tm_sec = second; 17 | t.tm_wday = week; 18 | return t; 19 | time_t currentTime = time_t(); // 获取当前时间戳 20 | } 21 | 22 | // 设置系统时间 23 | void setSysTime(int year, int month, int day, int hour, int minute, int second) { 24 | tm t = create_tm(year, month, day, hour, minute, second, 0); 25 | time_t t_of_day = mktime(&t); 26 | struct timeval tv = {.tv_sec = t_of_day}; 27 | settimeofday(&tv, NULL); 28 | } 29 | 30 | // 将RTC时间转换为系统时间 31 | bool RTCtoSysTime() { 32 | setenv("TZ", "CST-8", 1); // 设置时区 33 | tzset(); 34 | tm t = rtc.time(); 35 | if (t.tm_year < 124 || t.tm_year > 199) { 36 | return false; 37 | } 38 | time_t t_of_day = mktime(&t); 39 | struct timeval tv = {.tv_sec = t_of_day}; 40 | settimeofday(&tv, NULL); 41 | return true; 42 | } 43 | 44 | // 初始化RTC 45 | void initRTC() { 46 | rtc = DS1302(RTC_RST, RTC_DAT, RTC_CLK); 47 | rtc.writeProtect(true); 48 | rtc.halt(false); 49 | } 50 | 51 | // 设置RTC时间 52 | void setRTCtime(int year, int month, int day, int hour, int minute, int second, int week) { 53 | rtc.writeProtect(false); 54 | tm t = create_tm(year, month, day, hour, minute, second, week); 55 | rtc.time(t); 56 | rtc.halt(false); 57 | rtc.writeProtect(true); 58 | } 59 | 60 | // 使用 tm 结构设置 RTC 时间 61 | void setRTCtime(tm t) { 62 | rtc.writeProtect(false); 63 | rtc.time(t); 64 | rtc.halt(false); 65 | rtc.writeProtect(true); 66 | } 67 | 68 | // 使用时间戳设置 RTC 时间 69 | void setRTCtime(int timestamp) { 70 | // 将时间戳转换为 tm 结构 71 | std::time_t rawtime = timestamp; 72 | std::tm *timeinfo = std::localtime(&rawtime); 73 | // 调用已经定义的 setRTCtime(tm t) 函数来设置 RTC 时间 74 | setRTCtime(*timeinfo); 75 | } 76 | 77 | // 获取RTC时间 78 | tm getRTCtime() { 79 | tm t = rtc.time(); 80 | return t; 81 | } 82 | 83 | // 打印RTC时间 84 | void printTime() { 85 | tm t = rtc.time(); 86 | Serial.println(&t, "%F %A %T"); 87 | delay(1000); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /RTC.h: -------------------------------------------------------------------------------- 1 | #ifndef RTC_H 2 | #define RTC_H 3 | 4 | void printTime(); 5 | void initRTC(); 6 | void setSysTime(int year, int month, int day, int hour, int minute, int second); 7 | bool RTCtoSysTime(); 8 | void setRTCtime(int year, int month, int day, int hour, int minute, int second, int week); 9 | void setRTCtime(tm t); 10 | void setRTCtime(int timestamp); 11 | 12 | #endif -------------------------------------------------------------------------------- /SmartMatrix.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | #include "preferencesUtil.h" 4 | #include "light.h" 5 | #include "buzzer.h" 6 | #include "net.h" 7 | #include "task.h" 8 | #include "BLE.h" 9 | #include "RTC.h" 10 | 11 | /** 12 | SmartMatrix像素时钟 版本 1.0 13 | 14 | 本项目基于 B 站 UP 主 "@大聪明的二手脑袋" 的 EasyMatrix v1.4 修改而来。 15 | 16 | 相比原版新增功能: 17 | 添加了蓝牙配置功能,可使用小程序调整亮度、时钟颜色、动画、对时等; 18 | 添加外置 DS1302 时钟模块,断电后也可以保持时间,不用每次启动时重新对时; 19 | 添加生日功能,生日当天会有特殊提醒,该功能默认关闭,可在小程序中进行开关和设置日期; 20 | 新增两首生日歌曲,生日当天会随机播放一首,并且将加入了闹钟列表,可作为闹钟铃声使用; 21 | 添加纪念日界面,该功能默认关闭,同样可在小程序中进行开关和设置日期。 22 | 删除原版 AP 模式配网相关代码,减少不必要的占用。 23 | 24 | 注意: 25 | 烧录前请将 Erase All Flash 选项选为 Enable,将 Partition Scheme 设置为 Huge App,否则程序可能会有错误。 26 | (所有功能均为现学现写,可能存在冗余代码或逻辑错误,欢迎各位指正) 27 | */ 28 | 29 | unsigned long prevDisplay = 0; 30 | unsigned long prevSampling = 0; 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | // 初始外置RTC 35 | initRTC(); 36 | // 从NVS中获取信息 37 | getInfos(); 38 | // 初始化蓝牙 39 | initBLE(); 40 | // 初始化点阵屏(包含拾音器) 41 | initMatrix(); 42 | // 创建加载动画任务 43 | createShowTextTask("START"); 44 | // 初始化按键 45 | btnInit(); 46 | Serial.println("各外设初始化成功"); 47 | 48 | //先判断ssid是否为空 49 | if (ssid == "") { 50 | // 尝试使用RTC时间 51 | if (RTCtoSysTime()) { 52 | if (showTextTask != NULL) { // 删除加载动画任务 53 | delay(3000); // 延迟3秒,作为开机动画 54 | vTaskDelete(showTextTask); 55 | showTextTask = NULL; 56 | } 57 | RTCSuccess = true; 58 | currentPage = TIME; 59 | startAdvertising(); 60 | } else { 61 | // RTC时间失败后进入蓝牙设置 62 | apConfig = true; 63 | } 64 | } else { 65 | // ssid不为空,先执行WIFI对时 66 | connectWiFi(5); 67 | if(wifiConnected){ 68 | checkTime(5); 69 | disConnectWifi(); 70 | if (RTCSuccess) { 71 | if (showTextTask != NULL) { 72 | vTaskDelete(showTextTask); 73 | showTextTask = NULL; 74 | } 75 | currentPage = TIME; 76 | } else { 77 | // WIFI对时失败后使用RTC时间 78 | if (RTCtoSysTime()) { 79 | RTCSuccess = true; 80 | currentPage = TIME; 81 | startAdvertising(); 82 | } else { 83 | // RTC时间使用失败再进入蓝牙设置 84 | apConfig = true; 85 | } 86 | } 87 | } else { 88 | // WIFI连接失败,尝试使用RTC时间 89 | if (RTCtoSysTime()) { 90 | RTCSuccess = true; 91 | currentPage = TIME; 92 | startAdvertising(); 93 | } else { 94 | // RTC时间使用失败再进入蓝牙设置 95 | apConfig = true; 96 | } 97 | } 98 | } 99 | 100 | 101 | if (apConfig){ 102 | if (showTextTask != NULL) { 103 | vTaskDelete(showTextTask); 104 | showTextTask = NULL; 105 | delay(300); 106 | } 107 | createShowTextTask("BLE"); 108 | startAdvertising(); 109 | currentPage = SETTING; 110 | } else{ 111 | // 开启对时任务 112 | startTickerCheckTime(); 113 | } 114 | 115 | // 如果闹钟开启,设置闹钟倒计时 116 | if(clockOpen){ 117 | startTickerClock(getClockRemainSeconds()); 118 | } 119 | } 120 | 121 | void loop() { 122 | watchBtn(); 123 | if(brightModel == BRIGHT_MODEL_AUTO && ((millis() - prevSampling) >= 1000 || prevSampling > millis())){ 124 | brightSamplingValue+=analogRead(LIGHT_ADC); 125 | brightSamplingTime++; 126 | prevSampling = millis(); 127 | if(brightSamplingTime >= BRIGHT_SAMPLING_TIMES){ // 每轮采样N次重新计算一次亮度值 128 | calculateBrightnessValue(); 129 | clearBrightSampling(); 130 | } 131 | } 132 | if(isCheckingTime){ // 对时中 133 | Serial.println("开始对时"); 134 | long start = millis(); // 记录开始对时的时间 135 | // 绘制对时提示文字 136 | // drawCheckTimeText(); 137 | drawText(4, 6, "TIME..."); // 个人不喜欢对时的提示文字,改为 TIME... 138 | // 执行对时逻辑 139 | checkTimeTicker(); 140 | // 将对时标志置为false 141 | isCheckingTime = false; 142 | // 让整个对时过程持续超过4秒,不然时间太短,提示文字一闪而过,让人感觉鬼畜了 143 | while((millis() - start) < 4000){ 144 | delay(200); 145 | } 146 | // 清屏 147 | clearMatrix(); 148 | Serial.println("结束对时"); 149 | // 结束对时后,重新绘制之前的页面 150 | if(currentPage == CLOCK){ 151 | drawClock(); 152 | }else if(currentPage == BRIGHT){ 153 | drawBright(); 154 | }else if(currentPage == ANIM){ 155 | lightedCount = 0; 156 | memset(matrixArray,0,sizeof(matrixArray)); 157 | } 158 | }else{ 159 | switch(currentPage){ 160 | case SETTING: // 配置页面 161 | if (!apConfig){ // 如果不是启动时蓝牙配置模式,就延迟3秒再切换时间界面,保证提示文字时长 162 | delay(3000); 163 | if (showTextTask != NULL) { 164 | vTaskDelete(showTextTask); 165 | showTextTask = NULL; 166 | } 167 | clearMatrix(); 168 | currentPage = TIME; 169 | } 170 | if (BLEConnected){ 171 | if (showTextTask != NULL) { 172 | vTaskDelete(showTextTask); 173 | showTextTask = NULL; 174 | } 175 | if (RTCSuccess){ 176 | clearMatrix(); 177 | currentPage = TIME; 178 | } 179 | } 180 | break; 181 | case TIME: // 时钟页面 182 | if((millis() - prevDisplay) >= 50 || prevDisplay > millis()){ 183 | // 绘制时间 184 | drawTime(); 185 | prevDisplay = millis(); 186 | } 187 | break; 188 | case RHYTHM: // 节奏灯页面 189 | drawRHYTHM(); 190 | break; 191 | case ANIM: // 动画页面 192 | drawAnim(); 193 | break; 194 | case CLOCK: // 闹钟设置页面 195 | drawClock(); 196 | break; 197 | case BRIGHT: // 亮度调节页面 198 | drawBright(); 199 | break; 200 | case ANNIVERSARY: // 纪念日页面 201 | drawAnniversary(); 202 | break; 203 | default: 204 | break; 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /buzzer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | #include "songs.h" 4 | #include "light.h" 5 | 6 | int songCount = 7; // 歌曲数量 7 | 8 | void playSong(int note[],int duration[],int legth,float timeCoefficient){ 9 | for (int thisNote = 0; thisNote < legth; thisNote++) { 10 | int noteDuration = 1000 / duration[thisNote]; 11 | tone(BUZZER, note[thisNote], noteDuration); 12 | int pauseBetweenNotes = noteDuration * timeCoefficient; 13 | delay(pauseBetweenNotes); 14 | noTone(BUZZER); 15 | } 16 | } 17 | 18 | void playSong(bool bell){ 19 | int songNum; 20 | if(bell){ 21 | songNum = clockBellNum; 22 | }else{ 23 | songNum = tmpClockBellNum; 24 | } 25 | switch(songNum){ 26 | case 0: 27 | playSong(SuperMario_note,SuperMario_duration,sizeof(SuperMario_note)/sizeof(int),2.1); 28 | break; 29 | case 1: 30 | playSong(AlwaysWithMe_note,AlwaysWithMe_duration,sizeof(AlwaysWithMe_note)/sizeof(int),3); 31 | break; 32 | case 2: 33 | playSong(DreamWedding_note,DreamWedding_duration,sizeof(DreamWedding_note)/sizeof(int),2.7); 34 | break; 35 | case 3: 36 | playSong(CastleInTheSky_note,CastleInTheSky_duration,sizeof(CastleInTheSky_note)/sizeof(int),2.8); 37 | break; 38 | case 4: 39 | playSong(Canon_note,Canon_duration,sizeof(Canon_note)/sizeof(int),1.8); 40 | break; 41 | case 5: 42 | playSong(HappyBirthday_note, HappyBirthday_duration,sizeof(HappyBirthday_note)/sizeof(int),1.2); 43 | break; 44 | case 6: 45 | playSong(HB_note,HB_duration,sizeof(HB_note)/sizeof(int), 1.5); 46 | break; 47 | default: 48 | break; 49 | } 50 | } 51 | 52 | // 随机挑选一首生日歌曲播放 53 | void playBirthdaySong(){ 54 | randomSeed(analogRead(RANDOM_SEED_PIN)); 55 | int songNumber = random(0, 2); 56 | if(songNumber == 0){ 57 | playSong(HappyBirthday_note, HappyBirthday_duration,sizeof(HappyBirthday_note)/sizeof(int),1.2); 58 | }else if(songNumber == 1){ 59 | playSong(HB_note,HB_duration,sizeof(HB_note)/sizeof(int), 1.5); 60 | } 61 | } -------------------------------------------------------------------------------- /buzzer.h: -------------------------------------------------------------------------------- 1 | #ifndef __BUZZER_H 2 | #define __BUZZER_H 3 | 4 | void playSong(bool bell); 5 | void playBirthdaySong(); 6 | extern int songCount; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_H 2 | #define __COMMON_H 3 | 4 | #include "img/timeAnim0.h" 5 | #include "img/timeAnim1.h" 6 | #include "img/timeAnim2.h" 7 | #include "img/timeAnim3.h" 8 | #include "img/timeAnim4.h" 9 | #include "img/timeAnim5.h" 10 | #include "img/timeAnim6.h" 11 | #include "img/timeAnim7.h" 12 | #include "img/timeAnim8.h" 13 | #include "img/animHack.h" 14 | #include "img/bell.h" 15 | #include "img/checkingTime.h" 16 | #include "img/success.h" 17 | #include "img/heart.h" 18 | #include "img/heart2.h" 19 | #include "img/cake.h" 20 | 21 | #define DATAPIN 6 // 灯珠矩阵输入引脚 22 | #define LIGHTCOUNT 256 // 灯珠个数 23 | #define BTN1 5 // 按钮1 24 | #define BTN2 4 // 按钮2 25 | #define BTN3 8 // 按钮3 26 | #define BUZZER 0 // 蜂鸣器 27 | #define AUDIO_IN_PIN 1 // 拾音器 28 | #define LIGHT_ADC 2 // 光敏电阻ADC引脚 29 | #define RTC_RST 12 // RTC复位引脚 30 | #define RTC_DAT 13 // RTC数据引脚 31 | #define RTC_CLK 3 // RTC时钟引脚 32 | #define RANDOM_SEED_PIN 11 // 随机数种子引脚 33 | 34 | #define BRIGHTNESS 40 // 默认亮度 35 | #define MAX_BRIGHTNESS 145 // 最大亮度 36 | #define MIN_BRIGHTNESS 5 // 最小亮度 37 | #define BRIGHTNESS_SPACING 20 //每次调节亮度的间隔大小 38 | 39 | #define MATRIX_SIDE 8 //每个矩阵的边长 40 | #define MATRIX_WIDTH 32 //矩阵总宽度 41 | #define MATRIX_COUNT 4 //矩阵数量 42 | 43 | #define NTP3 "ntp4.ntsc.ac.cn" 44 | #define NTP2 "ntp3.ict.ac.cn" 45 | #define NTP1 "ntp2.aliyun.com" 46 | 47 | #define SAMPLES 256 //采样个数 48 | #define SAMPLING_FREQ 10000 //采样频率 49 | #define AMPLITUDE 1000 //声音强度调整倍率(柱状高度倍率) 50 | #define NUM_BANDS 32 //频段个数 51 | #define NOISE 1770 //噪音 52 | #define BAR_WIDTH 1 //每个频段的宽度 53 | 54 | #define ONE_DAY_SECONDS 24 * 60 * 60 // 一天的秒数 55 | #define ANIM_INTERVAL 200 // 时钟页面每帧动画间隔(ms) 56 | #define TIME_CHECK_INTERVAL 18000 // NTP对时间隔(s), 18000秒即为5小时 57 | #define BRIGHT_SAMPLING_TIMES 1 // 每轮亮度采样次数 58 | 59 | // 时钟页面下的小页面 60 | const int TIME_H_M_S = 1; 61 | const int TIME_H_M = 2; 62 | const int TIME_DATE = 3; 63 | // 节奏灯页面下的小页面 64 | const int RHYTHM_MODEL1 = 1; 65 | const int RHYTHM_MODEL2 = 2; 66 | const int RHYTHM_MODEL3 = 3; 67 | const int RHYTHM_MODEL4 = 4; 68 | // 节奏灯频率模式 69 | const int RHYTHM_BANDS_MODEL1 = 1; 70 | const int RHYTHM_BANDS_MODEL2 = 2; 71 | // 动画页面下的小页面 72 | const int ANIM_MODEL1 = 1; 73 | const int ANIM_MODEL2 = 2; 74 | const int ANIM_MODEL3 = 3; 75 | // 闹钟页面选中项 76 | const int CLOCK_H = 1; 77 | const int CLOCK_M = 2; 78 | const int CLOCK_BELL = 3; 79 | // 亮度模式 80 | const int BRIGHT_MODEL_MANUAL = 1; 81 | const int BRIGHT_MODEL_AUTO = 2; 82 | // 纪念日页面子项 83 | const int ANNIVERSARY_A = 1; 84 | const int ANNIVERSARY_B = 2; 85 | // 时间跳变模式 86 | const int TIME_MODEL_DIRECT = 1; 87 | const int TIME_MODEL_ANIM = 2; 88 | 89 | // 定义页面枚举 SETTING-配网页面 TIME-时间页面 RHYTHM-节奏灯页面 ANIM-动画页面 CLOCK-闹钟设置页面 BRIGHT-亮度调节 ANNIVERSARY-纪念日界面 90 | enum CurrentPage{ 91 | SETTING, TIME, RHYTHM, ANIM, CLOCK, BRIGHT, ANNIVERSARY 92 | }; 93 | #endif 94 | 95 | -------------------------------------------------------------------------------- /img/animHack.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t animHack[] PROGMEM = { 3 | 0x1161, 4 | 0x0203, 5 | 0x238c, 6 | 0x6595, 7 | 0x9ebc, 8 | 0xffff 9 | }; -------------------------------------------------------------------------------- /img/bell.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint8_t bell[] PROGMEM = { 3 | 0x18, 4 | 0x2A, 5 | 0xC9, 6 | 0xC9, 7 | 0x2A, 8 | 0x18 9 | }; -------------------------------------------------------------------------------- /img/cake.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t cake [] PROGMEM = { 3 | // 'cake, 11x8px 4 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 5 | 0x0000, 0x0000, 0x0000, 0xf3e6, 0x0000, 0xf3e6, 0x0000, 0xf3e6, 0x0000, 0x0000, 0x0000, 6 | 0x0000, 0x0000, 0x0000, 0x960f, 0x0000, 0x960f, 0x0000, 0x960f, 0x0000, 0x0000, 0x0000, 7 | 0x0000, 0x0000, 0x0000, 0x960f, 0x0000, 0x960f, 0x0000, 0x960f, 0x0000, 0x0000, 0x0000, 8 | 0x0000, 0x0000, 0xfaec, 0xfaec, 0xfaec, 0xfaec, 0xfaec, 0xfaec, 0xfaec, 0x0000, 0x0000, 9 | 0x0000, 0xfaec, 0x767d, 0xfaec, 0x767d, 0xfaec, 0x767d, 0xfaec, 0x767d, 0xfaec, 0x0000, 10 | 0x0000, 0xfe4b, 0x767d, 0x767d, 0x767d, 0x767d, 0x767d, 0x767d, 0x767d, 0xfe4b, 0x0000, 11 | 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b, 0xfe4b 12 | }; 13 | 14 | // 像素画绘制:https://www.pixilart.com/draw 15 | // 像素代码生成:https://jlamch.net/MXChipWelcome/ -------------------------------------------------------------------------------- /img/checkingTime.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint8_t checkingTime[] PROGMEM = { 3 | 0x00,0x40,0x08,0x00, 4 | 0x00,0x40,0x08,0x00, 5 | 0x1D,0xE3,0xBC,0x00, 6 | 0x14,0x42,0x88,0x00, 7 | 0x09,0x43,0xA8,0x00, 8 | 0x14,0x42,0x88,0x00, 9 | 0x00,0x43,0x88,0x00, 10 | 0x00,0xC0,0x18,0x54 11 | }; 12 | 13 | // 00000000 01000000 00001000 00000000 14 | // 00000000 01000000 00001000 00000000 15 | // 00011101 11100011 10111100 00000000 16 | // 00010100 01000010 10001000 00000000 17 | // 00001001 01000011 10101000 00000000 18 | // 00010100 01000010 10001000 00000000 19 | // 00000000 01000011 10001000 00000000 20 | // 00000000 11000000 00011000 01010100 21 | 22 | 23 | -------------------------------------------------------------------------------- /img/heart.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t heart [] PROGMEM = { 3 | // 'heart, 9x8px 4 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 5 | 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0x0000, 0xfa6b, 0xfa6b, 0x0000, 0x0000, 6 | 0x0000, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0x0000, 7 | 0x0000, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0x0000, 8 | 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0x0000, 0x0000, 9 | 0x0000, 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0xfa6b, 0x0000, 0x0000, 0x0000, 10 | 0x0000, 0x0000, 0x0000, 0x0000, 0xfa6b, 0x0000, 0x0000, 0x0000, 0x0000, 11 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 12 | }; -------------------------------------------------------------------------------- /img/heart2.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t heart2 [] PROGMEM = { 3 | // 'heart2, 10x8px 4 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0x0000, 0xfa6b, 5 | 0xfa6b, 0x0000, 0x0000, 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0xfa6b, 0xfa6b, 0xe8e4, 0xfa6b, 0xe8e4, 0x0000, 0x0000, 0x0000, 0xfa6b, 6 | 0xfa6b, 0xfa6b, 0xe8e4, 0xe8e4, 0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0xe8e4, 0xe8e4, 0xe8e4, 0xe8e4, 7 | 0xe8e4, 0x0000, 0x0000, 0x0000, 0x0000, 0xfa6b, 0xfa6b, 0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 8 | 0xfa6b, 0x0000, 0xe8e4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 9 | }; -------------------------------------------------------------------------------- /img/success.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t Success [] PROGMEM = { 3 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x0000, 0x0000, 0x0000, 0x0000, 0x07e0, 5 | 0x0000, 0x0000, 0x0000, 0x07e0, 0x0000, 6 | 0x07e0, 0x0000, 0x07e0, 0x0000, 0x0000, 7 | 0x0000, 0x07e0, 0x0000, 0x0000, 0x0000 8 | }; -------------------------------------------------------------------------------- /img/timeAnim0.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim0[] PROGMEM = { 3 | 0x0801, 0x0001, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x0000, 0x0040, 0x0020, 0x0000, 0x0001, 0x0000, 0x0020, 0x0800, 0x0000, 0x0000, 0x0000, 5 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0020, 0x0000, 0x0040, 0x0020, 0x0000, 0x0000, 6 | 0x0020, 0x0000, 0x35bc, 0x359c, 0x357d, 0x357c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x35be, 0x2dbc, 0x0000, 0x2d9d, 0x357d, 0x2d9d, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0000, 0x25bd, 0x2d9e, 0x359c, 0x2d9d, 0x2dbc, 0x25bc, 0x0020, 0x0000, 0x359c, 0x359c, 9 | 0x0000, 0xffff, 0xffff, 0xffff, 0x2d9c, 0x357d, 0x35bd, 0x25dc, 0x359d, 0x2dbd, 0x0000, 10 | 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0x2dbc, 0x3d5d, 0x2d9c, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim1.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim1[] PROGMEM = { 3 | 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 5 | 0x0000, 0x0020, 0x0800, 0x871e, 0x0020, 0x0000, 0x0000, 0x0020, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0000, 0x2d9d, 0x357d, 0x359c, 0x357d, 0x0800, 0x0800, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x259c, 0x359e, 0x0000, 0x2dbc, 0x25bd, 0x2dbd, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0000, 0x2dbd, 0x2d9c, 0x357e, 0x2d9b, 0x25bc, 0x357d, 0x0020, 0x0000, 0x359c, 0x359c, 9 | 0x0020, 0xf7ff, 0xffff, 0xffdf, 0x359e, 0x2dbc, 0x2d5c, 0x2dbc, 0x359d, 0x2dbd, 0x0000, 10 | 0x0800, 0x0000, 0xf7ff, 0xffff, 0xfffe, 0x357f, 0x2ddc, 0x2d7d, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim2.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim2[] PROGMEM = { 3 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x0000, 0x0000, 0x0000, 0x76fe, 0x0801, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 5 | 0x0001, 0x0001, 0x0020, 0x86bf, 0x0040, 0x0000, 0x0000, 0x0040, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0020, 0x2d7d, 0x259d, 0x2d9c, 0x2d9c, 0x0040, 0x0800, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x35bd, 0x2dbd, 0x0800, 0x357d, 0x359e, 0x2d9c, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0020, 0x359c, 0x259c, 0x357d, 0x259d, 0x359d, 0x2dbe, 0x0020, 0x0000, 0x359c, 0x359c, 9 | 0x0000, 0xffff, 0xffff, 0xffff, 0x2dbc, 0x359c, 0x359e, 0x25bd, 0x359d, 0x2dbd, 0x0000, 10 | 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0x35bc, 0x2d9b, 0x359d, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim3.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim3[] PROGMEM = { 3 | 0x0800, 0x0020, 0x7edf, 0x0000, 0x86df, 0x0800, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x0000, 0x0020, 0x0000, 0x76ff, 0x0001, 0x0000, 0x0000, 0x0800, 0x0000, 0x0000, 0x0000, 5 | 0x0000, 0x0800, 0x0000, 0x871f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0000, 0x25de, 0x2d9d, 0x357d, 0x3d7c, 0x0020, 0x0800, 0x0000, 0x0000, 0x0020, 7 | 0x0801, 0x357d, 0x2dbd, 0x0000, 0x359b, 0x2ddd, 0x2d7d, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0000, 0x359c, 0x359e, 0x2dbc, 0x2dbd, 0x25bb, 0x259d, 0x0000, 0x0000, 0x359c, 0x359c, 9 | 0x0000, 0xffff, 0xffff, 0xffff, 0x2d7c, 0x357d, 0x35bb, 0x359d, 0x359d, 0x2dbd, 0x0000, 10 | 0x0020, 0x0000, 0xffff, 0xffff, 0xffff, 0x2dbc, 0x2d9d, 0x2d9c, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim4.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim4[] PROGMEM = { 3 | 0x0000, 0x771f, 0x7eff, 0x0000, 0x7eff, 0x869f, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x0800, 0x0000, 0x0000, 0x771f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 5 | 0x0020, 0x0801, 0x0000, 0x7f1f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0000, 0x359d, 0x2d7c, 0x357e, 0x2d9e, 0x0000, 0x0800, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x355d, 0x2dbc, 0x0000, 0x2d9c, 0x2dbd, 0x2d7c, 0x0021, 0x0000, 0x359d, 0x0000, 8 | 0x0020, 0x2dbd, 0x2d9c, 0x25bb, 0x35bd, 0x2ddc, 0x2dbb, 0x0000, 0x0000, 0x359c, 0x359c, 9 | 0x0000, 0xfffe, 0xffdf, 0xffff, 0x2d7d, 0x2d7d, 0x357d, 0x2dbd, 0x359d, 0x2dbd, 0x0000, 10 | 0x0000, 0x0000, 0xffff, 0xffdf, 0xffff, 0x2dbc, 0x2dbd, 0x2d9d, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim5.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim5[] PROGMEM = { 3 | 0x0000, 0x7eff, 0x7edf, 0x0000, 0x86df, 0x7edf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x76fe, 0x0000, 0x0000, 0x7efe, 0x0020, 0x0000, 0x76df, 0x0000, 0x0000, 0x0000, 0x0000, 5 | 0x0001, 0x0800, 0x0022, 0x7edf, 0x0020, 0x0020, 0x0001, 0x0000, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0000, 0x2dbc, 0x2dbd, 0x2d5c, 0x35bd, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x35be, 0x353d, 0x0000, 0x359e, 0x2d7c, 0x2d9c, 0x0021, 0x0000, 0x359d, 0x0000, 8 | 0x0001, 0x357b, 0x2d9d, 0x359d, 0x2d7c, 0x35be, 0x35bc, 0x0000, 0x0000, 0x359c, 0x359c, 9 | 0x0040, 0xffff, 0xffff, 0xf7ff, 0x35bd, 0x257c, 0x2dbc, 0x359d, 0x359d, 0x2dbd, 0x0000, 10 | 0x0000, 0x0000, 0xfffe, 0xffff, 0xffff, 0x2dbd, 0x2d9c, 0x2d9c, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim6.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim6[] PROGMEM = { 3 | 0x0000, 0x7edf, 0x7efe, 0x0800, 0x7f1f, 0x7ebf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x7eff, 0x0001, 0x0020, 0x0041, 0x0000, 0x0800, 0x76ff, 0x0020, 0x0000, 0x0000, 0x0000, 5 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0001, 0x2d9d, 0x3d9c, 0x2d7d, 0x2d7d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x357d, 0x2d9d, 0x0000, 0x2dbd, 0x359e, 0x259c, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0000, 0x2d5d, 0x2d9e, 0x359d, 0x355b, 0x35bc, 0x359f, 0x0001, 0x0000, 0x359c, 0x359c, 9 | 0x0020, 0xffff, 0xffff, 0xfffe, 0x2ddd, 0x355c, 0x2d9b, 0x35bc, 0x359d, 0x2dbd, 0x0000, 10 | 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0x25dd, 0x2d9d, 0x357c, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim7.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim7[] PROGMEM = { 3 | 0x0000, 0x76df, 0x0800, 0x0020, 0x0000, 0x76ff, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x7ede, 0x0001, 0x0020, 0x0800, 0x0000, 0x0000, 0x869f, 0x0001, 0x0000, 0x0000, 0x0000, 5 | 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0020, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0000, 0x359e, 0x259c, 0x35bd, 0x2d7e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x2d9e, 0x357c, 0x0020, 0x2d9b, 0x359c, 0x2d7d, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0020, 0x2d9d, 0x2d9d, 0x2dbd, 0x2d7c, 0x35bd, 0x2dbc, 0x0000, 0x0000, 0x359c, 0x359c, 9 | 0x0000, 0xffff, 0xffdf, 0xffff, 0x2dbe, 0x259c, 0x357d, 0x25bd, 0x359d, 0x2dbd, 0x0000, 10 | 0x0800, 0x0020, 0xffff, 0xffde, 0xffff, 0x2dbe, 0x355d, 0x359d, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /img/timeAnim8.h: -------------------------------------------------------------------------------- 1 | #include 2 | const uint16_t timeAnim8[] PROGMEM = { 3 | 0x0020, 0x0000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 4 | 0x76df, 0x0000, 0x0000, 0x0022, 0x0800, 0x0040, 0x86ff, 0x0001, 0x0000, 0x0000, 0x0000, 5 | 0x0020, 0x0000, 0x0000, 0x0000, 0x0040, 0x0020, 0x0000, 0x0001, 0x0020, 0x0000, 0x0000, 6 | 0x0000, 0x0020, 0x2dbd, 0x25bb, 0x35be, 0x2d7e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 7 | 0x0000, 0x2d7d, 0x35bd, 0x0000, 0x359c, 0x359b, 0x35bd, 0x0000, 0x0000, 0x359d, 0x0000, 8 | 0x0020, 0x25bc, 0x2d9d, 0x2dde, 0x2d7c, 0x2dbe, 0x259c, 0x0020, 0x0000, 0x359c, 0x359c, 9 | 0x0000, 0xffff, 0xffdf, 0xffff, 0x2dbd, 0x2d9b, 0x359e, 0x2dbb, 0x359d, 0x2dbd, 0x0000, 10 | 0x0020, 0x0800, 0xffff, 0xffff, 0xf7ff, 0x359d, 0x2d7c, 0x359e, 0x2d9d, 0x0000, 0x0000 11 | }; -------------------------------------------------------------------------------- /light.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "common.h" 8 | #include "preferencesUtil.h" 9 | #include "task.h" 10 | #include "buzzer.h" 11 | 12 | int clockColor[3]; 13 | uint16_t mainColor; 14 | uint16_t weekColor; 15 | int brightness; 16 | int weekChromatism = 50; // 绘制星期时的色差基础值 17 | // 时钟页面 18 | bool drawTimeFirstTime = true; // 直接显示时间 19 | int hour,minu,sec; // 之前显示的时、分、秒的数值 20 | int timeIndex = 0; // 滑动动画帧数指针 21 | int animIndex = 0; // 时钟页面动画索引 22 | int timeModel; // 时间跳变模式 23 | // 闹钟页面 24 | bool clockOpen; // 闹钟是否开启 25 | int clockH,clockM,clockBellNum; // 闹钟时、分、闹铃编号 26 | int tmpClockH,tmpClockM,tmpClockBellNum; // 用来显示的临时闹钟时、分、闹铃编号 27 | int clockChoosed = CLOCK_H; // 默认选中时 28 | // 记录分页面 29 | int timePage; 30 | int rhythmPage; 31 | int animPage; 32 | int anniversaryPage; 33 | // 亮度页面 34 | int brightModel; // 亮度调节模式 35 | int brightSamplingTime; // 亮度采样次数 36 | int brightSamplingValue; // 采样值 37 | // 动画页相关 38 | int matrixArray[13][32]; // 点阵二维数组,用来记录一些数据 39 | int animInterval1 = 60; // 动画1每一帧动画间隔 40 | int animInterval2 = 80; // 动画2每一帧动画间隔 41 | int animInterval3 = 100; // 动画3每一帧动画间隔 42 | int hackAnimProbability = 8; // 骇客动画产生的概率,数值越大,新产生的下坠动画就越少 43 | int lightedCount = 0; // 随机点动画已点亮的个数 44 | bool increasing = true; // 随机点动画正在增加状态 45 | unsigned long animTime; // 记录上一次动画的时间 46 | // 节奏灯页面 47 | int rhythmBandsModel; // 节奏灯频段模式 48 | uint16_t hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value); 49 | unsigned int sampling_period_us; //采样周期 50 | byte peak[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 51 | int oldBarHeights[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 52 | int bandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 53 | int bandIndex[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 54 | double vReal[SAMPLES]; 55 | double vImag[SAMPLES]; 56 | int maxBandValue; 57 | unsigned long starTime; 58 | unsigned long peekDecayTime; 59 | unsigned long changeColorTime; 60 | int colorTime; 61 | arduinoFFT FFT = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ); 62 | int model2ColorArray[2][3] = {{0, 220, 255}, {240, 45, 255}}; 63 | int model4ColorArrar[8][3] = {{240, 45, 255},{253, 98, 248},{253, 169, 205},{255, 196, 123},{253, 214, 200},{253, 192, 255},{249, 175, 255},{0, 220, 255}}; 64 | 65 | //纪念日相关 66 | bool birthdayOpen; // 生日是否开启 67 | bool anniversaryOpen; // 纪念日是否开启 68 | unsigned long birthday; // 生日日期时间戳 69 | unsigned long anniversaryA; // 纪念日日期A时间戳 70 | unsigned long anniversaryB; // 纪念日日期B时间戳 71 | 72 | Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(MATRIX_SIDE, MATRIX_SIDE, MATRIX_COUNT, 1, DATAPIN, 73 | NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE + 74 | NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS + NEO_TILE_PROGRESSIVE, NEO_GRB + NEO_KHZ800); 75 | 76 | // 重置亮度采样值 77 | void clearBrightSampling(){ 78 | brightSamplingTime = 0; 79 | brightSamplingValue = 0; 80 | } 81 | 82 | // 根据亮度采样值和次数,计算自动亮度 83 | void calculateBrightnessValue(){ 84 | int val = brightSamplingValue / brightSamplingTime; // 三次采样平均值 85 | // Serial.println(val); 86 | brightness = map(val, 5, 4095, 0, 65); 87 | // if(val >= 3000){ 88 | // brightness = 65; 89 | // }else if(val >= 2000){ 90 | // brightness = 45; 91 | // }else{ 92 | // brightness = map(val, 0, 1999, 0, 44); 93 | // } 94 | // Serial.print("亮度:"); 95 | // Serial.println(brightness); 96 | } 97 | 98 | // 初始化矩阵 99 | void initMatrix(){ 100 | matrix.begin(); 101 | matrix.setFont(&MyFont); 102 | matrix.setTextWrap(false); 103 | // 计算采样周期(单位:uS) 104 | sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQ)); 105 | // 初始化拾音器 106 | pinMode(AUDIO_IN_PIN, INPUT); 107 | // 采样一次亮度值,确定初始亮度 108 | if(brightModel == BRIGHT_MODEL_AUTO){ 109 | brightSamplingValue+=analogRead(LIGHT_ADC); 110 | brightSamplingTime++; 111 | calculateBrightnessValue(); 112 | clearBrightSampling(); 113 | } 114 | } 115 | 116 | // 清空屏幕 117 | void clearMatrix(){ 118 | matrix.fillScreen(0); 119 | matrix.show(); 120 | } 121 | 122 | // 绘制对时文字 123 | void drawCheckTimeText(){ 124 | matrix.fillScreen(0); 125 | matrix.setBrightness(brightness); 126 | matrix.drawBitmap(0, 0, checkingTime, 32, 8, mainColor); 127 | matrix.show(); 128 | } 129 | 130 | // 绘制文字 131 | void drawText(int x, int y, String text){ 132 | matrix.setBrightness(brightness); 133 | matrix.setFont(&MyFont); 134 | matrix.fillScreen(0); 135 | matrix.setCursor(x,y); 136 | matrix.setTextColor(mainColor); 137 | matrix.print(text); 138 | matrix.show(); 139 | } 140 | 141 | // 绘制失败时的文字和图案 142 | void drawFailed(int textX, int failedX, String text){ 143 | drawText(textX, 6, text); 144 | matrix.setCursor(failedX,6); 145 | matrix.setTextColor(matrix.Color(255, 0, 0)); 146 | matrix.print("X"); 147 | matrix.setBrightness(brightness); 148 | matrix.show(); 149 | } 150 | 151 | // 绘制成功时的文字和图案 152 | void drawSuccess(int textX, int successX, String text){ 153 | drawText(textX, 6, text); 154 | matrix.drawRGBBitmap(successX, 2, Success, 5, 5); 155 | matrix.setBrightness(brightness); 156 | matrix.show(); 157 | } 158 | 159 | // 绘制时间页面 160 | void drawTime(){ 161 | // 获取RTC时间 162 | struct tm timeinfo; 163 | if (!getLocalTime(&timeinfo)){ 164 | Serial.println("获取RTC时间失败"); 165 | return; 166 | } 167 | matrix.setBrightness(brightness); 168 | matrix.setFont(&MyFont); 169 | matrix.setTextColor(mainColor); 170 | if(timePage == TIME_H_M_S){ 171 | // 清屏 172 | matrix.fillScreen(0); 173 | // 绘制时 174 | if(timeModel == TIME_MODEL_DIRECT || drawTimeFirstTime || hour == timeinfo.tm_hour){ 175 | matrix.setCursor(2, 5); 176 | String h; 177 | if(timeinfo.tm_hour < 10){ 178 | h = "0" + String(timeinfo.tm_hour); 179 | }else{ 180 | h = String(timeinfo.tm_hour); 181 | } 182 | matrix.print(h); 183 | }else if(hour != timeinfo.tm_hour){ 184 | if(hour / 10 != timeinfo.tm_hour / 10){ // 时进位了,两个数字都要动画刷新 185 | matrix.setCursor(2, 6 + timeIndex); 186 | if(hour < 10){ 187 | matrix.print("0" + String(hour)); 188 | }else{ 189 | matrix.print(String(hour)); 190 | } 191 | matrix.setCursor(2, 0 + timeIndex); 192 | if(timeinfo.tm_hour < 10){ 193 | matrix.print("0" + String(timeinfo.tm_hour)); 194 | }else{ 195 | matrix.print(String(timeinfo.tm_hour)); 196 | } 197 | }else{ // 只需刷新个位数 198 | matrix.setCursor(2, 5); 199 | matrix.print(String(hour / 10)); 200 | matrix.setCursor(6, 6 + timeIndex); 201 | matrix.print(String(hour % 10)); 202 | matrix.setCursor(6, 0 + timeIndex); 203 | matrix.print(String(timeinfo.tm_hour % 10)); 204 | } 205 | } 206 | // 绘制分 207 | if(timeModel == TIME_MODEL_DIRECT || drawTimeFirstTime || minu == timeinfo.tm_min){ 208 | matrix.setCursor(12, 5); 209 | String m; 210 | if(timeinfo.tm_min < 10){ 211 | m = "0" + String(timeinfo.tm_min); 212 | }else{ 213 | m = String(timeinfo.tm_min); 214 | } 215 | matrix.print(m); 216 | }else if(minu != timeinfo.tm_min){ 217 | if(minu / 10 != timeinfo.tm_min / 10){ // 分进位了,两个数字都要动画刷新 218 | matrix.setCursor(12, 6 + timeIndex); 219 | if(minu < 10){ 220 | matrix.print("0" + String(minu)); 221 | }else{ 222 | matrix.print(String(minu)); 223 | } 224 | matrix.setCursor(12, 0 + timeIndex); 225 | if(timeinfo.tm_min < 10){ 226 | matrix.print("0" + String(timeinfo.tm_min)); 227 | }else{ 228 | matrix.print(String(timeinfo.tm_min)); 229 | } 230 | }else{ // 只需刷新个位数 231 | matrix.setCursor(12, 5); 232 | matrix.print(String(minu / 10)); 233 | matrix.setCursor(16, 6 + timeIndex); 234 | matrix.print(String(minu % 10)); 235 | matrix.setCursor(16, 0 + timeIndex); 236 | matrix.print(String(timeinfo.tm_min % 10)); 237 | } 238 | } 239 | // 绘制秒 240 | if(timeModel == TIME_MODEL_DIRECT || drawTimeFirstTime || sec == timeinfo.tm_sec){ 241 | matrix.setCursor(22, 5); 242 | String s; 243 | if(timeinfo.tm_sec < 10){ 244 | s = "0" + String(timeinfo.tm_sec); 245 | }else{ 246 | s = String(timeinfo.tm_sec); 247 | } 248 | matrix.print(s); 249 | }else if(sec != timeinfo.tm_sec){ 250 | if(sec / 10 != timeinfo.tm_sec / 10){ // 秒进位了,两个数字都要动画刷新 251 | matrix.setCursor(22, 6 + timeIndex); 252 | if(sec < 10){ 253 | matrix.print("0" + String(sec)); 254 | }else{ 255 | matrix.print(String(sec)); 256 | } 257 | matrix.setCursor(22, 0 + timeIndex); 258 | if(timeinfo.tm_sec < 10){ 259 | matrix.print("0" + String(timeinfo.tm_sec)); 260 | }else{ 261 | matrix.print(String(timeinfo.tm_sec)); 262 | } 263 | }else{ // 只需刷新个位数 264 | matrix.setCursor(22, 5); 265 | matrix.print(String(sec / 10)); 266 | matrix.setCursor(26, 6 + timeIndex); 267 | matrix.print(String(sec % 10)); 268 | matrix.setCursor(26, 0 + timeIndex); 269 | matrix.print(String(timeinfo.tm_sec % 10)); 270 | } 271 | } 272 | // 绘制两个冒号 273 | matrix.setCursor(10, 5); 274 | matrix.print(":"); 275 | matrix.setCursor(20, 5); 276 | matrix.print(":"); 277 | // 绘制星期 278 | matrix.fillRect(0, 7, 31, 7, 0); 279 | int weekNum = timeinfo.tm_wday; 280 | if(weekNum == 0){ 281 | weekNum = 7; 282 | } 283 | // 绘制6条直线(除了当天的) 284 | for(int i = 1; i <= 7; i++){ 285 | if(i == weekNum){ 286 | continue; 287 | } 288 | matrix.drawFastHLine(2 + (i - 1) * 4, 7, 3, weekColor); 289 | } 290 | matrix.drawFastHLine(2 + (weekNum - 1) * 4, 7, 3, mainColor); 291 | }else if(timePage == TIME_H_M){ 292 | // 清屏 293 | matrix.fillScreen(0); 294 | switch(animIndex){ 295 | case 0: 296 | case 1: 297 | matrix.drawRGBBitmap(0,0,timeAnim0,11,8); 298 | break; 299 | case 2: 300 | case 3: 301 | matrix.drawRGBBitmap(0,0,timeAnim1,11,8); 302 | break; 303 | case 4: 304 | case 5: 305 | matrix.drawRGBBitmap(0,0,timeAnim2,11,8); 306 | break; 307 | case 6: 308 | case 7: 309 | matrix.drawRGBBitmap(0,0,timeAnim3,11,8); 310 | break; 311 | case 8: 312 | case 9: 313 | matrix.drawRGBBitmap(0,0,timeAnim4,11,8); 314 | break; 315 | case 10: 316 | case 11: 317 | matrix.drawRGBBitmap(0,0,timeAnim5,11,8); 318 | break; 319 | case 12: 320 | case 13: 321 | matrix.drawRGBBitmap(0,0,timeAnim6,11,8); 322 | break; 323 | case 14: 324 | case 15: 325 | matrix.drawRGBBitmap(0,0,timeAnim7,11,8); 326 | break; 327 | case 16: 328 | case 17: 329 | matrix.drawRGBBitmap(0,0,timeAnim8,11,8); 330 | break; 331 | case 18: 332 | case 19: 333 | matrix.drawRGBBitmap(0,0,timeAnim0,11,8); 334 | break; 335 | default: 336 | animIndex = 9; 337 | break; 338 | } 339 | animIndex++; 340 | if(animIndex == 20){ 341 | animIndex = 0; 342 | } 343 | // 绘制时 344 | if(timeModel == TIME_MODEL_DIRECT || drawTimeFirstTime || hour == timeinfo.tm_hour){ 345 | matrix.setCursor(14, 5); 346 | String h; 347 | if(timeinfo.tm_hour < 10){ 348 | h = "0" + String(timeinfo.tm_hour); 349 | }else{ 350 | h = String(timeinfo.tm_hour); 351 | } 352 | matrix.print(h); 353 | }else if(hour != timeinfo.tm_hour){ 354 | if(hour / 10 != timeinfo.tm_hour / 10){ // 时进位了,两个数字都要动画刷新 355 | matrix.setCursor(14, 6 + timeIndex); 356 | if(hour < 10){ 357 | matrix.print("0" + String(hour)); 358 | }else{ 359 | matrix.print(String(hour)); 360 | } 361 | matrix.setCursor(14, 0 + timeIndex); 362 | if(timeinfo.tm_hour < 10){ 363 | matrix.print("0" + String(timeinfo.tm_hour)); 364 | }else{ 365 | matrix.print(String(timeinfo.tm_hour)); 366 | } 367 | }else{ // 只需刷新个位数 368 | matrix.setCursor(14, 5); 369 | matrix.print(String(hour / 10)); 370 | matrix.setCursor(18, 6 + timeIndex); 371 | matrix.print(String(hour % 10)); 372 | matrix.setCursor(18, 0 + timeIndex); 373 | matrix.print(String(timeinfo.tm_hour % 10)); 374 | } 375 | } 376 | // 绘制分 377 | if(timeModel == TIME_MODEL_DIRECT || drawTimeFirstTime || minu == timeinfo.tm_min){ 378 | matrix.setCursor(24, 5); 379 | String m; 380 | if(timeinfo.tm_min < 10){ 381 | m = "0" + String(timeinfo.tm_min); 382 | }else{ 383 | m = String(timeinfo.tm_min); 384 | } 385 | matrix.print(m); 386 | }else if(minu != timeinfo.tm_min){ 387 | if(minu / 10 != timeinfo.tm_min / 10){ // 分进位了,两个数字都要动画刷新 388 | matrix.setCursor(24, 6 + timeIndex); 389 | if(minu < 10){ 390 | matrix.print("0" + String(minu)); 391 | }else{ 392 | matrix.print(String(minu)); 393 | } 394 | matrix.setCursor(24, 0 + timeIndex); 395 | if(timeinfo.tm_min < 10){ 396 | matrix.print("0" + String(timeinfo.tm_min)); 397 | }else{ 398 | matrix.print(String(timeinfo.tm_min)); 399 | } 400 | }else{ // 只需刷新个位数 401 | matrix.setCursor(24, 5); 402 | matrix.print(String(minu / 10)); 403 | matrix.setCursor(28, 6 + timeIndex); 404 | matrix.print(String(minu % 10)); 405 | matrix.setCursor(28, 0 + timeIndex); 406 | matrix.print(String(timeinfo.tm_min % 10)); 407 | } 408 | } 409 | // 绘制冒号 410 | matrix.setCursor(22, 5); 411 | matrix.print(":"); 412 | // 绘制星期 413 | matrix.fillRect(12, 7, 31, 7, 0); 414 | int weekNum = timeinfo.tm_wday; 415 | if(weekNum == 0){ 416 | weekNum = 7; 417 | } 418 | // 绘制6条直线(除了当天的) 419 | for(int i = 1; i <= 7; i++){ 420 | if(i == weekNum){ 421 | continue; 422 | } 423 | matrix.drawFastHLine(12 + (i - 1) * 3, 7, 2, weekColor); 424 | } 425 | matrix.drawFastHLine(12 + (weekNum - 1) * 3, 7, 2, mainColor); 426 | }else if(timePage == TIME_DATE){ 427 | matrix.fillScreen(0); 428 | struct timeval tv; 429 | gettimeofday(&tv, NULL); 430 | unsigned long currentTime = tv.tv_sec; 431 | if (birthdayOpen && currentTime >= birthday && currentTime < birthday + 86400){ 432 | matrix.drawRGBBitmap(0, 0, cake, 11, 8); 433 | if (!birthdayBell){ 434 | birthdayBell = true; 435 | createPlayBirthdaySongTask(); 436 | } 437 | }else{ 438 | switch(animIndex){ 439 | case 0: 440 | case 1: 441 | matrix.drawRGBBitmap(0,0,timeAnim0,11,8); 442 | break; 443 | case 2: 444 | case 3: 445 | matrix.drawRGBBitmap(0,0,timeAnim1,11,8); 446 | break; 447 | case 4: 448 | case 5: 449 | matrix.drawRGBBitmap(0,0,timeAnim2,11,8); 450 | break; 451 | case 6: 452 | case 7: 453 | matrix.drawRGBBitmap(0,0,timeAnim3,11,8); 454 | break; 455 | case 8: 456 | case 9: 457 | matrix.drawRGBBitmap(0,0,timeAnim4,11,8); 458 | break; 459 | case 10: 460 | case 11: 461 | matrix.drawRGBBitmap(0,0,timeAnim5,11,8); 462 | break; 463 | case 12: 464 | case 13: 465 | matrix.drawRGBBitmap(0,0,timeAnim6,11,8); 466 | break; 467 | case 14: 468 | case 15: 469 | matrix.drawRGBBitmap(0,0,timeAnim7,11,8); 470 | break; 471 | case 16: 472 | case 17: 473 | matrix.drawRGBBitmap(0,0,timeAnim8,11,8); 474 | break; 475 | case 18: 476 | case 19: 477 | matrix.drawRGBBitmap(0,0,timeAnim0,11,8); 478 | break; 479 | default: 480 | animIndex = 9; 481 | break; 482 | } 483 | animIndex++; 484 | if(animIndex == 20){ 485 | animIndex = 0; 486 | } 487 | } 488 | // 写入日期 489 | matrix.setCursor(12, 5); 490 | String month = timeinfo.tm_mon < 9 ? "0" : ""; 491 | month = month + (timeinfo.tm_mon + 1); 492 | String day = timeinfo.tm_mday < 10 ? "0" : ""; 493 | day = day + timeinfo.tm_mday; 494 | matrix.print(month + "-" + day); 495 | // 绘制星期 496 | int weekNum = timeinfo.tm_wday; 497 | if(weekNum == 0){ 498 | weekNum = 7; 499 | } 500 | // 绘制6条直线(除了当天的) 501 | for(int i = 1; i <= 7; i++){ 502 | if(i == weekNum){ 503 | continue; 504 | } 505 | matrix.drawFastHLine(12 + (i - 1) * 3, 7, 2, weekColor); 506 | } 507 | matrix.drawFastHLine(12 + (weekNum - 1) * 3, 7, 2, mainColor); 508 | } 509 | if(drawTimeFirstTime){ 510 | drawTimeFirstTime = false; 511 | hour = timeinfo.tm_hour; 512 | minu = timeinfo.tm_min; 513 | sec = timeinfo.tm_sec; 514 | }else if(sec != timeinfo.tm_sec){ 515 | timeIndex++; 516 | if(timeIndex == 6){ 517 | timeIndex = 0; 518 | hour = timeinfo.tm_hour; 519 | minu = timeinfo.tm_min; 520 | sec = timeinfo.tm_sec; 521 | } 522 | } 523 | matrix.show(); 524 | } 525 | 526 | // 绘制亮度调节页面 527 | void drawBright(){ 528 | matrix.setBrightness(brightness); 529 | matrix.setFont(&MyFont); 530 | matrix.fillScreen(0); 531 | matrix.setTextColor(mainColor); 532 | if(brightModel == BRIGHT_MODEL_MANUAL){ 533 | // 亮度图标 534 | matrix.drawFastVLine(13, 3, 2, mainColor); 535 | matrix.drawPixel(14, 2, mainColor); 536 | matrix.drawPixel(14, 5, mainColor); 537 | matrix.drawPixel(15, 1, mainColor); 538 | matrix.drawPixel(15, 6, mainColor); 539 | matrix.drawFastVLine(16, 1, 6, mainColor); 540 | matrix.drawFastVLine(17, 2, 4, mainColor); 541 | matrix.drawFastVLine(18, 3, 2, mainColor); 542 | // +=号 543 | if(brightness > MIN_BRIGHTNESS){ 544 | matrix.setCursor(3, 6); 545 | matrix.print("-"); 546 | } 547 | if(brightness < MAX_BRIGHTNESS){ 548 | matrix.setCursor(26, 6); 549 | matrix.print("+"); 550 | } 551 | }else{ 552 | drawText(7,6,"AUTO"); 553 | } 554 | matrix.show(); 555 | } 556 | 557 | // 绘制动画页面 558 | void drawAnim(){ 559 | if(animPage == ANIM_MODEL1){ 560 | if((millis() - animTime) < animInterval1) return; 561 | matrix.fillScreen(0); 562 | randomSeed(analogRead(RANDOM_SEED_PIN)); 563 | for(int i = 0; i < MATRIX_COUNT * MATRIX_SIDE; i++){ 564 | int animCount = 0; // 记录往下移一格后这一列还有几条动画存在(矩阵高度为8,动画高度为6,所以一列最多存在两条动画) 565 | int index1 = 0; // 记录往下移一格后第一个动画的头部位置 566 | int index2 = 0; // 记录往下移一格后第二个动画的头部位置 567 | // 根据矩阵二维数组判断这一列是否已经有动画,动画的起始位置(最上端)的值为1 568 | for(int j = 0; j < 13; j++){ 569 | if(matrixArray[j][i] == 1){ // 这个位置是有动画的 570 | // 把这个动画往下移一格 571 | if((j + 1) <= 12){ 572 | matrix.drawRGBBitmap(i,(j + 1 - 5),animHack,1,6); 573 | animCount++; 574 | if(animCount == 1){ 575 | index1 = j + 1; 576 | } 577 | if(animCount == 2){ 578 | index2 = j + 1; 579 | } 580 | } 581 | } 582 | // 将这个位置置为0 583 | matrixArray[j][i] = 0; 584 | } 585 | // 将新位置的值置为1 586 | if(animCount == 2){ 587 | matrixArray[index1][i] = 1; 588 | matrixArray[index2][i] = 1; 589 | }else if(animCount == 1){ 590 | matrixArray[index1][i] = 1; 591 | } 592 | int x = random(0, hackAnimProbability); // 随机数,如果是1则产生新动画 593 | if(x == 1){ 594 | // 如果这一列没有动画,或者只有1个动画,并且这条动画与这条新生成的动画距离>=(6+1),就进入下面的判断 595 | if(animCount == 0 || (animCount == 1 && index1 >= 7)){ 596 | // 判断左边同一行有没有动画,有的话也不生成新动画 597 | if(i == 0 || matrixArray[0][i - 1] == 0){ 598 | matrix.drawRGBBitmap(i,-5,animHack,1,6); 599 | matrixArray[0][i] = 1; 600 | } 601 | } 602 | } 603 | } 604 | }else if(animPage == ANIM_MODEL2){ 605 | if((millis() - animTime) < animInterval2) return; 606 | // matrixArray[0][0]记录大步骤,0:红色→黄色 1:黄色→绿色 2:绿色→蓝色 3:蓝色→紫色 4:紫色→红色 607 | // matrixArray[0][1]记录每一个大步骤的小步,每一种颜色渐变过程持续50步 608 | // matrixArray[0][2]记录颜色R 609 | // matrixArray[0][3]记录颜色G 610 | // matrixArray[0][4]记录颜色B 611 | if(matrixArray[0][0] == 0 && matrixArray[0][1] == 0){ // 刚开始设置为红色,初始值250 612 | matrixArray[0][2] = 250; 613 | } 614 | // 循环绘制竖线 615 | int tmpR = matrixArray[0][2]; 616 | int tmpG = matrixArray[0][3]; 617 | int tmpB = matrixArray[0][4]; 618 | int tmpStep = matrixArray[0][0]; 619 | for(int i = 0; i < MATRIX_SIDE * MATRIX_COUNT; i++){ 620 | matrix.drawFastVLine(i, 0, 8, matrix.Color(tmpR, tmpG, tmpB)); 621 | // 计算出下一个循环的颜色 622 | if(i != MATRIX_SIDE * MATRIX_COUNT - 1){ 623 | if(tmpStep == 0){ // 红色→黄色 624 | tmpG = tmpG + 5; 625 | if(tmpG == 250){ 626 | tmpStep++; 627 | } 628 | }else if(tmpStep == 1){ // 黄色→绿色 629 | tmpR = tmpR - 5; 630 | if(tmpR == 0){ 631 | tmpStep++; 632 | } 633 | }else if(tmpStep == 2){ // 绿色→蓝色 634 | tmpG = tmpG - 5; 635 | tmpB = tmpB + 5; 636 | if(tmpG == 0){ 637 | tmpStep++; 638 | } 639 | }else if(tmpStep == 3){ // 蓝色→紫色 640 | tmpR = tmpR + 5; 641 | if(tmpR == 250){ 642 | tmpStep++; 643 | } 644 | }else if(tmpStep == 4){ // 紫色→红色 645 | tmpB = tmpB - 5; 646 | if(tmpB == 0){ 647 | tmpStep = 0; 648 | } 649 | } 650 | } 651 | } 652 | if(matrixArray[0][0] == 0){ // 红色→黄色 653 | matrixArray[0][3] = matrixArray[0][3] + 5; 654 | }else if(matrixArray[0][0] == 1){ // 黄色→绿色 655 | matrixArray[0][2] = matrixArray[0][2] - 5; 656 | }else if(matrixArray[0][0] == 2){ // 绿色→蓝色 657 | matrixArray[0][3] = matrixArray[0][3] - 5; 658 | matrixArray[0][4] = matrixArray[0][4] + 5; 659 | }else if(matrixArray[0][0] == 3){ // 蓝色→紫色 660 | matrixArray[0][2] = matrixArray[0][2] + 5; 661 | }else if(matrixArray[0][0] == 4){ // 紫色→红色 662 | matrixArray[0][4] = matrixArray[0][4] - 5; 663 | } 664 | matrixArray[0][1] = matrixArray[0][1] + 1; 665 | if(matrixArray[0][1] == 50){ 666 | matrixArray[0][1] = 0; 667 | matrixArray[0][0] = matrixArray[0][0] + 1; 668 | if(matrixArray[0][0] == 5){ 669 | matrixArray[0][0] = 0; 670 | } 671 | } 672 | }else if(animPage == ANIM_MODEL3){ 673 | if((millis() - animTime) < animInterval3) return; 674 | // 计算是填充过程还是消失过程 675 | if(lightedCount == 0){ 676 | increasing = true; 677 | }else if(lightedCount == MATRIX_SIDE * MATRIX_SIDE * MATRIX_COUNT){ 678 | increasing = false; 679 | } 680 | randomSeed(analogRead(RANDOM_SEED_PIN)); 681 | if(increasing){ 682 | int num = random(1, 4); // 每次随机填充num个点 683 | if(num + lightedCount > MATRIX_SIDE * MATRIX_SIDE * MATRIX_COUNT){ // 不能超过灯珠总数量 684 | num = MATRIX_SIDE * MATRIX_SIDE * MATRIX_COUNT - lightedCount; 685 | } 686 | for(int i = 0; i < num; i++){ 687 | int x = random(0, 32); 688 | int y = random(0, 8); 689 | while(matrixArray[y][x] == 1){ // 这个随机生成的坐标已经点亮 690 | x = random(0, 32); 691 | y = random(0, 8); 692 | } 693 | matrixArray[y][x] = 1; 694 | // 已点亮灯的个数 + 1 695 | lightedCount++; 696 | int r = random(10, 256); 697 | int g = random(10, 256); 698 | int b = random(10, 256); 699 | matrix.drawPixel(x, y, matrix.Color(r, g, b)); 700 | } 701 | }else{ 702 | int num = random(1, 4); // 每次随机消失num个点 703 | if(num > lightedCount){ // 不能大于剩余已点亮数量 704 | num = lightedCount; 705 | } 706 | for(int i = 0; i < num; i++){ 707 | int x = random(0, 32); 708 | int y = random(0, 8); 709 | while(matrixArray[y][x] == 0){ // 这个随机生成的坐标已经熄灭 710 | x = random(0, 32); 711 | y = random(0, 8); 712 | } 713 | matrixArray[y][x] = 0; 714 | // 已点亮灯的个数 - 1 715 | lightedCount--; 716 | matrix.drawPixel(x, y, 0); 717 | } 718 | } 719 | } 720 | matrix.setBrightness(brightness); 721 | matrix.show(); 722 | animTime = millis(); 723 | } 724 | 725 | // 绘制节奏灯页面 726 | void drawRHYTHM(){ 727 | matrix.clear(); 728 | // 重置频率块的值 729 | for (int i = 0; i < NUM_BANDS; i++){ 730 | bandValues[i] = 0; 731 | } 732 | // 采样SAMPLES次 733 | for (int i = 0; i < SAMPLES; i++) { 734 | starTime = micros(); 735 | vReal[i] = analogRead(AUDIO_IN_PIN); 736 | vImag[i] = 0; 737 | // Serial.println(micros() - starTime); 738 | while ((micros() - starTime) < sampling_period_us) { /* chill */ } 739 | // Serial.println(vReal[i]); 740 | } 741 | // 进行FFT计算 742 | FFT.DCRemoval(); 743 | FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD); 744 | FFT.Compute(FFT_FORWARD); 745 | FFT.ComplexToMagnitude(); 746 | // 解析计算结果 747 | if(rhythmBandsModel == RHYTHM_BANDS_MODEL1){ // 左高频,右低频,32个频段 748 | for (int i = 2; i < (SAMPLES/2); i++){ 749 | if (vReal[i] > NOISE) { 750 | // Serial.println(vReal[i]); 751 | // 去除前面6段低频杂音和一些高频尖叫 752 | if (i>6 && i<=9 ) bandValues[0] += (int)vReal[i]; 753 | if (i>9 && i<=11 ) bandValues[1] += (int)vReal[i]; 754 | if (i>11 && i<=13 ) bandValues[2] += (int)vReal[i]; 755 | if (i>13 && i<=15 ) bandValues[3] += (int)vReal[i]; 756 | if (i>15 && i<=17 ) bandValues[4] += (int)vReal[i]; 757 | if (i>17 && i<=19 ) bandValues[5] += (int)vReal[i]; 758 | if (i>19 && i<=21 ) bandValues[6] += (int)vReal[i]; 759 | if (i>21 && i<=23 ) bandValues[7] += (int)vReal[i]; 760 | if (i>23 && i<=25 ) bandValues[8] += (int)vReal[i]; 761 | if (i>25 && i<=27 ) bandValues[9] += (int)vReal[i]; 762 | if (i>27 && i<=29 ) bandValues[10] += (int)vReal[i]; 763 | if (i>29 && i<=31 ) bandValues[11] += (int)vReal[i]; 764 | if (i>31 && i<=33 ) bandValues[12] += (int)vReal[i]; 765 | if (i>33 && i<=35 ) bandValues[13] += (int)vReal[i]; 766 | if (i>35 && i<=38 ) bandValues[14] += (int)vReal[i]; 767 | if (i>38 && i<=41 ) bandValues[15] += (int)vReal[i]; 768 | if (i>41 && i<=44 ) bandValues[16] += (int)vReal[i]; 769 | if (i>44 && i<=47 ) bandValues[17] += (int)vReal[i]; 770 | if (i>47 && i<=50 ) bandValues[18] += (int)vReal[i]; 771 | if (i>50 && i<=53 ) bandValues[19] += (int)vReal[i]; 772 | if (i>53 && i<=56 ) bandValues[20] += (int)vReal[i]; 773 | if (i>56 && i<=59 ) bandValues[21] += (int)vReal[i]; 774 | if (i>59 && i<=62 ) bandValues[22] += (int)vReal[i]; 775 | if (i>62 && i<=65 ) bandValues[23] += (int)vReal[i]; 776 | if (i>65 && i<=68 ) bandValues[24] += (int)vReal[i]; 777 | if (i>68 && i<=71 ) bandValues[25] += (int)vReal[i]; 778 | if (i>71 && i<=74 ) bandValues[26] += (int)vReal[i]; 779 | if (i>74 && i<=77 ) bandValues[27] += (int)vReal[i]; 780 | if (i>77 && i<=80 ) bandValues[28] += (int)vReal[i]; 781 | if (i>80 && i<=83 ) bandValues[29] += (int)vReal[i]; 782 | if (i>83 && i<=87 ) bandValues[30] += (int)vReal[i]; 783 | if (i>87 && i<=91 ) bandValues[31] += (int)vReal[i]; 784 | } 785 | } 786 | }else{ 787 | for (int i = 2; i < (SAMPLES/2); i++){ 788 | if (vReal[i] > NOISE) { 789 | // Serial.println(vReal[i]); 790 | // 去除前面6段低频杂音和一些高频尖叫 791 | if (i>6 && i<=9 ) bandValues[15] += (int)vReal[i]; 792 | if (i>9 && i<=11 ) bandValues[16] += (int)vReal[i]; 793 | if (i>11 && i<=13 ) bandValues[14] += (int)vReal[i]; 794 | if (i>13 && i<=15 ) bandValues[17] += (int)vReal[i]; 795 | if (i>15 && i<=17 ) bandValues[13] += (int)vReal[i]; 796 | if (i>17 && i<=19 ) bandValues[18] += (int)vReal[i]; 797 | if (i>19 && i<=21 ) bandValues[12] += (int)vReal[i]; 798 | if (i>21 && i<=23 ) bandValues[19] += (int)vReal[i]; 799 | if (i>23 && i<=25 ) bandValues[11] += (int)vReal[i]; 800 | if (i>25 && i<=27 ) bandValues[20] += (int)vReal[i]; 801 | if (i>27 && i<=29 ) bandValues[10] += (int)vReal[i]; 802 | if (i>29 && i<=31 ) bandValues[21] += (int)vReal[i]; 803 | if (i>31 && i<=33 ) bandValues[9] += (int)vReal[i]; 804 | if (i>33 && i<=35 ) bandValues[22] += (int)vReal[i]; 805 | if (i>35 && i<=38 ) bandValues[8] += (int)vReal[i]; 806 | if (i>38 && i<=41 ) bandValues[23] += (int)vReal[i]; 807 | if (i>41 && i<=44 ) bandValues[7] += (int)vReal[i]; 808 | if (i>44 && i<=47 ) bandValues[24] += (int)vReal[i]; 809 | if (i>47 && i<=50 ) bandValues[6] += (int)vReal[i]; 810 | if (i>50 && i<=53 ) bandValues[25] += (int)vReal[i]; 811 | if (i>53 && i<=56 ) bandValues[5] += (int)vReal[i]; 812 | if (i>56 && i<=59 ) bandValues[26] += (int)vReal[i]; 813 | if (i>59 && i<=62 ) bandValues[4] += (int)vReal[i]; 814 | if (i>62 && i<=65 ) bandValues[27] += (int)vReal[i]; 815 | if (i>65 && i<=68 ) bandValues[3] += (int)vReal[i]; 816 | if (i>68 && i<=71 ) bandValues[28] += (int)vReal[i]; 817 | if (i>71 && i<=74 ) bandValues[2] += (int)vReal[i]; 818 | if (i>74 && i<=77 ) bandValues[29] += (int)vReal[i]; 819 | if (i>77 && i<=80 ) bandValues[1] += (int)vReal[i]; 820 | if (i>80 && i<=83 ) bandValues[30] += (int)vReal[i]; 821 | if (i>83 && i<=87 ) bandValues[0] += (int)vReal[i]; 822 | if (i>87 && i<=91 ) bandValues[31] += (int)vReal[i]; 823 | } 824 | } 825 | } 826 | // 寻找bandValues最大值并根据最大值计算倍率 827 | maxBandValue = 0; 828 | for(int i = 0; i < NUM_BANDS; i++){ 829 | if(bandValues[i] > maxBandValue){ 830 | maxBandValue = bandValues[i]; 831 | } 832 | if(bandValues[i] > MATRIX_SIDE * AMPLITUDE){ 833 | bandIndex[i] = 1; 834 | }else{ 835 | bandIndex[i] = 0; 836 | } 837 | } 838 | // 将FFT数据处理为条形高度 839 | int color = 0; 840 | int r, g, b; 841 | for (byte band = 0; band < NUM_BANDS; band++) { 842 | // 根据倍率缩放条形图高度 843 | int barHeight; 844 | if(bandIndex[band] == 1){ 845 | barHeight = (MATRIX_SIDE * bandValues[band]) / maxBandValue; 846 | }else{ 847 | barHeight = bandValues[band] / AMPLITUDE; 848 | } 849 | if (barHeight > MATRIX_SIDE) barHeight = MATRIX_SIDE; 850 | // 旧的高度值和新的高度值平均一下 851 | barHeight = ((oldBarHeights[band] * 1) + barHeight) / 2; 852 | // 如果条形的高度大于顶点高度,则调整顶点高度 853 | if (barHeight > peak[band]) { 854 | peak[band] = min(MATRIX_SIDE - 0, barHeight); 855 | } 856 | // 绘制操作 857 | switch (rhythmPage){ 858 | case RHYTHM_MODEL1: 859 | // 绘制条形 860 | matrix.drawFastVLine((MATRIX_WIDTH - 1 - band), (MATRIX_SIDE - barHeight), barHeight, hsv2rgb(color,100,100)); 861 | color += 360 / (NUM_BANDS + 4); 862 | // 绘制顶点 863 | matrix.drawPixel((MATRIX_WIDTH - 1 - band), MATRIX_SIDE - peak[band] - 1, matrix.Color(150,150,150)); 864 | break; 865 | case RHYTHM_MODEL2: 866 | // 绘制条形 867 | r = model2ColorArray[0][0]; 868 | g = model2ColorArray[0][1]; 869 | b = model2ColorArray[0][2]; 870 | for (int y = MATRIX_SIDE; y >= MATRIX_SIDE - barHeight; y--) { 871 | matrix.drawPixel((MATRIX_WIDTH - 1 - band), y, matrix.Color(r, g, b)); 872 | r+=(model2ColorArray[1][0] - model2ColorArray[0][0]) / barHeight; 873 | g+=(model2ColorArray[1][1] - model2ColorArray[0][1]) / barHeight; 874 | b+=(model2ColorArray[1][2] - model2ColorArray[0][2]) / barHeight; 875 | } 876 | // 绘制顶点 877 | matrix.drawPixel((MATRIX_WIDTH - 1 - band), MATRIX_SIDE - peak[band] - 1, matrix.Color(150,150,150)); 878 | break; 879 | case RHYTHM_MODEL3: 880 | // 绘制条形,此模式不绘制顶点 881 | for (int y = MATRIX_SIDE; y >= MATRIX_SIDE - barHeight; y--) { 882 | matrix.drawPixel((MATRIX_WIDTH - 1 - band), y, hsv2rgb(y * (255 / MATRIX_WIDTH / 5) + colorTime, 100, 100)); 883 | } 884 | break; 885 | case RHYTHM_MODEL4: 886 | // 此模式下,只绘制顶点 887 | matrix.drawPixel((MATRIX_WIDTH - 1 - band), (MATRIX_SIDE - peak[band] - 1), 888 | matrix.Color(model4ColorArrar[peak[band]][0], model4ColorArrar[peak[band]][1], model4ColorArrar[peak[band]][2])); 889 | break; 890 | default: 891 | break; 892 | } 893 | // 将值记录到oldBarHeights 894 | oldBarHeights[band] = barHeight; 895 | } 896 | // 70毫秒降低一次顶点 897 | if((millis() - peekDecayTime) >= 70){ 898 | for (byte band = 0; band < NUM_BANDS; band++){ 899 | if (peak[band] > 0) peak[band] -= 1; 900 | } 901 | colorTime++; 902 | peekDecayTime = millis(); 903 | } 904 | // 10毫秒变换一次颜色 905 | if((millis() - changeColorTime) >= 10){ 906 | colorTime++; 907 | changeColorTime = millis(); 908 | } 909 | matrix.setBrightness(brightness); 910 | matrix.show(); 911 | } 912 | 913 | // HSV转RGB格式 914 | uint16_t hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value){ 915 | uint8_t r, g, b; 916 | uint16_t h = (hue / 60) % 6; 917 | uint16_t F = 100 * hue / 60 - 100 * h; 918 | uint16_t P = value * (100 - saturation) / 100; 919 | uint16_t Q = value * (10000 - F * saturation) / 10000; 920 | uint16_t T = value * (10000 - saturation * (100 - F)) / 10000; 921 | switch (h){ 922 | case 0: 923 | r = value; 924 | g = T; 925 | b = P; 926 | break; 927 | case 1: 928 | r = Q; 929 | g = value; 930 | b = P; 931 | break; 932 | case 2: 933 | r = P; 934 | g = value; 935 | b = T; 936 | break; 937 | case 3: 938 | r = P; 939 | g = Q; 940 | b = value; 941 | break; 942 | case 4: 943 | r = T; 944 | g = P; 945 | b = value; 946 | break; 947 | case 5: 948 | r = value; 949 | g = P; 950 | b = Q; 951 | break; 952 | default: 953 | return matrix.Color(255, 0, 0); 954 | } 955 | r = r * 255 / 100; 956 | g = g * 255 / 100; 957 | b = b * 255 / 100; 958 | return matrix.Color(r, g, b); 959 | } 960 | 961 | // 重新设置闹钟相关的临时数据 962 | void resetTmpClockData(){ 963 | tmpClockH = clockH; 964 | tmpClockM = clockM; 965 | tmpClockBellNum = clockBellNum; 966 | clockChoosed = CLOCK_H; 967 | } 968 | 969 | // 绘制闹钟页面 970 | void drawClock(){ 971 | // 清屏 972 | matrix.fillScreen(0); 973 | if(clockOpen){ 974 | // 绘制时间 975 | matrix.setTextColor(mainColor); 976 | matrix.setCursor(2, 5); 977 | String h; 978 | if(tmpClockH < 10){ 979 | h = "0" + String(tmpClockH); 980 | }else{ 981 | h = String(tmpClockH); 982 | } 983 | String m; 984 | if(tmpClockM < 10){ 985 | m = "0" + String(tmpClockM); 986 | }else{ 987 | m = String(tmpClockM); 988 | } 989 | matrix.print(h + ":" + m); 990 | // 绘制闹钟图标 991 | matrix.drawBitmap(22, 1, bell, 8, 6, mainColor); 992 | // 绘制指示线 993 | if(clockChoosed == CLOCK_H){ 994 | matrix.drawFastHLine(2, 7, 7, weekColor); 995 | }else if(clockChoosed == CLOCK_M){ 996 | matrix.drawFastHLine(12, 7, 7, weekColor); 997 | }else if(clockChoosed == CLOCK_BELL){ 998 | matrix.drawFastHLine(22, 7, 8, weekColor); 999 | } 1000 | }else{ 1001 | matrix.drawBitmap(8, 1, bell, 8, 6, mainColor); 1002 | matrix.setCursor(20, 6); 1003 | matrix.setTextColor(matrix.Color(255, 0, 0)); 1004 | matrix.print("X"); 1005 | } 1006 | matrix.setBrightness(brightness); 1007 | matrix.show(); 1008 | } 1009 | 1010 | void drawAnniversary(){ 1011 | struct timeval tv; 1012 | gettimeofday(&tv, NULL); 1013 | unsigned long currentTime = tv.tv_sec; 1014 | matrix.fillScreen(0); 1015 | matrix.setBrightness(brightness); 1016 | matrix.setFont(&MyFont); 1017 | matrix.setTextColor(mainColor); 1018 | if (anniversaryPage == ANNIVERSARY_A){ 1019 | int betweenDays = std::labs(currentTime - anniversaryA) / 86400 + 1; // 计算两个时间戳之间相差的天数 1020 | char betweenDaysStr[6]; // 创建一个足够大的字符数组来存储格式化的字符串 1021 | sprintf(betweenDaysStr, "%05d", betweenDays); // 使用sprintf来格式化betweenDays 1022 | matrix.drawRGBBitmap(1, 0, heart, 9, 8); 1023 | matrix.fillRect(11, 0, 20, 8, 0); 1024 | matrix.setCursor(11, 6); 1025 | matrix.print(betweenDaysStr); 1026 | } else if (anniversaryPage == ANNIVERSARY_B){ 1027 | int betweenDays = std::labs(currentTime - anniversaryB) / 86400 + 1; // 计算两个时间戳之间相差的天数 1028 | char betweenDaysStr[6]; // 创建一个足够大的字符数组来存储格式化的字符串 1029 | sprintf(betweenDaysStr, "%05d", betweenDays); // 使用sprintf来格式化betweenDays 1030 | matrix.drawRGBBitmap(0, 0, heart2, 10, 8); 1031 | matrix.fillRect(11, 0, 20, 8, 0); 1032 | matrix.setCursor(11, 6); 1033 | matrix.print(betweenDaysStr); 1034 | } 1035 | matrix.show(); 1036 | } 1037 | -------------------------------------------------------------------------------- /light.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIGHT_H 2 | #define __LIGHT_H 3 | 4 | #include 5 | 6 | extern int clockColor[3]; 7 | extern int brightness; 8 | extern uint16_t mainColor; 9 | extern uint16_t weekColor; 10 | extern Adafruit_NeoMatrix matrix; 11 | extern int timePage; 12 | extern int rhythmPage; 13 | extern int animPage; 14 | extern int anniversaryPage; 15 | extern int rhythmBandsModel; 16 | extern int timeModel; 17 | extern int brightModel; 18 | extern int brightSamplingTime; 19 | extern int brightSamplingValue; 20 | extern int matrixArray[8][32]; 21 | extern byte peak[]; 22 | extern int oldBarHeights[]; 23 | extern int lightedCount; 24 | extern int clockH; 25 | extern int clockM; 26 | extern int clockBellNum; 27 | extern int tmpClockH; 28 | extern int tmpClockM; 29 | extern int tmpClockBellNum; 30 | extern bool clockOpen; 31 | extern int clockChoosed; 32 | 33 | extern bool birthdayOpen; 34 | extern bool anniversaryOpen; 35 | extern unsigned long birthday; 36 | extern unsigned long anniversaryA; 37 | extern unsigned long anniversaryB; 38 | extern bool drawTimeFirstTime; 39 | extern int timeIndex; 40 | void initMatrix(); 41 | void drawText(int x, int y, String text); 42 | void drawCheckTimeText(); 43 | void drawFailed(int textX, int failedX, String text); 44 | void drawSuccess(int textX, int successX, String text); 45 | void clearMatrix(); 46 | void drawTime(); 47 | void drawBright(); 48 | void drawAnim(); 49 | void drawRHYTHM(); 50 | void resetTmpClockData(); 51 | void drawClock(); 52 | void calculateBrightnessValue(); 53 | void clearBrightSampling(); 54 | void drawAnniversary(); 55 | 56 | #endif 57 | 58 | -------------------------------------------------------------------------------- /net.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | #include "preferencesUtil.h" 4 | #include "light.h" 5 | #include "task.h" 6 | #include "RTC.h" 7 | 8 | // Wifi相关 9 | String ssid = ""; //WIFI名称 10 | String pass = ""; //WIFI密码 11 | bool apConfig; // 系统启动时是否需要配网 12 | // 是否顺利连接上wifi 13 | bool wifiConnected = false; 14 | // 是否NTP对时成功 15 | bool RTCSuccess = false; 16 | 17 | // 连接WiFi 18 | void connectWiFi(int timeOut_s){ 19 | int connectTime = 0; //用于连接计时,如果长时间连接不成功,则提示失败 20 | Serial.print("正在连接网络"); 21 | Serial.println(ssid); 22 | WiFi.begin(ssid, pass); 23 | while (WiFi.status() != WL_CONNECTED) { 24 | Serial.print("."); 25 | delay(500); 26 | connectTime++; 27 | if (connectTime > 2 * timeOut_s){ //长时间连接不上,提示连接失败 28 | Serial.println("网络连接失败..."); 29 | // 停止启动加载动画 30 | if (showTextTask != NULL) { 31 | vTaskDelete(showTextTask); 32 | showTextTask = NULL; 33 | delay(300); 34 | } 35 | // 屏幕显示wifi连接失败 36 | drawFailed(4, 24, "WIFI"); 37 | delay(1000); 38 | // 清空屏幕 39 | clearMatrix(); 40 | return; 41 | } 42 | } 43 | wifiConnected = true; 44 | Serial.println("网络连接成功"); 45 | Serial.print("本地IP: "); 46 | Serial.println(WiFi.localIP()); 47 | } 48 | 49 | // 获取NTP并同步RTC时间 50 | void getNTPTime(){ 51 | // 8 * 3600 东八区时间修正 52 | // 使用夏令时 daylightOffset_sec 就填写3600,否则就填写0; 53 | Serial.println("执行NTP对时"); 54 | configTime( 8 * 3600, 0, NTP1, NTP2, NTP3); 55 | struct tm now; 56 | if (getLocalTime(&now)){ 57 | setRTCtime(now); 58 | } 59 | } 60 | 61 | // 系统启动时进行NTP对时 62 | void checkTime(int limitTime){ 63 | // 记录此时的时间,在NTP对时时,超过一定的时间,就直接结束 64 | time_t start; 65 | time(&start); 66 | // 获取NTP并同步至RTC,第一次同步失败,就一直尝试同步 67 | getNTPTime(); 68 | struct tm timeinfo; 69 | while (!getLocalTime(&timeinfo)){ 70 | time_t end; 71 | time(&end); 72 | if((end - start) > limitTime){ 73 | Serial.println("网络对时超时..."); 74 | // 停止启动加载动画 75 | if (showTextTask != NULL) { 76 | vTaskDelete(showTextTask); 77 | showTextTask = NULL; 78 | } 79 | clearMatrix(); 80 | // 屏幕显示TIME获取失败 81 | drawFailed(4, 24, "TIME"); 82 | delay(1000); 83 | // 清空屏幕 84 | clearMatrix(); 85 | // 跳出循环 86 | return; 87 | } 88 | Serial.println("时钟对时失败..."); 89 | getNTPTime(); 90 | } 91 | // 到了这一步,说明对时成功 92 | RTCSuccess = true; 93 | setRTCtime(timeinfo); 94 | } 95 | 96 | // 关闭wifi 97 | void disConnectWifi(){ 98 | WiFi.disconnect(); 99 | } 100 | 101 | // 定时对时 102 | void checkTimeTicker(){ 103 | // 重新连接wifi 104 | Serial.println("重新连接网络..."); 105 | int connectTime = 0; //连接计时 106 | WiFi.begin(ssid, pass); 107 | while (WiFi.status() != WL_CONNECTED) { 108 | delay(500); 109 | connectTime++; 110 | if (connectTime > 10){ //循环10次(5秒)连接不上,就退出 111 | Serial.println("网络连接失败..."); 112 | wifiConnected = false; 113 | // 跳出循环 114 | break; 115 | } 116 | } 117 | // wifi连接成功,进行对时 118 | if(WiFi.status() == WL_CONNECTED){ 119 | Serial.println("网络连接成功"); 120 | // 只对一次,不管是否成功,因为启动时已经成功进行了对时,这次无关紧要 121 | getNTPTime(); 122 | // Serial.println("对时完成"); 123 | // 对时后如果闹钟开启,就重置闹钟时间 124 | if(clockOpen){ 125 | tickerClock.detach(); 126 | startTickerClock(getClockRemainSeconds()); 127 | } 128 | } 129 | // 关闭wifi功能 130 | disConnectWifi(); 131 | } 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /net.h: -------------------------------------------------------------------------------- 1 | #ifndef __NET_H 2 | #define __NET_H 3 | #include "common.h" 4 | 5 | void connectWiFi(int timeOut_s); 6 | void getNTPTime(void); 7 | void checkTime(int limitTime); 8 | void disConnectWifi(); 9 | void checkTimeTicker(); 10 | extern String ssid; 11 | extern String pass; 12 | extern bool wifiConnected; 13 | extern bool apConfig; 14 | extern bool RTCSuccess; 15 | 16 | #endif -------------------------------------------------------------------------------- /preferencesUtil.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | #include "net.h" 4 | #include "light.h" 5 | #include "BLE.h" 6 | 7 | Preferences prefs; // 声明Preferences对象 8 | 9 | void setBLEInfo(); 10 | 11 | // 上电时读取Wifi账号、密码、默认颜色等一系列信息 12 | void getInfos(){ 13 | prefs.begin("matrix"); 14 | ssid = prefs.getString("ssid", ""); 15 | pass = prefs.getString("pass", ""); 16 | int r = prefs.getInt("red", 0); 17 | int g = prefs.getInt("green", 250); 18 | int b = prefs.getInt("blue", 250); 19 | apConfig = prefs.getBool("apConfig", false); 20 | timePage = prefs.getInt("timePage",TIME_H_M_S); 21 | rhythmPage = prefs.getInt("rhythmPage",RHYTHM_MODEL1); 22 | animPage = prefs.getInt("animPage",ANIM_MODEL1); 23 | anniversaryPage = prefs.getInt("anniversaryPage",ANNIVERSARY_A); 24 | rhythmBandsModel = prefs.getInt("bandsModel",RHYTHM_BANDS_MODEL1); 25 | brightModel = prefs.getInt("brightModel",BRIGHT_MODEL_MANUAL); 26 | timeModel = prefs.getInt("timeModel",TIME_MODEL_DIRECT); 27 | clockH = prefs.getInt("clockH",0); 28 | clockM = prefs.getInt("clockM",0); 29 | clockBellNum = prefs.getInt("clockBellNum",0); 30 | clockOpen = prefs.getBool("clockOpen",false); 31 | clockColor[0] = r; 32 | clockColor[1] = g; 33 | clockColor[2] = b; 34 | mainColor = matrix.Color(clockColor[0], clockColor[1], clockColor[2]); 35 | // 时间下面的星期条颜色,最大的RGB值不变,其他两个取反色 36 | int maxRGB = max(r, max(g, b)); // 求出RGB三色里的最大值 37 | bool findMax = false; // 是否找到最大值,防止3个值一样大,都不取反 38 | int weekR, weekG, weekB; 39 | if(r == maxRGB){ 40 | findMax = true; 41 | weekR = r; 42 | }else{ 43 | weekR = 255 - r; 44 | } 45 | if(g == maxRGB && !findMax){ 46 | findMax = true; 47 | weekG = g; 48 | }else{ 49 | weekG = 255 - g; 50 | } 51 | if(b == maxRGB && !findMax){ 52 | findMax = true; 53 | weekB = b; 54 | }else{ 55 | weekB = 255 - b; 56 | } 57 | weekColor = matrix.Color(weekR, weekG, weekB); 58 | brightness = prefs.getInt("brightness", BRIGHTNESS); 59 | birthdayOpen = prefs.getBool("birthdayOpen", false); //生日功能默认关闭 60 | anniversaryOpen = prefs.getBool("anniversaryOpen", false); //纪念日功能默认关闭 61 | birthday = prefs.getULong("birthday", 0); //默认生日时间戳为 0 62 | anniversaryA = prefs.getULong("anniversaryA", 0); //默认纪念日 A 时间戳为 0 63 | anniversaryB = prefs.getULong("anniversaryB", 0); //默认纪念日 B 时间戳为 0 64 | prefs.end(); 65 | } 66 | 67 | // 将读取到的配置信息写入到蓝牙特征值 68 | void setBLEInfo(){ 69 | setBLEBrightness(brightness); 70 | setBLEColor(clockColor[0], clockColor[1], clockColor[2]); 71 | setBLEAnniversary(anniversaryOpen, anniversaryA, anniversaryB); 72 | setBLEBirthday(birthdayOpen, birthday); 73 | setBLEWIFI(ssid, pass); 74 | setBLEAnim(timeModel); 75 | } 76 | 77 | // 写入WIFI配置 78 | void recordWifiConfig(){ 79 | prefs.begin("matrix"); 80 | prefs.putString("ssid", ssid); 81 | prefs.putString("pass", pass); 82 | prefs.end(); 83 | } 84 | 85 | // 写入时钟颜色 86 | void recordClockColor(int r,int g,int b){ 87 | prefs.begin("matrix"); 88 | prefs.putInt("red", r); 89 | prefs.putInt("green", g); 90 | prefs.putInt("blue", b); 91 | clockColor[0] = r; 92 | clockColor[1] = g; 93 | clockColor[2] = b; 94 | mainColor = matrix.Color(clockColor[0], clockColor[1], clockColor[2]); 95 | // 时间下面的星期条颜色,最大的RGB值不变,其他两个取反色 96 | int maxRGB = max(r, max(g, b)); // 求出RGB三色里的最大值 97 | bool findMax = false; // 是否找到最大值,防止3个值一样大,都不取反 98 | int weekR, weekG, weekB; 99 | if(r == maxRGB){ 100 | findMax = true; 101 | weekR = r; 102 | }else{ 103 | weekR = 255 - r; 104 | } 105 | if(g == maxRGB && !findMax){ 106 | findMax = true; 107 | weekG = g; 108 | }else{ 109 | weekG = 255 - g; 110 | } 111 | if(b == maxRGB && !findMax){ 112 | findMax = true; 113 | weekB = b; 114 | }else{ 115 | weekB = 255 - b; 116 | } 117 | weekColor = matrix.Color(weekR, weekG, weekB); 118 | prefs.end(); 119 | } 120 | 121 | // 写入亮度 122 | void recordBrightness(){ 123 | prefs.begin("matrix"); 124 | prefs.putInt("brightness", brightness); 125 | prefs.putInt("brightModel", brightModel); 126 | prefs.end(); 127 | } 128 | 129 | // 获取亮度 130 | void getBrightness(){ 131 | prefs.begin("matrix"); 132 | brightness = prefs.getInt("brightness", BRIGHTNESS); 133 | prefs.end(); 134 | } 135 | 136 | // 写入启动时是否需要配网的标志位 137 | void setApConfigWhenStart(bool apConfig){ 138 | prefs.begin("matrix"); 139 | prefs.putBool("apConfig",apConfig); 140 | prefs.end(); 141 | } 142 | 143 | // 写入小页面和其他页面模式 144 | void recordExtensionPage(){ 145 | prefs.begin("matrix"); 146 | prefs.putInt("timePage", timePage); 147 | prefs.putInt("rhythmPage", rhythmPage); 148 | prefs.putInt("animPage", animPage); 149 | prefs.putInt("anniversaryPage", anniversaryPage); 150 | prefs.putInt("bandsModel", rhythmBandsModel); 151 | prefs.putInt("brightModel", brightModel); 152 | prefs.putInt("timeModel", timeModel); 153 | prefs.end(); 154 | } 155 | 156 | // 写入闹钟相关的数据 157 | void recordClockPage(){ 158 | prefs.begin("matrix"); 159 | prefs.putInt("clockH", clockH); 160 | prefs.putInt("clockM", clockM); 161 | prefs.putInt("clockBellNum", clockBellNum); 162 | prefs.putBool("clockOpen", clockOpen); 163 | prefs.end(); 164 | } 165 | 166 | // 写入生日相关的数据 167 | void recordBirthday(){ 168 | prefs.begin("matrix"); 169 | prefs.putBool("birthdayOpen", birthdayOpen); 170 | prefs.putULong("birthday", birthday); 171 | prefs.end(); 172 | } 173 | 174 | // 写入纪念日相关的数据 175 | void recordAnniversary(){ 176 | prefs.begin("matrix"); 177 | prefs.putBool("anniversaryOpen", anniversaryOpen); 178 | prefs.putULong("anniversaryA", anniversaryA); 179 | prefs.putULong("anniversaryB", anniversaryB); 180 | prefs.end(); 181 | } 182 | 183 | // 写入时间跳变模式 184 | void recordAnim(){ 185 | prefs.begin("matrix"); 186 | prefs.putInt("timeModel", timeModel); 187 | prefs.end(); 188 | } -------------------------------------------------------------------------------- /preferencesUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef __PreferencesUtil_H 2 | #define __PreferencesUtil_H 3 | 4 | void getInfos(); 5 | void recordInfos(String ssid,String pass,int r,int g,int b,bool apConfig); 6 | void recordBrightness(); 7 | void getBrightness(); 8 | void setApConfigWhenStart(bool apConfig); 9 | void recordExtensionPage(); 10 | void recordClockPage(); 11 | void recordWifiConfig(); 12 | void recordClockColor(int r,int g,int b); 13 | void setBLEInfo(); 14 | void recordBirthday(); 15 | void recordAnniversary(); 16 | void recordAnim(); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /songs.h: -------------------------------------------------------------------------------- 1 | #define NOTE_B0 31 2 | #define NOTE_C1 33 3 | #define NOTE_CS1 35 4 | #define NOTE_D1 37 5 | #define NOTE_DS1 39 6 | #define NOTE_E1 41 7 | #define NOTE_F1 44 8 | #define NOTE_FS1 46 9 | #define NOTE_G1 49 10 | #define NOTE_GS1 52 11 | #define NOTE_A1 55 12 | #define NOTE_AS1 58 13 | #define NOTE_B1 62 14 | #define NOTE_C2 65 15 | #define NOTE_CS2 69 16 | #define NOTE_D2 73 17 | #define NOTE_DS2 78 18 | #define NOTE_E2 82 19 | #define NOTE_F2 87 20 | #define NOTE_FS2 93 21 | #define NOTE_G2 98 22 | #define NOTE_GS2 104 23 | #define NOTE_A2 110 24 | #define NOTE_AS2 117 25 | #define NOTE_B2 123 26 | #define NOTE_C3 131 27 | #define NOTE_CS3 139 28 | #define NOTE_D3 147 29 | #define NOTE_DS3 156 30 | #define NOTE_E3 165 31 | #define NOTE_F3 175 32 | #define NOTE_FS3 185 33 | #define NOTE_G3 196 34 | #define NOTE_GS3 208 35 | #define NOTE_A3 220 36 | #define NOTE_AS3 233 37 | #define NOTE_B3 247 38 | #define NOTE_C4 262 39 | #define NOTE_CS4 277 40 | #define NOTE_D4 294 41 | #define NOTE_DS4 311 42 | #define NOTE_E4 330 43 | #define NOTE_F4 349 44 | #define NOTE_FS4 370 45 | #define NOTE_G4 392 46 | #define NOTE_GS4 415 47 | #define NOTE_A4 440 48 | #define NOTE_AS4 466 49 | #define NOTE_B4 494 50 | #define NOTE_C5 523 51 | #define NOTE_CS5 554 52 | #define NOTE_D5 587 53 | #define NOTE_DS5 622 54 | #define NOTE_E5 659 55 | #define NOTE_F5 698 56 | #define NOTE_FS5 740 57 | #define NOTE_G5 784 58 | #define NOTE_GS5 831 59 | #define NOTE_A5 880 60 | #define NOTE_AS5 932 61 | #define NOTE_B5 988 62 | #define NOTE_C6 1047 63 | #define NOTE_CS6 1109 64 | #define NOTE_D6 1175 65 | #define NOTE_DS6 1245 66 | #define NOTE_E6 1319 67 | #define NOTE_F6 1397 68 | #define NOTE_FS6 1480 69 | #define NOTE_G6 1568 70 | #define NOTE_GS6 1661 71 | #define NOTE_A6 1760 72 | #define NOTE_AS6 1865 73 | #define NOTE_B6 1976 74 | #define NOTE_C7 2093 75 | #define NOTE_CS7 2217 76 | #define NOTE_D7 2349 77 | #define NOTE_DS7 2489 78 | #define NOTE_E7 2637 79 | #define NOTE_F7 2794 80 | #define NOTE_FS7 2960 81 | #define NOTE_G7 3136 82 | #define NOTE_GS7 3322 83 | #define NOTE_A7 3520 84 | #define NOTE_AS7 3729 85 | #define NOTE_B7 3951 86 | #define NOTE_C8 4186 87 | #define NOTE_CS8 4435 88 | #define NOTE_D8 4699 89 | #define NOTE_DS8 4978 90 | 91 | // 0:超级马里奥 92 | int SuperMario_note[] = { 93 | NOTE_E5,0,NOTE_E5,0,NOTE_E5,0,NOTE_C5,NOTE_E5,NOTE_G5,NOTE_G4, 94 | NOTE_C5,NOTE_G4,NOTE_E4,0,NOTE_A4,NOTE_B4,NOTE_AS4,NOTE_A4, 95 | NOTE_G4,NOTE_E5,NOTE_G5,NOTE_A5,NOTE_F5,NOTE_G5,NOTE_E5,NOTE_C5,NOTE_D5,NOTE_B4, 96 | NOTE_C5,NOTE_G4,NOTE_E4,0,NOTE_A4,NOTE_B4,NOTE_AS4,NOTE_A4, 97 | NOTE_G4,NOTE_E5,NOTE_G5,NOTE_A5,NOTE_F5,NOTE_G5,NOTE_E5,NOTE_C5,NOTE_D5,NOTE_B4, 98 | 0,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_DS5,NOTE_E5,0,NOTE_GS4,NOTE_A4,NOTE_C5,0,NOTE_A4,NOTE_C5,NOTE_D5, 99 | 0,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_DS5,NOTE_E5,0,NOTE_C6,0,NOTE_C6,0,NOTE_C6, 100 | 0,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_DS5,NOTE_E5,0,NOTE_GS4,NOTE_A4,NOTE_C5,0,NOTE_A4,NOTE_C5,NOTE_D5, 101 | 0,NOTE_DS5,NOTE_D5,NOTE_C5,0, 102 | 0,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_DS5,NOTE_E5,0,NOTE_GS4,NOTE_A4,NOTE_C5,0,NOTE_A4,NOTE_C5,NOTE_D5, 103 | 0,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_DS5,NOTE_E5,0,NOTE_C6,0,NOTE_C6,0,NOTE_C6, 104 | 0,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_DS5,NOTE_E5,0,NOTE_GS4,NOTE_A4,NOTE_C5,0,NOTE_A4,NOTE_C5,NOTE_D5, 105 | 0,NOTE_DS5,NOTE_D5,NOTE_C5,0, 106 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 107 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_E5,0, 108 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 109 | NOTE_E5,0,NOTE_E5,0,NOTE_E5,0,NOTE_C5,NOTE_E5,NOTE_G5,NOTE_G4, 110 | NOTE_C5,NOTE_G4,NOTE_E4,0,NOTE_A4,NOTE_B4,NOTE_AS4,NOTE_A4, 111 | NOTE_G4,NOTE_E5,NOTE_G5,NOTE_A5,NOTE_F5,NOTE_G5,NOTE_E5,NOTE_C5,NOTE_D5,NOTE_B4, 112 | NOTE_C5,NOTE_G4,NOTE_E4,0,NOTE_A4,NOTE_B4,NOTE_AS4,NOTE_A4, 113 | NOTE_G4,NOTE_E5,NOTE_G5,NOTE_A5,NOTE_F5,NOTE_G5,NOTE_E5,NOTE_C5,NOTE_D5,NOTE_B4, 114 | NOTE_E5,NOTE_C5,NOTE_G4,0,NOTE_GS4,NOTE_A4,NOTE_F5,0,NOTE_F5,NOTE_A4,0, 115 | NOTE_B4,NOTE_A5,0,NOTE_A5,0,NOTE_A5,NOTE_G5,NOTE_F5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 116 | NOTE_E5,NOTE_C5,NOTE_G4,0,NOTE_GS4,NOTE_A4,NOTE_F5,0,NOTE_F5,NOTE_A4,0, 117 | NOTE_B4,NOTE_F5,0,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_G4,NOTE_E4,NOTE_C4,0, 118 | NOTE_E5,NOTE_C5,NOTE_G4,0,NOTE_GS4,NOTE_A4,NOTE_F5,0,NOTE_F5,NOTE_A4,0, 119 | NOTE_B4,NOTE_A5,0,NOTE_A5,0,NOTE_A5,NOTE_G5,NOTE_F5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 120 | NOTE_E5,NOTE_C5,NOTE_G4,0,NOTE_GS4,NOTE_A4,NOTE_F5,0,NOTE_F5,NOTE_A4,0, 121 | NOTE_B4,NOTE_F5,0,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_G4,NOTE_E4,NOTE_C4,0, 122 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 123 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_E5,0, 124 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 125 | NOTE_E5,0,NOTE_E5,0,NOTE_E5,0,NOTE_C5,NOTE_E5,NOTE_G5,NOTE_G4, 126 | NOTE_E5,NOTE_C5,NOTE_G4,0,NOTE_GS4,NOTE_A4,NOTE_F5,0,NOTE_F5,NOTE_A4,0, 127 | NOTE_B4,NOTE_A5,0,NOTE_A5,0,NOTE_A5,NOTE_G5,NOTE_F5,NOTE_E5,NOTE_C5,NOTE_A4,NOTE_G4,0, 128 | NOTE_E5,NOTE_C5,NOTE_G4,0,NOTE_GS4,NOTE_A4,NOTE_F5,0,NOTE_F5,NOTE_A4,0, 129 | NOTE_B4,NOTE_F5,0,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_G4,NOTE_E4,NOTE_C4,0, 130 | }; 131 | int SuperMario_duration[] = { 132 | 16,128,8,128,16,16,16,8,4,4, 133 | 4,8,8,4,8,8,16,8, 134 | 8,8,8,8,16,8,8,16,16,4, 135 | 4,8,8,4,8,8,16,8, 136 | 8,8,8,8,16,8,8,16,16,4, 137 | 8,16,16,16,8,16,16,16,16,16,16,16,16,16, 138 | 8,16,16,16,8,16,16,8,16,16,16,4, 139 | 8,16,16,16,8,16,16,16,16,16,16,16,16,16, 140 | 8,4,4,4,8, 141 | 8,16,16,16,8,16,16,16,16,16,16,16,16,16, 142 | 8,16,16,16,8,16,16,8,16,16,16,4, 143 | 8,16,16,16,8,16,16,16,16,16,16,16,16,16, 144 | 8,4,4,4,8, 145 | 16,128,8,128,16,16,16,8,16,8,16,8,8, 146 | 16,128,8,128,16,16,16,8,4,4, 147 | 16,128,8,128,16,16,16,8,16,8,16,8,8, 148 | 16,128,8,128,16,16,16,8,4,4, 149 | 4,8,8,4,8,8,16,8, 150 | 8,8,8,8,16,8,8,16,16,4, 151 | 4,8,8,4,8,8,16,8, 152 | 8,8,8,8,16,8,8,16,16,4, 153 | 16,8,16,8,8,16,8,128,16,8,8, 154 | 8,8,128,8,128,8,8,8,16,8,16,8,8, 155 | 16,8,16,8,8,16,8,128,16,8,8, 156 | 8,8,128,8,128,8,8,8,16,8,16,8,8, 157 | 16,8,16,8,8,16,8,128,16,8,8, 158 | 8,8,128,8,128,8,8,8,16,8,16,8,8, 159 | 16,8,16,8,8,16,8,128,16,8,8, 160 | 8,8,128,8,128,8,8,8,16,8,16,8,8, 161 | 16,128,8,128,16,16,16,8,16,8,16,8,8, 162 | 16,128,8,128,16,16,16,8,4,4, 163 | 16,128,8,128,16,16,16,8,16,8,16,8,8, 164 | 16,128,8,128,16,16,16,8,4,4, 165 | 16,8,16,8,8,16,8,128,16,8,8, 166 | 8,8,128,8,128,8,8,8,16,8,16,8,8, 167 | 16,8,16,8,8,16,8,128,16,8,8, 168 | 8,8,128,8,128,8,8,8,16,8,16,8,8, 169 | }; 170 | 171 | // 1:Always With Me 172 | int AlwaysWithMe_note[] = { 173 | NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,NOTE_G5,NOTE_E5,NOTE_D5,NOTE_G5,NOTE_D5, 174 | NOTE_C5,NOTE_A4,NOTE_E5,NOTE_C5,NOTE_B4,0,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_G4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_D5,NOTE_C5,NOTE_D5, 175 | NOTE_E5,NOTE_C5,NOTE_G5,NOTE_E5,NOTE_D5,NOTE_G5,NOTE_D5,NOTE_C5,NOTE_A4,0,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_G4,0,NOTE_G4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_G4,NOTE_C5,NOTE_D5,NOTE_E5, 176 | NOTE_D5,0,NOTE_D5,0,NOTE_D5,NOTE_C5,0,NOTE_C5,0,0,NOTE_E5,NOTE_F5,NOTE_G5,0,NOTE_G5,0,NOTE_G5,0,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_F5,NOTE_E5,0,NOTE_E5,0,NOTE_E5,0,NOTE_E5,0,NOTE_E5,NOTE_F5,NOTE_E5,NOTE_D5, 177 | NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_B4,0,NOTE_B4,NOTE_C5,NOTE_D5,0,NOTE_D5,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5,0,NOTE_G5,0,NOTE_G5,0,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_F5, 178 | NOTE_E5,0,NOTE_E5,0,NOTE_E5,0,NOTE_E5,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_G4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_D5,0,NOTE_D5,0,NOTE_D5,NOTE_C5,0,NOTE_C5,0,0,NOTE_C5,NOTE_D5, 179 | NOTE_E5,NOTE_C5,NOTE_G5,NOTE_E5,NOTE_D5,NOTE_G5,NOTE_D5,NOTE_C5,NOTE_A4,NOTE_E5,NOTE_C5,NOTE_B4,0,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_G4,NOTE_C5,NOTE_D5,NOTE_E5, 180 | NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_D5,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,NOTE_G5,NOTE_E5,NOTE_D5,NOTE_G5,NOTE_D5,NOTE_C5,NOTE_A4,0,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_G4,0,NOTE_G4, 181 | NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_G4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_D5,0,NOTE_D5,0,NOTE_D5,NOTE_C5,0,NOTE_C5,0,NOTE_C5, 182 | }; 183 | int AlwaysWithMe_duration[] = { 184 | 12,12,12,12,5,12,6,6,6, 185 | 12,12,5,12,3,256,6,6,6,12,12,6,6,12,12,6,256,12,12,12,12,3,12,12, 186 | 12,12,5,12,6,6,6,12,12,256,6,12,12,3,256,6,6,6,12,12,6,6,12,12, 187 | 4,256,12,256,12,12,256,2,6,6,12,12,6,256,6,256,6,256,6,256,12,12,12,12,6,256,6,256,6,256,6,256,12,12,12,12, 188 | 6,256,6,256,12,12,6,6,256,12,12,6,256,12,12,12,12,3,12,12,6,256,6,256,6,256,6,256,12,12,12,12, 189 | 6,256,6,256,6,256,12,12,12,12,12,12,6,6,12,12,6,6,12,12,4,256,12,256,12,12,256,2,6,6,12,12, 190 | 12,12,5,12,6,6,6,12,12,5,12,3,256,6,6,6,12,12,6,6,12,12, 191 | 6,256,12,12,12,12,3,12,12,12,12,5,12,6,6,6,12,12,256,6,12,12,3,256,6, 192 | 6,6,12,12,6,6,12,12,4,256,12,256,12,12,256,2,256,2, 193 | }; 194 | 195 | // 2:梦中的婚礼 196 | int DreamWedding_note[] = { 197 | NOTE_A5,0,NOTE_A5,NOTE_B5,0,NOTE_B5,NOTE_C6,0,NOTE_C6,NOTE_B5,0,NOTE_B5,NOTE_A5,0,NOTE_A5,NOTE_E5,0,NOTE_E5,NOTE_C5,0,NOTE_C5,NOTE_A4,0,NOTE_A4,NOTE_G5,0, 198 | NOTE_G5,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_F5,0,NOTE_F5,0,NOTE_F5,NOTE_G5,0,NOTE_G5,NOTE_A5,0,NOTE_A5,NOTE_B5,0,NOTE_B5,NOTE_G5,0,NOTE_G5,NOTE_D5,0,NOTE_D5,NOTE_F5,0, 199 | NOTE_F5,NOTE_E5,0,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_E5,0,0,0,0,NOTE_E5,NOTE_A4,NOTE_C5,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_A4,NOTE_C5,NOTE_E5,NOTE_D5, 200 | NOTE_E5,NOTE_A4,NOTE_C5,NOTE_F5,NOTE_E5,NOTE_F5,NOTE_A4,NOTE_C5,NOTE_F5,NOTE_E5,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_F5,NOTE_FS5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_A5,NOTE_E5, 201 | NOTE_E5,NOTE_A4,NOTE_C5,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_A4,NOTE_C5,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_A4,NOTE_C5,NOTE_F5,NOTE_E5,NOTE_F5,NOTE_A4,NOTE_C5,NOTE_F5,NOTE_E5,NOTE_F5,0,NOTE_F5,NOTE_E5,NOTE_F5,NOTE_FS5, 202 | NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_A5,NOTE_E5,NOTE_C6,NOTE_E5,0,NOTE_E5,NOTE_F5,0,NOTE_F5,NOTE_D5,NOTE_B5,NOTE_A5,NOTE_B5,NOTE_D5,0,NOTE_D5,NOTE_E5,0, 203 | NOTE_E5,NOTE_C5,0,NOTE_C5,NOTE_A5,NOTE_G5,NOTE_A5,NOTE_C5,0,NOTE_C5,NOTE_D5,0,NOTE_D5,NOTE_B4,NOTE_E5,NOTE_D5,NOTE_E5, 204 | NOTE_C6,0,NOTE_C6,0,NOTE_C6,NOTE_D6,0,NOTE_D6,NOTE_C6,NOTE_B5,NOTE_A5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_E5, 205 | NOTE_C6,0,NOTE_C6,0,NOTE_C6,NOTE_D6,0,NOTE_D6,NOTE_C6,NOTE_B5,NOTE_A5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_A5, 206 | NOTE_C6,0,NOTE_C6,0,NOTE_C6,NOTE_D6,0,NOTE_D6,NOTE_C6,NOTE_B5,NOTE_A5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_E5, 207 | NOTE_C6,0,NOTE_C6,0,NOTE_C6,NOTE_D6,0,NOTE_D6,NOTE_C6,NOTE_B5,NOTE_A5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_A5, 208 | NOTE_C6,0,NOTE_C6,0,NOTE_C6,NOTE_D6,0,NOTE_D6,NOTE_C6,NOTE_B5,NOTE_A5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_E5, 209 | NOTE_C6,0,NOTE_C6,0,NOTE_C6,NOTE_D6,0,NOTE_D6,NOTE_C6,NOTE_B5,NOTE_A5,NOTE_G5,0,NOTE_G5,NOTE_A5,NOTE_G5,NOTE_A5, 210 | }; 211 | int DreamWedding_duration[] = { 212 | 12,256,12,12,256,12,12,256,12,12,256,12,12,256,12,12,256,12,12,256,12,12,256,12,12,256, 213 | 12,12,256,12,12,12,12,12,4,12,256,12,12,256,12,12,256,12,12,256,12,12,256,12,12,256,12,12,256, 214 | 12,12,256,12,12,12,12,6,6,6,6,6,6,12,12,12,12,6,12,12,12,12, 215 | 6,12,12,12,12,6,12,12,12,12,6,256,12,12,12,12,6,256,12,12,12,12,2, 216 | 6,12,12,12,12,6,12,12,12,12,6,12,12,12,12,6,12,12,12,12,6,256,12,12,12,12, 217 | 6,256,12,12,12,12,2,4,12,256,12,12,256,4,12,12,12,4,12,256,12,12,256, 218 | 6,12,256,12,12,12,4,12,256,12,12,256,4,12,12,12,2, 219 | 4,256,12,256,12,12,256,4,12,12,12,4,256,12,12,12,2, 220 | 4,256,12,256,12,12,256,4,12,12,12,4,256,12,12,12,2, 221 | 4,256,12,256,12,12,256,4,12,12,12,4,256,12,12,12,2, 222 | 4,256,12,256,12,12,256,4,12,12,12,4,256,12,12,12,2, 223 | 4,256,12,256,12,12,256,4,12,12,12,4,256,12,12,12,2, 224 | 4,256,12,256,12,12,256,4,12,12,12,4,256,12,12,12,2, 225 | }; 226 | 227 | // 3:天空之城 228 | int CastleInTheSky_note[] = { 229 | NOTE_A4,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_E5,NOTE_B4,NOTE_E4,NOTE_A4,NOTE_G4,NOTE_A4,NOTE_C5,NOTE_G4,NOTE_E4,0,NOTE_E4, 230 | NOTE_F4,NOTE_E4,NOTE_E4,NOTE_C5,NOTE_E4,0,NOTE_C5,0,NOTE_C5,0,NOTE_C5,NOTE_B4,NOTE_FS4,NOTE_F4,NOTE_B4,0,NOTE_B4, 231 | NOTE_A4,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_E5,NOTE_B4,NOTE_E4,NOTE_A4,NOTE_G4,NOTE_A4,NOTE_C5,NOTE_G4,NOTE_E4,0,NOTE_E4, 232 | NOTE_F4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_C5,0,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_GS4,NOTE_A4, 233 | NOTE_C5,NOTE_D5,NOTE_E5,NOTE_D5,NOTE_E5,NOTE_G5,NOTE_D5,NOTE_G4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_E5,0,NOTE_E5,NOTE_G4,0,NOTE_G4, 234 | NOTE_A4,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_C5,NOTE_G4,0,NOTE_G4,NOTE_F5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_E5, 235 | NOTE_E5,NOTE_A5,NOTE_G5,NOTE_E5,NOTE_D5,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_C5,NOTE_D5,NOTE_G5,NOTE_E5, 236 | NOTE_E5,NOTE_A5,NOTE_G5,NOTE_E5,NOTE_D5,NOTE_C5,0,NOTE_C5,NOTE_D5,NOTE_C5,NOTE_D5,NOTE_B4,NOTE_A4, 237 | }; 238 | int CastleInTheSky_duration[] = { 239 | 12,12,4,12,6,6,2,6,4,12,6,6,2,12,256,12, 240 | 4,12,12,4,3,12,12,256,12,256,12,4,12,6,6,256,2, 241 | 12,12,4,12,6,6,2,6,4,12,6,6,2,12,256,12, 242 | 6,12,4,6,6,12,2,256,12,12,6,6,6,2, 243 | 12,12,4,12,6,6,2,6,4,12,6,6,256,2,12,256,12, 244 | 12,12,6,12,12,6,4,12,256,3,6,6,6,6,2, 245 | 6,3,3,12,12,3,256,6,4,12,6,6,2, 246 | 6,3,3,12,12,3,256,6,4,12,6,6,2, 247 | }; 248 | 249 | // 4:卡农 250 | int Canon_note[] = { 251 | NOTE_G5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_G4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5, 252 | NOTE_E5,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_E4,NOTE_F4,NOTE_G4,NOTE_A4,NOTE_G4,NOTE_F4,NOTE_G4,NOTE_C5,NOTE_B4,NOTE_C5, 253 | NOTE_A4,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_G4,NOTE_F4,NOTE_G4,NOTE_F4,NOTE_E4,NOTE_F4,NOTE_G4,NOTE_A4,NOTE_B4,NOTE_C5, 254 | NOTE_A4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5, 255 | NOTE_G5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_G4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5, 256 | NOTE_E5,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_E4,NOTE_F4,NOTE_G4,NOTE_A4,NOTE_G4,NOTE_F4,NOTE_G4,NOTE_C5,NOTE_B4,NOTE_C5, 257 | NOTE_A4,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_G4,NOTE_F4,NOTE_G4,NOTE_F4,NOTE_E4,NOTE_F4,NOTE_G4,NOTE_A4,NOTE_B4,NOTE_C5, 258 | NOTE_A4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5, 259 | NOTE_E5,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_D5,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_D5,NOTE_C5,NOTE_B4, 260 | NOTE_C5,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_C4,NOTE_D4,NOTE_E4,NOTE_F4,NOTE_E4,NOTE_D4,NOTE_E4,NOTE_C5,NOTE_B4,NOTE_C5, 261 | NOTE_A4,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_G4,NOTE_F4,NOTE_G4,NOTE_F4,NOTE_E4,NOTE_F4,NOTE_G4,NOTE_A4,NOTE_B4,NOTE_C5, 262 | NOTE_A4,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_B4,NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_C5,NOTE_B4,NOTE_C5,NOTE_A4,NOTE_B4, 263 | NOTE_C5, 264 | }; 265 | int Canon_duration[] = { 266 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 267 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 268 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 269 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 270 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 271 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 272 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 273 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 274 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 275 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 276 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 277 | 4,8,8,4,8,8,8,8,8,8,8,8,8,8, 278 | 1, 279 | }; 280 | 281 | // 5: HAPPY BIRTHDAY 282 | int HappyBirthday_note[] = { 283 | NOTE_C4, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_F4, NOTE_E4, 284 | NOTE_C4, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_G4, NOTE_F4, 285 | NOTE_C4, NOTE_C4, NOTE_C5, NOTE_A4, NOTE_F4, NOTE_E4, NOTE_D4, 286 | NOTE_AS4, NOTE_AS4, NOTE_A4, NOTE_F4, NOTE_G4, NOTE_F4 287 | }; 288 | 289 | int HappyBirthday_duration[] = { 290 | 4, 4, 2, 2, 2, 1, 291 | 4, 4, 2, 2, 2, 1, 292 | 4, 4, 2, 2, 2, 2, 1, 293 | 4, 4, 2, 2, 2, 1, 294 | }; 295 | 296 | //6.生日快乐歌 297 | int HB_note[] = { 298 | NOTE_G3,NOTE_E4,NOTE_C4,NOTE_D4,NOTE_E4,NOTE_C4,NOTE_D4,NOTE_G3,NOTE_G3, 299 | NOTE_E3,NOTE_C4,NOTE_A3,NOTE_B3,NOTE_C4,NOTE_A3,NOTE_B3,NOTE_G4,NOTE_G4, 300 | NOTE_F4,NOTE_F4,NOTE_E4,NOTE_D4,NOTE_E4,NOTE_F4,NOTE_E4,NOTE_D4,NOTE_C4,NOTE_A3, 301 | NOTE_C4,NOTE_C4,NOTE_A3,NOTE_C4,NOTE_D4,NOTE_E4,NOTE_D4, 302 | 303 | NOTE_G3,NOTE_E4,NOTE_C4,NOTE_D4,NOTE_E4,NOTE_C4,NOTE_C4,NOTE_D4,NOTE_G3,NOTE_G3, 304 | NOTE_E3,NOTE_C4,NOTE_A3,NOTE_B3,NOTE_C4,NOTE_A3,NOTE_A3,NOTE_B3,NOTE_G4,NOTE_G4, 305 | NOTE_F4,NOTE_F4,NOTE_E4,NOTE_D4,NOTE_E4,NOTE_F4,NOTE_E4,NOTE_D4,NOTE_C4,NOTE_A3, 306 | NOTE_C4,NOTE_C4,NOTE_A3,NOTE_C4,NOTE_D4,NOTE_E4,NOTE_D4, 307 | NOTE_B3,NOTE_C4,NOTE_C4,NOTE_D4,NOTE_C4 308 | }; 309 | 310 | int HB_duration[] = { 311 | 8,4,8,8,4,4,4,4,2, 312 | 8,4,8,8,4,4,4,4,2, 313 | 4,8,8,4,8,8,4,4,4,4, 314 | 4,8,8,4,8,8,1, 315 | 316 | 8,4,8,8,4,8,8,4,4,2, 317 | 8,4,8,8,4,8,8,4,4,2, 318 | 4,8,8,4,8,8,4,4,4,4, 319 | 4,8,8,4,8,8,2, 320 | 8,4,8,8,1 321 | }; -------------------------------------------------------------------------------- /task.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Ticker.h" 3 | #include "net.h" 4 | #include "common.h" 5 | #include "light.h" 6 | #include "buzzer.h" 7 | #include "preferencesUtil.h" 8 | #include "BLE.h" 9 | 10 | enum CurrentPage currentPage = SETTING; 11 | bool playingMusic = false; // 是否正在播放音乐 12 | bool belling = false; // 是否正在播放铃声 13 | bool birthdayBell = false; // 是否正在播放生日歌 14 | bool isCheckingTime = false; // 是否正在NTP对时中 15 | 16 | void startShowText(void *param); 17 | void startPlaySongs(void *param); 18 | void startPlayBell(void *param); 19 | void startPlayBirthdaySong(void *param); 20 | void startTickerClock(int32_t seconds); 21 | int32_t getClockRemainSeconds(); 22 | void btn1click(); 23 | void btn2click(); 24 | void btn3click(); 25 | void btn1LongClick(); 26 | void btn2LongClick(); 27 | void btn3LongClick(); 28 | void createPlaySongsTask(); 29 | void createBellTask(); 30 | void createPlayBirthdaySongTask(); 31 | void cancelBell(); 32 | 33 | ///////////////////////////////////Freertos区域/////////////////////////////////////// 34 | // 按钮 35 | OneButton button1(BTN1, true); 36 | OneButton button2(BTN2, true); 37 | OneButton button3(BTN3, true); 38 | //闹钟响铃任务句柄 39 | TaskHandle_t bellTask; 40 | //播放歌曲的任务句柄(闹钟页面使用) 41 | TaskHandle_t playSongsTask; 42 | //播放生日歌的任务句柄 43 | TaskHandle_t playBirthdaySongTask; 44 | //系统启动动画的任务句柄 45 | TaskHandle_t showTextTask; 46 | 47 | //创建闹铃任务 48 | void createBellTask(){ 49 | xTaskCreate(startPlayBell, "startPlayBell", 1000, NULL, 1, &bellTask); 50 | } 51 | //创建音乐播放任务(闹钟页面使用) 52 | void createPlaySongsTask(){ 53 | xTaskCreate(startPlaySongs, "startPlaySongs", 1000, NULL, 1, &playSongsTask); 54 | } 55 | //创建生日歌播放任务 56 | void createPlayBirthdaySongTask(){ 57 | xTaskCreate(startPlayBirthdaySong, "startPlayBirthdaySong", 4096, NULL, 1, &playBirthdaySongTask); 58 | } 59 | //创建文字显示任务 60 | void createShowTextTask(const char *text){ 61 | xTaskCreate(startShowText, "startShowText", 1000, (void *)text, 1, &showTextTask); 62 | } 63 | 64 | // 启动文字显示 65 | void startShowText(void *param){ 66 | String text = (char *)param; 67 | int x; 68 | if(text.equals("START")){ 69 | x = 2; 70 | }else if(text.equals("CONFIG")){ 71 | x = 0; 72 | }else if(text.equals("BLE")){ 73 | x = 7; 74 | } 75 | int index = 0; 76 | while (true) { 77 | if(index % 3 == 0){ 78 | drawText(x, 6, text + "."); 79 | }else if(index % 3 == 1){ 80 | drawText(x, 6, text + ".."); 81 | }else if(index % 3 == 2){ 82 | drawText(x, 6, text + "..."); 83 | } 84 | vTaskDelay(1000); 85 | index++; 86 | } 87 | Serial.println("退出启动文字显示"); 88 | } 89 | 90 | // 播放歌曲(闹钟页面使用) 91 | void startPlaySongs(void *param){ 92 | while (true) { 93 | playSong(false); 94 | vTaskDelay(3000); 95 | } 96 | Serial.println("退出音乐播放"); 97 | } 98 | // 响铃 99 | void startPlayBell(void *param){ 100 | while (true) { 101 | playSong(true); 102 | vTaskDelay(3000); 103 | } 104 | Serial.println("退出响铃任务"); 105 | } 106 | // 生日歌播放 107 | void startPlayBirthdaySong(void *param){ 108 | while (true) { 109 | playBirthdaySong(); 110 | vTaskDelay(3000); 111 | } 112 | Serial.println("退出生日歌播放"); 113 | } 114 | 115 | ////////////////////////////////////////////////////////////////////////////////////// 116 | 117 | 118 | ///////////////////////////////////定时器区域/////////////////////////////////////// 119 | Ticker tickerClock; 120 | Ticker tickerCheckTime; 121 | // NTP对时 122 | void checkTime(){ 123 | // 只要将对时标志置为true即可,在主循环中进行对时操作 124 | isCheckingTime = true; 125 | } 126 | // 根据NVS中的闹钟时间计算出定时器需要多久之后触发 127 | int32_t getClockRemainSeconds(){ 128 | // 获取RTC时间 129 | struct tm timeinfo; 130 | if (!getLocalTime(&timeinfo)){ 131 | Serial.println("获取RTC时间失败"); 132 | return -1; 133 | } 134 | int32_t nowTime = (timeinfo.tm_hour * 60 + timeinfo.tm_min) * 60 + timeinfo.tm_sec; 135 | int32_t clockTime = (clockH * 60 + clockM) * 60; 136 | if(clockTime > nowTime){ // 闹钟时间在当天 137 | return (clockTime - nowTime); 138 | }else{ // 闹钟时间在第二天 139 | return (clockTime + ONE_DAY_SECONDS - nowTime); 140 | } 141 | } 142 | // 奏响闹铃 143 | void ringingBell() { 144 | // 配置页面和闹钟设置页面不响铃 145 | if(currentPage == SETTING || currentPage == CLOCK){ 146 | return; 147 | } 148 | // 进入时钟页面 149 | if(currentPage == RHYTHM){ 150 | // 将二维数组重置为0 151 | memset(matrixArray,0,sizeof(matrixArray)); 152 | // 将已点亮灯的个数置零 153 | lightedCount = 0; 154 | } 155 | currentPage = TIME; 156 | drawTimeFirstTime = true; 157 | // 创建播放音乐的任务 158 | createBellTask(); 159 | belling = true; 160 | // 将定时器重置 161 | tickerClock.detach(); 162 | startTickerClock(getClockRemainSeconds()); 163 | } 164 | // 开启定时器 165 | void startTickerClock(int32_t seconds){ 166 | // seconds秒后,执行一次 167 | tickerClock.once(seconds, ringingBell); 168 | } 169 | void startTickerCheckTime(){ 170 | // 每隔一段时间进行一次NTP对时 171 | tickerCheckTime.attach(TIME_CHECK_INTERVAL, checkTime); 172 | } 173 | // 在闹钟响铃时,任何按键按下,都取消闹铃 174 | void cancelBell(){ 175 | vTaskDelete(bellTask); 176 | delay(300); 177 | belling = false; 178 | } 179 | 180 | ////////////////////////////////////////////////////////////////////////////////////// 181 | 182 | 183 | /////////////////////////////////////// 按键区//////////////////////////////////////// 184 | // 初始化各按键 185 | void btnInit(){ 186 | button1.attachClick(btn1click); 187 | button1.setDebounceMs(10); //设置消抖时长 188 | button2.attachClick(btn2click); 189 | button2.setDebounceMs(10); //设置消抖时长 190 | button3.attachClick(btn3click); 191 | button3.setDebounceMs(10); //设置消抖时长 192 | button1.attachLongPressStart(btn1LongClick); 193 | button1.setPressMs(1200); //设置长按时间 194 | button2.attachLongPressStart(btn2LongClick); 195 | button2.setPressMs(1200); //设置长按时间 196 | button3.attachLongPressStart(btn3LongClick); 197 | button3.setPressMs(1200); //设置长按时间 198 | } 199 | // 监控按键 200 | void watchBtn(){ 201 | button1.tick(); 202 | button2.tick(); 203 | button3.tick(); 204 | } 205 | // 按键方法 206 | // >按键 207 | void btn1click(){ 208 | if(belling){ 209 | cancelBell(); 210 | return; 211 | } 212 | if (playBirthdaySongTask != NULL) { // 在播放生日歌时,任何按键按下,都取消播放,并且后续不再播放 213 | vTaskDelete(playBirthdaySongTask); 214 | playBirthdaySongTask = NULL; 215 | return; 216 | } 217 | switch(currentPage){ 218 | case TIME: 219 | if(timePage == TIME_H_M_S){ 220 | timePage = TIME_H_M; 221 | }else if(timePage == TIME_H_M){ 222 | timePage = TIME_DATE; 223 | }else{ 224 | timePage = TIME_H_M_S; 225 | } 226 | // 清屏 227 | clearMatrix(); 228 | // 保存设置 229 | recordExtensionPage(); 230 | // 绘制时间 231 | drawTimeFirstTime = true; 232 | drawTime(); 233 | break; 234 | case RHYTHM: 235 | if(rhythmPage == RHYTHM_MODEL1){ 236 | rhythmPage = RHYTHM_MODEL2; 237 | }else if(rhythmPage == RHYTHM_MODEL2){ 238 | rhythmPage = RHYTHM_MODEL3; 239 | }else if(rhythmPage == RHYTHM_MODEL3){ 240 | rhythmPage = RHYTHM_MODEL4; 241 | }else if(rhythmPage == RHYTHM_MODEL4){ 242 | rhythmPage = RHYTHM_MODEL1; 243 | } 244 | // 保存设置 245 | recordExtensionPage(); 246 | break; 247 | case ANIM: 248 | if(animPage == ANIM_MODEL1){ 249 | animPage = ANIM_MODEL2; 250 | }else if(animPage == ANIM_MODEL2){ 251 | // 将已点亮灯的个数置零 252 | lightedCount = 0; 253 | animPage = ANIM_MODEL3; 254 | }else if(animPage == ANIM_MODEL3){ 255 | animPage = ANIM_MODEL1; 256 | } 257 | // 将二维数组重置为0 258 | memset(matrixArray,0,sizeof(matrixArray)); 259 | // 保存设置 260 | recordExtensionPage(); 261 | // 清屏 262 | clearMatrix(); 263 | break; 264 | case CLOCK: 265 | if(!clockOpen){ 266 | return; 267 | } 268 | if(clockChoosed == CLOCK_H){ 269 | tmpClockH++; 270 | if(tmpClockH == 24){ 271 | tmpClockH = 0; 272 | } 273 | drawClock(); 274 | }else if(clockChoosed == CLOCK_M){ 275 | tmpClockM++; 276 | if(tmpClockM == 60){ 277 | tmpClockM = 0; 278 | } 279 | drawClock(); 280 | }else if(clockChoosed == CLOCK_BELL){ 281 | tmpClockBellNum++; 282 | if(tmpClockBellNum == songCount){ 283 | tmpClockBellNum = 0; 284 | } 285 | // 停止音乐播放 286 | vTaskDelete(playSongsTask); 287 | delay(300); 288 | // 开始音乐播放 289 | createPlaySongsTask(); 290 | } 291 | break; 292 | case BRIGHT: 293 | if(brightModel == BRIGHT_MODEL_AUTO) return; 294 | if(brightness + BRIGHTNESS_SPACING > MAX_BRIGHTNESS){ 295 | brightness = MAX_BRIGHTNESS; 296 | recordBrightness(); 297 | setBLEBrightness(brightness); 298 | drawBright(); 299 | Serial.println("已达最大亮度"); 300 | return; 301 | } 302 | brightness+=BRIGHTNESS_SPACING; 303 | Serial.print("当前亮度:");Serial.println(brightness); 304 | recordBrightness(); 305 | setBLEBrightness(brightness); 306 | drawBright(); 307 | break; 308 | case ANNIVERSARY: 309 | if(anniversaryPage == ANNIVERSARY_A){ 310 | anniversaryPage = ANNIVERSARY_B; 311 | }else if (anniversaryPage == ANNIVERSARY_B){ 312 | anniversaryPage = ANNIVERSARY_A; 313 | } 314 | default: 315 | break; 316 | } 317 | } 318 | // <按键 319 | void btn2click(){ 320 | if(belling){ 321 | cancelBell(); 322 | return; 323 | } 324 | if (playBirthdaySongTask != NULL) { 325 | vTaskDelete(playBirthdaySongTask); 326 | playBirthdaySongTask = NULL; 327 | return; 328 | } 329 | switch(currentPage){ 330 | case TIME: 331 | if(timePage == TIME_H_M_S){ 332 | timePage = TIME_DATE; 333 | }else if(timePage == TIME_H_M){ 334 | timePage = TIME_H_M_S; 335 | }else{ 336 | timePage = TIME_H_M; 337 | } 338 | // 清屏 339 | clearMatrix(); 340 | // 保存设置 341 | recordExtensionPage(); 342 | // 绘制时间 343 | drawTimeFirstTime = true; 344 | drawTime(); 345 | break; 346 | case RHYTHM: 347 | if(rhythmPage == RHYTHM_MODEL1){ 348 | rhythmPage = RHYTHM_MODEL4; 349 | }else if(rhythmPage == RHYTHM_MODEL2){ 350 | rhythmPage = RHYTHM_MODEL1; 351 | }else if(rhythmPage == RHYTHM_MODEL3){ 352 | rhythmPage = RHYTHM_MODEL2; 353 | }else if(rhythmPage == RHYTHM_MODEL4){ 354 | rhythmPage = RHYTHM_MODEL3; 355 | } 356 | // 保存设置 357 | recordExtensionPage(); 358 | break; 359 | case ANIM: 360 | if(animPage == ANIM_MODEL1){ 361 | // 将已点亮灯的个数置零 362 | lightedCount = 0; 363 | animPage = ANIM_MODEL3; 364 | }else if(animPage == ANIM_MODEL2){ 365 | animPage = ANIM_MODEL1; 366 | }else if(animPage == ANIM_MODEL3){ 367 | animPage = ANIM_MODEL2; 368 | } 369 | // 将二维数组重置为0 370 | memset(matrixArray,0,sizeof(matrixArray)); 371 | // 保存设置 372 | recordExtensionPage(); 373 | // 清屏 374 | clearMatrix(); 375 | break; 376 | case CLOCK: 377 | if(!clockOpen){ 378 | return; 379 | } 380 | if(clockChoosed == CLOCK_H){ 381 | tmpClockH--; 382 | if(tmpClockH == -1){ 383 | tmpClockH = 23; 384 | } 385 | drawClock(); 386 | }else if(clockChoosed == CLOCK_M){ 387 | tmpClockM--; 388 | if(tmpClockM == -1){ 389 | tmpClockM = 59; 390 | } 391 | drawClock(); 392 | }else if(clockChoosed == CLOCK_BELL){ 393 | tmpClockBellNum--; 394 | if(tmpClockBellNum == -1){ 395 | tmpClockBellNum = (songCount - 1); 396 | } 397 | // 停止音乐播放 398 | vTaskDelete(playSongsTask); 399 | delay(300); 400 | // 开始音乐播放 401 | createPlaySongsTask(); 402 | } 403 | break; 404 | case BRIGHT: 405 | if(brightModel == BRIGHT_MODEL_AUTO) return; 406 | if(brightness <= MIN_BRIGHTNESS + BRIGHTNESS_SPACING){ 407 | brightness = MIN_BRIGHTNESS; 408 | recordBrightness(); 409 | setBLEBrightness(brightness); 410 | drawBright(); 411 | Serial.println("已达最小亮度"); 412 | return; 413 | } 414 | brightness -= BRIGHTNESS_SPACING; 415 | Serial.print("当前亮度:");Serial.println(brightness); 416 | recordBrightness(); 417 | setBLEBrightness(brightness); 418 | drawBright(); 419 | break; 420 | case ANNIVERSARY: 421 | if(anniversaryPage == ANNIVERSARY_A){ 422 | anniversaryPage = ANNIVERSARY_B; 423 | }else if (anniversaryPage == ANNIVERSARY_B){ 424 | anniversaryPage = ANNIVERSARY_A; 425 | } 426 | default: 427 | break; 428 | } 429 | } 430 | void btn3click(){ 431 | if(belling){ 432 | cancelBell(); 433 | return; 434 | } 435 | if (playBirthdaySongTask != NULL) { 436 | vTaskDelete(playBirthdaySongTask); 437 | playBirthdaySongTask = NULL; 438 | return; 439 | } 440 | switch(currentPage){ 441 | case SETTING: 442 | // 关闭等待蓝牙连接的动画任务 443 | if (showTextTask != NULL && !RTCSuccess) { 444 | vTaskDelete(showTextTask); 445 | showTextTask = NULL; 446 | delay(300); 447 | // 清空屏幕 448 | clearMatrix(); 449 | // 进入节奏灯界面 450 | currentPage = RHYTHM; 451 | } 452 | break; 453 | case TIME: 454 | // 清屏 455 | clearMatrix(); 456 | currentPage = RHYTHM; 457 | break; 458 | case RHYTHM: 459 | // 将二维数组重置为0 460 | memset(matrixArray,0,sizeof(matrixArray)); 461 | // 将已点亮灯的个数置零 462 | lightedCount = 0; 463 | // 清屏 464 | clearMatrix(); 465 | currentPage = ANIM; 466 | break; 467 | case ANIM: 468 | if (!RTCSuccess){ 469 | // 至亮度调节页面 470 | currentPage = BRIGHT; 471 | drawBright(); 472 | }else{ 473 | currentPage = CLOCK; 474 | resetTmpClockData(); 475 | drawClock(); 476 | } 477 | break; 478 | case CLOCK: 479 | // 保存当前设置的闹钟信息 480 | clockH = tmpClockH; 481 | clockM = tmpClockM; 482 | clockBellNum = tmpClockBellNum; 483 | // 记录闹钟配置信息 484 | recordClockPage(); 485 | // 将定时器重置 486 | tickerClock.detach(); 487 | if(clockOpen){ 488 | startTickerClock(getClockRemainSeconds()); 489 | } 490 | // 如果正在播放音乐,则停止音乐播放 491 | if(playingMusic){ 492 | vTaskDelete(playSongsTask); 493 | delay(300); 494 | } 495 | playingMusic = false; 496 | currentPage = BRIGHT; 497 | drawBright(); 498 | break; 499 | case BRIGHT: 500 | if (anniversaryOpen){ 501 | currentPage = ANNIVERSARY; 502 | } else { 503 | currentPage = TIME; 504 | } 505 | break; 506 | case ANNIVERSARY: 507 | if (!RTCSuccess){ 508 | // 至节奏灯页面 509 | currentPage = RHYTHM; 510 | }else{ 511 | currentPage = TIME; 512 | drawTimeFirstTime = true; 513 | } 514 | break; 515 | default: 516 | break; 517 | } 518 | } 519 | void btn1LongClick(){ 520 | if(belling){ 521 | cancelBell(); 522 | return; 523 | } 524 | if(currentPage != CLOCK || !clockOpen){ 525 | return; 526 | } 527 | if(clockChoosed == CLOCK_H){ 528 | clockChoosed = CLOCK_M; 529 | drawClock(); 530 | }else if(clockChoosed == CLOCK_M){ 531 | clockChoosed = CLOCK_BELL; 532 | drawClock(); 533 | // 演奏当前铃声 534 | createPlaySongsTask(); 535 | playingMusic = true; 536 | }else if(clockChoosed == CLOCK_BELL){ 537 | // 停止演奏 538 | vTaskDelete(playSongsTask); 539 | delay(300); 540 | playingMusic = false; 541 | clockChoosed = CLOCK_H; 542 | drawClock(); 543 | } 544 | } 545 | void btn2LongClick(){ 546 | if(belling){ 547 | cancelBell(); 548 | return; 549 | } 550 | if(currentPage != CLOCK || !clockOpen){ 551 | return; 552 | } 553 | if(clockChoosed == CLOCK_H){ 554 | clockChoosed = CLOCK_BELL; 555 | drawClock(); 556 | // 演奏当前铃声 557 | createPlaySongsTask(); 558 | playingMusic = true; 559 | }else if(clockChoosed == CLOCK_M){ 560 | clockChoosed = CLOCK_H; 561 | drawClock(); 562 | }else if(clockChoosed == CLOCK_BELL){ 563 | // 停止演奏 564 | vTaskDelete(playSongsTask); 565 | delay(300); 566 | playingMusic = false; 567 | clockChoosed = CLOCK_M; 568 | drawClock(); 569 | } 570 | } 571 | void btn3LongClick(){ 572 | if(belling){ 573 | cancelBell(); 574 | return; 575 | } 576 | if(currentPage == SETTING){ 577 | if (advertising && !BLEConnected) { 578 | if (RTCSuccess){ 579 | disconnectBLE(); 580 | stopAdvertising(); 581 | if (showTextTask != NULL) { 582 | vTaskDelete(showTextTask); 583 | showTextTask = NULL; 584 | } 585 | clearMatrix(); 586 | drawFailed(6, 21, "BLE"); 587 | currentPage = TIME; 588 | } 589 | } 590 | return; 591 | } 592 | if(currentPage == CLOCK){ // 闹钟页面长按,关闭/开启闹钟 593 | if(clockChoosed == CLOCK_BELL){ 594 | if(playingMusic){ 595 | // 停止演奏 596 | vTaskDelete(playSongsTask); 597 | delay(300); 598 | playingMusic = false; 599 | }else{ 600 | // 演奏当前铃声 601 | createPlaySongsTask(); 602 | playingMusic = true; 603 | } 604 | } 605 | clockOpen = !clockOpen; 606 | drawClock(); 607 | recordClockPage(); 608 | }else if(currentPage == RHYTHM){ // 节奏灯页面长按,切换频段模式 609 | if(rhythmBandsModel == RHYTHM_BANDS_MODEL1){ 610 | rhythmBandsModel = RHYTHM_BANDS_MODEL2; 611 | }else{ 612 | rhythmBandsModel = RHYTHM_BANDS_MODEL1; 613 | } 614 | // 将相关数组重置为0 615 | memset(peak,0,NUM_BANDS); 616 | memset(oldBarHeights,0,NUM_BANDS); 617 | // 保存新模式 618 | recordExtensionPage(); 619 | }else if(currentPage == BRIGHT){ // 亮度页面长按,切换亮度调节模式 620 | if(brightModel == BRIGHT_MODEL_MANUAL){ 621 | brightModel = BRIGHT_MODEL_AUTO; 622 | setBLEBrightness(brightness); 623 | // 重置采样值 624 | clearBrightSampling(); 625 | }else{ 626 | brightModel = BRIGHT_MODEL_MANUAL; 627 | setBLEBrightness(brightness); 628 | } 629 | // 保存新模式 630 | recordExtensionPage(); 631 | // 重新读取NVS中的亮度值 632 | getBrightness(); 633 | // 刷新页面 634 | drawBright(); 635 | }else if(currentPage == TIME){ // 时钟页面长按,开关蓝牙 636 | if (advertising || BLEConnected){ 637 | clearMatrix(); 638 | drawFailed(6, 21, "BLE"); 639 | disconnectBLE(); 640 | stopAdvertising(); 641 | currentPage = SETTING; 642 | } else if (!advertising && !BLEConnected) { 643 | currentPage = SETTING; 644 | startAdvertising(); 645 | createShowTextTask("BLE"); 646 | } else if (advertising && !BLEConnected) { 647 | if (RTCSuccess){ 648 | disconnectBLE(); 649 | stopAdvertising(); 650 | if (showTextTask != NULL) { 651 | vTaskDelete(showTextTask); 652 | showTextTask = NULL; 653 | } 654 | clearMatrix(); 655 | drawFailed(6, 21, "BLE"); 656 | currentPage = TIME; 657 | } 658 | } 659 | } 660 | } 661 | /////////////////////////////////////////////////////////////// 662 | 663 | -------------------------------------------------------------------------------- /task.h: -------------------------------------------------------------------------------- 1 | #ifndef __TASK_H 2 | #define __TASK_H 3 | 4 | #include "common.h" 5 | #include "Ticker.h" 6 | 7 | extern enum CurrentPage currentPage; 8 | extern TaskHandle_t btnTask; 9 | extern TaskHandle_t showTextTask; 10 | extern TaskHandle_t showIpTask; 11 | extern TaskHandle_t playBirthdaySongTask; 12 | void watchBtn(); 13 | void createShowTextTask(const char *text); 14 | void createBellTask(); 15 | void createPlaySongsTask(); 16 | void createPlayBirthdaySongTask(); 17 | void btnInit(); 18 | int32_t getClockRemainSeconds(); 19 | extern Ticker tickerClock; 20 | extern Ticker tickerCheckTime; 21 | extern Ticker tickerAnim; 22 | extern bool isCheckingTime; 23 | void startTickerClock(int32_t seconds); 24 | void startTickerCheckTime(); 25 | extern bool birthdayBell; 26 | 27 | #endif 28 | 29 | --------------------------------------------------------------------------------