From b01d768d28228536723e707655feb18888276e5e Mon Sep 17 00:00:00 2001 From: chenxin Date: Tue, 17 Jun 2025 17:24:26 +0800 Subject: [PATCH] 25/06/17 --- platformio.ini | 3 +- src/DHT11.cpp | 240 +++++++++++++++++++++++++++ src/DHT11.h | 110 +++++++++++++ src/DS18B20.cpp | 234 -------------------------- src/DS18B20.h | 66 -------- src/DS3502.cpp | 4 +- src/LightControl.cpp | 56 +++++-- src/LightControl.h | 7 +- src/RunTime.cpp | 5 +- src/SHT31.cpp | 380 +++++++++++++++++++++++++++++++++++++++++++ src/SHT31.h | 122 ++++++++++++++ src/TJC_Show.cpp | 327 ++++++++++++++++++++++++++++++++----- src/TJC_Show.h | 28 +++- src/WebServer.h | 1 - src/WiFiControl.cpp | 1 + src/main.cpp | 339 ++++++++++++++++++++++++-------------- src/webpages.h | 143 +++++++++++----- 17 files changed, 1535 insertions(+), 531 deletions(-) create mode 100644 src/DHT11.cpp create mode 100644 src/DHT11.h delete mode 100644 src/DS18B20.cpp delete mode 100644 src/DS18B20.h create mode 100644 src/SHT31.cpp create mode 100644 src/SHT31.h diff --git a/platformio.ini b/platformio.ini index 916fe35..07be14b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,6 +14,7 @@ board = esp32-s3-devkitc-1 framework = arduino lib_deps = robtillaart/INA226@^0.6.0 - robtillaart/DS18B20@^0.2.4 esphome/ESPAsyncWebServer-esphome@^3.3.0 + bblanchon/ArduinoJson@^7.4.1 monitor_speed = 115200 + diff --git a/src/DHT11.cpp b/src/DHT11.cpp new file mode 100644 index 0000000..55ebb6c --- /dev/null +++ b/src/DHT11.cpp @@ -0,0 +1,240 @@ +/** + * DHT11.cpp + * Library for reading temperature and humidity from the DHT11 sensor. + * + * Author: Dhruba Saha + * Version: 2.1.0 + * License: MIT + */ + +#include "DHT11.h" + +/** + * Constructor for the DHT11 class. + * Initializes the pin to be used for communication and sets it to output mode. + * + * @param pin: Digital pin number on the Arduino board to which the DHT11 sensor is connected. + */ +DHT11::DHT11(int pin) : _pin(pin) +{ + pinMode(_pin, OUTPUT); + digitalWrite(_pin, HIGH); +} + +/** + * Sets the delay between consecutive sensor readings. + * If this method is not called, a default delay of 500 milliseconds is used. + * + * @param delay: Delay duration in milliseconds between sensor readings. + */ +void DHT11::setDelay(unsigned long delay) +{ + _delayMS = delay; +} + +/** + * Reads raw data from the DHT11 sensor. + * This method handles the direct communication with the DHT11 sensor and retrieves the raw data. + * It's used internally by the readTemperature, readHumidity, and readTemperatureHumidity methods. + * + * @param data: An array of bytes where the raw sensor data will be stored. + * The array must be at least 5 bytes long, as the DHT11 sensor returns 5 bytes of data. + * @return: Returns 0 if the data is read successfully and the checksum matches. + * Returns DHT11::ERROR_TIMEOUT if the sensor does not respond or communication times out. + * Returns DHT11::ERROR_CHECKSUM if the data is read but the checksum does not match. + */ +int DHT11::readRawData(byte data[5]) +{ + delay(_delayMS); + startSignal(); + unsigned long timeout_start = millis(); + + while (digitalRead(_pin) == HIGH) + { + if (millis() - timeout_start > TIMEOUT_DURATION) + { + return DHT11::ERROR_TIMEOUT; + } + } + + if (digitalRead(_pin) == LOW) + { + delayMicroseconds(80); + if (digitalRead(_pin) == HIGH) + { + delayMicroseconds(80); + for (int i = 0; i < 5; i++) + { + data[i] = readByte(); + if (data[i] == DHT11::ERROR_TIMEOUT) + { + return DHT11::ERROR_TIMEOUT; + } + } + + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) + { + return 0; // Success + } + else + { + return DHT11::ERROR_CHECKSUM; + } + } + } + return DHT11::ERROR_TIMEOUT; +} + +/** + * Reads a byte of data from the DHT11 sensor during the communication process. + * + * @return: A byte of data read from the sensor. + */ +byte DHT11::readByte() +{ + byte value = 0; + + for (int i = 0; i < 8; i++) + { + while (digitalRead(_pin) == LOW) + ; + delayMicroseconds(30); + if (digitalRead(_pin) == HIGH) + { + value |= (1 << (7 - i)); + } + while (digitalRead(_pin) == HIGH) + ; + } + return value; +} + +/** + * Sends a start signal to the DHT11 sensor to initiate a data read. + * This involves setting the data pin low for a specific duration, then high, + * and finally setting it to input mode to read the data. + */ +void DHT11::startSignal() +{ + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); + delay(18); + digitalWrite(_pin, HIGH); + delayMicroseconds(40); + pinMode(_pin, INPUT); +} + +/** + * Reads and returns the temperature from the DHT11 sensor. + * Utilizes the readRawData method to retrieve raw data from the sensor and then extracts + * the temperature from the data array. + * + * @return: Temperature value in Celsius. Returns DHT11::ERROR_TIMEOUT if reading times out, + * or DHT11::ERROR_CHECKSUM if checksum validation fails. + */ +int DHT11::readTemperature() +{ + byte data[5]; + int error = readRawData(data); + if (error != 0) + { + return error; + } + //return data[2]; + int temp = data[2]; + if (temp != DHT11::ERROR_CHECKSUM && temp != DHT11::ERROR_TIMEOUT) + { + return temp; + } +} + +/** + * Reads and returns the humidity from the DHT11 sensor. + * Utilizes the readRawData method to retrieve raw data from the sensor and then extracts + * the humidity from the data array. + * + * @return: Humidity value in percentage. Returns DHT11::ERROR_TIMEOUT if reading times out, + * or DHT11::ERROR_CHECKSUM if checksum validation fails. + */ +int DHT11::readHumidity() +{ + byte data[5]; + int error = readRawData(data); + if (error != 0) + { + return error; + } + //return data[0]; + int humi = data[0]; + if (humi != DHT11::ERROR_CHECKSUM && humi != DHT11::ERROR_TIMEOUT) + { + return humi; + } +} + +/** + * Reads and returns the temperature and humidity from the DHT11 sensor. + * Utilizes the readRawData method to retrieve raw data from the sensor and then extracts + * both temperature and humidity from the data array. + * + * @param temperature: Reference to a variable where the temperature value will be stored. + * @param humidity: Reference to a variable where the humidity value will be stored. + * @return: An integer representing the status of the read operation. + * Returns 0 if the reading is successful, DHT11::ERROR_TIMEOUT if a timeout occurs, + * or DHT11::ERROR_CHECKSUM if a checksum error occurs. + */ +int DHT11::readTemperatureHumidity(int &temperature, int &humidity) +{ + byte data[5]; + int error = readRawData(data); + if (error != 0) + { + return error; + } + humidity = data[0]; + temperature = data[2]; + return 0; // Indicate success +} + +/** + * Returns a human-readable error message based on the provided error code. + * This method facilitates easier debugging and user feedback by translating + * numeric error codes into descriptive strings. + * + * @param errorCode The error code for which the description is required. + * @return A descriptive string explaining the error. + */ +String DHT11::getErrorString(int errorCode) +{ + switch (errorCode) + { + case DHT11::ERROR_TIMEOUT: + return "Error 253 Reading from DHT11 timed out."; + case DHT11::ERROR_CHECKSUM: + return "Error 254 Checksum mismatch while reading from DHT11."; + default: + return "Error Unknown."; + } +} + +int DHT11::getTempHumi(int &temperature, int &humidity) +{ + // Attempt to read the temperature and humidity values from the DHT11 sensor. + int result = readTemperatureHumidity(temperature, humidity); + + // Check the results of the readings. + // If the reading is successful, print the temperature and humidity values. + // If there are errors, print the appropriate error messages. + if (result == 0) { + Serial.print("Temperature: "); + Serial.print(temperature); + Serial.print(" °C\tHumidity: "); + Serial.print(humidity); + Serial.println(" %"); + } else { + // Print error message based on the error code. + Serial.println(DHT11::getErrorString(result)); + } +} + + diff --git a/src/DHT11.h b/src/DHT11.h new file mode 100644 index 0000000..5f20068 --- /dev/null +++ b/src/DHT11.h @@ -0,0 +1,110 @@ +/** + * DHT11.h + * Header file for the DHT11 library, providing functionalities to interface with + * the DHT11 temperature & humidity sensor. + * + * Author: Dhruba Saha + * Version: 2.1.0 + * License: MIT + */ + +#ifndef DHT11_h +#define DHT11_h + +#include "Arduino.h" + +/** + * DHT11 Class + * Provides methods to read temperature and humidity data from the DHT11 sensor. + */ +class DHT11 +{ +public: + /** + * Constructor + * Initializes the data pin to be used for communication with the DHT11 sensor. + * + * @param pin: Digital pin number on the Arduino board to which the DHT11 sensor is connected. + */ + DHT11(int pin); + + /** + * Sets the delay between consecutive sensor readings. + * If this method is not called, a default delay of 500 milliseconds is used. + * + * @param delay: Delay duration in milliseconds between sensor readings. + */ + void setDelay(unsigned long delay); + + /** + * Reads and returns the humidity from the DHT11 sensor. + * + * @return: Humidity value in percentage. Returns DHT11_ERROR_TIMEOUT if reading times out. + * Returns DHT11_ERROR_CHECKSUM if checksum validation fails. + */ + int readHumidity(); + + /** + * Reads and returns the temperature from the DHT11 sensor. + * + * @return: Temperature value in Celsius. Returns DHT11_ERROR_TIMEOUT if reading times out. + * Returns DHT11_ERROR_CHECKSUM if checksum validation fails. + */ + int readTemperature(); + + /** + * Reads and returns the temperature and humidity from the DHT11 sensor. + * + * @param temperature: Reference to a variable where the temperature value will be stored. + * @param humidity: Reference to a variable where the humidity value will be stored. + * @return: true if the reading is successful, false if it fails due to timeout or checksum error. + */ + int readTemperatureHumidity(int &temperature, int &humidity); + + int getTempHumi(int &temperature, int &humidity); + + // Constants to represent error codes. + static const int ERROR_CHECKSUM = 254; // Error code indicating checksum mismatch. + static const int ERROR_TIMEOUT = 253; // Error code indicating a timeout occurred during reading. + static const int TIMEOUT_DURATION = 1000; // Duration (in milliseconds) to wait before timing out. + + /** + * Returns a human-readable error message based on the provided error code. + * + * @param errorCode: The error code for which the message is required. + * @return: A string describing the error. + */ + static String getErrorString(int errorCode); + +private: + int _pin; // Pin number used for communication with the DHT11 sensor. + unsigned long _delayMS = 500; // Default delay in milliseconds between sensor readings. + + /** + * Private method to read raw data from the DHT11 sensor. + * This method encapsulates the communication with the sensor and data reading process, + * and is utilized by public methods to get temperature and humidity data. + * + * @param data: Array to store the raw data read from the sensor. + * @return: An integer representing the status of the read operation. + * Returns 0 if the reading is successful, DHT11::ERROR_TIMEOUT if a timeout occurs, + * or DHT11::ERROR_CHECKSUM if a checksum error occurs. + */ + int readRawData(byte data[5]); + + /** + * Reads a byte of data from the DHT11 sensor. + * + * @return: A byte of data read from the sensor. + */ + byte readByte(); + + /** + * Sends a start signal to the DHT11 sensor to initiate a data read. + * This involves setting the data pin low for a specific duration, then high, + * and finally setting it to input mode to read the data. + */ + void startSignal(); +}; + +#endif diff --git a/src/DS18B20.cpp b/src/DS18B20.cpp deleted file mode 100644 index 2a983e9..0000000 --- a/src/DS18B20.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// -// FILE: DS18B20.cpp -// AUTHOR: Rob.Tillaart -// VERSION: 0.2.4 -// DATE: 2017-07-25 -// PURPOSE: library for DS18B20 temperature sensor with minimal footprint -// URL: https://github.com/RobTillaart/DS18B20_RT -// https://github.com/RobTillaart/DS18B20_INT - - -#include "DS18B20.h" - - -// OneWire commands -#define STARTCONVO 0x44 -#define READSCRATCH 0xBE -#define WRITESCRATCH 0x4E - - -// Scratchpad locations -#define TEMP_LSB 0 -#define TEMP_MSB 1 -#define HIGH_ALARM_TEMP 2 -#define LOW_ALARM_TEMP 3 -#define CONFIGURATION 4 -#define INTERNAL_BYTE 5 -#define COUNT_REMAIN 6 -#define COUNT_PER_C 7 -#define SCRATCHPAD_CRC 8 - - -// Device resolution -#define TEMP_9_BIT 0x1F // 9 bit -#define TEMP_10_BIT 0x3F // 10 bit -#define TEMP_11_BIT 0x5F // 11 bit -#define TEMP_12_BIT 0x7F // 12 bit - - -DS18B20::DS18B20(OneWire* ow, uint8_t resolution) -{ - _oneWire = ow; - _addressFound = false; - _resolution = resolution; - _config = DS18B20_CLEAR; - _offset = 0; -} - - -bool DS18B20::begin(uint8_t retries) -{ - _config = DS18B20_CLEAR; - if (isConnected(retries)) - { - _setResolution(); - } - return _addressFound; -} - - -bool DS18B20::isConnected(uint8_t retries) -{ - _addressFound = false; - for (uint8_t rtr = retries; (rtr > 0) && (_addressFound == false); rtr--) - { - _oneWire->reset(); - _oneWire->reset_search(); - _deviceAddress[0] = 0x00; - _oneWire->search(_deviceAddress); - _addressFound = (_deviceAddress[0] != 0x00) && - (_oneWire->crc8(_deviceAddress, 7) == _deviceAddress[7]); - } - return _addressFound; -} - - -void DS18B20::requestTemperatures(void) -{ - _oneWire->reset(); - _oneWire->skip(); - _oneWire->write(STARTCONVO, 0); -} - - -bool DS18B20::isConversionComplete(void) -{ - return (_oneWire->read_bit() == 1); -} - - -float DS18B20::getTempC(bool checkConnect) -{ - ScratchPad scratchPad; - if (checkConnect) - { - if (isConnected(3) == false) - { - return DEVICE_DISCONNECTED; - } - } - - if (_config & DS18B20_CRC) - { - readScratchPad(scratchPad, 9); - if (_oneWire->crc8(scratchPad, 8) != scratchPad[SCRATCHPAD_CRC]) - { - return DEVICE_CRC_ERROR; - } - } - else - { - readScratchPad(scratchPad, 2); - } - - int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB]; - float temp = 0.0625 * rawTemperature; - if (temp < -55) - { - return DEVICE_DISCONNECTED; - } - if (_offset != 0) - { - temp += _offset; - } - return temp; -} - - -void DS18B20::setOffset(float offset) -{ - _offset = offset; -} - - -float DS18B20::getOffset() -{ - return _offset; -} - - -bool DS18B20::getAddress(uint8_t* buf) -{ - if (_addressFound) - { - for (uint8_t i = 0; i < 8; i++) - { - buf[i] = _deviceAddress[i]; - } - } - return _addressFound; -} - - -bool DS18B20::setResolution(uint8_t resolution) -{ - if (isConnected()) - { - _resolution = resolution; - _setResolution(); - } - return _addressFound; -} - - -uint8_t DS18B20::getResolution() -{ - return _resolution; -} - - -void DS18B20::setConfig(uint8_t config) -{ - _config = config; -} - - -uint8_t DS18B20::getConfig() -{ - return _config; -} - - -////////////////////////////////////////////////// -// -// PRIVATE -// -void DS18B20::readScratchPad(uint8_t *scratchPad, uint8_t fields) -{ - _oneWire->reset(); - _oneWire->select(_deviceAddress); - _oneWire->write(READSCRATCH); - - for (uint8_t i = 0; i < fields; i++) - { - scratchPad[i] = _oneWire->read(); - } - _oneWire->reset(); -} - - -void DS18B20::_setResolution() -{ - uint8_t res; - switch (_resolution) - { - case 12: res = TEMP_12_BIT; break; - case 11: res = TEMP_11_BIT; break; - case 10: res = TEMP_10_BIT; break; - // lowest as default as we do only integer math. - default: res = TEMP_9_BIT; break; - } - - _oneWire->reset(); - _oneWire->select(_deviceAddress); - _oneWire->write(WRITESCRATCH); - // two dummy values for LOW & HIGH ALARM - _oneWire->write(0); - _oneWire->write(100); - _oneWire->write(res); - _oneWire->reset(); -} - - -void DS18B20::printTemperature() -{ - requestTemperatures(); // 发送请求以开始温度转换 - //float temperature = getTempC(); // 获取温度(摄氏度) - Serial.printf("Temp: %.2f\n", getTempC()); - //Serial.print(getTempC()); - //Serial.println("℃ "); -} - - -// -- END OF FILE -- - diff --git a/src/DS18B20.h b/src/DS18B20.h deleted file mode 100644 index 372dfae..0000000 --- a/src/DS18B20.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef DS18B20_H -#define DS18B20_H - -#include "OneWire.h" - - - -#define DS18B20_LIB_VERSION (F("0.2.4")) - -// Error Code -#define DEVICE_DISCONNECTED -127 -#define DEVICE_CRC_ERROR -128 - -// configuration codes -#define DS18B20_CLEAR 0x00 -#define DS18B20_CRC 0x01 - - -typedef uint8_t DeviceAddress[8]; -typedef uint8_t ScratchPad[9]; - - -class DS18B20 -{ -public: - void printTemperature(); - explicit DS18B20(OneWire * ow, uint8_t resolution = 9); - bool begin(uint8_t retries = 3); - bool isConnected(uint8_t retries = 3); - - void requestTemperatures(void); - bool isConversionComplete(void); - // backwards compatible - float getTempC(bool checkConnect = true); - // conversion wrapper Fahrenheit - // (keep in .h for footprint) - float getTempF() { return 32.0 + getTempC() * 1.8; }; - - void setOffset(float offset = 0); - float getOffset(); - - bool getAddress(uint8_t * buf); - - bool setResolution(uint8_t resolution = 9); - uint8_t getResolution(); // returns cached value - - void setConfig(uint8_t config); - uint8_t getConfig(); - - -private: - void readScratchPad(uint8_t *, uint8_t); - void _setResolution(); - - DeviceAddress _deviceAddress; - OneWire* _oneWire; - bool _addressFound; - - uint8_t _resolution; - uint8_t _config; - float _offset; -}; - -#endif // DS18B20_H -// -- END OF FILE -- - diff --git a/src/DS3502.cpp b/src/DS3502.cpp index e0a028e..fac85df 100644 --- a/src/DS3502.cpp +++ b/src/DS3502.cpp @@ -29,7 +29,9 @@ uint8_t DS3502::getWiper() { * 设置新的 Wiper 值,范围为 0 到 127 */ void DS3502::setWiper(uint8_t new_wiper_value) { - //if (new_wiper_value > 127) return; // 超出范围则不进行操作 + if (new_wiper_value > 99) + new_wiper_value = 100; + //return; // 超出范围则不进行操作 _wire->beginTransmission(_address); _wire->write(DS3502_WIPER); // 指定写入 Wiper 寄存器 diff --git a/src/LightControl.cpp b/src/LightControl.cpp index 9f3f470..0c7b6e2 100644 --- a/src/LightControl.cpp +++ b/src/LightControl.cpp @@ -14,22 +14,48 @@ float LightControl::getCurrentLight() { return lightSensor.readLightLevel(); } -void LightControl::adjustWiper() { - float currentLight = getCurrentLight(); - float error = targetLightLevel - currentLight; +void LightControl::runUntilTargetReached(float target, int maxAttempts) { + const float tolerance = 100.0f; // 容许误差范围(±100 lux) + float currentLight; + float res; - // 计算输出变化量(比例控制) - int delta = (int)(abs(error) * Kp); - if (delta < 1) delta = 1; // 最小调节步长 + int attempt = 0; + const int max_retries = maxAttempts; // 最大尝试次数,防止死循环 - if (error > 0) { - // 需要增加亮度 -> 减小电位器阻值 - lastWiperValue = max(0, lastWiperValue - delta); - } else if (error < 0) { - // 需要减少亮度 -> 增大电位器阻值 - lastWiperValue = min(127, lastWiperValue + delta); - } + do { + currentLight = getCurrentLight(); + res = target - currentLight; - digitalPot.setWiper(lastWiperValue); - Serial.printf("Current Light: %.0f lux | Wiper Value: %d\n", currentLight, lastWiperValue); + if (abs(res) <= tolerance) { + Serial.println("Target reached."); + break; + } + + int lastWiperValue = digitalPot.getWiper(); + + if (res > 0) { + // 需要增加亮度 -> 增加电位器阻值 + if (lastWiperValue <= 127) { + lastWiperValue++; + } + } else if (res < 0) { + // 需要减少亮度 -> 减少电位器阻值 + if (lastWiperValue > 0) { + lastWiperValue--; + } + } + + digitalPot.setWiper(lastWiperValue); + Serial.printf("Current light: %.0f, Target: %.0f, Res: %.0f, Wiper: %d\n", currentLight, target, res, lastWiperValue); + delay(200); // 等待响应,必要!! + + attempt++; + + // 超出最大尝试次数退出 + if (attempt >= max_retries) { + Serial.println("Max attempts reached. Exiting..."); + break; + } + + } while (true); } \ No newline at end of file diff --git a/src/LightControl.h b/src/LightControl.h index f76ca07..03bcd2e 100644 --- a/src/LightControl.h +++ b/src/LightControl.h @@ -17,15 +17,14 @@ public: // 获取当前照度 float getCurrentLight(); - // 执行一次调节 - void adjustWiper(); + void runUntilTargetReached(float target, int maxAttempts = 500); private: BH1750& lightSensor; // 引用 BH1750 传感器对象 DS3502& digitalPot; // 引用 DS3502 数字电位器对象 float targetLightLevel; // 目标照度值 - int lastWiperValue = 0; // 上一次 Wiper 值 - float Kp = 0.5; // 比例增益(可根据实际情况调整) + int lastWiperValue; // 上一次 Wiper 值 + //float Kp = 0.5; // 比例增益 }; #endif \ No newline at end of file diff --git a/src/RunTime.cpp b/src/RunTime.cpp index b1a8c65..c9de806 100644 --- a/src/RunTime.cpp +++ b/src/RunTime.cpp @@ -14,8 +14,9 @@ void RunTime::begin() { void RunTime::checkCurrent() //时长判定 { - float current = INA.getBusVoltage(); - if (current > 10.0) { + float current = INA.getCurrent(); + //电流大于1.0A,开始计时 + if (current > 1.0) { if (!isActive) { // 开始计时 isActive = true; diff --git a/src/SHT31.cpp b/src/SHT31.cpp new file mode 100644 index 0000000..d6dce47 --- /dev/null +++ b/src/SHT31.cpp @@ -0,0 +1,380 @@ +// +// FILE: SHT31.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.5.1 +// DATE: 2019-02-08 +// PURPOSE: Arduino library for the SHT31 temperature and humidity sensor +// https://www.adafruit.com/product/2857 +// URL: https://github.com/RobTillaart/SHT31 + + +#include "SHT31.h" + + +// SUPPORTED COMMANDS - single shot mode only +static constexpr uint16_t SHT31_READ_STATUS = 0xF32D; +static constexpr uint16_t SHT31_CLEAR_STATUS = 0x3041; + +static constexpr uint16_t SHT31_SOFT_RESET = 0x30A2; +static constexpr uint16_t SHT31_HARD_RESET = 0x0006; + +static constexpr uint16_t SHT31_MEASUREMENT_FAST = 0x2416; // page 10 datasheet +static constexpr uint16_t SHT31_MEASUREMENT_SLOW = 0x2400; // no clock stretching + +static constexpr uint16_t SHT31_HEAT_ON = 0x306D; +static constexpr uint16_t SHT31_HEAT_OFF = 0x3066; +static constexpr uint32_t SHT31_HEATER_TIMEOUT = 180000UL; // milliseconds + +static constexpr uint16_t SHT31_GET_SERIAL_NUMBER = 0x3682; // no clock stretching + + +SHT31::SHT31(uint8_t address, TwoWire *wire) +{ + _address = address; + _wire = wire; + _lastRead = 0; + _rawTemperature = 0; + _rawHumidity = 0; + _heatTimeout = 0; + _heaterStart = 0; + _heaterStop = 0; + _heaterOn = false; + _error = SHT31_OK; +} + + +bool SHT31::begin() +{ + if ((_address != 0x44) && (_address != 0x45)) + { + return false; + } + return reset(); +} + + +bool SHT31::isConnected() +{ + _wire->beginTransmission(_address); + int rv = _wire->endTransmission(); + if (rv != 0) _error = SHT31_ERR_NOT_CONNECT; + return (rv == 0); +} + + +uint8_t SHT31::getAddress() +{ + return _address; +} + + +bool SHT31::read(bool fast) +{ + if (writeCmd(fast ? SHT31_MEASUREMENT_FAST : SHT31_MEASUREMENT_SLOW) == false) + { + return false; + } + delay(fast ? 4 : 15); // table 4 datasheet + return readData(fast); +} + + +///////////////////////////////////////////////////////////////// +// +// STATUS +// +#ifdef doc +// bit - description +// ================== +// 15 Alert pending status +// '0': no pending alerts +// '1': at least one pending alert - default +// 14 Reserved ‘0’ +// 13 Heater status +// '0’ : Heater OFF - default +// '1’ : Heater ON +// 12 Reserved '0’ +// 11 Humidity tracking alert +// '0’ : no alert - default +// '1’ : alert +// 10 Temp tracking alert +// '0’ : no alert - default +// '1’ : alert +// 9:5 Reserved '00000’ +// 4 System reset detected +// '0': no reset since last ‘clear status register’ command +// '1': reset detected (hard or soft reset command or supply fail) - default +// 3:2 Reserved ‘00’ +// 1 Command status +// '0': last command executed successfully +// '1': last command not processed. Invalid or failed checksum +// 0 Write data checksum status +// '0': checksum of last write correct +// '1': checksum of last write transfer failed +#endif + + +uint16_t SHT31::readStatus() +{ + uint8_t status[3] = { 0, 0, 0 }; + // page 13 datasheet + if (writeCmd(SHT31_READ_STATUS) == false) + { + return 0xFFFF; + } + // 16 bit status + CRC + if (readBytes(3, (uint8_t*) &status[0]) == false) + { + return 0xFFFF; + } + + if (status[2] != crc8(status, 2)) + { + _error = SHT31_ERR_CRC_STATUS; + return 0xFFFF; + } + return (uint16_t) (status[0] << 8) + status[1]; +} + + +// resets the following bits +// 15 Alert pending status +// 11 Humidity tracking alert +// 10 Temp tracking alert +// 4 System reset detected +bool SHT31::clearStatus() +{ + if (writeCmd(SHT31_CLEAR_STATUS) == false) + { + return false; + } + return true; +} + + +bool SHT31::reset(bool hard) +{ + bool b = writeCmd(hard ? SHT31_HARD_RESET : SHT31_SOFT_RESET); + if (b == false) + { + return false; + } + delay(1); // table 4 datasheet + return true; +} + + +void SHT31::setHeatTimeout(uint8_t seconds) +{ + _heatTimeout = seconds; + if (_heatTimeout > 180) _heatTimeout = 180; +} + + +///////////////////////////////////////////////////////////////// +// +// HEATER +// +bool SHT31::heatOn() +{ + if (isHeaterOn()) return true; + if ((_heaterStop > 0) && (millis() - _heaterStop < SHT31_HEATER_TIMEOUT)) + { + _error = SHT31_ERR_HEATER_COOLDOWN; + return false; + } + if (writeCmd(SHT31_HEAT_ON) == false) + { + _error = SHT31_ERR_HEATER_ON; + return false; + } + _heaterStart = millis(); + _heaterOn = true; + return true; +} + + +bool SHT31::heatOff() +{ + // always switch off the heater - ignore _heaterOn flag. + if (writeCmd(SHT31_HEAT_OFF) == false) + { + _error = SHT31_ERR_HEATER_OFF; // can be serious! + return false; + } + _heaterStop = millis(); + _heaterOn = false; + return true; +} + + +bool SHT31::isHeaterOn() +{ + if (_heaterOn == false) + { + return false; + } + // did not exceed time out + if (millis() - _heaterStart < (_heatTimeout * 1000UL)) + { + return true; + } + heatOff(); + return false; +} + + +///////////////////////////////////////////////////////////////// +// +// ASYNCHRONUOUS INTERFACE +// +bool SHT31::requestData() +{ + if (writeCmd(SHT31_MEASUREMENT_SLOW) == false) + { + return false; + } + _lastRequest = millis(); + return true; +} + + +bool SHT31::dataReady() +{ + return ((millis() - _lastRequest) > 15); // TODO MAGIC NR +} + + +bool SHT31::readData(bool fast) +{ + uint8_t buffer[6]; + if (readBytes(6, (uint8_t*) &buffer[0]) == false) + { + return false; + } + + if (!fast) + { + if (buffer[2] != crc8(buffer, 2)) + { + _error = SHT31_ERR_CRC_TEMP; + return false; + } + if (buffer[5] != crc8(buffer + 3, 2)) + { + _error = SHT31_ERR_CRC_HUM; + return false; + } + } + + _rawTemperature = (buffer[0] << 8) + buffer[1]; + _rawHumidity = (buffer[3] << 8) + buffer[4]; + + _lastRead = millis(); + return true; +} + + +///////////////////////////////////////////////////////////////// +// +// MISC +// +int SHT31::getError() +{ + int rv = _error; + _error = SHT31_OK; + return rv; +} + + +/** + * See https://sensirion.com/media/documents/E5762713/63D103C2/Sensirion_electronic_identification_code_SHT3x.pdf + */ +bool SHT31::getSerialNumber(uint32_t &serial, bool fast) { + if (writeCmd(SHT31_GET_SERIAL_NUMBER) == false) { + return false; + } + delay(1); + uint8_t buffer[6]; + if (readBytes(6, &buffer[0]) == false) { + return false; + } + + if (!fast) { + if (buffer[2] != crc8(buffer, 2)) { + _error = SHT31_ERR_SERIAL_NUMBER_CRC; + return false; + } + if (buffer[5] != crc8(buffer + 3, 2)) { + _error = SHT31_ERR_SERIAL_NUMBER_CRC; + return false; + } + } + serial = buffer[0]; + serial <<= 8; + serial += buffer[1]; + serial <<= 8; + serial += buffer[3]; + serial <<= 8; + serial += buffer[4]; + return true; +} + + +///////////////////////////////////////////////////////////////// +// +// PROTECTED +// +uint8_t SHT31::crc8(const uint8_t *data, uint8_t len) +{ + // CRC-8 formula from page 14 of SHT spec pdf + const uint8_t POLY(0x31); + uint8_t crc(0xFF); + + for (uint8_t j = len; j; --j) + { + crc ^= *data++; + + for (uint8_t i = 8; i; --i) + { + crc = (crc & 0x80) ? (crc << 1) ^ POLY : (crc << 1); + } + } + return crc; +} + + +bool SHT31::writeCmd(uint16_t cmd) +{ + _wire->beginTransmission(_address); + _wire->write(cmd >> 8 ); + _wire->write(cmd & 0xFF); + if (_wire->endTransmission() != 0) + { + _error = SHT31_ERR_WRITECMD; + return false; + } + _error = SHT31_OK; + return true; +} + + +bool SHT31::readBytes(uint8_t n, uint8_t *val) +{ + int rv = _wire->requestFrom(_address, (uint8_t) n); + if (rv == n) + { + for (uint8_t i = 0; i < n; i++) + { + val[i] = _wire->read(); + } + _error = SHT31_OK; + return true; + } + _error = SHT31_ERR_READBYTES; + return false; +} + + +// -- END OF FILE -- + diff --git a/src/SHT31.h b/src/SHT31.h new file mode 100644 index 0000000..e311e48 --- /dev/null +++ b/src/SHT31.h @@ -0,0 +1,122 @@ +#pragma once +// +// FILE: SHT31.h +// AUTHOR: Rob Tillaart +// VERSION: 0.5.2 +// DATE: 2019-02-08 +// PURPOSE: Arduino library for the SHT31 temperature and humidity sensor +// https://www.adafruit.com/product/2857 +// URL: https://github.com/RobTillaart/SHT31 + + +#include "Arduino.h" +#include "Wire.h" + + +#define SHT31_LIB_VERSION (F("0.5.2")) + +#ifndef SHT_DEFAULT_ADDRESS +#define SHT_DEFAULT_ADDRESS 0x44 +#endif + +// fields readStatus +#define SHT31_STATUS_ALERT_PENDING (1 << 15) +#define SHT31_STATUS_HEATER_ON (1 << 13) +#define SHT31_STATUS_HUM_TRACK_ALERT (1 << 11) +#define SHT31_STATUS_TEMP_TRACK_ALERT (1 << 10) +#define SHT31_STATUS_SYSTEM_RESET (1 << 4) +#define SHT31_STATUS_COMMAND_STATUS (1 << 1) +#define SHT31_STATUS_WRITE_CRC_STATUS (1 << 0) + +// error codes +#define SHT31_OK 0x00 +#define SHT31_ERR_WRITECMD 0x81 +#define SHT31_ERR_READBYTES 0x82 +#define SHT31_ERR_HEATER_OFF 0x83 +#define SHT31_ERR_NOT_CONNECT 0x84 +#define SHT31_ERR_CRC_TEMP 0x85 +#define SHT31_ERR_CRC_HUM 0x86 +#define SHT31_ERR_CRC_STATUS 0x87 +#define SHT31_ERR_HEATER_COOLDOWN 0x88 +#define SHT31_ERR_HEATER_ON 0x89 +#define SHT31_ERR_SERIAL_NUMBER_CRC 0x8A + + +class SHT31 +{ +public: + SHT31(uint8_t address = SHT_DEFAULT_ADDRESS, TwoWire *wire = &Wire); + + bool begin(); + uint8_t getAddress(); + + // check sensor is reachable over I2C + virtual bool isConnected(); + + // blocks 15 milliseconds + actual read + math + bool read(bool fast = true); + + // details see datasheet; summary in SHT31.cpp file + uint16_t readStatus(); + bool clearStatus(); + + // lastRead is in milliSeconds since start + uint32_t lastRead() { return _lastRead; }; + + bool reset(bool hard = false); + + // do not use heater for long periods, + // use it for max 3 minutes to heat up + // and let it cool down at least 3 minutes. + void setHeatTimeout(uint8_t seconds); + uint8_t getHeatTimeout() { return _heatTimeout; }; + bool heatOn(); + bool heatOff(); + bool isHeaterOn(); // is the sensor still heating up? + // [[deprecated("Use isHeaterOn() instead")]] + bool heatUp() { return isHeaterOn(); }; // will be obsolete + + // 0..100% + float getHumidity() { return _rawHumidity * (100.0 / 65535); }; + // getTemperature returns Celsius + float getTemperature() { return _rawTemperature * (175.0 / 65535) - 45; }; + float getFahrenheit() { return _rawTemperature * (63.0 /13107.0) - 49; }; + // raw data e.g. debugging or efficient logging / transmit. + uint16_t getRawHumidity() { return _rawHumidity; }; + uint16_t getRawTemperature() { return _rawTemperature; }; + + + // ASYNC INTERFACE + bool requestData(); + bool dataReady(); + bool readData(bool fast = true); + + // MISC + int getError(); // clears error flag + // fast == true, => skips CRC check + bool getSerialNumber(uint32_t &serial, bool fast = true); + + +protected: + uint8_t _address; + uint8_t _heatTimeout; // seconds + uint32_t _lastRead; + uint32_t _lastRequest; // for async interface + uint32_t _heaterStart; + uint32_t _heaterStop; + bool _heaterOn; + uint16_t _rawHumidity; + uint16_t _rawTemperature; + uint8_t _error; + +private: + uint8_t crc8(const uint8_t *data, uint8_t len); + virtual bool writeCmd(uint16_t cmd); + virtual bool readBytes(uint8_t n, uint8_t *val); + TwoWire* _wire; +}; + + +// -- END OF FILE -- + + diff --git a/src/TJC_Show.cpp b/src/TJC_Show.cpp index 7490e65..0a7ba17 100644 --- a/src/TJC_Show.cpp +++ b/src/TJC_Show.cpp @@ -1,74 +1,321 @@ #include "TJC_Show.h" #include "WiFiControl.h" - +#include "LightControl.h" extern RunTime runtime; extern DS3502 ds3502; +extern BH1750 bh1750_a, bh1750_b; extern uint16_t wiperValue; -TJC_Show::TJC_Show(DS18B20& ds18b20, BH1750& bh1750_a, BH1750& bh1750_b, DS3502& ds3502, INA226& INA) - : _serial(TJC_SERIAL), _ds18b20(ds18b20), _bh1750_a(bh1750_a), _bh1750_b(bh1750_b), _ds3502(ds3502), _INA(INA) {} +extern void LightOpen(); +extern void LightClose(); +TJC_Show::TJC_Show(SHT31& sht31, BH1750& bh1750_a, BH1750& bh1750_b, DS3502& ds3502, INA226& INA) + : _serial(TJC_SERIAL), _sht31(sht31), _bh1750_a(bh1750_a), _bh1750_b(bh1750_b), _ds3502(ds3502), _INA(INA) {} + +void TJC_Show::clearTJCSerialBuffer() { + while (_serial.read() >= 0); // 清空串口缓冲区 +} + +void TJC_Show::sendCommand(const char* command) { + _serial.print(command); +} + +void TJC_Show::goToPage(const char* pageName) { + char cmd[32]; + sprintf(cmd, "page %s\xff\xff\xff", pageName); + sendCommand(cmd); +} void TJC_Show::init() { _serial.begin(115200, SERIAL_8N1, TJC_RX_PIN, TJC_TX_PIN); - clearSerialBuffer(); - sendCommand("page main\xff\xff\xff"); + clearTJCSerialBuffer(); + //sendCommand("page main\xff\xff\xff"); + + goToPage("start"); // 跳转到开机页面 } void TJC_Show::showQR() { char addr[64]; IPAddress ip; + #if SELECTED_WIFI_MODE == WIFI_MODE_STA + ip = WiFi.localIP(); + snprintf(addr, sizeof(addr), "wifi.t0.txt=\"STA:%d.%d.%d.%d\"\xff\xff\xff", ip[0], ip[1], ip[2], ip[3]); + sendCommand(addr); + #elif SELECTED_WIFI_MODE == WIFI_MODE_AP + ip = WiFi.softAPIP(); + snprintf(addr, sizeof(addr), "wifi.t0.txt=\"AP:%d.%d.%d.%d\"\xff\xff\xff", ip[0], ip[1], ip[2], ip[3]); + sendCommand(addr); + #endif + + snprintf(addr, sizeof(addr), "wifi.qr0.txt=\"http://%d.%d.%d.%d/\"\xff\xff\xff", ip[0], ip[1], ip[2], ip[3]); + sendCommand(addr); + + // if (SELECTED_WIFI_MODE == WIFI_MODE_STA){ + // snprintf(addr, sizeof(addr), "t15.txt=\"STA:%d.%d.%d.%d\"\xff\xff\xff", ip[0], ip[1], ip[2], ip[3]); + // sendCommand(addr); + // } + // if(SELECTED_WIFI_MODE == WIFI_MODE_AP){ + // snprintf(addr, sizeof(addr), "t15.txt=\"AP:%d.%d.%d.%d\"\xff\xff\xff", ip[0], ip[1], ip[2], ip[3]); + // sendCommand(addr); + // } + +} + +void TJC_Show::showInfo() { + char str[256]; + + // === main 页面 === + snprintf(str, sizeof(str), + "main.t0.txt=\"%d℃\"\xff\xff\xff" + "main.t16.txt=\"%dRH\"\xff\xff\xff" + "main.t1.txt=\"照度A:%d\"\xff\xff\xff" + "main.t2.txt=\"照度B:%d\"\xff\xff\xff" + "main.t14.txt=\"%s\"\xff\xff\xff" + //"main.t8.txt=\"%d\"\xff\xff\xff" + "main.t9.txt=\"%.2f\"\xff\xff\xff" + //"main.t10.txt=\"%.2f\"\xff\xff\xff" + "main.t11.txt=\"%.2f\"\xff\xff\xff" + "main.t12.txt=\"%.2f\"\xff\xff\xff", + sensorData.temperature, + sensorData.humidity, + sensorData.lightA, + sensorData.lightB, + runtime.formatDuration(runtime.getActiveDuration()), + //_ds3502.getWiper(), + sensorData.busVoltage, + //sensorData.shuntVoltage_mV, + sensorData.current, + sensorData.power + ); + sendCommand(str); + + // === wiper 页面 === + snprintf(str, sizeof(str), + "wiper.t1.txt=\"照度A:%d\"\xff\xff\xff" + "wiper.t2.txt=\"照度B:%d\"\xff\xff\xff" + "wiper.t9.txt=\"%.2f\"\xff\xff\xff" + //"wiper.t10.txt=\"%.2f\"\xff\xff\xff" + "wiper.t11.txt=\"%.2f\"\xff\xff\xff" + "wiper.t12.txt=\"%.2f\"\xff\xff\xff" + "wiper.t0.txt=\"%d\"\xff\xff\xff", + sensorData.lightA, + sensorData.lightB, + sensorData.busVoltage, + //sensorData.shuntVoltage_mV, + sensorData.current, + sensorData.power, + _ds3502.getWiper() + ); + sendCommand(str); + + // === autolight 页面 === + snprintf(str, sizeof(str), + "autolight.t1.txt=\"照度A:%d\"\xff\xff\xff" + "autolight.t2.txt=\"照度B:%d\"\xff\xff\xff", + sensorData.lightA, + sensorData.lightB + ); + sendCommand(str); + + // === wifi 页面信息 === + IPAddress ip; #if SELECTED_WIFI_MODE == WIFI_MODE_STA ip = WiFi.localIP(); #elif SELECTED_WIFI_MODE == WIFI_MODE_AP ip = WiFi.softAPIP(); #endif - snprintf(addr, sizeof(addr), "qr0.txt=\"http://%d.%d.%d.%d/\"\xff\xff\xff", ip[0], ip[1], ip[2], ip[3]); - sendCommand(addr); + snprintf(str, sizeof(str), + "wifi.t0.txt=\"STA:%d.%d.%d.%d\"\xff\xff\xff" + "wifi.qr0.txt=\"http://%d.%d.%d.%d/\"\xff\xff\xff", + ip[0], ip[1], ip[2], ip[3], + ip[0], ip[1], ip[2], ip[3]); + sendCommand(str); } +// void TJC_Show::processSerial() { +// #define FRAME_LENGTH 10 +// extern bool isAutoAdjustEnabled; +// extern LightControl lightcontrol; -void TJC_Show::showInfo() { - char str[128]; +// if (_serial.available() >= FRAME_LENGTH) { +// uint8_t frame_header = _serial.peek(); - if (SELECTED_WIFI_MODE == WIFI_MODE_STA){ - snprintf(str, sizeof(str), "t15.txt=\"MODE_STA:%s\"\xff\xff\xff", WiFi.localIP()); - sendCommand(str); - } - else if(SELECTED_WIFI_MODE == WIFI_MODE_AP){ - snprintf(str, sizeof(str), "t15.txt=\"MODE_AP:%s\"\xff\xff\xff", WiFi.softAPIP()); - sendCommand(str); +// if (frame_header == 0x55) { +// uint8_t buffer[FRAME_LENGTH]; +// _serial.readBytes(buffer, FRAME_LENGTH); + +// char str[128]; + +// // 验证帧尾是否正确 +// if (buffer[4] == 0xff && buffer[5] == 0xff && buffer[6] == 0xff) { +// switch (buffer[1]) { +// case 0x00: +// break; + +// //自动调节 +// case 0x01: +// //55 01 00 01 ff ff ff,含义:0号打开 +// //55 01 00 00 ff ff ff,含义:0号关闭 +// // snprintf(str, sizeof(str), "msg.txt=\"led %d is %s\"\xff\xff\xff", +// // buffer[2], buffer[3] ? "on" : "off"); +// // sendCommand(str); +// // isAutoAdjustEnabled == false; +// // Serial.printf("AutoLightControl is %s\n", buffer[3] ? "on" : "off"); +// // if (buffer[3] == 1) +// // { +// // //isAutoAdjustEnabled = true; +// // //lightcontrol.setTargetLight(buffer[2]); +// // Serial.printf("targetLight is %d\n", buffer[2]); +// // } +// Serial.printf("targetLight is %d\n", buffer[2]); + +// if (buffer[3] == 0) +// { +// isAutoAdjustEnabled = false; +// } + + +// break; + +// //滑块设置wiper +// case 0x02: +// ds3502.setWiper(buffer[2]); +// // snprintf(str, sizeof(str), "msg.txt=\"h0.val is %d\"\xff\xff\xff", buffer[2]); +// // sendCommand(str); +// break; + +// case 0x03: +// snprintf(str, sizeof(str), "msg.txt=\"h1.val is %d\"\xff\xff\xff", buffer[2]); +// sendCommand(str); +// break; + +// default: +// Serial.printf("Unknown command: 0x%02X\n", buffer[1]); +// break; +// } +// } + +// } else { +// _serial.read(); // 跳过无效字节 +// } +// } +// } + extern LightControl lightCtrl; +void TJC_Show::processSerial() { + extern bool isAutoAdjustEnabled; + + + if (_serial.available() < 3) return; // 至少要有帧头 + 指令 + 帧尾标志 + + uint8_t frame_header = _serial.peek(); + if (frame_header != 0x55) { + _serial.read(); // 跳过无效字节 + return; } - snprintf(str, sizeof(str), "t0.txt=\"%.2f\"\xff\xff\xff", _ds18b20.getTempC()); - sendCommand(str); + // 读掉帧头 + _serial.read(); - snprintf(str, sizeof(str), "t1.txt=\"%.f\"\xff\xff\xff", _bh1750_a.readLightLevel()); - sendCommand(str); - snprintf(str, sizeof(str), "t2.txt=\"%.f\"\xff\xff\xff", _bh1750_b.readLightLevel()); - sendCommand(str); + // 尝试读取命令字节 + if (_serial.available() < 1) return; + uint8_t cmdByte = _serial.read(); - snprintf(str, sizeof(str), "t14.txt=\"%s\"\xff\xff\xff", runtime.formatDuration(runtime.getActiveDuration())); - sendCommand(str); + // 定义最大可能帧长度 + const int MAX_FRAME_LENGTH = 12; + uint8_t buffer[MAX_FRAME_LENGTH]; + buffer[0] = 0x55; + buffer[1] = cmdByte; - snprintf(str, sizeof(str), "t8.txt=\"%d\"\xff\xff\xff", _ds3502.getWiper()); - sendCommand(str); + // 最大尝试读取长度 + int remaining = _serial.available(); + int toRead = (remaining > MAX_FRAME_LENGTH - 2) ? MAX_FRAME_LENGTH - 2 : remaining; + _serial.readBytes(buffer + 2, toRead); - snprintf(str, sizeof(str), "t9.txt=\"%.2f\"\xff\xff\xff", _INA.getBusVoltage()); - sendCommand(str); - snprintf(str, sizeof(str), "t10.txt=\"%.2f\"\xff\xff\xff", _INA.getShuntVoltage_mV()); - sendCommand(str); - snprintf(str, sizeof(str), "t11.txt=\"%.2f\"\xff\xff\xff", _INA.getCurrent()); - sendCommand(str); - snprintf(str, sizeof(str), "t12.txt=\"%.2f\"\xff\xff\xff", _INA.getPower()); - sendCommand(str); -} + // 当前已读取的总字节数 + int totalRead = 2 + toRead; -void TJC_Show::clearSerialBuffer() { - while (_serial.read() >= 0); // 清空串口缓冲区 -} + // 查找帧尾位置(三个连续的 0xFF) + int footerPos = -1; + for (int i = 4; i < totalRead - 2; i++) { + if (buffer[i] == 0xFF && buffer[i+1] == 0xFF && buffer[i+2] == 0xFF) { + footerPos = i; + break; + } + } -void TJC_Show::sendCommand(const char* command) { - _serial.print(command); + if (footerPos == -1) { + Serial.println("No valid footer found."); + return; + } + + // 确定帧总长度 + int totalLength = footerPos + 3; + + if (totalLength > totalRead) { + Serial.println("Incomplete frame received."); + return; + } + + // 截断缓冲区到实际帧长度 + int dataStart = 2; // 数据从 buffer[2] 开始 + int dataLength = footerPos - dataStart; + + char asciiStr[16] = {0}; + + for (int i = 0; i < dataLength; i++) { + asciiStr[i] = static_cast(buffer[dataStart + i]); + } + + switch (cmdByte) { + case 0x00: + //开关灯指令 + //55 00 00 00 FF FF FF + //55 00 00 01 FF FF FF + if (totalLength == 7) { + if (buffer[2] == 0x00 && buffer[3] == 0x01) { + LightOpen(); + } else if (buffer[2] == 0x00 && buffer[3] == 0x00) { + LightClose(); + } else { + Serial.printf("Unknown cmd: %02X %02X\n", buffer[2], buffer[3]); + } + } + break; + + case 0x01: + // 自动调节指令,根据帧长度输出 ASCII 字符串 + // Serial.print("Received ASCII: "); + // Serial.println(asciiStr); + if(totalLength == 7 && buffer[3] == 0) + { + isAutoAdjustEnabled = false; + Serial.printf("AutoLightControl is %s\n", isAutoAdjustEnabled ? "on" : "off"); + } + else{ + lightCtrl.runUntilTargetReached(atoi(asciiStr), 150); + Serial.printf("Set TargetLight %d\n", atoi(asciiStr)); + } + break; + + case 0x02: + // 设置 wiper 值 + if (totalLength == 7) { + ds3502.setWiper(buffer[2]); + } + break; + + case 0x03: + // 示例:发送反馈信息 + if (totalLength == 7) { + char str[128]; + snprintf(str, sizeof(str), "msg.txt=\"h1.val is %d\"\xff\xff\xff", buffer[2]); + sendCommand(str); + } + break; + + default: + Serial.printf("Unknown command: 0x%02X\n", cmdByte); + break; + } } \ No newline at end of file diff --git a/src/TJC_Show.h b/src/TJC_Show.h index c151d55..02b4396 100644 --- a/src/TJC_Show.h +++ b/src/TJC_Show.h @@ -3,7 +3,7 @@ #include #include "BH1750.h" -#include "DS18B20.h" +#include "SHT31.h" #include "DS3502.h" #include "INA226.h" #include @@ -14,22 +14,40 @@ #define TJC_TX_PIN 42 #define TJC_RX_PIN 41 +struct SensorData { + int temperature; + int humidity; + int lightA; + int lightB; + float busVoltage; + float shuntVoltage_mV; + float current; + float power; +}; +// 外部声明 +extern SensorData sensorData; + + class TJC_Show { public: - TJC_Show(DS18B20& ds18b20, BH1750& bh1750_a, BH1750& bh1750_b, DS3502& DS3502, INA226& INA); + TJC_Show(SHT31& sht31, BH1750& bh1750_a, BH1750& bh1750_b, DS3502& DS3502, INA226& INA); void init(); void showQR(); void showInfo(); + void clearTJCSerialBuffer(); + void sendCommand(const char* command); + void goToPage(const char* pageName); + void processSerial(); private: HardwareSerial& _serial; - DS18B20& _ds18b20; + SHT31& _sht31; BH1750& _bh1750_a; BH1750& _bh1750_b; DS3502& _ds3502; INA226& _INA; - void clearSerialBuffer(); - void sendCommand(const char* command); + }; +void parseTJCCommand(unsigned char* ubuffer); #endif // TJC_SHOW_H \ No newline at end of file diff --git a/src/WebServer.h b/src/WebServer.h index 6ade132..2e90d86 100644 --- a/src/WebServer.h +++ b/src/WebServer.h @@ -4,7 +4,6 @@ #include "webpages.h" // 包含 HTML 页面内容 #include #include -#include "DS18B20.h" #include "BH1750.h" #include "Wire.h" //#include "MCP45HVX1.h" diff --git a/src/WiFiControl.cpp b/src/WiFiControl.cpp index 243d54d..3bb4767 100644 --- a/src/WiFiControl.cpp +++ b/src/WiFiControl.cpp @@ -63,6 +63,7 @@ bool connectToKnownNetworks() void switchToAPMode() { + SELECTED_WIFI_MODE == WIFI_MODE_AP; Serial.println("Switching to AP mode."); WiFi.softAP(AP_SSID, AP_PASSWORD); Serial.print("AP IP address: "); diff --git a/src/main.cpp b/src/main.cpp index 91bccd2..3ca2490 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,4 @@ -#include "DS18B20.h" +#include "SHT31.h" #include "BH1750.h" #include "Wire.h" #include @@ -15,6 +15,10 @@ #include "DS3502.h" #include "LightControl.h" +#include + +// #include + // Ticker 用于定时检查电流状态 Ticker currentCheckTicker; @@ -28,17 +32,18 @@ INA226 INA(0x40); AsyncWebServer server(80); -// DS18B20 温度传感器 -#define DS18B20_PIN 8 -OneWire oneWire(DS18B20_PIN); -DS18B20 ds18b20(&oneWire); +// DHT11 温湿度传感器 +//DHT11 dht11(8); +//SHT31 温湿度 +#define SHT31_ADDRESS 0x44 +SHT31 sht31; BH1750 bh1750_a; BH1750 bh1750_b; // 按键引脚 -#define INCREASE_BUTTON_PIN 3 -#define DECREASE_BUTTON_PIN 46 +#define INCREASE_BUTTON_PIN 9 +#define DECREASE_BUTTON_PIN 10 // 风扇控制引脚 #define FAN_PIN1 15 @@ -47,98 +52,56 @@ BH1750 bh1750_b; // 蜂鸣器引脚 #define BUZZER_PIN 17 +//灯光控制引脚 +#define LIGHT_CONTROL_PIN 1 + extern RunTime runtime; Preferences prefs; -TJC_Show tjcShow(ds18b20, bh1750_a, bh1750_b, ds3502, INA); +TJC_Show tjcShow(sht31, bh1750_a, bh1750_b, ds3502, INA); // 比例调节,bh1750_a LightControl lightCtrl(bh1750_a, ds3502); -bool autoAdjustEnabled = false; // 是否启用自动调节,默认关闭 -float targetLightValue = 2000; // 默认目标照度 -void WebServer_Init(void) { - // 根路径的页面 - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send_P(200, "text/html", index_html); - }); +bool isAutoAdjustEnabled = false; // 是否启用自动调节,默认关闭 - // 温度接口 - server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request) { - String temp = String(ds18b20.getTempC()); - request->send(200, "text/plain", temp); - }); +SensorData sensorData; - // 光照强度(传感器A) - server.on("/lightA", HTTP_GET, [](AsyncWebServerRequest *request) { - String lightA = String(bh1750_a.readLightLevel()); - request->send(200, "text/plain", lightA); - }); - // 光照(传感器B) - server.on("/lightB", HTTP_GET, [](AsyncWebServerRequest *request) { - String lightB = String(bh1750_b.readLightLevel()); - request->send(200, "text/plain", lightB); - }); +unsigned long lastReadTime = 0; +const long readInterval = 2500; // *秒间隔 +void UpdateSensorData(SensorData& data) { - // 配置Wiper接口 - server.on("/wiper", HTTP_GET, [](AsyncWebServerRequest *request) { - String wiper = String(ds3502.getWiper()); - request->send(200, "text/plain", wiper); - }); + sht31.read(); // default = true/fast slow = false + data.temperature = sht31.getTemperature(), 1; + data.humidity = sht31.getHumidity(), 1; + delay(100); - server.on("/busVoltage", HTTP_GET, [](AsyncWebServerRequest *request) { - String busVoltage = String(INA.getBusVoltage(), 3); - request->send(200, "text/plain", busVoltage); - }); + data.lightA = bh1750_a.readLightLevel(); + data.lightB = bh1750_b.readLightLevel(); + data.busVoltage = INA.getBusVoltage(); + data.shuntVoltage_mV = INA.getShuntVoltage_mV(); + data.current = INA.getCurrent(); + data.power = INA.getPower(); - server.on("/shuntVoltage", HTTP_GET, [](AsyncWebServerRequest *request) { - String shuntVoltage = String(INA.getShuntVoltage_mV(), 3); - request->send(200, "text/plain", shuntVoltage); - }); - - server.on("/current", HTTP_GET, [](AsyncWebServerRequest *request) { - String current = String(INA.getCurrent_mA(), 3); - request->send(200, "text/plain", current); - }); - - server.on("/power", HTTP_GET, [](AsyncWebServerRequest *request) { - String power = String(INA.getPower_mW(), 3); - request->send(200, "text/plain", power); - }); - - // 获取累计时长接口 - server.on("/duration", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(200, "text/plain", runtime.formatDuration(runtime.getActiveDuration())); - }); - - // 清空累计时长接口 - server.on("/resetDuration", HTTP_POST, [](AsyncWebServerRequest *request) { - runtime.resetActiveDuration(); - request->send(200, "text/plain", "Duration reset"); - }); - - // 接收设置Wiper的请求 - server.on("/setWiper", HTTP_POST, [](AsyncWebServerRequest *request) { - if (request->hasParam("value", true)) { - int wiperValue = request->getParam("value", true)->value().toInt(); - ds3502.setWiper(wiperValue); - request->send(200, "text/plain", "Wiper value set"); - } else { - request->send(400, "text/plain", "Missing value parameter"); - } - }); - - server.begin(); + // if (millis() - lastReadTime >= readInterval) { + // int res = dht11.readTemperatureHumidity(temp, humi); + // if(res == 0){ + // data.temperature = temp; + // data.humidity = humi; + // }else{ + // data.temperature=0; + // data.humidity=0; + // } + // lastReadTime = millis(); + // } } - - // 风扇控制 void FansControl(void) { - if (ds18b20.getTempC() > 30) { + if (sensorData.power > 10) { digitalWrite(FAN_PIN1, HIGH); digitalWrite(FAN_PIN2, HIGH); } else { @@ -149,7 +112,7 @@ void FansControl(void) { // 蜂鸣器 void Buzzer(void) { - if (ds18b20.getTempC() > 45) { + if (sensorData.temperature > 40) { digitalWrite(BUZZER_PIN, HIGH); //tone(BUZZER_PIN, 1000, 1000); } else { @@ -162,11 +125,11 @@ void Buzzer(void) { void ButtonControl(void) { // 按键检测 static int lastWiperValue = ds3502.getWiper(); - static const int step = 5; // 步进值 + static const int step = 1; // 步进值 if (digitalRead(INCREASE_BUTTON_PIN) == LOW) { int currentWiperValue = ds3502.getWiper(); - if (currentWiperValue < 127) { + if (currentWiperValue < 100) { ds3502.setWiper(currentWiperValue + step); lastWiperValue = currentWiperValue + step; Serial.printf("Increased Wiper Value: %d\n", lastWiperValue); @@ -185,22 +148,175 @@ void ButtonControl(void) { } } +//灯光开关控制 +void LightOpen() { + digitalWrite(LIGHT_CONTROL_PIN, HIGH); // 低电平开灯【继电器默认闭合】 + Serial.println("Light ON"); +} +void LightClose() { + digitalWrite(LIGHT_CONTROL_PIN, LOW); // 高电平关灯 + Serial.println("Light OFF"); +} + + +void WebServer_Init(void) { + // 根路径页面 + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send_P(200, "text/html", index_html); + }); + + // 合并数据接口 + server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request){ + StaticJsonDocument<256> doc; + doc["temperature"] = sensorData.temperature; + doc["humidity"] = sensorData.humidity; + doc["lightA"] = sensorData.lightA; + doc["lightB"] = sensorData.lightB; + doc["wiper"] = ds3502.getWiper(); + doc["busVoltage"] = sensorData.busVoltage; + doc["shuntVoltage"] = sensorData.shuntVoltage_mV; + doc["current"] = sensorData.current; + doc["power"] = sensorData.power; + doc["duration"] = runtime.formatDuration(runtime.getActiveDuration()); + + String json; + serializeJson(doc, json); + request->send(200, "application/json", json); + }); + + // 设置Wiper值 + server.on("/setWiper", HTTP_POST, [](AsyncWebServerRequest *request){ + if (request->hasParam("value", true)) { + int wiperValue = request->getParam("value", true)->value().toInt(); + ds3502.setWiper(wiperValue); + request->send(200, "text/plain", "Wiper value set"); + } else { + request->send(400, "text/plain", "Missing value parameter"); + } + }); + + // 清空累计时长 + server.on("/resetDuration", HTTP_POST, [](AsyncWebServerRequest *request){ + runtime.resetActiveDuration(); + request->send(200, "text/plain", "Duration reset"); + }); + + // 灯光控制接口 + server.on("/LightOpen", HTTP_POST, [](AsyncWebServerRequest *request){ + LightOpen(); + request->send(200, "text/plain", "Light ON"); + }); + + server.on("/LightClose", HTTP_POST, [](AsyncWebServerRequest *request){ + LightClose(); + request->send(200, "text/plain", "Light OFF"); + }); + + // 获取灯光状态接口 + server.on("/lightStatus", HTTP_GET, [](AsyncWebServerRequest *request){ + bool isOn = (digitalRead(LIGHT_CONTROL_PIN) == HIGH); // 高电平为开灯 + request->send(200, "text/plain", isOn ? "on" : "off"); + }); + + // 启动自动调节 + server.on("/startAutoAdjust", HTTP_POST, [](AsyncWebServerRequest *request){ + if (request->hasParam("target", true)) { + int targetLux = request->getParam("target", true)->value().toInt(); + if (targetLux > 0) { + lightCtrl.runUntilTargetReached(targetLux, 150); + //lightCtrl.setTargetLight(targetLux); + isAutoAdjustEnabled = true; + request->send(200, "text/plain", "Auto adjust started"); + } else { + request->send(400, "text/plain", "Invalid target lux"); + } + } else { + request->send(400, "text/plain", "Missing target parameter"); + } + }); + + // 停止自动调节 + server.on("/stopAutoAdjust", HTTP_POST, [](AsyncWebServerRequest *request){ + isAutoAdjustEnabled = false; + request->send(200, "text/plain", "Auto adjust stopped"); + }); + + // 获取自动调节状态 + server.on("/autoAdjustStatus", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", isAutoAdjustEnabled ? "enabled" : "disabled"); + }); + + // 启动服务器 + server.begin(); +} + + + + +//usb串口打印信息 +// void SerialPrintInfo(void) { +// // 获取温湿度数据 +// dht11.readTemperatureHumidity(temperature, humidity); + +// // 获取光照数据 +// float lightA = bh1750_a.readLightLevel(); +// float lightB = bh1750_b.readLightLevel(); + +// // 获取电流监控数据 +// float busVoltage = INA.getBusVoltage(); +// float shuntVoltage_mV = INA.getShuntVoltage_mV(); +// float current = INA.getCurrent(); +// float power = INA.getPower(); + +// // 打印信息(只使用变量) +// Serial.printf("Temperature: %dC, Humidity: %d%%\n", temperature, humidity); +// Serial.printf("A: %.0f lux :: B: %.0f lux \n", lightA, lightB); + +// Serial.println("\nBUS\tSHUNT\tCURRENT\tPOWER"); +// Serial.printf("%.2f\t%.2f\t%.2f\t%.2f\n", busVoltage, shuntVoltage_mV, current, power); +// } + +void SerialPrintInfo(void) { + UpdateSensorData(sensorData); + + // 打印日志 + Serial.printf("Temperature: %dC, Humidity: %d%%\n", sensorData.temperature, sensorData.humidity); + Serial.printf("A: %d lux :: B: %d lux \n", sensorData.lightA, sensorData.lightB); + + // Serial.println("\nBUS\tSHUNT\tCURRENT\tPOWER"); + // Serial.printf("%.2f\t%.2f\t%.2f\t%.2f\n", + // sensorData.busVoltage, + // sensorData.shuntVoltage_mV, + // sensorData.current, + // sensorData.power); + Serial.println("\nBUS\tCURRENT\tPOWER"); + Serial.printf("%.2f\t%.2f\t%.2f\n", + sensorData.busVoltage, + //sensorData.shuntVoltage_mV, + sensorData.current, + sensorData.power); +} void setup(void) -{ +{ + // esp_task_wdt_init(5, true); // 5秒超时,触发重启 + // esp_task_wdt_add(NULL); // 添加当前任务到 WDT 监控 + + //dht11.setDelay(150);// Set this to the desired delay. Default is 500ms. + Serial.begin(115200); // 初始化 I2C 总线 Wire.begin(5, 4); // SDA SCL + sht31.begin(); + //Wire.setClock(100000); ds3502.begin(5, 4); // INA226 初始化 INA.setMaxCurrentShunt(10.0, 0.005); // 设置最大电流和分流电阻 - // MCP45HVX1 初始化 - //digiPot.begin(5, 4); // 启动定时器,每秒检查电流状态 currentCheckTicker.attach(1, []() { runtime.checkCurrent(); }); @@ -208,7 +324,6 @@ void setup(void) //sensor.begin(); // BH1750 初始化 //bh1750_init(bh1750_a, bh1750_b, 36, 35, 21, 20); //1750初始化。sda, scl - //Wire.begin(36,35); Wire1.begin(36, 35); bh1750_a.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire); bh1750_b.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23, &Wire1); @@ -224,10 +339,10 @@ void setup(void) // 串口屏初始化 tjcShow.init(); - tjcShow.showQR(); + ds3502.setWiper(2); - delay(300); + delay(200); Serial.printf("pot: %d\n", ds3502.getWiper()); pinMode(FAN_PIN1, OUTPUT); @@ -238,50 +353,38 @@ void setup(void) pinMode(INCREASE_BUTTON_PIN, INPUT_PULLUP); pinMode(DECREASE_BUTTON_PIN, INPUT_PULLUP); + pinMode(LIGHT_CONTROL_PIN, OUTPUT); + + tjcShow.goToPage("main"); // 替代 sendCommand("page main\xff\xff\xff"); + } +unsigned long lastSensorRead = 0; +const long sensorInterval = 1000; // 每 * 秒打印一次传感器 void loop(void) { - ds18b20.printTemperature(); //ds18b20温度 + //esp_task_wdt_reset(); // 喂狗 - //printLightLevels(bh1750_a, bh1750_b); //bh1750照度 - Serial.printf("A: %.0f lux :: B: %.0f lux \n", - bh1750_a.readLightLevel(), - bh1750_b.readLightLevel()); + if (millis() - lastSensorRead >= sensorInterval) { + SerialPrintInfo(); // 更新传感器数据 -// // MCP45HVX1 操作 -// //digiPot.writeWiper(127); // 随机设置 Wiper 值,random(0, 256) -// ds3502.setWiper(random(0, 127)); -// Serial.print("Current Wiper Value: "); -// Serial.println(ds3502.getWiper()); - + tjcShow.showInfo(); //串口屏显示 - // INA226 数据读取 - Serial.println("\nBUS\tSHUNT\tCURRENT\tPOWER"); - Serial.print(INA.getBusVoltage(), 3); //总线电压 MAX 36V - Serial.print("\t"); - Serial.print(INA.getShuntVoltage_mV(), 3); //分流电压 - Serial.print("\t"); - Serial.print(INA.getCurrent(), 3); //通过分流器的电流 - Serial.print("\t"); - Serial.print(INA.getPower(), 3); //Current x BusVoltage - Serial.println(); + lastSensorRead = millis(); + } - tjcShow.showInfo(); //串口屏 - + //串口解析tjc控制指令 + tjcShow.processSerial(); FansControl(); Buzzer(); ButtonControl(); - if (autoAdjustEnabled) { - lightCtrl.adjustWiper(); // 比例调节 - } - - - delay(1000); +// if (isAutoAdjustEnabled) { +// lightCtrl.runUntilTargetReached(lightCtrl.getTargetLight(), 100); // 每次最多尝试100次 +// } } diff --git a/src/webpages.h b/src/webpages.h index 102bb36..229fef6 100644 --- a/src/webpages.h +++ b/src/webpages.h @@ -111,47 +111,38 @@ const char index_html[] PROGMEM = R"rawliteral( ESP32 Data Display

实时数据显示

+

累计使用时长: 加载中...

+

温度: 加载中... °C

+

湿度: 加载中... %RH

光照强度(A): 加载中... lx

光照强度(B): 加载中... lx

电位器Wiper值: 加载中...

总线电压: 加载中... V

分流电压: 加载中... mV

-

电流: 加载中... mA

-

功率: 加载中... mW

+

电流: 加载中... A

+

功率: 加载中... W

+

设置Wiper值

+ +

灯光控制

+

灯光状态: 加载中...

+ + + +

自动调节

+

状态: 加载中...

+

目标照度: lux

+ + -)rawliteral"; // 添加分号 - - +)rawliteral"; #endif // WEBPAGES_H \ No newline at end of file