ESPHome 2026.3.3
Loading...
Searching...
No Matches
micronova.cpp
Go to the documentation of this file.
1#include "micronova.h"
2#include "esphome/core/log.h"
3
4namespace esphome::micronova {
5
6static const char *const TAG = "micronova";
7static constexpr uint8_t STOVE_REPLY_SIZE = 2;
8static constexpr uint32_t STOVE_REPLY_TIMEOUT = 200; // ms
9static constexpr uint8_t WRITE_BIT = 1 << 7; // 0x80
10
11bool MicroNovaCommand::is_write() const { return this->memory_location & WRITE_BIT; }
12
14 ESP_LOGCONFIG(TAG,
15 " Memory Location: %02X\n"
16 " Memory Address: %02X",
18}
19
24
28
34
36 ESP_LOGCONFIG(TAG, "MicroNova:");
37 LOG_PIN(" Enable RX Pin: ", this->enable_rx_pin_);
38}
39
40#ifdef MICRONOVA_LISTENER_COUNT
42 this->listeners_.push_back(listener);
43 // Request initial value
44 this->queue_read_request(listener->get_memory_location(), listener->get_memory_address());
45}
46
48 ESP_LOGD(TAG, "Requesting update from all listeners");
49 for (auto *listener : this->listeners_) {
50 this->queue_read_request(listener->get_memory_location(), listener->get_memory_address());
51 }
52}
53#endif
54
56 // Check if we're processing a command and waiting for reply
57 if (this->reply_pending_) {
58 // Check if all reply bytes have arrived
59 if (this->available() >= STOVE_REPLY_SIZE) {
60#ifdef MICRONOVA_LISTENER_COUNT
61 int stove_reply_value = this->read_stove_reply_();
62 if (this->current_command_.is_write()) {
63 if (stove_reply_value == -1) {
64 ESP_LOGW(TAG, "Write to [0x%02X:0x%02X] may have failed (checksum mismatch in reply)",
65 this->current_command_.memory_location & ~WRITE_BIT, this->current_command_.memory_address);
66 }
67 } else {
68 // For READ commands, notify all listeners registered for this address
69 uint8_t loc = this->current_command_.memory_location;
70 uint8_t addr = this->current_command_.memory_address;
71 for (auto *listener : this->listeners_) {
72 if (listener->get_memory_location() == loc && listener->get_memory_address() == addr) {
73 listener->process_value_from_stove(stove_reply_value);
74 }
75 }
76 }
77#else
78 this->read_stove_reply_();
79#endif
80 this->reply_pending_ = false;
81 } else if (millis() - this->transmission_time_ > STOVE_REPLY_TIMEOUT) {
82 // Timeout - no reply received (buffer cleared before next command)
83 ESP_LOGW(TAG, "Timeout waiting for reply from [0x%02X:0x%02X], available: %d",
84 this->current_command_.memory_location, this->current_command_.memory_address, this->available());
85 this->reply_pending_ = false;
86 }
87 return;
88 }
89
90 // No reply pending - process next command (writes have priority over reads)
91#ifdef USE_MICRONOVA_WRITER
92 if (!this->write_queue_.empty()) {
93 this->current_command_ = this->write_queue_.front();
94 this->write_queue_.pop();
96 return;
97 }
98#endif
99#ifdef MICRONOVA_LISTENER_COUNT
100 if (!this->read_queue_.empty()) {
101 this->current_command_ = this->read_queue_.front();
102 this->read_queue_.pop();
103 this->send_current_command_();
104 }
105#endif
106}
107
108#ifdef MICRONOVA_LISTENER_COUNT
109void MicroNova::queue_read_request(uint8_t location, uint8_t address) {
110 // Check if this read is already queued
111 for (const auto &queued : this->read_queue_) {
112 if (queued.memory_location == location && queued.memory_address == address) {
113 ESP_LOGV(TAG, "Read [%02X,%02X] already queued, skipping", location, address);
114 return;
115 }
116 }
117
119 cmd.memory_location = location;
121 cmd.data = 0;
122
123 if (!this->read_queue_.push(cmd)) {
124 ESP_LOGW(TAG, "Read queue full, dropping read [%02X,%02X]", location, address);
125 return;
126 }
127 ESP_LOGV(TAG, "Queued read [%02X,%02X] (queue size: %u)", location, address, this->read_queue_.size());
128}
129#endif
130
132 uint8_t trash_rx;
133
134 // Clear rx buffer - stove hiccups may cause late replies in the rx
135 while (this->available()) {
136 this->read_byte(&trash_rx);
137 ESP_LOGW(TAG, "Reading excess byte 0x%02X", trash_rx);
138 }
139
140 uint8_t write_data[4] = {this->current_command_.memory_location, this->current_command_.memory_address, 0, 0};
141 size_t write_len;
142
143 if (this->current_command_.is_write()) {
144 write_len = 4;
145 write_data[2] = this->current_command_.data;
146 // calculate checksum
147 write_data[3] = write_data[0] + write_data[1] + write_data[2];
148 ESP_LOGV(TAG, "Sending write request [%02X,%02X,%02X,%02X]", write_data[0], write_data[1], write_data[2],
149 write_data[3]);
150 } else {
151 write_len = 2;
152 ESP_LOGV(TAG, "Sending read request [%02X,%02X]", write_data[0], write_data[1]);
153 }
154
155 this->enable_rx_pin_->digital_write(true);
156 this->write_array(write_data, write_len);
157 this->flush();
158 this->enable_rx_pin_->digital_write(false);
159
160 this->transmission_time_ = millis();
161 this->reply_pending_ = true;
162}
163
165 uint8_t reply_data[2] = {0, 0};
166
167 this->read_array(reply_data, 2);
168
169 ESP_LOGV(TAG, "Reply from stove [%02X,%02X]", reply_data[0], reply_data[1]);
170
171 uint8_t checksum = this->current_command_.memory_location + this->current_command_.memory_address + reply_data[1];
172 if (reply_data[0] != checksum) {
173 ESP_LOGE(TAG, "Checksum mismatch! From [0x%02X:0x%02X] received [0x%02X,0x%02X]. Expected 0x%02X, got 0x%02X",
174 this->current_command_.memory_location, this->current_command_.memory_address, reply_data[0],
175 reply_data[1], checksum, reply_data[0]);
176 return -1;
177 }
178 return ((int) reply_data[1]);
179}
180
181#ifdef USE_MICRONOVA_WRITER
182bool MicroNova::queue_write_command(uint8_t location, uint8_t address, uint8_t data) {
184 cmd.memory_location = location | WRITE_BIT;
186 cmd.data = data;
187
188 // Check if a write to the same address is already queued - update data in-place
189 for (auto &queued : this->write_queue_) {
190 if (queued.memory_location == cmd.memory_location && queued.memory_address == cmd.memory_address) {
191 if (queued.data != cmd.data) {
192 ESP_LOGD(TAG, "Updating queued write [%02X,%02X] data 0x%02X -> 0x%02X", location, address, queued.data, data);
193 queued.data = cmd.data;
194 } else {
195 ESP_LOGV(TAG, "Write [%02X,%02X] with data 0x%02X already queued, skipping", location, address, data);
196 }
197 return true;
198 }
199 }
200
201 if (!this->write_queue_.push(cmd)) {
202 ESP_LOGW(TAG, "Write queue full, dropping command");
203 return false;
204 }
205 ESP_LOGD(TAG, "Queued write [%02X,%02X] (queue size: %u)", location, address, this->write_queue_.size());
206#ifdef MICRONOVA_LISTENER_COUNT
207 // Automatically queue sensor updates after write commands
209#endif
210 return true;
211}
212#endif
213
214} // namespace esphome::micronova
uint8_t checksum
Definition bl0906.h:3
uint8_t address
Definition bl0906.h:4
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
virtual void digital_write(bool value)=0
MicroNovaCommand current_command_
Definition micronova.h:103
StaticVector< MicroNovaListener *, MICRONOVA_LISTENER_COUNT > listeners_
Definition micronova.h:108
bool reply_pending_
True if we are waiting for a reply from the stove.
Definition micronova.h:105
StaticRingBuffer< MicroNovaCommand, MICRONOVA_LISTENER_COUNT > read_queue_
Definition micronova.h:101
void queue_read_request(uint8_t location, uint8_t address)
Queue a read request to the stove (low priority - added at back) All listeners registered for this ad...
bool queue_write_command(uint8_t location, uint8_t address, uint8_t data)
Queue a write command to the stove (processed before reads)
uint32_t transmission_time_
Time when current command was sent.
Definition micronova.h:104
StaticRingBuffer< MicroNovaCommand, WRITE_QUEUE_SIZE > write_queue_
Definition micronova.h:98
void register_micronova_listener(MicroNovaListener *listener)
Definition micronova.cpp:41
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:38
bool read_byte(uint8_t *data)
Definition uart.h:34
FlushResult flush()
Definition uart.h:48
void write_array(const uint8_t *data, size_t len)
Definition uart.h:26
@ FLAG_OUTPUT
Definition gpio.h:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
static void uint32_t
Represents a command to be sent to the stove Write commands have the high bit (0x80) set in memory_lo...
Definition micronova.h:15
uint8_t data
Only used for write commands.
Definition micronova.h:18