Yet another Software Serial

In a recent Thread the usual complaints were made about the SoftwareSerial library not functioning alongside the Servo library. In Reply #17 @oric_dan suggested a limited version of SoftwareSerial for the Uno that would work in parallel with other libraries.

That prompted the realization that I had a few projects that together probably contained all of the concepts necessary to achieve that.

The attached code sss.ino is the result. It uses Pins 3 (INT1) and 4 and Timer2A and seems to work quite happily with the Servo library. It is only intended for use on an Uno (or standalone Atmega 328).

The file DemoSimpleSoftSerial.ino illustrates the use of sss.ino. And the file DemoPartner.ino is a program that I have run on a Mega (because it has multiple hardware Serial Ports) to receive data from and send data to the Uno.

I concluded that there is little advantage in converting the code into a Class or into a proper Library. Because of its simplicity there cannot be multiple instances of it - in other words you can only use it to add one Software Serial connection to an Uno. Because of the way the Arduino manages projects the variables in sss.ino are not accessible to the code in the main .ino file but the various functions are. I have prefixed the names of all the functions and variables with sss to reduce the possibility of clashing with names in other parts of a project.

To use the code you just need to add a copy of the file sss.ino into the Arduino project. Using the demo program attached you would put both of the file DemoSimpleSoftSerial.ino and sss.ino into a directory called DemoSimpleSoftSerial.

Interestingly the code works exactly as I expected it would when the idea first occurred to me but I could fill a whole book with the stupid mistakes that wasted time while bringing the various pieces together.

...R

sss.ino (11.5 KB)

DemoSimpleSoftSerial.ino (1.82 KB)

DemoPartner.ino (701 Bytes)

1 Like

Hello!

great job! I was reading the code along, and tried to make it to work with an ATMEGA 328, but in Freaduino Pro Mini 16MHz 5V. Only the RX pin seems to work, as I can't send any data successfully with that sss.ino and my project...

I am using a compatible arduino pro mini board, from elecfreaks, and need to control a servo at same time I need to communicate with a GSM board. And I need that 5V version, as it is for a robot... so can't use big batteries and so on.

Can you explain me a little bit more about that code? I need a software serial using timer2 and not fighting with Servo Library... on pro mini...

Can this program be ported to that board? Is there another library?

Thanks a lot! I'll keep trying to make it to work.

cheers.

You also asked this question somewhere else and I replied to it there.

DON'T DOUBLE POST - it just wastes everyone's time.

I don't have a Freaduino ProMini so I can't try the code on one.

Have you got my code working on an Uno?

What sort of differences might there be with the Freaduino - the clock speed springs to mind?

...R

yeah, sorry. I double posted because this thread is speaking about sss.ino. And the other post is speaking about a generalistic sollution ;).

I've asked you before. Thanks a lot, and sorry for asking you double time :stuck_out_tongue:

Hello,

I have this code:

#define rb(NBIT, SFR) ((SFR & ( 1 << NBIT )) >> NBIT)


volatile char rx_buf[200], rx_byte;
volatile int rx_buf_len = 0;
volatile unsigned char tcnt2;

void setup() {
  Serial.begin(57600);
  pinMode(3, INPUT);
  attachInterrupt(1, gsm_rx_int1, FALLING);
}

void (*gsm_rx_timer2)() = NULL;

void gsm_rx_int1 () {
  detachInterrupt(1);
  rx_byte = 0;
  gsm_rx_timer2 = gsm_rx_bit0;
  TIMSK2 &= ~(1<<TOIE2);
  TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
  TCCR2B &= ~(1<<WGM22);
  ASSR &= ~(1<<AS2);
  TIMSK2 &= ~(1<<OCIE2A);
  TCCR2B |= (1<<CS21);
  TCCR2B &= ~((1<<CS22) | (1<<CS20));
  float prescaler = 8.0;
  tcnt2 = 256 - (int)((float)F_CPU * 0.0001045 / prescaler);
  TCNT2 = tcnt2;
  TIMSK2 |= (1<<TOIE2);
}

ISR(TIMER2_OVF_vect){
  TCNT2 = tcnt2;
  gsm_rx_timer2();
}

