Skip to content

Commit 23dff1e

Browse files
committed
uart: Add support for specifying the number of bits and parity.
ESP8266SwSerial doesn't really check parity but just read the parity bit and ignore it when receiving data. Signed-off-by: 0hax <[email protected]>
1 parent c1dfed5 commit 23dff1e

File tree

3 files changed

+148
-30
lines changed

3 files changed

+148
-30
lines changed

esphome/components/uart/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@ def validate_rx_pin(value):
2929

3030

3131
CONF_STOP_BITS = 'stop_bits'
32+
CONF_NR_BITS = 'nr_bits'
33+
CONF_PARITY = 'parity'
34+
3235
CONFIG_SCHEMA = cv.All(cv.Schema({
3336
cv.GenerateID(): cv.declare_id(UARTComponent),
3437
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
3538
cv.Optional(CONF_TX_PIN): pins.output_pin,
3639
cv.Optional(CONF_RX_PIN): validate_rx_pin,
3740
cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
41+
cv.Optional(CONF_NR_BITS, default=8): cv.int_range(min=5, max=8),
42+
cv.Optional(CONF_PARITY, default="none"): cv.one_of("none", "even", "odd", lower=True),
3843
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
3944

4045

@@ -50,6 +55,8 @@ def to_code(config):
5055
if CONF_RX_PIN in config:
5156
cg.add(var.set_rx_pin(config[CONF_RX_PIN]))
5257
cg.add(var.set_stop_bits(config[CONF_STOP_BITS]))
58+
cg.add(var.set_nr_bits(config[CONF_NR_BITS]))
59+
cg.add(var.set_parity(config[CONF_PARITY]))
5360

5461

5562
# A schema to use for all UART devices, all UART integrations must extend this!

esphome/components/uart/uart.cpp

Lines changed: 131 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,65 @@ uint8_t next_uart_num = 1;
1818
#endif
1919

2020
#ifdef ARDUINO_ARCH_ESP32
21+
22+
#define UART_PARITY_EVEN 0 << 0
23+
#define UART_PARITY_ODD 1 << 0
24+
#define UART_PARITY_EN 1 << 1
25+
#define UART_NB_BIT_5 0 << 2
26+
#define UART_NB_BIT_6 1 << 2
27+
#define UART_NB_BIT_7 2 << 2
28+
#define UART_NB_BIT_8 3 << 2
29+
#define UART_NB_STOP_BIT_1 1 << 4
30+
#define UART_NB_STOP_BIT_2 3 << 4
31+
#define UART_TICK_APB_CLOCK 1 << 27
32+
33+
uint32_t UARTComponent::get_config() {
34+
uint32_t config = 0;
35+
36+
/*
37+
* All bits numbers below come from
38+
* framework-arduinoespressif32/cores/esp32/esp32-hal-uart.h
39+
* And more specifically conf0 union in uart_dev_t.
40+
*
41+
* Below is bit used from conf0 union.
42+
* <name>:<bits position> <values>
43+
* parity:0 0:even 1:odd
44+
* parity_en:1 Set this bit to enable uart parity check.
45+
* bit_num:2-4 0:5bits 1:6bits 2:7bits 3:8bits
46+
* stop_bit_num:4-6 stop bit. 1:1bit 2:1.5bits 3:2bits
47+
* tick_ref_always_on:27 select the clock.1:apb clock:ref_tick
48+
*/
49+
50+
if (this->parity_ == "even")
51+
config |= UART_PARITY_EVEN | UART_PARITY_EN;
52+
else if (this->parity_ == "odd")
53+
config |= UART_PARITY_ODD | UART_PARITY_EN;
54+
55+
switch (this->nr_bits_) {
56+
case 5:
57+
config |= UART_NB_BIT_5;
58+
break;
59+
case 6:
60+
config |= UART_NB_BIT_6;
61+
break;
62+
case 7:
63+
config |= UART_NB_BIT_7;
64+
break;
65+
case 8:
66+
config |= UART_NB_BIT_8;
67+
break;
68+
}
69+
70+
if (this->stop_bits_ == 1)
71+
config |= UART_NB_STOP_BIT_1;
72+
else
73+
config |= UART_NB_STOP_BIT_2;
74+
75+
config |= UART_TICK_APB_CLOCK;
76+
77+
return config;
78+
}
79+
2180
void UARTComponent::setup() {
2281
ESP_LOGCONFIG(TAG, "Setting up UART...");
2382
// Use Arduino HardwareSerial UARTs if all used pins match the ones
@@ -30,10 +89,7 @@ void UARTComponent::setup() {
3089
}
3190
int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1;
3291
int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1;
33-
uint32_t config = SERIAL_8N1;
34-
if (this->stop_bits_ == 2)
35-
config = SERIAL_8N2;
36-
this->hw_serial_->begin(this->baud_rate_, config, rx, tx);
92+
this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx);
3793
}
3894

3995
void UARTComponent::dump_config() {
@@ -45,6 +101,8 @@ void UARTComponent::dump_config() {
45101
ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_);
46102
}
47103
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
104+
ESP_LOGCONFIG(TAG, " Bits: %u", this->nr_bits_);
105+
ESP_LOGCONFIG(TAG, " Parity: %s", this->parity_.c_str());
48106
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
49107
this->check_logger_conflict_();
50108
}
@@ -107,17 +165,46 @@ void UARTComponent::flush() {
107165
#endif // ESP32
108166

109167
#ifdef ARDUINO_ARCH_ESP8266
168+
uint32_t UARTComponent::get_config() {
169+
uint32_t config = 0;
170+
171+
if (this->parity_ == "none")
172+
config |= UART_PARITY_NONE;
173+
else if (this->parity_ == "even")
174+
config |= UART_PARITY_EVEN;
175+
else if (this->parity_ == "odd")
176+
config |= UART_PARITY_ODD;
177+
178+
switch (this->nr_bits_) {
179+
case 5:
180+
config |= UART_NB_BIT_5;
181+
break;
182+
case 6:
183+
config |= UART_NB_BIT_6;
184+
break;
185+
case 7:
186+
config |= UART_NB_BIT_7;
187+
break;
188+
case 8:
189+
config |= UART_NB_BIT_8;
190+
break;
191+
}
192+
193+
if (this->stop_bits_ == 1)
194+
config |= UART_NB_STOP_BIT_1;
195+
else
196+
config |= UART_NB_STOP_BIT_2;
197+
198+
return config;
199+
}
200+
110201
void UARTComponent::setup() {
111202
ESP_LOGCONFIG(TAG, "Setting up UART bus...");
112203
// Use Arduino HardwareSerial UARTs if all used pins match the ones
113204
// preconfigured by the platform. For example if RX disabled but TX pin
114205
// is 1 we still want to use Serial.
115-
uint32_t mode = UART_NB_BIT_8 | UART_PARITY_NONE;
116-
if (this->stop_bits_ == 1)
117-
mode |= UART_NB_STOP_BIT_1;
118-
else
119-
mode |= UART_NB_STOP_BIT_2;
120-
SerialConfig config = static_cast<SerialConfig>(mode);
206+
SerialConfig config = static_cast<SerialConfig>(get_config());
207+
121208
if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) {
122209
this->hw_serial_ = &Serial;
123210
this->hw_serial_->begin(this->baud_rate_, config);
@@ -132,7 +219,7 @@ void UARTComponent::setup() {
132219
this->sw_serial_ = new ESP8266SoftwareSerial();
133220
int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1;
134221
int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1;
135-
this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_);
222+
this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_, this->nr_bits_, this->parity_);
136223
}
137224
}
138225

