Modify software serial for 8E1

Hy, I need to modify software serial library for 8E1.
I had found an old post on this forum, from back when IDE version was 1.0.5

I had followed instructions to replace void recv() and write() with this:

// The receive routine called by the interrupt handler
//
void SoftwareSerial8e1::recv()
{

#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Preserve the registers that the compiler misses
// (courtesy of Arduino forum user *etracer*)
  asm volatile(
    "push r18 \n\t"
    "push r19 \n\t"
    "push r20 \n\t"
    "push r21 \n\t"
    "push r22 \n\t"
    "push r23 \n\t"
    "push r26 \n\t"
    "push r27 \n\t"
    ::);
#endif  

  uint8_t d = 0;

  // If RX line is high, then we don't see any start bit
  // so interrupt is probably not for us
  if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
  {
    // Wait approximately 1/2 of a bit width to "center" the sample
    tunedDelay(_rx_delay_centering);
    DebugPulse(_DEBUG_PIN2, 1);

    // Read each of the 8 bits
    for (uint8_t i=0x1; i; i <<= 1)
    {
      tunedDelay(_rx_delay_intrabit);
      DebugPulse(_DEBUG_PIN2, 1);
      uint8_t noti = ~i;
      if (rx_pin_read())
        d |= i;
      else // else clause added to ensure function timing is ~balanced
        d &= noti;
    }
    // skip the parity bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    // skip the stop bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    if (_inverse_logic)
      d = ~d;

    // if buffer full, set the overflow flag and return
    if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) 
    {
      // save new data in buffer: tail points to where byte goes
      _receive_buffer[_receive_buffer_tail] = d; // save new byte
      _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
    } 
    else 
    {
#if _DEBUG // for scope: pulse pin as overflow indictator
      DebugPulse(_DEBUG_PIN1, 1);
#endif
      _buffer_overflow = true;
    }
  }

#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Restore the registers that the compiler misses
  asm volatile(
    "pop r27 \n\t"
    "pop r26 \n\t"
    "pop r23 \n\t"
    "pop r22 \n\t"
    "pop r21 \n\t"
    "pop r20 \n\t"
    "pop r19 \n\t"
    "pop r18 \n\t"
    ::);
#endif
}

size_t SoftwareSerial8e1::write(uint8_t b)
{
  if (_tx_delay == 0) {
    setWriteError();
    return 0;
  }

  uint8_t oldSREG = SREG;
  cli();  // turn off interrupts for a clean txmit

  // Write the start bit
  tx_pin_write(_inverse_logic ? HIGH : LOW);
  tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
  uint8_t p = 0;
  uint8_t t;
  for (t = 0x80; t; t >>= 1)
    if (b & t) p++;
  

  // Write each of the 8 bits
  if (_inverse_logic)
  {
    for (byte mask = 0x01; mask; mask <<= 1)
    {
      if (b & mask) // choose bit
        tx_pin_write(LOW); // send 1
      else
        tx_pin_write(HIGH); // send 0
    
      tunedDelay(_tx_delay);
    }
    // parity
    if (!(p & 0x01))
      tx_pin_write(LOW); // send 1
    else 
      tx_pin_write(HIGH); // send 0
    tunedDelay(_tx_delay);

    tx_pin_write(LOW); // restore pin to natural state
  }
  else
  {
    for (byte mask = 0x01; mask; mask <<= 1)
    {
      if (b & mask) // choose bit
        tx_pin_write(HIGH); // send 1
      else
        tx_pin_write(LOW); // send 0
    
      tunedDelay(_tx_delay);
    }
    // parity
    if (!(p & 0x01))
      tx_pin_write(HIGH); // send 1
    else 
      tx_pin_write(LOW); // send 0
    tunedDelay(_tx_delay);

    tx_pin_write(HIGH); // restore pin to natural state
  }

  SREG = oldSREG; // turn interrupts back on
  tunedDelay(_tx_delay);
  
  return 1;
}

It was obvious to remove 8E1 from void SoftwareSerial8e1::recv() and void SoftwareSerial8e1::write().

Now compiler gives these errors:

C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src\SoftwareSerial.cpp: In member function 'virtual size_t SoftwareSerial::write(uint8_t)':
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src\SoftwareSerial.cpp:424:3: error: 'tx_pin_write' was not declared in this scope
tx_pin_write(_inverse_logic ? HIGH : LOW);
^~~~~~~~~~~~
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src\SoftwareSerial.cpp:424:3: note: suggested alternative: 'rx_pin_read'
tx_pin_write(_inverse_logic ? HIGH : LOW);
^~~~~~~~~~~~
rx_pin_read
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src\SoftwareSerial.cpp:425:26: error: 'XMIT_START_ADJUSTMENT' was not declared in this scope
tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
^~~~~~~~~~~~~~~~~~~~~
exit status 1
Error compiling for board Arduino Nano.

Some things had changed in new version obviously, but I cannot find instructions for new version...

Any help would be appreciated.

As I wrote the code for the 1.0.5 version I almost urged to post an updated version (qucik hack, untested code):

