Software serial Duemilanove

Hi
I’m struggling to get this communication to work and can’t work out why.
I’ve read tutorials all day but can’t make sense of it.

I have a breadboard atmega328 which will ultimately read a temperature sensor, then send the signal over serial (normal pin 2 and 3 hardware style) to a second arduino some distance away displaying the results.
My test set up is the breadboard arduino just spitting out pre set number over serial, and the second arduino receiving over software serial and then telling me what it sees over usb to the PC serial monitor.

The second board will need its hardware serial for something else when fully implemented so software serial is my only option.

Please help me work out why it wont read the test number.

breadboard arduino test code
//remote board
void setup() {
Serial.begin(4800);
}
void loop()
{
Serial.write(302); //example reading
delay(1000);
}

receiving/interpreting board code
//official board
//#include <ctype.h>

#define bit9600Delay 84
#define halfBit9600Delay 42
#define bit4800Delay 188
#define halfBit4800Delay 94

byte rx = 6;
byte tx = 7;
byte SWval;

void setup() {
Serial.begin(9600);

pinMode(rx,INPUT);
pinMode(tx,OUTPUT);
digitalWrite(tx,HIGH);
digitalWrite(13,HIGH); //turn on debugging LED
//SWprint(‘h’); //debugging hello
//SWprint(‘i’);
//SWprint(10); //carriage return
Serial.println(“official board initialised”);
}

/*void SWprint(int data)
{
byte mask;
//startbit
digitalWrite(tx,LOW);
delayMicroseconds(bit9600Delay);
for (mask = 0x01; mask>0; mask <<= 1) {
if (data & mask){ // choose bit
digitalWrite(tx,HIGH); // send 1
}
else{
digitalWrite(tx,LOW); // send 0
}
delayMicroseconds(bit9600Delay);
}
//stop bit
digitalWrite(tx, HIGH);
delayMicroseconds(bit9600Delay);
}
*/

int SWread()
{
byte val = 0;
while (digitalRead(rx));
//wait for start bit
if (digitalRead(rx) == LOW) {
delayMicroseconds(halfBit4800Delay);
for (int offset = 0; offset < 8; offset++) {
delayMicroseconds(bit4800Delay);
val |= digitalRead(rx) << offset;
}
//wait for stop bit + extra
delayMicroseconds(bit4800Delay);
delayMicroseconds(bit4800Delay);
return val;
}
}

void loop()
{
SWval = SWread();
//SWprint(toupper(SWval));
Serial.println(SWval,DEC);
}

I have tried all sorted of SWval, DEC, OCT, HEX etc. I’ve tried sending via print instead of write.Serial.
I’ve slowed it down to 4800 just in case.
I could successfully send a single digit like 3 for instance but nothing else will send :frowning:
Any help would be much appreciated

I could successfully send a single digit like 3 for instance but nothing else will send

I think that the first thing you need to look at is the Serial.write() documentation. It accepts a byte argument. 304 is not a byte-sized value.

Second, there is a SoftwareSerial library distributed with Arduino 1.0 and later, or available as NewSoftSerial for 0023 and earlier that manages sending and receiving serial data in software rather than hardware. That library is MUCH simpler to use than trying to bit-bang your own code.

You might want to try the proven SoftwareSerial library rather than implementing your own software serial.

http://arduino.cc/en/Reference/SoftwareSerial

fantastic

thank you so much

new software serial (and the latest gui download) has seen me right. Silly me not realising bytes are only 0 - 255.

Thanks

OK so I’ve played with it a bit more with varying levels of success.
At one stage this morning I had it sending 123 and 567 and it appearing on my screen, but only for a limited period then it all goes wrong. like a buffer is filling or something isn’t clearing.
the inexplicable part of it is that I was only requesting 123

I’ve tried putting serial.available on the write commands as well as the read. I’ve tried putting in delays. I’ve read a whole bunch of stuff about serial but whatever I try it still gives silly results.