void gsm_rx_bit0 () {
  rx_byte = rx_byte | (rb(3, PIND) << 0);
  gsm_rx_timer2 = gsm_rx_bit1;
}
void gsm_rx_bit1 () {
  rx_byte = rx_byte | (rb(3, PIND) << 1);
  gsm_rx_timer2 = gsm_rx_bit2;
}
void gsm_rx_bit2 () {
  rx_byte = rx_byte | (rb(3, PIND) << 2);
  gsm_rx_timer2 = gsm_rx_bit3;
}
void gsm_rx_bit3 () {
  rx_byte = rx_byte | (rb(3, PIND) << 3);
  gsm_rx_timer2 = gsm_rx_bit4;
}
void gsm_rx_bit4 () {
  rx_byte = rx_byte | (rb(3, PIND) << 4);
  gsm_rx_timer2 = gsm_rx_bit5;
}
void gsm_rx_bit5 () {
  rx_byte = rx_byte | (rb(3, PIND) << 5);
  gsm_rx_timer2 = gsm_rx_bit6;
}
void gsm_rx_bit6 () {
  rx_byte = rx_byte | (rb(3, PIND) << 6);
  gsm_rx_timer2 = gsm_rx_bit7;
}
void gsm_rx_bit7 () {
  rx_byte = rx_byte | (rb(3, PIND) << 7);
  gsm_rx_timer2 = gsm_rx_end;
}

void gsm_rx_end () {
  TIMSK2 &= ~(1<<TOIE2);
  rx_buf [ rx_buf_len ++ ] = rx_byte;
  attachInterrupt(1, gsm_rx_int1, FALLING);
}

void loop () {
  delay (1000);
  Serial.print("\r\n");
  for (int i = 0; i < rx_buf_len; i++) {
    Serial.print(rx_buf[i]);
  }
  Serial.print("\r\n");
}

I've copied the TIMER2 code from an URL that I have closed trying to copy the URL... lol.

I'm receiving strange chars in the arduino console, but I want to see human readable ones, as the gsm module sends to me, but i am not able to read well.

I implemented a delayed serial rx disabling interrupts, and everything read ok. but I need to do with interrupts, and the reason because I am doing "my own softwareserial" it's because I'm searching the way of making to work together my servo and my gsm module in my own robot, which uses an ATMEGA328 at 5V. So there's no extra hardware serial for working with gsm module. And the softwareserial with the servo library doesn't work fine at all... i had a lot of buzz in the servo while rx from gsm...

the true, is i could rx data from gsm and handle the servo at same time, but the reading functions were using delayMicroseconds(), and used to detect bit falling in a continous reading, so some data wen't to trash... now I want to try with interrupts, but I can detect bit falling and use delays again... but I don't really like, because I must disable interrupts, and then the servo has some spikes, and if I don't disable them, then the servo works fine, but reading sometimes is bad, so the sollution is the FALLING int1, but as I said, I also want to take profit of these delaytimes to handle the servo, so better performance if I find the sollution.

Hope that project helps others, and to get helped.

Thanks,

cheers.

void gsm_rx_int1 () {
  cli();
  detachInterrupt(1);
  TCCR2A = 0;// set entire TCCR2A register to 0
 .
.
.
.
  rx_byte = 0;
  gsm_rx_timer2 = gsm_rx_bit0;
  sei();
}

There is no need for the cli(), as interrupts are automatically of at this point any way.

Why are you detaching interrupt 1? leave it alone!

Never ever use sei() within an interrupt handler. It results in random crashes these are rare but will happen.

And the rest of your code makes on sense at all. Why are you only looking for a falling edge to start things off.

All your gsm_rx_bitX functions are called from with in an ISR so whats with the cli/sei - pust not needed and th sei may crash your program!.

Mark

Mark

@abeltomillo - you seem to be spreading stuff all over the place like a shotgun. make it easier for everyone by sticking to a single Thread.

I already gave you a link to my software serial implementation in this Thread.

It did not seem to work perfectly for you but until your most recent post in that other Thread you had not given any feedback which would give me a chance to fix it if something is wrong.

Please the moderator to merge this Thread with the earlier one.

...R

Hi,

thanks for the answers.

I need to disable the INT1 and re-enable it after reading the whole byte. because if not, the rx process will be reset on every bit falling from same pin... about cli and sei I didn't know it.

about you Robin2, yes, you're right, the best is to merge the thread, sorry. I'll keep in mind for the next time, ok?
about your code, I want to understand it, but I don't really understand every it all. Things like "2.5 times lenght of...", "1.5 times", and dividing by 2, the baud rate timings...

other thing is, I have in mind what I want to do, but I don't want to waste time in modifying your program as I haven't clear at all everything. If you don't mind, I'll try to do a library called: SoftSerialServo, which will be the first library which supports handling an emulated serial comm. by soft. and handle a servo (for sure 2 later) which the people could modify it.

