ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
fusb302b.cpp
Go to the documentation of this file.
1#include "fusb302b.h"
2
3#ifdef USE_ESP32
4
5#include <algorithm>
6#include <cstdio>
7
9#include "esphome/core/log.h"
10
11#include "fusb302b_registers.h"
12
13namespace esphome::fusb302b {
14
15ESPHOME_LOG_TAG(TAG, "fusb302b");
16
17// Task notification bits
18static constexpr uint32_t NOTIFY_IRQ = 1 << 0;
19static constexpr uint32_t NOTIFY_STOP = 1 << 1;
20static constexpr uint32_t NOTIFY_REQUEST = 1 << 2;
21
22static constexpr uint32_t TASK_STACK_SIZE = 4096;
23static constexpr UBaseType_t TASK_PRIORITY = 18;
24
25static constexpr uint32_t STARTUP_DELAY_MS = 2000;
26static constexpr uint32_t CC_SETTLE_MS = 5;
27static constexpr uint32_t CC_POLL_INTERVAL_MS = 500;
28static constexpr uint32_t PD_POLL_INTERVAL_MS = 100;
29// Safety net in case an interrupt edge is missed while attached and idle
30static constexpr uint32_t IDLE_POLL_INTERVAL_MS = 1000;
31// Time to wait for the source to send its capabilities on its own after attach
32static constexpr uint32_t SOURCE_CAP_WAIT_MS = 5000;
33// Time to wait after each GET_SOURCE_CAP / soft reset before the next recovery step
34static constexpr uint32_t SOURCE_CAP_RETRY_MS = 2500;
35// Upper bound on back-to-back interrupt handling while INT_N stays asserted
36static constexpr uint8_t MAX_IRQ_ROUNDS = 8;
37// BC_LVL is read this many extra times to confirm a stable measurement
38static constexpr uint8_t CC_STABLE_READS = 5;
39static constexpr uint8_t CC_LEVEL_UNSTABLE = 0xFF;
40
41void IRAM_ATTR FUSB302B::gpio_intr(FUSB302B *arg) {
42 TaskHandle_t handle = arg->task_.get_handle();
43 if (handle == nullptr)
44 return;
45 BaseType_t woken = pdFALSE;
46 xTaskNotifyFromISR(handle, NOTIFY_IRQ, eSetBits, &woken);
47 portYIELD_FROM_ISR(woken);
48}
49
50void FUSB302B::task_func(void *arg) { static_cast<FUSB302B *>(arg)->run_task_(); }
51
53 uint8_t device_id = 0;
54 if (!this->read_byte(FUSB_DEVICE_ID, &device_id)) {
55 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
56 return;
57 }
58 const uint8_t version = device_id & FUSB_DEVICE_ID_VERSION_MASK;
59 if (version != FUSB_DEVICE_ID_VERSION_A && version != FUSB_DEVICE_ID_VERSION_B) {
60 ESP_LOGE(TAG, "Unknown device ID 0x%02X", device_id);
61 this->mark_failed();
62 return;
63 }
64 ESP_LOGD(TAG, "Device ID 0x%02X (product %u, revision %u)", device_id, (device_id >> 2) & 0x3, device_id & 0x3);
65 if (!this->init_chip_()) {
66 this->mark_failed(LOG_STR("Initialization failed"));
67 return;
68 }
69
70 this->interrupt_pin_->setup();
71 // The task must exist before the interrupt is attached, since the ISR notifies it
72 if (!this->task_.create(FUSB302B::task_func, "fusb302b", TASK_STACK_SIZE, this, TASK_PRIORITY, false)) {
73 this->mark_failed(LOG_STR("Failed to create task"));
74 return;
75 }
77}
78
79void FUSB302B::queue_event_(uint8_t event) {
80 const uint8_t head = this->event_queue_head_.load(std::memory_order_relaxed);
81 if (static_cast<uint8_t>(head - this->event_queue_tail_.load(std::memory_order_acquire)) < EVENT_QUEUE_SIZE) {
82 this->event_queue_[head % EVENT_QUEUE_SIZE] = event;
83 this->event_queue_head_.store(head + 1, std::memory_order_release);
84 } else {
85 if (event == EVENT_REQUEST_FAILED)
86 this->dropped_request_failures_.fetch_add(1, std::memory_order_relaxed);
87 this->event_queue_overflow_ = true;
88 }
90}
91
93 const PdState state = this->state_;
94 if (state == this->queued_state_) {
95 // Only the contract changed
97 return;
98 }
99 this->queued_state_ = state;
100 this->queue_event_(static_cast<uint8_t>(state));
101}
102
104
106 const uint32_t contract = this->contract_;
107 if (contract != this->published_contract_) {
108 this->published_contract_ = contract;
109 this->publish_contract_(PackedContract{contract});
110 }
111
112 uint8_t tail = this->event_queue_tail_.load(std::memory_order_relaxed);
113 const uint8_t head = this->event_queue_head_.load(std::memory_order_acquire);
114 while (tail != head) {
115 const uint8_t event = this->event_queue_[tail % EVENT_QUEUE_SIZE];
116 this->event_queue_tail_.store(++tail, std::memory_order_release);
117 if (event == EVENT_REQUEST_FAILED) {
118 this->request_failed_callback_.call();
119 } else {
120 this->publish_state_(static_cast<PdState>(event));
121 }
122 }
123 if (this->event_queue_overflow_.exchange(false)) {
124 ESP_LOGW(TAG, "Events arrived faster than the main loop handled them; some state triggers were skipped");
125 // Drop the queued states before reading the state, so no older state is replayed after it. Request failures
126 // are only counted, so every one of them still fires.
127 uint8_t failures = this->dropped_request_failures_.exchange(0, std::memory_order_relaxed);
128 const uint8_t end = this->event_queue_head_.load(std::memory_order_acquire);
129 for (; tail != end; tail++) {
131 failures++;
132 }
133 this->event_queue_tail_.store(end, std::memory_order_release);
134 this->publish_state_(this->state_);
135 for (; failures > 0; failures--)
136 this->request_failed_callback_.call();
137 }
138 // Re-enabled by on_state_changed() or on_request_failed() from the PHY task
139 this->disable_loop();
140}
141
143 if (state == this->published_state_)
144 return;
145 const PdState previous = this->published_state_;
146 this->published_state_ = state;
147 this->state_callback_.call(state, previous);
148}
149
151#ifdef USE_SENSOR
152 if (this->voltage_sensor_ != nullptr)
153 this->voltage_sensor_->publish_state(contract.voltage());
154 if (this->current_sensor_ != nullptr)
155 this->current_sensor_->publish_state(contract.current());
156#endif
157#ifdef USE_TEXT_SENSOR
158 if (this->contract_text_sensor_ != nullptr) {
159 if (!contract.is_set()) {
160 this->contract_text_sensor_->publish_state("Detached");
161 } else {
162 char buf[24];
163 snprintf(buf, sizeof(buf), "%.2fA @ %.0fV", contract.current(), contract.voltage());
165 }
166 }
167#endif
168}
169
171 ESP_LOGCONFIG(TAG,
172 "FUSB302B:\n"
173 " Request Voltage: %u V",
174 this->get_request_voltage());
175 LOG_I2C_DEVICE(this);
176 LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
177#ifdef USE_SENSOR
178 LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
179 LOG_SENSOR(" ", "Current", this->current_sensor_);
180#endif
181#ifdef USE_TEXT_SENSOR
182 LOG_TEXT_SENSOR(" ", "Contract", this->contract_text_sensor_);
183#endif
184}
185
188 // The task parks itself instead of being deleted, so it can never be stopped in the middle of an I2C transfer
189 if (this->task_.is_created())
190 xTaskNotify(this->task_.get_handle(), NOTIFY_STOP, eSetBits);
191}
192
193void FUSB302B::request_voltage(int voltage) {
194 if (voltage < MIN_REQUEST_VOLTAGE || voltage > MAX_REQUEST_VOLTAGE) {
195 ESP_LOGW(TAG, "Requested %d V is outside %u-%u V, clamping", voltage, MIN_REQUEST_VOLTAGE, MAX_REQUEST_VOLTAGE);
196 }
197 this->set_request_voltage(static_cast<uint8_t>(std::clamp<int>(voltage, MIN_REQUEST_VOLTAGE, MAX_REQUEST_VOLTAGE)));
198 if (this->is_failed()) {
199 ESP_LOGW(TAG, "Cannot request %u V, the component failed", this->get_request_voltage());
200 // loop() no longer runs, so fire the trigger from the scheduler. Calling it here directly would recurse if the
201 // automation requests a voltage again.
202 App.scheduler.set_timeout(this, 0, [this]() { this->request_failed_callback_.call(); });
203 return;
204 }
205 // Before setup() the voltage is simply used at the first attach
206 if (this->task_.is_created())
207 xTaskNotify(this->task_.get_handle(), NOTIFY_REQUEST, eSetBits);
208}
209
211 // Give the source time to settle after power-up before the first CC measurement
212 vTaskDelay(pdMS_TO_TICKS(STARTUP_DELAY_MS));
213
214 while (true) {
215 uint32_t wait_ms = CC_POLL_INTERVAL_MS;
216 if (this->attached_) {
217 const bool busy = this->waiting_for_source_caps_ || this->request_pending_ || this->retry_request_pending_ ||
218 this->check_ams_();
219 wait_ms = busy ? PD_POLL_INTERVAL_MS : IDLE_POLL_INTERVAL_MS;
220 }
221 uint32_t bits = 0;
222 xTaskNotifyWait(0, UINT32_MAX, &bits, pdMS_TO_TICKS(wait_ms));
223
224 if (bits & NOTIFY_STOP) {
225 while (true)
226 vTaskSuspend(nullptr);
227 }
228 if (bits & NOTIFY_REQUEST) {
229 // When unattached, the new voltage is simply used at the next attach
230 this->request_pending_ = this->attached_;
231 }
232
233 // INT_N is held low until the interrupt registers are read, so a new event that arrives while it is
234 // already low has no falling edge: keep servicing while the pin stays asserted.
235 if (bits & NOTIFY_IRQ)
236 this->handle_interrupt_();
237 for (uint8_t i = 0; i < MAX_IRQ_ROUNDS && !this->interrupt_pin_->digital_read(); i++)
238 this->handle_interrupt_();
239
240 if (!this->attached_) {
241 this->try_attach_();
242 continue;
243 }
245 this->check_request_retry_();
246 // Waits for a pending Wait retry to end in a contract or a failure, so the two sequences never overlap
247 if (this->request_pending_ && !this->retry_request_pending_ && !this->check_ams_()) {
248 this->request_pending_ = false;
249 this->start_negotiation_();
250 }
251 this->check_source_caps_();
252 }
253}
254
256 // Software reset restores all registers to their defaults
257 if (!this->write_byte(FUSB_RESET, FUSB_RESET_SW_RES))
258 return false;
259 if (!this->reset_pd_())
260 return false;
261
262 // Unmask the interrupts used for PD messaging and VBUS changes
263 if (!this->write_byte(FUSB_MASK1, FUSB_MASK1_DEFAULT) || !this->write_byte(FUSB_MASKA, 0) ||
264 !this->write_byte(FUSB_MASKB, 0))
265 return false;
266
267 // Let the chip retransmit unacknowledged messages up to 3 times
268 uint8_t control3 = 0;
269 if (!this->read_byte(FUSB_CONTROL3, &control3))
270 return false;
271 control3 =
272 (control3 & ~FUSB_CONTROL3_N_RETRIES_MASK) | (3 << FUSB_CONTROL3_N_RETRIES_SHIFT) | FUSB_CONTROL3_AUTO_RETRY;
273 if (!this->write_byte(FUSB_CONTROL3, control3))
274 return false;
275
276 if (!this->write_byte(FUSB_POWER, FUSB_POWER_ALL))
277 return false;
278 return this->reset_pd_();
279}
280
282 this->reset_protocol_();
283 // Writing CONTROL0 as a whole byte also clears INT_MASK, which is what enables the INT_N pin
284 return this->write_byte(FUSB_CONTROL0, FUSB_CONTROL0_TX_FLUSH) &&
285 this->write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH) && this->write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
286}
287
289 uint8_t switches1 = 0;
290 return this->read_byte(FUSB_SWITCHES1, &switches1) &&
291 this->write_byte(FUSB_SWITCHES1, switches1 | FUSB_SWITCHES1_AUTO_CRC);
292}
293
295 // STATUS0A, STATUS1A, INTERRUPTA, INTERRUPTB, STATUS0, STATUS1, INTERRUPT; reading clears the interrupts
296 uint8_t regs[7];
297 if (this->read_register(FUSB_STATUS0A, regs, sizeof(regs)) != i2c::ERROR_OK) {
298 this->enter_error_();
299 return;
300 }
301 const uint8_t interrupta = regs[2];
302 const uint8_t status0 = regs[4];
303 uint8_t status1 = regs[5];
304 const uint8_t interrupt = regs[6];
305
306 if ((interrupt & FUSB_INTERRUPT_I_VBUSOK) && !(status0 & FUSB_STATUS0_VBUSOK)) {
307 if (this->attached_)
308 this->detach_();
309 return;
310 }
311 if (interrupta & FUSB_INTERRUPTA_I_HARDRST) {
312 if (this->attached_)
313 this->handle_hard_reset_();
314 return;
315 }
316 if (interrupta & FUSB_INTERRUPTA_I_SOFTRST) {
317 ESP_LOGV(TAG, "Soft reset received");
318 }
319 if ((interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) && this->attached_)
321
322 if (!this->attached_)
323 return;
324 PdMsg msg;
325 while (!(status1 & FUSB_STATUS1_RX_EMPTY)) {
326 bool is_sop = false;
327 if (!this->read_message_(msg, is_sop)) {
328 ESP_LOGW(TAG, "Reading message failed");
329 // A partial read leaves the FIFO misaligned
330 this->write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
331 return;
332 }
333 // Only SOP packets are addressed to a sink; SOP'/SOP'' are for cable plugs
334 if (is_sop)
335 this->handle_message_(msg);
336 if (!this->read_byte(FUSB_STATUS1, &status1))
337 return;
338 }
339}
340
341bool FUSB302B::read_message_(PdMsg &msg, bool &is_sop) {
342 uint8_t token = 0;
343 uint8_t header[2];
344 if (!this->read_byte(FUSB_FIFOS, &token) || this->read_register(FUSB_FIFOS, header, 2) != i2c::ERROR_OK)
345 return false;
346 msg.set_header(encode_uint16(header[1], header[0]));
347
348 // Data objects, followed by the CRC32 that the chip already checked
349 uint8_t buf[PD_MAX_NUM_DATA_OBJECTS * 4 + 4];
350 if (this->read_register(FUSB_FIFOS, buf, msg.num_of_obj * 4 + 4) != i2c::ERROR_OK)
351 return false;
352 for (uint8_t i = 0; i < msg.num_of_obj; i++) {
353 const uint8_t *obj = &buf[i * 4];
354 msg.data_objects[i] = encode_uint32(obj[3], obj[2], obj[1], obj[0]);
355 }
356 is_sop = (token & FUSB_FIFO_RX_TOKEN_BITS) == FUSB_FIFO_RX_SOP;
357 return true;
358}
359
361 // Register address, then the TX tokens; written in one transfer without the copy write_register() makes
362 uint8_t buf[1 + 4 + 1 + 2 + PD_MAX_NUM_DATA_OBJECTS * 4 + 4];
363 size_t len = 0;
364 const uint16_t header = msg.get_coded_header();
365
366 buf[len++] = FUSB_FIFOS;
367 buf[len++] = FUSB_TX_TOKEN_SYNC1;
368 buf[len++] = FUSB_TX_TOKEN_SYNC1;
369 buf[len++] = FUSB_TX_TOKEN_SYNC1;
370 buf[len++] = FUSB_TX_TOKEN_SYNC2;
371 buf[len++] = FUSB_TX_TOKEN_PACKSYM | (msg.num_of_obj * 4 + 2);
372 buf[len++] = header & 0xFF;
373 buf[len++] = header >> 8;
374 for (uint8_t i = 0; i < msg.num_of_obj; i++) {
375 const uint32_t obj = msg.data_objects[i];
376 buf[len++] = obj & 0xFF;
377 buf[len++] = (obj >> 8) & 0xFF;
378 buf[len++] = (obj >> 16) & 0xFF;
379 buf[len++] = obj >> 24;
380 }
381 buf[len++] = FUSB_TX_TOKEN_JAM_CRC;
382 buf[len++] = FUSB_TX_TOKEN_EOP;
383 buf[len++] = FUSB_TX_TOKEN_TXOFF;
384 buf[len++] = FUSB_TX_TOKEN_TXON;
385
386 i2c::ErrorCode err = this->write(buf, len);
387 if (err != i2c::ERROR_OK) {
388 ESP_LOGW(TAG, "Sending message type %u failed (error %d)", msg.type, static_cast<int>(err));
389 return false;
390 }
391 return true;
392}
393
394bool FUSB302B::measure_cc_(uint8_t meas_switch, uint8_t &level) {
395 if (!this->write_byte(FUSB_SWITCHES0, FUSB_SWITCHES0_PDWN_1 | FUSB_SWITCHES0_PDWN_2 | meas_switch))
396 return false;
397 vTaskDelay(pdMS_TO_TICKS(CC_SETTLE_MS));
398 uint8_t status0 = 0;
399 if (!this->read_byte(FUSB_STATUS0, &status0))
400 return false;
401 level = status0 & FUSB_STATUS0_BC_LVL;
402 for (uint8_t i = 0; i < CC_STABLE_READS; i++) {
403 if (!this->read_byte(FUSB_STATUS0, &status0))
404 return false;
405 if ((status0 & FUSB_STATUS0_BC_LVL) != level) {
406 level = CC_LEVEL_UNSTABLE;
407 return true;
408 }
409 }
410 return true;
411}
412
414 if (this->state_ == PdState::PD_STATE_ERROR) {
415 if (!this->init_chip_())
416 return;
417 ESP_LOGI(TAG, "Communication restored");
419 }
420
421 uint8_t status0 = 0;
422 if (!this->write_byte(FUSB_POWER, FUSB_POWER_ALL) || !this->read_byte(FUSB_STATUS0, &status0)) {
423 this->enter_error_();
424 return;
425 }
426 // A source may present its CC pull-up before VBUS is on; detach is only detected when VBUS drops
427 if (!(status0 & FUSB_STATUS0_VBUSOK)) {
428 this->cc_ambiguous_logged_ = false;
429 return;
430 }
431
432 uint8_t cc1 = 0;
433 uint8_t cc2 = 0;
434 if (!this->write_byte(FUSB_SWITCHES1, FUSB_SWITCHES1_SPECREV_REV2) ||
435 !this->write_byte(FUSB_MEASURE, FUSB_MEASURE_MDAC_CC) || !this->measure_cc_(FUSB_SWITCHES0_MEAS_CC1, cc1) ||
436 !this->measure_cc_(FUSB_SWITCHES0_MEAS_CC2, cc2)) {
437 this->enter_error_();
438 return;
439 }
440 // Exactly one CC line carries the source pull-up; the other one is open
441 uint8_t meas_switch;
442 uint8_t tx_switch;
443 if (cc1 != CC_LEVEL_UNSTABLE && cc1 > 0 && cc2 == 0) {
444 meas_switch = FUSB_SWITCHES0_MEAS_CC1;
445 tx_switch = FUSB_SWITCHES1_TXCC1;
446 } else if (cc2 != CC_LEVEL_UNSTABLE && cc2 > 0 && cc1 == 0) {
447 meas_switch = FUSB_SWITCHES0_MEAS_CC2;
448 tx_switch = FUSB_SWITCHES1_TXCC2;
449 } else {
450 if (!this->cc_ambiguous_logged_) {
451 ESP_LOGD(TAG, "VBUS present but no single CC line with a source pull-up (CC1 %u, CC2 %u)", cc1, cc2);
452 this->cc_ambiguous_logged_ = true;
453 }
454 return;
455 }
456
457 if (!this->write_byte(FUSB_SWITCHES0, FUSB_SWITCHES0_PDWN_1 | FUSB_SWITCHES0_PDWN_2 | meas_switch) ||
458 !this->write_byte(FUSB_SWITCHES1, tx_switch | FUSB_SWITCHES1_SPECREV_REV2) || !this->enable_auto_crc_() ||
459 !this->reset_pd_()) {
460 this->enter_error_();
461 return;
462 }
463 ESP_LOGD(TAG, "Attached on CC%u", meas_switch == FUSB_SWITCHES0_MEAS_CC1 ? 1 : 2);
464 this->attached_ = true;
465 this->cc_ambiguous_logged_ = false;
468}
469
471 ESP_LOGD(TAG, "Detached (VBUS lost)");
472 this->attached_ = false;
473 this->waiting_for_source_caps_ = false;
474 this->request_pending_ = false;
475 this->reset_pd_();
476 this->clear_contract_();
478}
479
481 ESP_LOGD(TAG, "Hard reset received");
482 // The source returns to Type-C default power and sends its capabilities again, using the current request voltage
483 this->request_pending_ = false;
484 // Also flushes the RX FIFO, so the caller has nothing left to read
485 if (!this->reset_pd_()) {
486 this->enter_error_();
487 return;
488 }
491}
492
494 if (this->state_ == PdState::PD_STATE_ERROR)
495 return;
496 ESP_LOGW(TAG, "Communication with the chip failed");
497 this->attached_ = false;
498 this->waiting_for_source_caps_ = false;
499 this->request_pending_ = false;
500 // Also stops a pending Wait retry, which would otherwise send its request to the failed chip
501 this->reset_protocol_();
502 this->clear_contract_();
504}
505
507 ESP_LOGD(TAG, "Requesting source capabilities for %u V", this->get_request_voltage());
508 // send_message() logs a failure; the soft reset is the next step either way, so do not wait for an answer
510 this->send_soft_reset_();
511 return;
512 }
514}
515
517 if (!this->reset_pd_()) {
518 this->enter_error_();
519 return;
520 }
522 // No answer can come, so do not wait for one
523 this->source_caps_failed_();
524 return;
525 }
526 // The source answers a soft reset with new source capabilities
528}
529
531 this->waiting_for_source_caps_ = false;
533 ESP_LOGW(TAG, "PD negotiation failed, staying at 5 V");
535 } else {
536 // A request made at runtime got no answer. A soft reset keeps the power level, so the contract in place stays.
537 ESP_LOGW(TAG, "Renegotiation failed, keeping the current contract");
538 }
539 this->on_request_failed();
540}
541
547
549 if (!this->waiting_for_source_caps_ || this->check_ams_())
550 return;
551 const uint32_t elapsed = millis() - this->source_cap_step_start_;
552 switch (this->source_cap_step_) {
554 if (elapsed < SOURCE_CAP_WAIT_MS)
555 return;
556 ESP_LOGD(TAG, "No source capabilities received, requesting them");
558 this->send_soft_reset_();
559 return;
560 }
562 break;
564 if (elapsed < SOURCE_CAP_RETRY_MS)
565 return;
566 ESP_LOGD(TAG, "No source capabilities received, sending soft reset");
567 this->send_soft_reset_();
568 return;
570 if (elapsed < SOURCE_CAP_RETRY_MS)
571 return;
572 this->source_caps_failed_();
573 return;
574 }
576}
577
578} // namespace esphome::fusb302b
579
580#endif // USE_ESP32
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void disable_loop()
Disable this component's loop.
bool is_created() const
Check if the task has been created and not yet destroyed.
Definition static_task.h:28
TaskHandle_t get_handle() const
Get the FreeRTOS task handle.
Definition static_task.h:31
bool create(TaskFunction_t fn, const char *name, uint32_t stack_size, void *param, UBaseType_t priority, bool use_psram, BaseType_t core_id=tskNO_AFFINITY)
Allocate the TCB (always internal RAM) and the stack, then create the task.
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:29
FUSB302B USB Type-C / Power Delivery sink controller.
Definition fusb302b.h:30
std::atomic< bool > event_queue_overflow_
Definition fusb302b.h:122
LazyCallbackManager< void(PdState, PdState)> state_callback_
Definition fusb302b.h:130
static constexpr uint8_t EVENT_QUEUE_SIZE
Definition fusb302b.h:117
void publish_contract_(PackedContract contract)
Definition fusb302b.cpp:150
void start_source_cap_wait_(SourceCapStep step)
Definition fusb302b.cpp:542
text_sensor::TextSensor * contract_text_sensor_
Definition fusb302b.h:137
bool read_message_(PdMsg &msg, bool &is_sop)
Definition fusb302b.cpp:341
void request_voltage(int voltage)
Negotiate a new contract with the highest source voltage that does not exceed voltage.
Definition fusb302b.cpp:193
InternalGPIOPin * interrupt_pin_
Definition fusb302b.h:101
std::array< uint8_t, EVENT_QUEUE_SIZE > event_queue_
Definition fusb302b.h:119
void publish_state_(PdState state)
Definition fusb302b.cpp:142
static constexpr uint8_t EVENT_REQUEST_FAILED
Definition fusb302b.h:118
void on_shutdown() override
Definition fusb302b.cpp:186
LazyCallbackManager< void()> request_failed_callback_
Definition fusb302b.h:131
sensor::Sensor * voltage_sensor_
Definition fusb302b.h:133
std::atomic< uint8_t > dropped_request_failures_
Definition fusb302b.h:124
std::atomic< uint8_t > event_queue_tail_
Definition fusb302b.h:121
void on_state_changed() override
Definition fusb302b.cpp:92
static void gpio_intr(FUSB302B *arg)
Definition fusb302b.cpp:41
std::atomic< uint8_t > event_queue_head_
Definition fusb302b.h:120
SourceCapStep source_cap_step_
Definition fusb302b.h:106
sensor::Sensor * current_sensor_
Definition fusb302b.h:134
void queue_event_(uint8_t event)
Definition fusb302b.cpp:79
static void task_func(void *arg)
Definition fusb302b.cpp:50
void on_request_failed() override
Definition fusb302b.cpp:103
bool send_message(const PdMsg &msg) override
Definition fusb302b.cpp:360
void dump_config() override
Definition fusb302b.cpp:170
bool measure_cc_(uint8_t meas_switch, uint8_t &level)
Definition fusb302b.cpp:394
PdMsg make_control_msg_(PdControlMsgType type) const
Definition pd.cpp:55
uint8_t get_request_voltage() const
Definition pd.h:111
std::atomic< uint32_t > contract_
Definition pd.h:156
void set_state_(PdState state)
Definition pd.cpp:80
std::atomic< PdState > state_
Definition pd.h:155
void set_request_voltage(uint8_t voltage)
Definition pd.h:108
void handle_message_(const PdMsg &msg)
Definition pd.cpp:176
void check_request_retry_()
Sends the last request again once tSinkRequest has passed after the source answered Wait.
Definition pd.cpp:128
void handle_transmit_failed_()
Called by the PHY when the chip gave up on a message after its retries without receiving a GoodCRC.
Definition pd.cpp:169
bool check_ams_()
Returns true while an atomic message sequence is in progress.
Definition pd.cpp:116
void set_state_and_contract_(PdState state, const PdContract &contract)
Stores both before calling on_state_changed() once, so the main loop never sees the new state with th...
Definition pd.cpp:98
void check_transition_timeout_()
Falls back to default power when the source accepted a request but did not report the new supply read...
Definition pd.cpp:160
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:183
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len)
reads an array of bytes from a specific register in the I²C device
Definition i2c.cpp:25
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
void publish_state(const std::string &state)
Definition text_sensor.h:41
bool state
Definition fan.h:2
ESPHOME_LOG_TAG(TAG, "fusb302b")
@ PD_STATE_PD_TIMEOUT
Negotiation is over without a PD contract (no capabilities, or the request was not accepted); the sou...
@ PD_CNTRL_SOFT_RESET
Definition pd.h:36
@ PD_CNTRL_GET_SOURCE_CAP
Definition pd.h:33
@ INTERRUPT_FALLING_EDGE
Definition gpio_pin.h:53
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:13
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
const void size_t len
Definition hal.h:68
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:988
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:980
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
A contract packed into one word so it can be shared between tasks as an atomic.
Definition pd.h:80
uint8_t num_of_obj
Definition pd.h:95
void set_header(uint16_t header)
Definition pd.cpp:40
uint16_t get_coded_header() const
Definition pd.cpp:48
uint32_t data_objects[PD_MAX_NUM_DATA_OBJECTS]
Definition pd.h:97
uint8_t end[39]
Definition sun_gtil2.cpp:17
spi_device_handle_t handle