TasmotaSerial:

- Added transmit parity to software TX.  Software RX just consumes in the incoming parity bit with no error checking.
- Fixed issue where Serial.begin for the ESP8266 was not passing the UART SerialConfig compatible values.

support.ino
- Cleanup to use already present ConvertSerialConfig API.

tasmota.ino:
- Force the baudrate + serial config settings after boot.  Previously, the baudrate would change, but any non 8N1 settings were not applied.

xdrv_08_serial_bridge.ino:
- Increased the software serial bridge buffer size and changed type to prevent overflow.
- Added missing serial config settings to the serial begin.  Previously was forcing 8N1.

xdrv_10_scripter.ino
- Cleanup to use already present ConvertSerialConfig API.
This commit is contained in:
wir3z 2021-12-22 09:23:15 -07:00
parent 8943656729
commit b8752b7272
6 changed files with 52 additions and 30 deletions

View File

@ -55,6 +55,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal
m_hardserial = false; m_hardserial = false;
m_hardswap = false; m_hardswap = false;
m_stop_bits = 1; m_stop_bits = 1;
m_parity = -1;
m_nwmode = nwmode; m_nwmode = nwmode;
serial_buffer_size = buffer_size; serial_buffer_size = buffer_size;
m_rx_pin = receive_pin; m_rx_pin = receive_pin;
@ -121,26 +122,23 @@ bool TasmotaSerial::isValidGPIOpin(int pin) {
bool TasmotaSerial::begin(uint32_t speed, uint32_t config) { bool TasmotaSerial::begin(uint32_t speed, uint32_t config) {
if (!m_valid) { return false; } if (!m_valid) { return false; }
if (config > 2) { // get the number of stop bits
// Legacy support where software serial fakes two stop bits if either stop bits is 2 or parity is not None m_stop_bits = (config &0x20) ? 2 : 1;
m_stop_bits = ((config &0x30) >> 5) +1; // check for parity
if ((1 == m_stop_bits) && (config &0x03)) { if (config &0x02) { // parity is enabled
m_stop_bits++; if (config &0x01) {
m_parity = HIGH; // odd parity
} else {
m_parity = LOW; // even parity
} }
} else { } else {
m_stop_bits = ((config -1) &1) +1; m_parity = -1; // no parity
#ifdef ESP8266
config = (2 == m_stop_bits) ? (uint32_t)SERIAL_8N2 : (uint32_t)SERIAL_8N1;
#endif // ESP8266
#ifdef ESP32
config = (2 == m_stop_bits) ? SERIAL_8N2 : SERIAL_8N1;
#endif // ESP32
} }
if (m_hardserial) { if (m_hardserial) {
#ifdef ESP8266 #ifdef ESP8266
Serial.flush(); Serial.flush();
Serial.begin(speed, (SerialConfig)config); Serial.begin(speed, (SerialConfig)ConvertSerialConfig(config));
if (m_hardswap) { if (m_hardswap) {
Serial.swap(); Serial.swap();
} }
@ -285,14 +283,23 @@ int TasmotaSerial::available(void) {
void IRAM_ATTR TasmotaSerial::_fast_write(uint8_t b) { void IRAM_ATTR TasmotaSerial::_fast_write(uint8_t b) {
uint32_t wait = m_bit_time; uint32_t wait = m_bit_time;
uint32_t start = ESP.getCycleCount(); uint32_t start = ESP.getCycleCount();
uint8_t numOnes = 0;
uint8_t bit;
// Start bit; // Start bit;
digitalWrite(m_tx_pin, LOW); digitalWrite(m_tx_pin, LOW);
TM_SERIAL_WAIT_SND_FAST; TM_SERIAL_WAIT_SND_FAST;
for (uint32_t i = 0; i < 8; i++) { for (uint32_t i = 0; i < 8; i++) {
digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW); bit = (b &1);
numOnes += bit;
digitalWrite(m_tx_pin, bit ? HIGH : LOW);
TM_SERIAL_WAIT_SND_FAST; TM_SERIAL_WAIT_SND_FAST;
b >>= 1; b >>= 1;
} }
// parity bit
if (m_parity != -1) {
digitalWrite(m_tx_pin, (numOnes %2) ? !m_parity : m_parity);
TM_SERIAL_WAIT_SND_FAST;
}
// Stop bit(s) // Stop bit(s)
digitalWrite(m_tx_pin, HIGH); digitalWrite(m_tx_pin, HIGH);
for (uint32_t i = 0; i < m_stop_bits; i++) { for (uint32_t i = 0; i < m_stop_bits; i++) {
@ -318,14 +325,23 @@ size_t TasmotaSerial::write(uint8_t b) {
uint32_t wait = m_bit_time; uint32_t wait = m_bit_time;
//digitalWrite(m_tx_pin, HIGH); // already in HIGH mode //digitalWrite(m_tx_pin, HIGH); // already in HIGH mode
uint32_t start = ESP.getCycleCount(); uint32_t start = ESP.getCycleCount();
uint8_t numOnes = 0;
uint8_t bit;
// Start bit; // Start bit;
digitalWrite(m_tx_pin, LOW); digitalWrite(m_tx_pin, LOW);
TM_SERIAL_WAIT_SND; TM_SERIAL_WAIT_SND;
for (uint32_t i = 0; i < 8; i++) { for (uint32_t i = 0; i < 8; i++) {
digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW); bit = (b &1);
numOnes += bit;
digitalWrite(m_tx_pin, bit ? HIGH : LOW);
TM_SERIAL_WAIT_SND; TM_SERIAL_WAIT_SND;
b >>= 1; b >>= 1;
} }
// parity bit
if (m_parity != -1) {
digitalWrite(m_tx_pin, (numOnes %2) ? !m_parity : m_parity);
TM_SERIAL_WAIT_SND;
}
// Stop bit(s) // Stop bit(s)
digitalWrite(m_tx_pin, HIGH); digitalWrite(m_tx_pin, HIGH);
// re-enable interrupts during stop bits, it's not an issue if they are longer than expected // re-enable interrupts during stop bits, it's not an issue if they are longer than expected
@ -359,6 +375,12 @@ void IRAM_ATTR TasmotaSerial::rxRead(void) {
m_in_pos = next; m_in_pos = next;
} }
// just consume the parity bit -- no parity checking is occuring
if (m_parity != -1) {
TM_SERIAL_WAIT_RCV_LOOP; // wait for the parity bit
wait += m_bit_time / 4;
}
TM_SERIAL_WAIT_RCV_LOOP; // wait for stop bit TM_SERIAL_WAIT_RCV_LOOP; // wait for stop bit
if (2 == m_stop_bits) { if (2 == m_stop_bits) {
wait += m_bit_time; wait += m_bit_time;

View File

@ -35,6 +35,8 @@
#include <HardwareSerial.h> #include <HardwareSerial.h>
#endif #endif
extern uint32_t ConvertSerialConfig(uint8_t serial_config);
class TasmotaSerial : public Stream { class TasmotaSerial : public Stream {
public: public:
TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback = 0, int nwmode = 0, int buffer_size = TM_SERIAL_BUFFER_SIZE); TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback = 0, int nwmode = 0, int buffer_size = TM_SERIAL_BUFFER_SIZE);
@ -68,6 +70,7 @@ class TasmotaSerial : public Stream {
int m_rx_pin; int m_rx_pin;
int m_tx_pin; int m_tx_pin;
uint32_t m_stop_bits; uint32_t m_stop_bits;
uint32_t m_parity;
uint32_t ss_byte; uint32_t ss_byte;
uint32_t ss_bstart; uint32_t ss_bstart;
uint32_t ss_index; uint32_t ss_index;

View File

@ -1885,15 +1885,14 @@ void SetSerialBegin(void) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), TasmotaGlobal.baudrate); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), TasmotaGlobal.baudrate);
Serial.flush(); Serial.flush();
#ifdef ESP8266 #ifdef ESP8266
Serial.begin(TasmotaGlobal.baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings->serial_config)); Serial.begin(TasmotaGlobal.baudrate, (SerialConfig)ConvertSerialConfig(Settings->serial_config));
SetSerialSwap(); SetSerialSwap();
#endif // ESP8266 #endif // ESP8266
#ifdef ESP32 #ifdef ESP32
delay(10); // Allow time to cleanup queues - if not used hangs ESP32 delay(10); // Allow time to cleanup queues - if not used hangs ESP32
Serial.end(); Serial.end();
delay(10); // Allow time to cleanup queues - if not used hangs ESP32 delay(10); // Allow time to cleanup queues - if not used hangs ESP32
uint32_t config = pgm_read_dword(kTasmotaSerialConfig + Settings->serial_config); Serial.begin(TasmotaGlobal.baudrate, ConvertSerialConfig(Settings->serial_config));
Serial.begin(TasmotaGlobal.baudrate, config);
#endif // ESP32 #endif // ESP32
} }

View File

@ -337,7 +337,7 @@ void setup(void) {
Settings->baudrate = APP_BAUDRATE / 300; Settings->baudrate = APP_BAUDRATE / 300;
Settings->serial_config = TS_SERIAL_8N1; Settings->serial_config = TS_SERIAL_8N1;
} }
SetSerialBaudrate(Settings->baudrate * 300); // Reset serial interface if current baudrate is different from requested baudrate SetSerialBegin(); // Reset serial interface if current serial settings are different from requested serial settings
if (1 == RtcReboot.fast_reboot_count) { // Allow setting override only when all is well if (1 == RtcReboot.fast_reboot_count) { // Allow setting override only when all is well
UpdateQuickPowerCycle(true); UpdateQuickPowerCycle(true);

View File

@ -25,7 +25,7 @@
#define XDRV_08 8 #define XDRV_08 8
#define HARDWARE_FALLBACK 2 #define HARDWARE_FALLBACK 2
const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; const uint16_t SERIAL_BRIDGE_BUFFER_SIZE = 256;
const char kSerialBridgeCommands[] PROGMEM = "|" // No prefix const char kSerialBridgeCommands[] PROGMEM = "|" // No prefix
D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE;
@ -106,7 +106,13 @@ void SerialBridgeInit(void)
serial_bridge_active = false; serial_bridge_active = false;
if (PinUsed(GPIO_SBR_RX) && PinUsed(GPIO_SBR_TX)) { if (PinUsed(GPIO_SBR_RX) && PinUsed(GPIO_SBR_TX)) {
SerialBridgeSerial = new TasmotaSerial(Pin(GPIO_SBR_RX), Pin(GPIO_SBR_TX), HARDWARE_FALLBACK); SerialBridgeSerial = new TasmotaSerial(Pin(GPIO_SBR_RX), Pin(GPIO_SBR_TX), HARDWARE_FALLBACK);
if (SerialBridgeSerial->begin(Settings->sbaudrate * 300)) { // Baud rate is stored div 300 so it fits into 16 bits #ifdef ESP8266
if (SerialBridgeSerial->begin(Settings->sbaudrate * 300, (SerialConfig)ConvertSerialConfig(Settings->serial_config))) // Baud rate is stored div 300 so it fits into 16 bits
#endif // ESP8266
#ifdef ESP32
if (SerialBridgeSerial->begin(Settings->sbaudrate * 300, ConvertSerialConfig(Settings->serial_config))) // Baud rate is stored div 300 so it fits into 16 bits
#endif // ESP32
{
if (SerialBridgeSerial->hardwareSerial()) { if (SerialBridgeSerial->hardwareSerial()) {
ClaimSerial(); ClaimSerial();
serial_bridge_buffer = TasmotaGlobal.serial_in_buffer; // Use idle serial buffer to save RAM serial_bridge_buffer = TasmotaGlobal.serial_in_buffer; // Use idle serial buffer to save RAM

View File

@ -3620,15 +3620,7 @@ chknext:
glob_script_mem.sp = new TasmotaSerial(rxpin, txpin, 1, 0, rxbsiz); glob_script_mem.sp = new TasmotaSerial(rxpin, txpin, 1, 0, rxbsiz);
if (glob_script_mem.sp) { if (glob_script_mem.sp) {
uint32_t config; fvar = glob_script_mem.sp->begin(br, ConvertSerialConfig(sconfig));
#ifdef ESP8266
config = pgm_read_byte(kTasmotaSerialConfig + sconfig);
#endif // ESP8266
#ifdef ESP32
config = pgm_read_dword(kTasmotaSerialConfig + sconfig);
#endif // ESP32
fvar = glob_script_mem.sp->begin(br, config);
uint32_t savc = Settings->serial_config; uint32_t savc = Settings->serial_config;
//setRxBufferSize(TMSBSIZ); //setRxBufferSize(TMSBSIZ);