I don’t need something obvious like pull up or down resistors do I?
I tried to read about that but no one seemed to be using them. Are the internally allocated by the serial libraries?
I literally have two wires coming out of digital 6 and 7 and going to the UART (pins 2 and 3) on the breadboard (remote board), which is powered off the 5V and ground, so definitely got my ground link in there.

Again any help would be appreciated. I’m really tearing my hair out with this one. I wish I could find a UART to soft serial, arduino to arduino, coms example.

main board

#include <SoftwareSerial.h>

SoftwareSerial mySerial(6, 7);

void setup()
{
Serial.begin(9600);
Serial.println(“NewSoftwareSerial initialised”);

pinMode(6, INPUT);
pinMode(7, OUTPUT);

// set the data rate for the SoftwareSerial port
mySerial.begin(4800);
}

void loop() // run over and over
{
Serial.print("Temp ");

mySerial.write(1);
if (mySerial.available())
Serial.print(mySerial.read());
delay(10);

mySerial.write(2);
if (mySerial.available())
Serial.println(mySerial.read());
delay(1000);
}

//remote board

void setup() {
Serial.begin(4800);
}

void loop()
{
int incomingByte = 0;

// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
}

if (incomingByte = 1){
Serial.write(12);
//Serial.write(4);
}

if (incomingByte = 6){ //deliberately not “2” so it shouldn’t carry out this instruction
Serial.write(56);
Serial.write(7);
}

//Serial.write(250); //example reading
//delay(1000);
}

if (incomingByte = 1){

How does assigning a value to incomingByte here help?

ultimately the main board will request either temperature or humidity information from the remote board. If it sends a 1 I want to return temp info. this might be 30.2°C which is easier for me to send as 30 and then a 2, so as not to exceed the 256 length byte. 12&4 were just some number I chucked in to test, i.e. 12.4°C I guess.

If the main board sends a 2 I want the remote board to return humidity information, 56.7% for example. I changed it to check for a 6 instead of a 2 to try and debug what was going on.

I expected it to return 12 and nothing else. It doesn't :(

I didn't really want my remote board spewing information all the time, just when requested.

I didn't really want my remote board spewing information all the time, just when requested.

That's reasonable, but doesn't answer the question.

The equality operator (==) and the assignment operator (=) are two distinctly different things, and are not at all interchangeable.

:blush: doh how could i have missed that? how embarrassing. It is giving much more predictable results now.

thank you very much

can't believe how much time I wasted without realising that

can't believe how much time I wasted without realising that

Yes, I can. Been there, done that, got the t-shirt.

I think in main board you should decouple write() and read(). Simply put: write every 1000 ms, but monitor continuously for incoming bytes.

(following code is untested)

unsigned long prev_millis = 0;

void loop() {
    unsigned long current_millis = millis();

    if (current_millis - prev_millis >= 1000) {    // if 1s has passed...
        prev_millis = current_millis;                   // restart elapsed time measurement

        Serial.print("Send...");                // this entire code block will execute once every second
        mySerial.write(1);
        delay(10);
        mySerial.write(2);
    }

    while (mySerial.available() > 0) {
        Serial.println(mySerial.read());
    }
}

It seems to me you are sending a byte and expecting an immediate response, which is not going to work.

mySerial.write(1);    // the byte is sent over the serial line to the other board...
// * (see below)
if (mySerial.available()) {  // from the time the last bit has been sent (i.e. write() has returned)
                                       // and this test there is not enough time for the other board to
                                       // process that byte and send us a response
                                       // therefore this test will always fail
    Serial.print(mySerial.read());
}
delay(10);

You could try to put a delay() between mySerial.write() and the if test (// *), although that's not the most robust approach I've ever seen :-)

can't believe how much time I wasted without realising that

Simple errors are often the most difficul to spot - I know from personal experience ;-)

Thanks so much for the help everyone.

mromani
I certainly think the delay(10) is a good idea, perhaps there is a more elegant way but I’m very new to this.
You’ll see from my code that I haven’t fully grasped handshaking yet but have something that works
As for the void loop(), I welcome the suggestions but this isn’t something which will run continuosly, the remote board will just be required to return data when the main board requests it.

I’ve yet to work out how I’m going to implement that, as the main board is busy running a display, it may just have to pause that briefly while it asks for temp/hum data, receives it and then displays it.

This code appears to be working great for me now, though I’m sure it could be more elegant.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(6, 7);

void setup()  
{
  Serial.begin(9600);
  Serial.println("Press 't' for temperature or 'h' for humidity");
  
  pinMode(6, INPUT);
  pinMode(7, OUTPUT);
  
  // set the data rate for the SoftwareSerial port
  mySerial.begin(4800);
}

void loop() // run over and over
{
  char inByte;
  if (Serial.available() > 0)
    inByte = Serial.read();
    
  if (inByte == 't'){
    Serial.print("Temperature: ");
    mySerial.write(1);
    delay(10);
    if (mySerial.available())
      Serial.print(mySerial.read());
      Serial.print(".");
    delay(10);  
    mySerial.write(2);
    delay(10);
    if (mySerial.available())
      Serial.println(mySerial.read());   
  }
  
  if (inByte == 'h'){
    Serial.print("Humidity: ");
    mySerial.write(6);
    delay(10);
    if (mySerial.available())
      Serial.print(mySerial.read());
      Serial.print(".");
    delay(10);  
    mySerial.write(7);
    delay(10);
    if (mySerial.available())
      Serial.println(mySerial.read());   
  }
}

and on the remote board

//remote board

void setup() {
  Serial.begin(4800);
}

void loop()
{
int incomingByte = 0;

// send data only when you receive data:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
  }
  
  if (incomingByte == 1)
    Serial.write(12);
  if (incomingByte == 2)
    Serial.write(4);
  
  
  if (incomingByte == 6)
    Serial.write(56);
  if (incomingByte == 7)
    Serial.write(7);

}