@@ -145,6 +232,8 @@ void UARTComponent::dump_config() {
145232
ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_);
146233
}
147234
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
235+
ESP_LOGCONFIG(TAG, " Bits: %u", this->nr_bits_);
236+
ESP_LOGCONFIG(TAG, " Parity: %s", this->parity_.c_str());
148237
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
149238
if (this->hw_serial_ != nullptr) {
150239
ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
@@ -249,8 +338,12 @@ void UARTComponent::flush() {
249338
}
250339
}
251340

252-
void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits) {
341+
void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t nr_bits,
342+
std::string &parity) {
253343
this->bit_time_ = F_CPU / baud_rate;
344+
this->stop_bits_ = stop_bits;
345+
this->nr_bits_ = nr_bits;
346+
this->parity_ = parity;
254347
if (tx_pin != -1) {
255348
auto pin = GPIOPin(tx_pin, OUTPUT);
256349
pin.setup();
@@ -264,21 +357,22 @@ void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_ra
264357
this->rx_buffer_ = new uint8_t[this->rx_buffer_size_];
265358
pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING);
266359
}
267-
this->stop_bits_ = stop_bits;
268360
}
269361
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
270362
uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
271363
const uint32_t start = ESP.getCycleCount();
272364
uint8_t rec = 0;
273365
// Manually unroll the loop
274-
rec |= arg->read_bit_(&wait, start) << 0;
275-
rec |= arg->read_bit_(&wait, start) << 1;
276-
rec |= arg->read_bit_(&wait, start) << 2;
277-
rec |= arg->read_bit_(&wait, start) << 3;
278-
rec |= arg->read_bit_(&wait, start) << 4;
279-
rec |= arg->read_bit_(&wait, start) << 5;
280-
rec |= arg->read_bit_(&wait, start) << 6;
281-
rec |= arg->read_bit_(&wait, start) << 7;
366+
for (int i = 0; i < arg->nr_bits_; i++)
367+
rec |= arg->read_bit_(&wait, start) << i;
368+
369+
/* If parity is enabled, just read it and ignore it. */
370+
/* TODO: Should we check parity? Or is it too slow for nothing added..*/
371+
if (arg->parity_ == "even")
372+
arg->read_bit_(&wait, start);
373+
else if (arg->parity_ == "odd")
374+
arg->read_bit_(&wait, start);
375+
282376
// Stop bit
283377
arg->wait_(&wait, start);
284378
if (arg->stop_bits_ == 2)
@@ -294,21 +388,29 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
294388
ESP_LOGE(TAG, "UART doesn't have TX pins set!");
295389
return;
296390
}
391+
bool parity_bit = false;
392+
bool need_parity_bit = true;
393+
if (this->parity_ == "even")
394+
parity_bit = true;
395+
else if (this->parity_ == "odd")
396+
parity_bit = false;
397+
else
398+
need_parity_bit = false;
297399

