ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
mt6701_i2c.cpp
Go to the documentation of this file.
1#include "mt6701_i2c.h"
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
4
6
7ESPHOME_LOG_TAG(TAG, "mt6701_i2c");
8
9// Registers apply_config_() may write, each read-modify-written once
10static const uint8_t CONFIG_REGS[] PROGMEM = {
11 REG_ABZ_MUX_DIR, REG_RES_H, REG_ABZ_RES_L, REG_CONFIG_H, REG_ZERO_L,
12 REG_HYST_L, REG_OUT, REG_A_HIGH, REG_A_START_L, REG_A_STOP_L,
13};
14static constexpr uint8_t CONFIG_REG_COUNT = sizeof(CONFIG_REGS);
15
17 uint16_t count;
18 if (!this->read_count(count)) {
19 this->mark_failed(LOG_STR("not responding on I2C"));
20 return;
21 }
22 this->apply_config_();
23}
24
26 ESP_LOGCONFIG(TAG, "MT6701 (I2C):");
27 LOG_I2C_DEVICE(this);
28 if (this->is_failed()) {
29 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
30 return;
31 }
32 if (this->direction_ != UNSET_U8) {
33 ESP_LOGCONFIG(TAG, " Direction bit: %u", this->direction_);
34 }
35 if (this->zero_offset_ != UNSET_U16) {
36 ESP_LOGCONFIG(TAG, " Zero offset: %u", this->zero_offset_);
37 }
38 if (this->hysteresis_ != UNSET_U8) {
39 ESP_LOGCONFIG(TAG, " Hysteresis code: %u", this->hysteresis_);
40 }
41 if (this->output_mode_ != UNSET_U8) {
42 ESP_LOGCONFIG(TAG, " Output mode: %s", this->output_mode_ != 0 ? LOG_STR_LITERAL("UVW") : LOG_STR_LITERAL("ABZ"));
43 }
45 ESP_LOGCONFIG(TAG, " ABZ pulses/rev: %u", this->abz_pulses_per_revolution_);
46 }
47 if (this->z_pulse_width_ != UNSET_U8) {
48 ESP_LOGCONFIG(TAG, " Z pulse width code: %u", this->z_pulse_width_);
49 }
50 if (this->uvw_pole_pairs_ != UNSET_U8) {
51 ESP_LOGCONFIG(TAG, " UVW pole pairs: %u", this->uvw_pole_pairs_);
52 }
53 if (this->out_pin_mode_ != UNSET_U8) {
54 ESP_LOGCONFIG(TAG, " Output pin mode: %s",
55 this->out_pin_mode_ != 0 ? LOG_STR_LITERAL("PWM") : LOG_STR_LITERAL("analog"));
56 }
57 if (this->pwm_frequency_ != UNSET_U8) {
58 ESP_LOGCONFIG(TAG, " PWM frequency code: %u", this->pwm_frequency_);
59 }
60 if (this->pwm_polarity_ != UNSET_U8) {
61 ESP_LOGCONFIG(TAG, " PWM polarity code: %u", this->pwm_polarity_);
62 }
63 if (this->analog_start_ != UNSET_U16) {
64 ESP_LOGCONFIG(TAG, " Analog start: %u", this->analog_start_);
65 }
66 if (this->analog_stop_ != UNSET_U16) {
67 ESP_LOGCONFIG(TAG, " Analog stop: %u", this->analog_stop_);
68 }
69}
70
72 // Single-byte reads, 0x03 first; datasheet 1.9 documents no burst read
73 uint8_t high;
74 uint8_t low;
75 if (this->read_register(REG_ANGLE_H, &high, 1) != i2c::ERROR_OK ||
76 this->read_register(REG_ANGLE_L, &low, 1) != i2c::ERROR_OK)
77 return false;
78 count = encode_uint16(high, low) >> 2;
79 return true;
80}
81
82bool MT6701I2CComponent::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
83 uint8_t current;
84 if (this->read_register(reg, &current, 1) != i2c::ERROR_OK)
85 return false;
86 uint8_t updated = (current & ~mask) | (value & mask);
87 if (updated == current)
88 return true;
89 return this->write_register(reg, &updated, 1) == i2c::ERROR_OK;
90}
91
93 // Collect the bits per register first; several options share one register
94 uint8_t masks[CONFIG_REG_COUNT] = {};
95 uint8_t values[CONFIG_REG_COUNT] = {};
96 auto add = [&masks, &values](uint8_t reg, uint8_t mask, uint8_t value) {
97 for (uint8_t i = 0; i < CONFIG_REG_COUNT; i++) {
98 if (progmem_read_byte(&CONFIG_REGS[i]) == reg) {
99 masks[i] |= mask;
100 values[i] |= value & mask;
101 return;
102 }
103 }
104 };
105
106 if (this->direction_ != UNSET_U8)
107 add(REG_ABZ_MUX_DIR, 0x02, this->direction_ << 1);
108 if (this->output_mode_ != UNSET_U8)
109 add(REG_ABZ_MUX_DIR, 0x40, (this->output_mode_ & 0x01) << 6);
110 if (this->zero_offset_ != UNSET_U16) {
111 add(REG_CONFIG_H, 0x0F, this->zero_offset_ >> 8);
112 add(REG_ZERO_L, 0xFF, this->zero_offset_ & 0xFF);
113 }
114 if (this->hysteresis_ != UNSET_U8) {
115 add(REG_CONFIG_H, 0x80, (this->hysteresis_ & 0x04) << 5); // HYST[2] -> bit7
116 add(REG_HYST_L, 0xC0, (this->hysteresis_ & 0x03) << 6); // HYST[1:0] -> bits7:6
117 }
119 // ABZ_RES holds pulses per revolution minus 1
120 uint16_t res = this->abz_pulses_per_revolution_ - 1;
121 add(REG_RES_H, 0x03, res >> 8);
122 add(REG_ABZ_RES_L, 0xFF, res & 0xFF);
123 }
124 if (this->z_pulse_width_ != UNSET_U8)
125 add(REG_CONFIG_H, 0x70, (this->z_pulse_width_ & 0x07) << 4);
126 if (this->uvw_pole_pairs_ != UNSET_U8) {
127 // UVW_RES holds pole pairs minus 1
128 add(REG_RES_H, 0xF0, (this->uvw_pole_pairs_ - 1) << 4);
129 }
130 if (this->out_pin_mode_ != UNSET_U8)
131 add(REG_OUT, 0x20, (this->out_pin_mode_ & 0x01) << 5);
132 if (this->pwm_frequency_ != UNSET_U8)
133 add(REG_OUT, 0x80, (this->pwm_frequency_ & 0x01) << 7);
134 if (this->pwm_polarity_ != UNSET_U8)
135 add(REG_OUT, 0x40, (this->pwm_polarity_ & 0x01) << 6);
136 if (this->analog_start_ != UNSET_U16) {
137 add(REG_A_HIGH, 0x0F, this->analog_start_ >> 8);
138 add(REG_A_START_L, 0xFF, this->analog_start_ & 0xFF);
139 }
140 if (this->analog_stop_ != UNSET_U16) {
141 add(REG_A_HIGH, 0xF0, (this->analog_stop_ >> 8) << 4);
142 add(REG_A_STOP_L, 0xFF, this->analog_stop_ & 0xFF);
143 }
144
145 bool ok = true;
146 for (uint8_t i = 0; i < CONFIG_REG_COUNT; i++) {
147 if (masks[i] != 0)
148 ok &= this->update_register_(progmem_read_byte(&CONFIG_REGS[i]), masks[i], values[i]);
149 }
150 if (!ok) {
151 // A half-configured chip must not be sampled or have its EEPROM saved
152 this->mark_failed(LOG_STR("configuration register write failed"));
153 }
154}
155
157 if (this->is_failed()) {
158 // A failed component gets no timeouts, so the bus would stay suspended
159 ESP_LOGW(TAG, "Not programming EEPROM: component is marked failed");
160 return;
161 }
162 if (this->suspend_sampling_) {
163 ESP_LOGW(TAG, "EEPROM programming already in progress");
164 return;
165 }
166 ESP_LOGW(TAG, "Programming MT6701 EEPROM. The EEPROM has a limited number of write "
167 "cycles, so avoid doing this repeatedly. Supply must be 4.5-5.5 V.");
168 this->suspend_sampling_ = true;
169
170 bool ok = this->write_byte(REG_EEPROM_KEY, EEPROM_KEY_VALUE) && this->write_byte(REG_EEPROM_CMD, EEPROM_CMD_VALUE);
171 if (!ok) {
172 ESP_LOGE(TAG, "Failed to start EEPROM programming");
173 this->suspend_sampling_ = false;
174 return;
175 }
176
177 // suspend_sampling_ keeps a second save out, so the timer needs no name
178 this->set_timeout(EEPROM_PROGRAM_DELAY_MS, [this]() {
179 this->suspend_sampling_ = false;
180 ESP_LOGI(TAG, "EEPROM programming finished. Power-cycle the MT6701 to verify the stored configuration");
181 });
182}
183
184} // namespace esphome::mt6701_i2c
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const
writes an array of bytes to a specific register in the I²C device
Definition i2c.cpp:37
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:312
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:164
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:28
void save_eeprom()
Write the registers to the EEPROM: pauses sampling for the programming time.
static constexpr uint16_t UNSET_U16
Definition mt6701_i2c.h:62
bool read_count(uint16_t &count) override
static constexpr uint8_t UNSET_U8
Definition mt6701_i2c.h:63
bool update_register_(uint8_t reg, uint8_t mask, uint8_t value)
unsigned count
Definition gif.cpp:51
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
ESPHOME_LOG_TAG(TAG, "mt6701_i2c")
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:998
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:51