25/06/17
This commit is contained in:
@ -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
|
||||
|
||||
|
||||
240
src/DHT11.cpp
Normal file
240
src/DHT11.cpp
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
110
src/DHT11.h
Normal file
110
src/DHT11.h
Normal file
@ -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
|
||||
234
src/DS18B20.cpp
234
src/DS18B20.cpp
@ -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 --
|
||||
|
||||
@ -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 --
|
||||
|
||||
@ -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 寄存器
|
||||
|
||||
@ -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;
|
||||
|
||||
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 lux | Wiper Value: %d\n", currentLight, 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);
|
||||
}
|
||||
@ -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
|
||||
@ -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;
|
||||
|
||||
380
src/SHT31.cpp
Normal file
380
src/SHT31.cpp
Normal file
@ -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 --
|
||||
|
||||
122
src/SHT31.h
Normal file
122
src/SHT31.h
Normal file
@ -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 --
|
||||
|
||||
|
||||
337
src/TJC_Show.cpp
337
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() {
|
||||
// if (_serial.available() >= FRAME_LENGTH) {
|
||||
// uint8_t frame_header = _serial.peek();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 读掉帧头
|
||||
_serial.read();
|
||||
|
||||
// 尝试读取命令字节
|
||||
if (_serial.available() < 1) return;
|
||||
uint8_t cmdByte = _serial.read();
|
||||
|
||||
// 定义最大可能帧长度
|
||||
const int MAX_FRAME_LENGTH = 12;
|
||||
uint8_t buffer[MAX_FRAME_LENGTH];
|
||||
buffer[0] = 0x55;
|
||||
buffer[1] = cmdByte;
|
||||
|
||||
// 最大尝试读取长度
|
||||
int remaining = _serial.available();
|
||||
int toRead = (remaining > MAX_FRAME_LENGTH - 2) ? MAX_FRAME_LENGTH - 2 : remaining;
|
||||
_serial.readBytes(buffer + 2, toRead);
|
||||
|
||||
// 当前已读取的总字节数
|
||||
int totalRead = 2 + toRead;
|
||||
|
||||
// 查找帧尾位置(三个连续的 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;
|
||||
}
|
||||
}
|
||||
|
||||
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<char>(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];
|
||||
|
||||
if (SELECTED_WIFI_MODE == WIFI_MODE_STA){
|
||||
snprintf(str, sizeof(str), "t15.txt=\"MODE_STA:%s\"\xff\xff\xff", WiFi.localIP());
|
||||
snprintf(str, sizeof(str), "msg.txt=\"h1.val is %d\"\xff\xff\xff", buffer[2]);
|
||||
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);
|
||||
break;
|
||||
|
||||
default:
|
||||
Serial.printf("Unknown command: 0x%02X\n", cmdByte);
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(str, sizeof(str), "t0.txt=\"%.2f\"\xff\xff\xff", _ds18b20.getTempC());
|
||||
sendCommand(str);
|
||||
|
||||
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);
|
||||
|
||||
snprintf(str, sizeof(str), "t14.txt=\"%s\"\xff\xff\xff", runtime.formatDuration(runtime.getActiveDuration()));
|
||||
sendCommand(str);
|
||||
|
||||
snprintf(str, sizeof(str), "t8.txt=\"%d\"\xff\xff\xff", _ds3502.getWiper());
|
||||
sendCommand(str);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void TJC_Show::clearSerialBuffer() {
|
||||
while (_serial.read() >= 0); // 清空串口缓冲区
|
||||
}
|
||||
|
||||
void TJC_Show::sendCommand(const char* command) {
|
||||
_serial.print(command);
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "BH1750.h"
|
||||
#include "DS18B20.h"
|
||||
#include "SHT31.h"
|
||||
#include "DS3502.h"
|
||||
#include "INA226.h"
|
||||
#include <WiFi.h>
|
||||
@ -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
|
||||
@ -4,7 +4,6 @@
|
||||
#include "webpages.h" // 包含 HTML 页面内容
|
||||
#include <WiFi.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include "DS18B20.h"
|
||||
#include "BH1750.h"
|
||||
#include "Wire.h"
|
||||
//#include "MCP45HVX1.h"
|
||||
|
||||
@ -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: ");
|
||||
|
||||
339
src/main.cpp
339
src/main.cpp
@ -1,4 +1,4 @@
|
||||
#include "DS18B20.h"
|
||||
#include "SHT31.h"
|
||||
#include "BH1750.h"
|
||||
#include "Wire.h"
|
||||
#include <WiFi.h>
|
||||
@ -15,6 +15,10 @@
|
||||
#include "DS3502.h"
|
||||
#include "LightControl.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// #include <esp_task_wdt.h>
|
||||
|
||||
|
||||
// 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(); //串口屏显示
|
||||
|
||||
lastSensorRead = millis();
|
||||
}
|
||||
|
||||
|
||||
// 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();
|
||||
|
||||
|
||||
tjcShow.showInfo(); //串口屏
|
||||
|
||||
//串口解析tjc控制指令
|
||||
tjcShow.processSerial();
|
||||
|
||||
FansControl();
|
||||
Buzzer();
|
||||
ButtonControl();
|
||||
|
||||
if (autoAdjustEnabled) {
|
||||
lightCtrl.adjustWiper(); // 比例调节
|
||||
}
|
||||
|
||||
|
||||
delay(1000);
|
||||
// if (isAutoAdjustEnabled) {
|
||||
// lightCtrl.runUntilTargetReached(lightCtrl.getTargetLight(), 100); // 每次最多尝试100次
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
149
src/webpages.h
149
src/webpages.h
@ -111,47 +111,38 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
<title>ESP32 Data Display</title>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
async function fetchData()
|
||||
{
|
||||
const durationResponse = await fetch('/duration');
|
||||
const durationText = await durationResponse.text();
|
||||
document.getElementById('activeDuration').innerText = durationText;
|
||||
|
||||
const tempResponse = await fetch('/temperature');
|
||||
const tempText = await tempResponse.text();
|
||||
document.getElementById('temperature').innerText = tempText;
|
||||
|
||||
const lightAResponse = await fetch('/lightA');
|
||||
const lightAText = await lightAResponse.text();
|
||||
document.getElementById('lightA').innerText = lightAText;
|
||||
|
||||
const lightBResponse = await fetch('/lightB');
|
||||
const lightBText = await lightBResponse.text();
|
||||
document.getElementById('lightB').innerText = lightBText;
|
||||
|
||||
const wiperResponse = await fetch('/wiper');
|
||||
const wiperText = await wiperResponse.text();
|
||||
document.getElementById('wiper').innerText = wiperText;
|
||||
|
||||
const busResponse = await fetch('/busVoltage');
|
||||
const busText = await busResponse.text();
|
||||
document.getElementById('busVoltage').innerText = busText;
|
||||
|
||||
const shuntResponse = await fetch('/shuntVoltage');
|
||||
const shuntText = await shuntResponse.text();
|
||||
document.getElementById('shuntVoltage').innerText = shuntText;
|
||||
|
||||
const currentResponse = await fetch('/current');
|
||||
const currentText = await currentResponse.text();
|
||||
document.getElementById('current').innerText = currentText;
|
||||
|
||||
const powerResponse = await fetch('/power');
|
||||
const powerText = await powerResponse.text();
|
||||
document.getElementById('power').innerText = powerText;
|
||||
async function fetchData() {
|
||||
const response = await fetch('/data');
|
||||
if (!response.ok) {
|
||||
alert('数据获取失败');
|
||||
return;
|
||||
}
|
||||
|
||||
async function setWiper()
|
||||
{
|
||||
const data = await response.json();
|
||||
|
||||
document.getElementById('activeDuration').innerText = data.duration;
|
||||
document.getElementById('temperature').innerText = data.temperature;
|
||||
document.getElementById('humidity').innerText = data.humidity;
|
||||
document.getElementById('lightA').innerText = data.lightA;
|
||||
document.getElementById('lightB').innerText = data.lightB;
|
||||
document.getElementById('wiper').innerText = data.wiper;
|
||||
document.getElementById('busVoltage').innerText = data.busVoltage;
|
||||
document.getElementById('shuntVoltage').innerText = data.shuntVoltage;
|
||||
document.getElementById('current').innerText = data.current;
|
||||
document.getElementById('power').innerText = data.power;
|
||||
|
||||
// 获取灯光状态
|
||||
const statusResponse = await fetch('/lightStatus');
|
||||
const statusText = await statusResponse.text();
|
||||
document.getElementById('lightStatus').innerText = statusText === 'on' ? '开启' : '关闭';
|
||||
|
||||
// 获取自动调节状态
|
||||
const autoAdjustStatusResponse = await fetch('/autoAdjustStatus');
|
||||
const autoAdjustStatusText = await autoAdjustStatusResponse.text();
|
||||
document.getElementById('autoAdjustStatus').innerText = autoAdjustStatusText === 'enabled' ? '开启' : '关闭';
|
||||
}
|
||||
|
||||
async function setWiper() {
|
||||
const wiperValue = document.getElementById('wiperValue').value;
|
||||
const response = await fetch('/setWiper', {
|
||||
method: 'POST',
|
||||
@ -165,8 +156,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
}
|
||||
}
|
||||
|
||||
async function resetDuration()
|
||||
{
|
||||
async function resetDuration() {
|
||||
const response = await fetch('/resetDuration', { method: 'POST' });
|
||||
if (response.ok) {
|
||||
alert('累计使用时长已清空');
|
||||
@ -175,28 +165,93 @@ const char index_html[] PROGMEM = R"rawliteral(
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(fetchData, 1000);
|
||||
async function turnOnLight() {
|
||||
const response = await fetch('/LightOpen', { method: 'POST' });
|
||||
if (response.ok) {
|
||||
updateLightStatus("开启");
|
||||
} else {
|
||||
alert('打开失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function turnOffLight() {
|
||||
const response = await fetch('/LightClose', { method: 'POST' });
|
||||
if (response.ok) {
|
||||
updateLightStatus("关闭");
|
||||
} else {
|
||||
alert('关闭失败');
|
||||
}
|
||||
}
|
||||
|
||||
function updateLightStatus(status) {
|
||||
document.getElementById('lightStatus').innerText = status;
|
||||
}
|
||||
|
||||
async function enableAutoAdjust() {
|
||||
const targetLux = document.getElementById('targetLux').value;
|
||||
if (!targetLux || isNaN(targetLux) || parseInt(targetLux) <= 0) {
|
||||
alert('请输入有效的正整数作为目标照度');
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送请求给 ESP32 开启自动调节并设置目标照度
|
||||
const response = await fetch('/startAutoAdjust', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: 'target=' + encodeURIComponent(targetLux)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('已开启自动调节');
|
||||
} else {
|
||||
alert('开启失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function disableAutoAdjust() {
|
||||
const response = await fetch('/stopAutoAdjust', { method: 'POST' });
|
||||
if (response.ok) {
|
||||
alert('已关闭自动调节');
|
||||
} else {
|
||||
alert('关闭失败');
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(fetchData, 1000); // 每秒刷新一次数据
|
||||
</script>
|
||||
</head>
|
||||
<body onload="fetchData()">
|
||||
<h1>实时数据显示</h1>
|
||||
|
||||
<p>累计使用时长: <span id="activeDuration">加载中...</span></p>
|
||||
<button onclick="resetDuration()">清空累计时长</button>
|
||||
|
||||
<p>温度: <span id="temperature">加载中...</span> °C</p>
|
||||
<p>湿度: <span id="humidity">加载中...</span> %RH</p>
|
||||
<p>光照强度(A): <span id="lightA">加载中...</span> lx</p>
|
||||
<p>光照强度(B): <span id="lightB">加载中...</span> lx</p>
|
||||
<p>电位器Wiper值: <span id="wiper">加载中...</span></p>
|
||||
<p>总线电压: <span id="busVoltage">加载中...</span> V</p>
|
||||
<p>分流电压: <span id="shuntVoltage">加载中...</span> mV</p>
|
||||
<p>电流: <span id="current">加载中...</span> mA</p>
|
||||
<p>功率: <span id="power">加载中...</span> mW</p>
|
||||
<p>电流: <span id="current">加载中...</span> A</p>
|
||||
<p>功率: <span id="power">加载中...</span> W</p>
|
||||
|
||||
<h2>设置Wiper值</h2>
|
||||
<input type="range" id="wiperValue" min="0" max="127" step="1" value="2" />
|
||||
<button onclick="setWiper()">设置</button>
|
||||
|
||||
<h2>灯光控制</h2>
|
||||
<p>灯光状态: <span id="lightStatus">加载中...</span></p>
|
||||
<button onclick="turnOnLight()">开灯</button>
|
||||
<button onclick="turnOffLight()">关灯</button>
|
||||
|
||||
<h2>自动调节</h2>
|
||||
<p>状态: <span id="autoAdjustStatus">加载中...</span></p>
|
||||
<p>目标照度: <input type="number" id="targetLux" min="0" max="100000" step="1" value="2000" /> lux</p>
|
||||
<button onclick="enableAutoAdjust()">开启</button>
|
||||
<button onclick="disableAutoAdjust()">关闭</button>
|
||||
</body>
|
||||
</html>
|
||||
)rawliteral"; // 添加分号
|
||||
|
||||
|
||||
)rawliteral";
|
||||
|
||||
#endif // WEBPAGES_H
|
||||
Reference in New Issue
Block a user