298400
{
299401
InterruptLock lock;
300402
uint32_t wait = this->bit_time_;
301403
const uint32_t start = ESP.getCycleCount();
302404
// Start bit
303405
this->write_bit_(false, &wait, start);
304-
this->write_bit_(data & (1 << 0), &wait, start);
305-
this->write_bit_(data & (1 << 1), &wait, start);
306-
this->write_bit_(data & (1 << 2), &wait, start);
307-
this->write_bit_(data & (1 << 3), &wait, start);
308-
this->write_bit_(data & (1 << 4), &wait, start);
309-
this->write_bit_(data & (1 << 5), &wait, start);
310-
this->write_bit_(data & (1 << 6), &wait, start);
311-
this->write_bit_(data & (1 << 7), &wait, start);
406+
for (int i = 0; i < this->nr_bits_; i++) {
407+
bool bit = data & (1 << i);
408+
this->write_bit_(bit, &wait, start);
409+
if (need_parity_bit)
410+
parity_bit ^= bit;
411+
}
412+
if (need_parity_bit)
413+
this->write_bit_(parity_bit, &wait, start);
312414
// Stop bit
313415
this->write_bit_(true, &wait, start);
314416
if (this->stop_bits_ == 2)

esphome/components/uart/uart.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ namespace uart {
1010
#ifdef ARDUINO_ARCH_ESP8266
1111
class ESP8266SoftwareSerial {
1212
public:
13-
void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits);
13+
void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t nr_bits,
14+
std::string &parity);
1415

1516
uint8_t read_byte();
1617
uint8_t peek_byte();
@@ -34,6 +35,8 @@ class ESP8266SoftwareSerial {
3435
volatile size_t rx_in_pos_{0};
3536
size_t rx_out_pos_{0};
3637
uint8_t stop_bits_;
38+
uint8_t nr_bits_;
39+
std::string parity_;
3740
ISRInternalGPIOPin *tx_pin_{nullptr};
3841
ISRInternalGPIOPin *rx_pin_{nullptr};
3942
};
@@ -43,6 +46,8 @@ class UARTComponent : public Component, public Stream {
4346
public:
4447
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
4548

49+
uint32_t get_config();
50+
4651
void setup() override;
4752

4853
void dump_config() override;
@@ -74,6 +79,8 @@ class UARTComponent : public Component, public Stream {
7479
void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; }
7580
void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; }
7681
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
82+
void set_nr_bits(uint8_t nr_bits) { this->nr_bits_ = nr_bits; }
83+
void set_parity(const char *parity) { this->parity_ = std::string(parity); }
7784

7885
protected:
7986
void check_logger_conflict_();
@@ -88,6 +95,8 @@ class UARTComponent : public Component, public Stream {
8895
optional<uint8_t> rx_pin_;
8996
uint32_t baud_rate_;
9097
uint8_t stop_bits_;
98+
uint8_t nr_bits_;
99+
std::string parity_;
91100
};
92101

93102
#ifdef ARDUINO_ARCH_ESP32

0 commit comments

Comments
 (0)