Reading serial and measuring sonar ping in the same loop

Hi,

I am working on a project for which I need to be able to intercept the signals from a hobby remote control receiver and modify the output signal based on reading taken from an ultrasonic ping sensor.
I have managed to find and adapt some code that reads the serial data from the remote control receiver and separate it in to the different RC channels and the ultrasonic sensor can be read quite easily using the NewPing library. I can get both of these processes to work on their own but am not too sure how to combine them into a single loop.
My attempt at reading the serial data fails as soon as some form of delay is introduced into the loop (such as the delay required by the Ping sensor reading).
I had a feeling just throwing the two processes into the same loop would not work but I am unsure about how to go about it properly.

I am using an Arduino Uno board with the chip placed on a bread board just like this:

but with the sonar (SR04) trigger and echo connected to pins 12 and 11 on the chip.

I have simplified the code to just perform the part that is failing.
The code is tabbed into three parts:

The main loop: This just calls the functions and prints the values to the serial monitor.

//These variables are for later use in a more useful sketch
int set_pitch;
int real_dist;

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

void loop(){
  
  // **** I need some way of regulating the sonar ping rate (max 30pings/sec)
  //without interfering with the serial read/print ****
  
  real_dist=get_distance();
  set_pitch=read_rx();

  //display the values on the serial monitor -when connected through USB-
  //Serial Rx is connected to the receiver, Tx is conencted to the USB FDTI
  Serial.println(set_pitch);
  Serial.print("\t");
  Serial.println(real_dist);
}

The receiver function tab: Only one of seven channels is being used in this test.

int channels[7]; //The RC transmitter used has 7 channels
int index = 0;
int prev;

int read_rx() {
  if (Serial.available() > 0) {
    int val = Serial.read();

    //Detect headers/New data set
    if(val == 0x01 && prev == 0x03){
      index = 0;
    }
    else { //Register value to apropriate channel
      switch(index){
      case 0:
        channels[0] = val * 256;
        break;
      case 1:
        channels[0] = channels[0] + val;
        break;
      case 2:
        channels[1] = val * 256;
        break;
      case 3:
        channels[1] = channels[1] + val;
        break;
      case 4:
        channels[2] = val * 256;
        break;
      case 5:
        channels[2] = channels[2] + val;
        break;
      case 6:
        channels[3] = val * 256;
        break;
      case 7:
        channels[3] = channels[3] + val;
        break;
      case 8:
        channels[4] = val * 256;
        break;
      case 9:
        channels[4] = channels[4] + val;
        break;
      case 10:
        channels[5] = val * 256;
        break;
      case 11:
        channels[5] = channels[5] + val;
        break;
      case 12:
        channels[6] = val * 256;
        break;
      case 13:
        channels[6] = channels[6] + val;
        break;
      default:
        break;
      }
      prev = val;
      index++;
    }
  }
  return channels[2]; //Channel 2 is the pitch control on the RC transmitter, only this channel is considered for this test sketch
}

The sonar function tab:

// A cheap HC-SR04 ping sonar is used for this test
#include <NewPing.h>

#define TRIGGER_PIN  12
#define ECHO_PIN     11
#define MAX_DISTANCE 250

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

int new_value;

int get_distance() {
  new_value = sonar.ping_cm(); //take a reading
  return new_value;
}

Would anyone have a few pointers as to how to get this to work or be able to give me some clues as to why this doesn't work?
Also, if anyone has any advice more efficient ways to write this code, it would we welcomed; I don't really have much of a clue about what I'm doing it has just been a lot of trial and error so far :smiley:

EDIT: replaced quotes with code, added more comments to the sketch and removed the delay in the sonar function.

The code you posted (incorrectly) does something. What that is isn't clear.

You want it to do something. What that is isn't clear.

You do have a fundamental misconception in your post, though.

My attempt at reading the serial data fails as soon as some form of delay is introduced into the loop (such as the delay required by the Ping sensor reading).

NewPing does NOT require a delay. It sends a ping, and uses interrupts to know when the return has come back. Certainly, using a delay BEFORE calling the function is silly. Look at the comment associated with the delay. It is there to control how often the function does it's thing. Get rid of it there, and simply call the function at the appropriate time.

What is sending the serial data? Does it make sense to send data back to it?

The code you posted (incorrectly)

With regards to the formatting etiquette of a post? Should I be using the [ /code] tags instead? I just used the "copy for forum" option in the Arduino IDE. I'll check through the stickies again.

something. What that is isn't clear.

Indeed. Right, in this particular test sketch I was just attempting to read the values and display them on the serial monitor when connected through USB (I have the serial Tx connected on USB, and I switch the Rx cable between the receiver and the USB when I need to upload the sketch).
And this is why I put those Serial.print commands at the end; I should have added comments.

What I am trying to achieve on the whole, is a sketch that reads the value of the RC receiver's channels, then looks at the ping measurement and uses a PID loop to modulate the values of the RC channels and then feeds them back out in serial, Pulse Position Modulation or sumPPM (I am not yet decided which one is easiest in my case depending on what external controller is reading the output).
The idea is to assist the RC user in not crashing the vehicle by reactively limiting his commands (limiting altitude range, collision avoidance etc...)
So ideally the rate of reading and feeding the RC signals through should be as un-restricted as possible but the ping readings should be kept below their max rate for reliability and the last ping measurement should be stored to modulate the RC signal in between pings.

Get rid of it there, and simply call the function at the appropriate time.

This is the bit I've been wanting to do, make it event driven? That delay before the reading just came from the "Sample NewPing Sketch" example from the Wiki. I kept it there because I assume that is what is keeping the sonar's measurement frequency below the max suggested 30pings/sec.
I did try removing the delay from the function and putting one in the loop instead but of course that made no difference. Removing the delay entirely (but with no other means of regulation) works but then the sonar just goes at a crazy rate. I'm not sure why I'm even saying this, it's the obvious thing to expect.

You know, I've only just realised the second example on that Wiki uses a timer. Actually, I did see that before but I'm still finding it hard to follow just because it is also being put into an array form. Simple things get confusing when one has no clue...
Any suggestions on how to go about defining the appropriate time? I'll be looking into how to set up a timer but maybe that isn't any good in my case either?

Thanks, a lot this is really useful. It's frustrating knowing so little in this field. I do feel really stupid :slight_smile:

With regards to the formatting etiquette of a post? Should I be using the [ /code] tags instead?

Yes. The # icon generates the tags. You then post your code between them. It would be nice to see the "copy for forum" option fixed (it's a trivial change) or removed.

I kept it there because I assume that is what is keeping the sonar's measurement frequency below the max suggested 30pings/sec.

You need to look at the blink without delay example to see how to do things after an interval has passed, without using delay() to diddle away that interval.

It's hard to comprehend someone taking on a project of this scope without knowing to not use delay(), and how not to.

You need to look at the blink without delay example to see how to do things after an interval has passed, without using delay() to diddle away that interval.

Great, that now does what I want it to do.

It's hard to comprehend someone taking on a project of this scope without knowing to not use delay(), and how not to.

I know! I've just got myself into a situation where I need this to get my system working, have a deadline and am ridiculously busy with other work. So every now and then I realise I am missing some basics but I'll just have to deal with that as I go along. I am still learning, and considering I basically knew nothing 3 days ago, seeming a bit dull at times is something I can put up with :slight_smile:

Thanks again for you patience. I know what it's like from being on forums in other subject areas to have new members come in and ask stupid questions about their overambitious projects when they clearly haven't researched enough in the first place. I'm usually the one answering the questions though XD