obviously, I am basing much ideas on your code, but really, I don't want to modify it because I want to do it from zero, and merge the 2 concepts the best as possible... because I have no clear 100% how AVRs works.

Hope you don't be angry with me, because you helped me a lot without knowing it, but that's the true.

thanks for all,

cheers.

p.d. Im doing some more tests and reporting back them, ok? :wink:

I'm going to edit the code, by the new one, which also doesn't work, but TIMER2 is configured as MsTimer2 shows, but instead for 1mS for 104.5uS, and it still doesn't show readable chars... :frowning:

p.d. it is modified now. :).

abeltomillo:
I don't really understand every it all. Things like "2.5 times lenght of...", "1.5 times", and dividing by 2, the baud rate timings...

It seems to me a clear understanding of baud rate timings is essential if one wants to write a software serial program. And I suspect that if you understand how serial transmission works those comments in my code will make sense.

If you want to ask questions about serial transmission I will try to answer them.

I'm not clear - have you asked the moderator to merge the Threads, or should I do so?

...R

I don't know how to contact moderator. If you explain me I can do. If you want you can do by yourself. :wink:

Ok, I think I know a bit about serial transmissions, look my minimal knowledges I guessed by myself and made them in a "working program" for atmega328 (correct me if wrong, please):

  1. When a byte is going to be transmitted, the value of a pin goes from HIGH to LOW.
  2. On every bit transmission, there's a timing/delay of 1000000/baud_rate.
  3. What does it happen when a byte is transfered? How many time is there between another byte transmission, is it maybe 100000/baudrate?

So, what did I do in my first implementation?

for transferring a byte at 9600bps,

  1. set TX pin from HIGH to LOW.
  2. wait 105 microseconds (100000/9600).
  3. send first bit (bit 0)
  4. delayMicroseconds(105).
  5. send second bit (bit 1).
  6. ... repeat step 4 and 5 until transfer the 7th bit.
  7. delay 105 microseconds before transfer another byte.

So, for RX is the same as TX but at inverse. And the best way of detecting a transmission start, is to setup the INT on FALLING edge for a pin. In my case pin 3.

But, what is wrong in my code? If using timer, knowing accuracy is better than delay routine, I can RX a byte every 1.5 * 104.1 uS, going to half, isn't it?

Thanks Robin2,

cheers.

I've been reading you sss.ino code again, I start to understand a lot more than before, I am happy to tell you you are so genius ;).

I think I am going to use your method for TIMER2 and INT1, let me a bit of more time, this is going to work soon :wink:

thanks for all robin, you really a good person.

cheers,

abel.

Ok Robin2, I decided to fix your code, if this time doesn't work. Because I got the same conclusion than you, use timer2 for serial and timer1 for servo (by servo library), i think this is going to work fine. So Im on it.

thanks for that great code!

cheers,

abel.

abeltomillo:
I don't know how to contact moderator. If you explain me I can do. If you want you can do by yourself. :wink:

Just stick to this thread from now on please.

Ok, thanks.

So, sorry because I am not english, i am spanish, and I didn't understand well. Do you mean that you are stick on the thread from now, or that we should use this thread instead of the "Yet another software Serial" ? hehe...

thanks!

cheers,

abel.

Robin!! Finally! It does work! Don't ask me how, but it does, without any modification. I knew I was doing something bad, for sure it was the reading, because I didn't know the protocol well.

I am using Freaduino Pro Mini 5V, which has an ATMEGA328P and it is Arduino Pro Mini 5V LIKE.

Fine!!! Thanks a lot, now I am going to try the servo with it's library :wink:

yupiiiyeiiii!!

cheers,

thanks.

How many threads did you start? (don't answer that).

I merged those two.

Thanks Nick, now it looks very good. :smiley:

It seems that you merged (some or all of) @abeltomillo's Threads into one of mine and managed to change my Title to his Title.
Imagine my surprise to discover I was the owner of a Thread I didn't know I started.

The merge probably doesn't matter because he might have started his questions in my Thread anyway.

I have changed the Thread title back to the original.

@abeltomillo - NO thanks for all the confusion.
YES thanks for confirming my code works.

The extra "half" bit width is to ensure the samples are taken in the middle of a bit rather than at the edge. It also provides some slack to cope with slight timing errors. The interrupt that detects the start-bit resets the timing measurements for every byte so there are no long-run cumulative errors.

...R

@Robin2 - sorry.