I've tried these steps and have had no luck. It's been over a year since the last post regarding this matter. It would be really nice if softwareserial supported changing the parity and stop bits. Can someone with more coding experience walk me through the changes needed to make softwareserial work with parity changes and different stop bits? Thanks in advance.
The stop bits should be quite easy do add (just insert a few tunedDelays in write() and recv()). Parity handling is a bit more complex but could be implemented in these two locations too. It's probably not an option because all possible combinations would require quite a bit additional code (uses flash) and almost nobody uses anything but 8N1 today. If you have other requirements I would make a special version of SoftwareSerial for exactly that purpose (let's say a 7E2 version of SoftwareSerial) and not produce a version that fulfills all possible needs. Which specific version do you need?
//
// The receive routine called by the interrupt handler
//
void SoftwareSerial::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 2 stop bits
tunedDelay(_rx_delay_stopbit);
DebugPulse(_DEBUG_PIN2, 1);
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
}
and
size_t SoftwareSerial::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(HIGH); // send 0
else
tx_pin_write(LOW); // send 1
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(LOW); // send 0
else
tx_pin_write(HIGH); // send 1
tunedDelay(_tx_delay);
tx_pin_write(HIGH); // restore pin to natural state
}
SREG = oldSREG; // turn interrupts back on
tunedDelay(_tx_delay << 1);
return 1;
}
and check if it works. It's just a quick hack, no testing done.
Are you sending or receiving? If sending you could send 8N1 and make sure the last 3 bits (the high order ones) are set to 1. The receiving end won't be able to tell the difference.
I have same problem. It is supposed I have configured Hyperterminal 9600,N,8,1 and Arduino UNO using 9600 but I have only garbash frow what I transmit from Arduino.
EDIT: After a wild guess I inserted the #ifdef with the pushing and poping of the registers also for the write function. Now it compiles.
Single chars (using Putty.exe) are transmitted ok (even typing very fast) but using the serial monitor from the IDE (sends a whole line) only transmits 2 chars ok, then some garbage appears:
Hmm. It works now after some hacking in SoftSerial.cpp
I probably (I am more a hacker than a programmer) found a bug in SoftSerial.
The size_t SoftwareSerial::write(uint8_t b) function is not sending a a stop bit.
Guess: when there are more than 2 chars (not sure why 2..) next chars comming too fast and the logical 1 on the pin is takes as new start bit, then the next bit as first etc. and so you get shifted garbage.
I added 2 stop bits (remember I need to communicate 8E2) like this (both for normal and inverse_logic):
@calli: Are you sure you use the current IDE (1.0.1)? The push and pop stuff isn't made by me but was there already. If you don't use the current version you may get a lot of problems with my patch.
The size_t SoftwareSerial::write(uint8_t b) function is not sending a a stop bit.
I guess you haven't understood the code.
This:
tx_pin_write(HIGH); // restore pin to natural state
and
tunedDelay(_tx_delay << 1);
is sending 2 stop bits. The TX line goes HIGH (logical 1) and stays there for twice a bit's length.
And of course you are right, I am not understanding the code completely. However I understood it so much that I added the Stop bits again Well in my hacky way.
What me really bugs (and make me again feeling like a fool) is that NOW the normal SoftareSerial transmits "suddenly" all correct. And I have no clue why it did not work before (same hardware, IDE, Sketch, connections). That sucks
calli:
Guess: when there are more than 2 chars (not sure why 2..) next chars comming too fast and the logical 1 on the pin is takes as new start bit, then the next bit as first etc. and so you get shifted garbage.
Just to clarify, a start bit is a 0, and stop bit is a 1. It is impossible for a stop bit to be mistaken for a start bit. In fact, that is why they are opposite of each other.
I tested very much now, and have now the following test scenario:
PC1 USB <->FTDI#1 <-> FTDI#2 <-> PC2
I tested it with RealTerm (very nice) and it worked in all configurations. Realterm can show HEX, and also (important) shows Errors. I could force an error when setting wrong Parity on PC1 and sending. So far so good.
Now:
PC1 USB <->FTDI#1 <-> HWSerial -> Arduino -> SoftSerial (8E2 Hacked) HW Pins to RX/TX of an FTDI#2 -> PC2
With an unhacked SoftSerial it works without errors (8N1).
With the hacked one I always get "Error" (Serial params set correct). My device I like to talk to of course doesn't like this and is not reacting (HW ok, works with direct terminal control).
I am (even more) clueless now. I am so close but...
And switched to HardwareSerial, which means that I need to disconnect the ftdi and two wires every time I need to upload a sketch. BUT, now it works (was quite easy to get HW Serial to talk 8E2). My dialog is now via Softserial, which works now in 8N1 Mode ofcourse. Later I will use a Ethernet Module.
I know this is an old thread, but I just found it now and figured someone else might be trying to do the same thing.
Although my understanding of the code is very weak, I tracked down the compile errors in pylon's code to his copy-paste of these lines in the receive routine:
For reasons I don't understand, running this once (as in the original code) is fine, but running it several times in a row as pylon does causes the compile errors mentioned above. To get around this, I replaced the three instances in pylon's code with this:
I didn't verify the timing with an oscilloscope (extra stop bits should appear as more "space" between bytes), but I did feed the output into a device expecting two stop bits and was able to communicate with it.
I can also confirm maser228 that the proposed code modification compiles and works. I ve noticed one strange thing however that I cant explain:
I am using the softserial port with 9600, 8E1 and at the same time I am transmitting all data on the hardware serial port for monitoring on the serial port monitor included in the arduino IDE. I can receive all data sent by the remote device to the softserial port and they are monitored on the IDE''s serial port monitor. However all or most data sent by the softserial port to the remote device seem to somehow get lost. I have noticed that for some reason the problem disappears if I close the serial port monitor on the PC. Any ideas?