I certainly think the delay(10) is a good idea, perhaps there is a more elegant way but I'm very new to this.

The important thing is that you put the delay() call between the write() and the available() calls. You have to give enough time to the slave board to receive the request, process it and send back a response. -- edit: yup, I see that you actually just did this, sorry! --

this isn't something which will run continuosly, the remote board will just be required to return data when the main board requests it.

The sketch does run continuously. :-) The remote board just sits there waiting for bytes to arrive(*); when they do, it processes them (whatever this means) and sends back a response.

() This is called *busy wait because the processor doesn't "stop", it just cycles at lightspeed over the two or three lines where it asks itself if a byte is waiting to be read...

(does all this makes some sense ? :) )

//remote board

void setup() {   Serial.begin(4800); }

void loop() { int incomingByte = 0;

// send data only when you receive data:   if (Serial.available() > 0) {     // read the incoming byte:     incomingByte = Serial.read();   }     if (incomingByte == 1)     Serial.write(12);   if (incomingByte == 2)     Serial.write(4);       if (incomingByte == 6)     Serial.write(56);   if (incomingByte == 7)     Serial.write(7);

}

To improve the readability of this sketch I'd do two modifications: - replace numeric constants with symbolic constants (e.g. const int CMD_READ_TEMP = 1;) so instead of testing wether incomingByte is "1" you'd compare it to "CMD_READ_TEMP". Much clearer IMHO; - replace the series of "if " statements with a switch/case statement.

HTH :)

You're right about switch case alright. And I'll change to a more sensibly named variable comparison so I know what I was doing when I look back later :)

I get that the main loop runs continuously, but in its final implementation the mySerial.write(1) command will only go out from the main board when the program needs a new temperature reading. This won't be once a second. It gets complicated but the processor will run a whole bunch of screen refreshes, then check humidity, keep the screen updated and then check temperature and update the screen, and round again. Perhaps I've missed something or we've got our wires crossed.

What's great is I have something that works.

To improve maintainability I'd put the "command" definitions in a .h file that main and remote board sketches would #include. That would keep the "master" and "slave" sides of the protocol automatically in sync. (I used this setup in a project of mine...)

The sequence of operations you describe makes perfect sense to me. Also I'm glad now your code works.