// The receive routine called by the interrupt handler
//
void SoftwareSerial8e1::recv()
{

#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Preserve the registers that the compiler misses
// (courtesy of Arduino forum user *etracer*)
  asm volatile(
    "push r18 \n\t"
    "push r19 \n\t"
    "push r20 \n\t"
    "push r21 \n\t"
    "push r22 \n\t"
    "push r23 \n\t"
    "push r26 \n\t"
    "push r27 \n\t"
    ::);
#endif  

  uint8_t d = 0;

  // If RX line is high, then we don't see any start bit
  // so interrupt is probably not for us
  if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
  {
    // Disable further interrupts during reception, this prevents
    // triggering another interrupt directly after we return, which can
    // cause problems at higher baudrates.
    setRxIntMsk(false);

    // Wait approximately 1/2 of a bit width to "center" the sample
    tunedDelay(_rx_delay_centering);
    DebugPulse(_DEBUG_PIN2, 1);

    // Read each of the 8 bits
    for (uint8_t i=8; i > 0; --i)
    {
      tunedDelay(_rx_delay_intrabit);
      d >>= 1;
      DebugPulse(_DEBUG_PIN2, 1);
      if (rx_pin_read())
        d |= 0x80;
    }

    if (_inverse_logic)
      d = ~d;

    // if buffer full, set the overflow flag and return
    uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
    if (next != _receive_buffer_head)
    {
      // save new data in buffer: tail points to where byte goes
      _receive_buffer[_receive_buffer_tail] = d; // save new byte
      _receive_buffer_tail = next;
    } 
    else 
    {
      DebugPulse(_DEBUG_PIN1, 1);
      _buffer_overflow = true;
    }

    // skip the parity bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    // skip the stop bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    // Re-enable interrupts when we're sure to be inside the stop bit
    setRxIntMsk(true);

    }
  }

#if GCC_VERSION < 40302
// Work-around for avr-gcc 4.3.0 OSX version bug
// Restore the registers that the compiler misses
  asm volatile(
    "pop r27 \n\t"
    "pop r26 \n\t"
    "pop r23 \n\t"
    "pop r22 \n\t"
    "pop r21 \n\t"
    "pop r20 \n\t"
    "pop r19 \n\t"
    "pop r18 \n\t"
    ::);
#endif
}

size_t SoftwareSerial::write(uint8_t b)
{
  if (_tx_delay == 0) {
    setWriteError();
    return 0;
  }

  // By declaring these as local variables, the compiler will put them
  // in registers _before_ disabling interrupts and entering the
  // critical timing sections below, which makes it a lot easier to
  // verify the cycle timings
  volatile uint8_t *reg = _transmitPortRegister;
  uint8_t reg_mask = _transmitBitMask;
  uint8_t inv_mask = ~_transmitBitMask;
  uint8_t oldSREG = SREG;
  bool inv = _inverse_logic;
  uint16_t delay = _tx_delay;

  // calculate the parity bit
  uint8_t p = 0;
  uint8_t t;
  for (t = 0x80; t; t >>= 1)
    if (b & t) p++;


  if (inv)
    b = ~b;

  cli();  // turn off interrupts for a clean txmit

  // Write the start bit
  if (inv)
    *reg |= reg_mask;
  else
    *reg &= inv_mask;

  tunedDelay(delay);

  // Write each of the 8 bits
  for (uint8_t i = 8; i > 0; --i)
  {
    if (b & 1) // choose bit
      *reg |= reg_mask; // send 1
    else
      *reg &= inv_mask; // send 0

    tunedDelay(delay);
    b >>= 1;
  }
  // parity
  if (!(p & 0x01) && !inv)
    *reg |= reg_mask; // send 1
  else 
    *reg &= inv_mask; // send 0
  tunedDelay(_tx_delay);

  // restore pin to natural state
  if (inv)
    *reg &= inv_mask;
  else
    *reg |= reg_mask;

  SREG = oldSREG; // turn interrupts back on
  tunedDelay(_tx_delay);
  
  return 1;
}

Please keep in mind that this isn't a complete implementation of parity support as the parity bit is simply ignored during reception.

1 Like

Hy thank you.
Now I get this:

C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src\SoftwareSerial.cpp:211:1: error: expected declaration before '}' token
}
^
In file included from C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src\SoftwareSerial.cpp:44:0:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\src/SoftwareSerial.h:82:15: warning: inline function 'void SoftwareSerial::setRxIntMsk(bool)' used but never defined
inline void setRxIntMsk(bool enable) attribute((always_inline));
^~~~~~~~~~~
exit status 1
Error compiling for board Arduino Nano.

cpp:211 from first error would be at the end of recv()

EDIT: I got it sorted...

   // skip the stop bit
    tunedDelay(_rx_delay_stopbit);
    DebugPulse(_DEBUG_PIN2, 1);

    // Re-enable interrupts when we're sure to be inside the stop bit
    setRxIntMsk(true);

    }
  }

Removed the very last } above.
It compiles now, will test tomorrow in my car as I need to read ibus (BMW) and report.

I tested it and it does not work, it does send the same content it would over hardware serial, but it does nothing....

Basicly Im trying to command BMW radio over ibus -to symulate steering wheel buttons.
It does work with hardware serial.

Library Im using is called ibus trx, I changed HardwareSerial to Stream, it should work...


Thats the data I`m seeing and bellow it is what it becomes if I copy paste it to notepad.

Info I found for ibus:
bps: 9600
data bits: 8
parity: none
stop bits: 1
Row control: none

You think this softwareSerial mod should work?

So why the hell do you think you need parity if the specs tell you that there is no parity?

It doesn't have to but might do.

What does that mean? You don't see any state changes on the TX pin?

Hy, sorry for late response. Problem was with ibus library... You mode is working superb

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.