mirror of
https://github.com/esphome/esphome.git
synced 2024-11-14 10:26:53 +01:00
commit
0cb715bb76
@ -60,6 +60,10 @@ async def to_code(config):
|
|||||||
image.seek(frameIndex)
|
image.seek(frameIndex)
|
||||||
frame = image.convert("L", dither=Image.NONE)
|
frame = image.convert("L", dither=Image.NONE)
|
||||||
pixels = list(frame.getdata())
|
pixels = list(frame.getdata())
|
||||||
|
if len(pixels) != height * width:
|
||||||
|
raise core.EsphomeError(
|
||||||
|
f"Unexpected number of pixels in frame {frameIndex}: {len(pixels)} != {height*width}"
|
||||||
|
)
|
||||||
for pix in pixels:
|
for pix in pixels:
|
||||||
data[pos] = pix
|
data[pos] = pix
|
||||||
pos += 1
|
pos += 1
|
||||||
@ -69,8 +73,14 @@ async def to_code(config):
|
|||||||
pos = 0
|
pos = 0
|
||||||
for frameIndex in range(frames):
|
for frameIndex in range(frames):
|
||||||
image.seek(frameIndex)
|
image.seek(frameIndex)
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
image.thumbnail(config[CONF_RESIZE])
|
||||||
frame = image.convert("RGB")
|
frame = image.convert("RGB")
|
||||||
pixels = list(frame.getdata())
|
pixels = list(frame.getdata())
|
||||||
|
if len(pixels) != height * width:
|
||||||
|
raise core.EsphomeError(
|
||||||
|
f"Unexpected number of pixels in frame {frameIndex}: {len(pixels)} != {height*width}"
|
||||||
|
)
|
||||||
for pix in pixels:
|
for pix in pixels:
|
||||||
data[pos] = pix[0]
|
data[pos] = pix[0]
|
||||||
pos += 1
|
pos += 1
|
||||||
|
@ -73,51 +73,52 @@ AnovaPacket *AnovaCodec::get_stop_request() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||||
memset(this->buf_, 0, 32);
|
char buf[32];
|
||||||
strncpy(this->buf_, (char *) data, length);
|
memset(buf, 0, sizeof(buf));
|
||||||
|
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
|
||||||
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||||
switch (this->current_query_) {
|
switch (this->current_query_) {
|
||||||
case READ_DEVICE_STATUS: {
|
case READ_DEVICE_STATUS: {
|
||||||
if (!strncmp(this->buf_, "stopped", 7)) {
|
if (!strncmp(buf, "stopped", 7)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = false;
|
this->running_ = false;
|
||||||
}
|
}
|
||||||
if (!strncmp(this->buf_, "running", 7)) {
|
if (!strncmp(buf, "running", 7)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = true;
|
this->running_ = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case START: {
|
case START: {
|
||||||
if (!strncmp(this->buf_, "start", 5)) {
|
if (!strncmp(buf, "start", 5)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = true;
|
this->running_ = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STOP: {
|
case STOP: {
|
||||||
if (!strncmp(this->buf_, "stop", 4)) {
|
if (!strncmp(buf, "stop", 4)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = false;
|
this->running_ = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_TARGET_TEMPERATURE: {
|
case READ_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
this->target_temp_ = parse_number<float>(buf, sizeof(buf)).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->target_temp_ = ftoc(this->target_temp_);
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SET_TARGET_TEMPERATURE: {
|
case SET_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
this->target_temp_ = parse_number<float>(buf, sizeof(buf)).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->target_temp_ = ftoc(this->target_temp_);
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_CURRENT_TEMPERATURE: {
|
case READ_CURRENT_TEMPERATURE: {
|
||||||
this->current_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
this->current_temp_ = parse_number<float>(buf, sizeof(buf)).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->current_temp_ = ftoc(this->current_temp_);
|
this->current_temp_ = ftoc(this->current_temp_);
|
||||||
this->has_current_temp_ = true;
|
this->has_current_temp_ = true;
|
||||||
@ -125,8 +126,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
|||||||
}
|
}
|
||||||
case SET_UNIT:
|
case SET_UNIT:
|
||||||
case READ_UNIT: {
|
case READ_UNIT: {
|
||||||
this->unit_ = this->buf_[0];
|
this->unit_ = buf[0];
|
||||||
this->fahrenheit_ = this->buf_[0] == 'f';
|
this->fahrenheit_ = buf[0] == 'f';
|
||||||
this->has_unit_ = true;
|
this->has_unit_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,6 @@ class AnovaCodec {
|
|||||||
bool has_current_temp_;
|
bool has_current_temp_;
|
||||||
bool has_unit_;
|
bool has_unit_;
|
||||||
bool has_running_;
|
bool has_running_;
|
||||||
char buf_[32];
|
|
||||||
bool fahrenheit_;
|
bool fahrenheit_;
|
||||||
|
|
||||||
CurrentQuery current_query_;
|
CurrentQuery current_query_;
|
||||||
|
@ -21,12 +21,19 @@ static const char *const TAG = "esp32_camera_web_server";
|
|||||||
#define CONTENT_TYPE "image/jpeg"
|
#define CONTENT_TYPE "image/jpeg"
|
||||||
#define CONTENT_LENGTH "Content-Length"
|
#define CONTENT_LENGTH "Content-Length"
|
||||||
|
|
||||||
static const char *const STREAM_HEADER =
|
static const char *const STREAM_HEADER = "HTTP/1.0 200 OK\r\n"
|
||||||
"HTTP/1.1 200\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY
|
"Access-Control-Allow-Origin: *\r\n"
|
||||||
"\r\n";
|
"Connection: close\r\n"
|
||||||
static const char *const STREAM_500 = "HTTP/1.1 500\r\nContent-Type: text/plain\r\n\r\nNo frames send.\r\n";
|
"Content-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY "\r\n"
|
||||||
static const char *const STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
"\r\n"
|
||||||
|
"--" PART_BOUNDARY "\r\n";
|
||||||
|
static const char *const STREAM_ERROR = "Content-Type: text/plain\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"No frames send.\r\n"
|
||||||
|
"--" PART_BOUNDARY "\r\n";
|
||||||
static const char *const STREAM_PART = "Content-Type: " CONTENT_TYPE "\r\n" CONTENT_LENGTH ": %u\r\n\r\n";
|
static const char *const STREAM_PART = "Content-Type: " CONTENT_TYPE "\r\n" CONTENT_LENGTH ": %u\r\n\r\n";
|
||||||
|
static const char *const STREAM_BOUNDARY = "\r\n"
|
||||||
|
"--" PART_BOUNDARY "\r\n";
|
||||||
|
|
||||||
CameraWebServer::CameraWebServer() {}
|
CameraWebServer::CameraWebServer() {}
|
||||||
|
|
||||||
@ -45,6 +52,7 @@ void CameraWebServer::setup() {
|
|||||||
config.ctrl_port = this->port_;
|
config.ctrl_port = this->port_;
|
||||||
config.max_open_sockets = 1;
|
config.max_open_sockets = 1;
|
||||||
config.backlog_conn = 2;
|
config.backlog_conn = 2;
|
||||||
|
config.lru_purge_enable = true;
|
||||||
|
|
||||||
if (httpd_start(&this->httpd_, &config) != ESP_OK) {
|
if (httpd_start(&this->httpd_, &config) != ESP_OK) {
|
||||||
mark_failed();
|
mark_failed();
|
||||||
@ -172,9 +180,6 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
|||||||
ESP_LOGW(TAG, "STREAM: failed to acquire frame");
|
ESP_LOGW(TAG, "STREAM: failed to acquire frame");
|
||||||
res = ESP_FAIL;
|
res = ESP_FAIL;
|
||||||
}
|
}
|
||||||
if (res == ESP_OK) {
|
|
||||||
res = httpd_send_all(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
|
||||||
}
|
|
||||||
if (res == ESP_OK) {
|
if (res == ESP_OK) {
|
||||||
size_t hlen = snprintf(part_buf, 64, STREAM_PART, image->get_data_length());
|
size_t hlen = snprintf(part_buf, 64, STREAM_PART, image->get_data_length());
|
||||||
res = httpd_send_all(req, part_buf, hlen);
|
res = httpd_send_all(req, part_buf, hlen);
|
||||||
@ -182,6 +187,9 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
|||||||
if (res == ESP_OK) {
|
if (res == ESP_OK) {
|
||||||
res = httpd_send_all(req, (const char *) image->get_data_buffer(), image->get_data_length());
|
res = httpd_send_all(req, (const char *) image->get_data_buffer(), image->get_data_length());
|
||||||
}
|
}
|
||||||
|
if (res == ESP_OK) {
|
||||||
|
res = httpd_send_all(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||||
|
}
|
||||||
if (res == ESP_OK) {
|
if (res == ESP_OK) {
|
||||||
frames++;
|
frames++;
|
||||||
int64_t frame_time = millis() - last_frame;
|
int64_t frame_time = millis() - last_frame;
|
||||||
@ -193,7 +201,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!frames) {
|
if (!frames) {
|
||||||
res = httpd_send_all(req, STREAM_500, strlen(STREAM_500));
|
res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR));
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames);
|
ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames);
|
||||||
|
@ -32,7 +32,7 @@ void EZOSensor::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EZOSensor::loop() {
|
void EZOSensor::loop() {
|
||||||
uint8_t buf[20];
|
uint8_t buf[21];
|
||||||
if (!(this->state_ & EZO_STATE_WAIT)) {
|
if (!(this->state_ & EZO_STATE_WAIT)) {
|
||||||
if (this->state_ & EZO_STATE_SEND_TEMP) {
|
if (this->state_ & EZO_STATE_SEND_TEMP) {
|
||||||
int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_);
|
int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_);
|
||||||
@ -74,7 +74,7 @@ void EZOSensor::loop() {
|
|||||||
if (buf[0] != 1)
|
if (buf[0] != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float val = parse_number<float>((char *) &buf[1], sizeof(buf) - 1).value_or(0);
|
float val = parse_number<float>((char *) &buf[1], sizeof(buf) - 2).value_or(0);
|
||||||
this->publish_state(val);
|
this->publish_state(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,17 +94,21 @@ UART_DIRECTIONS = {
|
|||||||
"BOTH": UARTDirection.UART_DIRECTION_BOTH,
|
"BOTH": UARTDirection.UART_DIRECTION_BOTH,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AFTER_DEFAULTS = {CONF_BYTES: 256, CONF_TIMEOUT: "100ms"}
|
||||||
|
|
||||||
DEBUG_SCHEMA = cv.Schema(
|
DEBUG_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger),
|
||||||
cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum(
|
cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum(
|
||||||
UART_DIRECTIONS, upper=True
|
UART_DIRECTIONS, upper=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AFTER): cv.Schema(
|
cv.Optional(CONF_AFTER, default=AFTER_DEFAULTS): cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_BYTES, default=256): cv.validate_bytes,
|
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_TIMEOUT, default="100ms"
|
CONF_BYTES, default=AFTER_DEFAULTS[CONF_BYTES]
|
||||||
|
): cv.validate_bytes,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_TIMEOUT, default=AFTER_DEFAULTS[CONF_TIMEOUT]
|
||||||
): cv.positive_time_period_milliseconds,
|
): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
|
cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
|
||||||
}
|
}
|
||||||
|
@ -57,38 +57,46 @@ void IRAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) {
|
|||||||
void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) {
|
void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) {
|
||||||
switch (message->type) {
|
switch (message->type) {
|
||||||
case HUMIDITY:
|
case HUMIDITY:
|
||||||
this->humidity = (message->value > 10000) ? NAN : (message->value / 100.0f);
|
this->humidity = message->value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEMPERATURE:
|
case TEMPERATURE:
|
||||||
this->temperature = (message->value > 5970) ? NAN : (message->value / 16.0f - 273.15f);
|
this->temperature = message->value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CO2:
|
case CO2:
|
||||||
this->co2 = (message->value > 10000) ? NAN : message->value;
|
this->co2 = message->value;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZyAuraSensor::publish_state_(sensor::Sensor *sensor, float *value) {
|
bool ZyAuraSensor::publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value) {
|
||||||
// Sensor doesn't added to configuration
|
// Sensor wasn't added to configuration
|
||||||
if (sensor == nullptr) {
|
if (sensor == nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sensor->publish_state(*value);
|
float value = NAN;
|
||||||
|
switch (data_type) {
|
||||||
|
case HUMIDITY:
|
||||||
|
value = (*data_value > 10000) ? NAN : (*data_value / 100.0f);
|
||||||
|
break;
|
||||||
|
case TEMPERATURE:
|
||||||
|
value = (*data_value > 5970) ? NAN : (*data_value / 16.0f - 273.15f);
|
||||||
|
break;
|
||||||
|
case CO2:
|
||||||
|
value = (*data_value > 10000) ? NAN : *data_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensor->publish_state(value);
|
||||||
|
|
||||||
// Sensor reported wrong value
|
// Sensor reported wrong value
|
||||||
if (std::isnan(*value)) {
|
if (std::isnan(value)) {
|
||||||
ESP_LOGW(TAG, "Sensor reported invalid data. Is the update interval too small?");
|
ESP_LOGW(TAG, "Sensor reported invalid data. Is the update interval too small?");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*value = NAN;
|
*data_value = -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +112,9 @@ void ZyAuraSensor::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ZyAuraSensor::update() {
|
void ZyAuraSensor::update() {
|
||||||
bool co2_result = this->publish_state_(this->co2_sensor_, &this->store_.co2);
|
bool co2_result = this->publish_state_(CO2, this->co2_sensor_, &this->store_.co2);
|
||||||
bool temperature_result = this->publish_state_(this->temperature_sensor_, &this->store_.temperature);
|
bool temperature_result = this->publish_state_(TEMPERATURE, this->temperature_sensor_, &this->store_.temperature);
|
||||||
bool humidity_result = this->publish_state_(this->humidity_sensor_, &this->store_.humidity);
|
bool humidity_result = this->publish_state_(HUMIDITY, this->humidity_sensor_, &this->store_.humidity);
|
||||||
|
|
||||||
if (co2_result && temperature_result && humidity_result) {
|
if (co2_result && temperature_result && humidity_result) {
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
|
@ -42,9 +42,9 @@ class ZaDataProcessor {
|
|||||||
|
|
||||||
class ZaSensorStore {
|
class ZaSensorStore {
|
||||||
public:
|
public:
|
||||||
float co2 = NAN;
|
uint16_t co2 = -1;
|
||||||
float temperature = NAN;
|
uint16_t temperature = -1;
|
||||||
float humidity = NAN;
|
uint16_t humidity = -1;
|
||||||
|
|
||||||
void setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data);
|
void setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data);
|
||||||
static void interrupt(ZaSensorStore *arg);
|
static void interrupt(ZaSensorStore *arg);
|
||||||
@ -79,7 +79,7 @@ class ZyAuraSensor : public PollingComponent {
|
|||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
|
|
||||||
bool publish_state_(sensor::Sensor *sensor, float *value);
|
bool publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace zyaura
|
} // namespace zyaura
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2021.11.1"
|
__version__ = "2021.11.2"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
|
|
||||||
|
@ -362,45 +362,47 @@ std::string str_sanitize(const std::string &str);
|
|||||||
/// @name Parsing & formatting
|
/// @name Parsing & formatting
|
||||||
///@{
|
///@{
|
||||||
|
|
||||||
/// Parse a unsigned decimal number.
|
/// Parse an unsigned decimal number (requires null-terminated string).
|
||||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
||||||
optional<T> parse_number(const char *str, size_t len) {
|
optional<T> parse_number(const char *str, size_t len) {
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
|
unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
|
||||||
if (end == nullptr || end != str + len || value > std::numeric_limits<T>::max())
|
if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
|
||||||
return {};
|
return {};
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
/// Parse an unsigned decimal number.
|
||||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
||||||
optional<T> parse_number(const std::string &str) {
|
optional<T> parse_number(const std::string &str) {
|
||||||
return parse_number<T>(str.c_str(), str.length());
|
return parse_number<T>(str.c_str(), str.length() + 1);
|
||||||
}
|
}
|
||||||
/// Parse a signed decimal number.
|
/// Parse a signed decimal number (requires null-terminated string).
|
||||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
||||||
optional<T> parse_number(const char *str, size_t len) {
|
optional<T> parse_number(const char *str, size_t len) {
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
|
signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
|
||||||
if (end == nullptr || end != str + len || value < std::numeric_limits<T>::min() ||
|
if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
|
||||||
value > std::numeric_limits<T>::max())
|
|
||||||
return {};
|
return {};
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
/// Parse a signed decimal number.
|
||||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
||||||
optional<T> parse_number(const std::string &str) {
|
optional<T> parse_number(const std::string &str) {
|
||||||
return parse_number<T>(str.c_str(), str.length());
|
return parse_number<T>(str.c_str(), str.length() + 1);
|
||||||
}
|
}
|
||||||
/// Parse a decimal floating-point number.
|
/// Parse a decimal floating-point number (requires null-terminated string).
|
||||||
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
||||||
optional<T> parse_number(const char *str, size_t len) {
|
optional<T> parse_number(const char *str, size_t len) {
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
float value = ::strtof(str, &end);
|
float value = ::strtof(str, &end);
|
||||||
if (end == nullptr || end != str + len || value == HUGE_VALF)
|
if (end == str || *end != '\0' || value == HUGE_VALF)
|
||||||
return {};
|
return {};
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
/// Parse a decimal floating-point number.
|
||||||
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
||||||
optional<T> parse_number(const std::string &str) {
|
optional<T> parse_number(const std::string &str) {
|
||||||
return parse_number<T>(str.c_str(), str.length());
|
return parse_number<T>(str.c_str(), str.length() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
|
@ -39,6 +39,12 @@ uart:
|
|||||||
tx_pin: GPIO22
|
tx_pin: GPIO22
|
||||||
rx_pin: GPIO23
|
rx_pin: GPIO23
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
|
# Specifically added for testing debug with no after: definition.
|
||||||
|
debug:
|
||||||
|
dummy_receiver: false
|
||||||
|
direction: rx
|
||||||
|
sequence:
|
||||||
|
- lambda: UARTDebug::log_hex(direction, bytes, ':');
|
||||||
|
|
||||||
ota:
|
ota:
|
||||||
safe_mode: True
|
safe_mode: True
|
||||||
|
Loading…
Reference in New Issue
Block a user