mirror of
https://github.com/esphome/esphome.git
synced 2025-01-22 13:54:04 +00:00
286 lines
10 KiB
C++
286 lines
10 KiB
C++
#include "nextion.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
namespace esphome {
|
|
namespace nextion {
|
|
|
|
static const char *TAG = "nextion";
|
|
|
|
void Nextion::setup() {
|
|
this->send_command_no_ack("");
|
|
this->send_command_printf("bkcmd=3");
|
|
this->goto_page("0");
|
|
}
|
|
float Nextion::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
|
void Nextion::update() {
|
|
if (this->writer_.has_value()) {
|
|
(*this->writer_)(*this);
|
|
}
|
|
}
|
|
void Nextion::send_command_no_ack(const char *command) {
|
|
// Flush RX...
|
|
this->loop();
|
|
|
|
this->write_str(command);
|
|
const uint8_t data[3] = {0xFF, 0xFF, 0xFF};
|
|
this->write_array(data, sizeof(data));
|
|
}
|
|
|
|
bool Nextion::ack_() {
|
|
if (!this->wait_for_ack_)
|
|
return true;
|
|
|
|
uint32_t start = millis();
|
|
while (!this->read_until_ack_()) {
|
|
if (millis() - start > 100) {
|
|
ESP_LOGW(TAG, "Waiting for ACK timed out!");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
void Nextion::set_component_text(const char *component, const char *text) {
|
|
this->send_command_printf("%s.txt=\"%s\"", component, text);
|
|
}
|
|
void Nextion::set_component_value(const char *component, int value) {
|
|
this->send_command_printf("%s.val=%d", component, value);
|
|
}
|
|
void Nextion::display_picture(int picture_id, int x_start, int y_start) {
|
|
this->send_command_printf("pic %d %d %d", x_start, y_start, picture_id);
|
|
}
|
|
void Nextion::set_component_background_color(const char *component, const char *color) {
|
|
this->send_command_printf("%s.bco=\"%s\"", component, color);
|
|
}
|
|
void Nextion::set_component_pressed_background_color(const char *component, const char *color) {
|
|
this->send_command_printf("%s.bco2=\"%s\"", component, color);
|
|
}
|
|
void Nextion::set_component_font_color(const char *component, const char *color) {
|
|
this->send_command_printf("%s.pco=\"%s\"", component, color);
|
|
}
|
|
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
|
|
this->send_command_printf("%s.pco2=\"%s\"", component, color);
|
|
}
|
|
void Nextion::set_component_coordinates(const char *component, int x, int y) {
|
|
this->send_command_printf("%s.xcen=%d", component, x);
|
|
this->send_command_printf("%s.ycen=%d", component, y);
|
|
}
|
|
void Nextion::set_component_font(const char *component, uint8_t font_id) {
|
|
this->send_command_printf("%s.font=%d", component, font_id);
|
|
}
|
|
void Nextion::goto_page(const char *page) { this->send_command_printf("page %s", page); }
|
|
bool Nextion::send_command_printf(const char *format, ...) {
|
|
char buffer[256];
|
|
va_list arg;
|
|
va_start(arg, format);
|
|
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
|
va_end(arg);
|
|
if (ret <= 0) {
|
|
ESP_LOGW(TAG, "Building command for format '%s' failed!", format);
|
|
return false;
|
|
}
|
|
this->send_command_no_ack(buffer);
|
|
if (!this->ack_()) {
|
|
ESP_LOGW(TAG, "Sending command '%s' failed because no ACK was received", buffer);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
void Nextion::hide_component(const char *component) { this->send_command_printf("vis %s,0", component); }
|
|
void Nextion::show_component(const char *component) { this->send_command_printf("vis %s,1", component); }
|
|
void Nextion::enable_component_touch(const char *component) { this->send_command_printf("tsw %s,1", component); }
|
|
void Nextion::disable_component_touch(const char *component) { this->send_command_printf("tsw %s,0", component); }
|
|
void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) {
|
|
this->send_command_printf("add %d,%u,%u", component_id, channel_number, value);
|
|
}
|
|
void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) {
|
|
this->send_command_printf("fill %d,%d,%d,%d,%s", x1, y1, width, height, color);
|
|
}
|
|
void Nextion::line(int x1, int y1, int x2, int y2, const char *color) {
|
|
this->send_command_printf("line %d,%d,%d,%d,%s", x1, y1, x2, y2, color);
|
|
}
|
|
void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) {
|
|
this->send_command_printf("draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color);
|
|
}
|
|
void Nextion::circle(int center_x, int center_y, int radius, const char *color) {
|
|
this->send_command_printf("cir %d,%d,%d,%s", center_x, center_y, radius, color);
|
|
}
|
|
void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) {
|
|
this->send_command_printf("cirs %d,%d,%d,%s", center_x, center_y, radius, color);
|
|
}
|
|
bool Nextion::read_until_ack_() {
|
|
while (this->available() >= 4) {
|
|
// flush preceding filler bytes
|
|
uint8_t temp;
|
|
while (this->available() && this->peek_byte(&temp) && temp == 0xFF)
|
|
this->read_byte(&temp);
|
|
|
|
if (!this->available())
|
|
break;
|
|
|
|
uint8_t event;
|
|
// event type
|
|
this->read_byte(&event);
|
|
|
|
uint8_t data[255];
|
|
// total length of data (including end bytes)
|
|
uint8_t data_length = 0;
|
|
// message is terminated by three consecutive 0xFF
|
|
// this variable keeps track of ohow many of those have
|
|
// been received
|
|
uint8_t end_length = 0;
|
|
while (this->available() && end_length < 3 && data_length < sizeof(data)) {
|
|
uint8_t byte;
|
|
this->read_byte(&byte);
|
|
if (byte == 0xFF) {
|
|
end_length++;
|
|
} else {
|
|
end_length = 0;
|
|
}
|
|
data[data_length++] = byte;
|
|
}
|
|
|
|
if (end_length != 3) {
|
|
ESP_LOGW(TAG, "Received unknown filler end bytes from Nextion!");
|
|
continue;
|
|
}
|
|
|
|
data_length -= 3; // remove filler bytes
|
|
|
|
bool invalid_data_length = false;
|
|
switch (event) {
|
|
case 0x01: // successful execution of instruction (ACK)
|
|
return true;
|
|
case 0x00: // invalid instruction
|
|
ESP_LOGW(TAG, "Nextion reported invalid instruction!");
|
|
break;
|
|
case 0x02: // component ID invalid
|
|
ESP_LOGW(TAG, "Nextion reported component ID invalid!");
|
|
break;
|
|
case 0x03: // page ID invalid
|
|
ESP_LOGW(TAG, "Nextion reported page ID invalid!");
|
|
break;
|
|
case 0x04: // picture ID invalid
|
|
ESP_LOGW(TAG, "Nextion reported picture ID invalid!");
|
|
break;
|
|
case 0x05: // font ID invalid
|
|
ESP_LOGW(TAG, "Nextion reported font ID invalid!");
|
|
break;
|
|
case 0x11: // baud rate setting invalid
|
|
ESP_LOGW(TAG, "Nextion reported baud rate invalid!");
|
|
break;
|
|
case 0x12: // curve control ID number or channel number is invalid
|
|
ESP_LOGW(TAG, "Nextion reported control/channel ID invalid!");
|
|
break;
|
|
case 0x1A: // variable name invalid
|
|
ESP_LOGW(TAG, "Nextion reported variable name invalid!");
|
|
break;
|
|
case 0x1B: // variable operation invalid
|
|
ESP_LOGW(TAG, "Nextion reported variable operation invalid!");
|
|
break;
|
|
case 0x1C: // failed to assign
|
|
ESP_LOGW(TAG, "Nextion reported failed to assign variable!");
|
|
break;
|
|
case 0x1D: // operate EEPROM failed
|
|
ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!");
|
|
break;
|
|
case 0x1E: // parameter quantity invalid
|
|
ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!");
|
|
break;
|
|
case 0x1F: // IO operation failed
|
|
ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!");
|
|
break;
|
|
case 0x20: // undefined escape characters
|
|
ESP_LOGW(TAG, "Nextion reported undefined escape characters!");
|
|
break;
|
|
case 0x23: // too long variable name
|
|
ESP_LOGW(TAG, "Nextion reported too long variable name!");
|
|
break;
|
|
case 0x65: { // touch event return data
|
|
if (data_length != 3) {
|
|
invalid_data_length = true;
|
|
break;
|
|
}
|
|
uint8_t page_id = data[0];
|
|
uint8_t component_id = data[1];
|
|
uint8_t touch_event = data[2]; // 0 -> release, 1 -> press
|
|
ESP_LOGD(TAG, "Got touch page=%u component=%u type=%s", page_id, component_id,
|
|
touch_event ? "PRESS" : "RELEASE");
|
|
for (auto *touch : this->touch_) {
|
|
touch->process(page_id, component_id, touch_event);
|
|
}
|
|
break;
|
|
}
|
|
case 0x67:
|
|
case 0x68: { // touch coordinate data
|
|
if (data_length != 5) {
|
|
invalid_data_length = true;
|
|
break;
|
|
}
|
|
uint16_t x = (uint16_t(data[0]) << 8) | data[1];
|
|
uint16_t y = (uint16_t(data[2]) << 8) | data[3];
|
|
uint8_t touch_event = data[4]; // 0 -> release, 1 -> press
|
|
ESP_LOGD(TAG, "Got touch at x=%u y=%u type=%s", x, y, touch_event ? "PRESS" : "RELEASE");
|
|
break;
|
|
}
|
|
case 0x66: // sendme page id
|
|
case 0x70: // string variable data return
|
|
case 0x71: // numeric variable data return
|
|
case 0x86: // device automatically enters into sleep mode
|
|
case 0x87: // device automatically wakes up
|
|
case 0x88: // system successful start up
|
|
case 0x89: // start SD card upgrade
|
|
case 0xFD: // data transparent transmit finished
|
|
case 0xFE: // data transparent transmit ready
|
|
break;
|
|
default:
|
|
ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", event);
|
|
break;
|
|
}
|
|
if (invalid_data_length) {
|
|
ESP_LOGW(TAG, "Invalid data length from nextion!");
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
void Nextion::loop() {
|
|
while (this->available() >= 4) {
|
|
this->read_until_ack_();
|
|
}
|
|
}
|
|
#ifdef USE_TIME
|
|
void Nextion::set_nextion_rtc_time(time::ESPTime time) {
|
|
this->send_command_printf("rtc0=%u", time.year);
|
|
this->send_command_printf("rtc1=%u", time.month);
|
|
this->send_command_printf("rtc2=%u", time.day_of_month);
|
|
this->send_command_printf("rtc3=%u", time.hour);
|
|
this->send_command_printf("rtc4=%u", time.minute);
|
|
this->send_command_printf("rtc5=%u", time.second);
|
|
}
|
|
#endif
|
|
|
|
void Nextion::set_backlight_brightness(uint8_t brightness) { this->send_command_printf("dim=%u", brightness); }
|
|
void Nextion::set_touch_sleep_timeout(uint16_t timeout) { this->send_command_printf("thsp=%u", timeout); }
|
|
|
|
void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; }
|
|
void Nextion::set_component_text_printf(const char *component, const char *format, ...) {
|
|
va_list arg;
|
|
va_start(arg, format);
|
|
char buffer[256];
|
|
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
|
va_end(arg);
|
|
if (ret > 0)
|
|
this->set_component_text(component, buffer);
|
|
}
|
|
void Nextion::set_wait_for_ack(bool wait_for_ack) { this->wait_for_ack_ = wait_for_ack; }
|
|
|
|
void NextionTouchComponent::process(uint8_t page_id, uint8_t component_id, bool on) {
|
|
if (this->page_id_ == page_id && this->component_id_ == component_id) {
|
|
this->publish_state(on);
|
|
}
|
|
}
|
|
|
|
} // namespace nextion
|
|
} // namespace esphome
|