Could I get some advice on Improving this coding please?

Hello
I'm starting to push myself abit harder with the arduino and have hit a point were I could use a nudge in the right direction please.
The project I am making is what I have called "An Emotional Companion Cube". Basically its an translucent box with some RGBs and arduino and a ultrasonic sensor. The idea is, at a distance it glows blue and green and shifts gently between them, if you get within 50cm of it, it changes to concerned yellows and oranges, and shifts faster. Then if you get within 25cm of it its gets angry and flashes red and purple and orange.

I have tested the principle with the RGBs and had them change colour as I get closer, now I have advanced that into the 3 moods, with it fading using the PWM. Here is the code: (theres a few redundant integers for expanding later)

const int Trigger = 8;
const int Receiver = 7;
const int ROne = 11;
const int GOne = 10;
const int BOne = 9;
const int RTwo = 6;
const int GTwo = 5;
const int BTwo = 3;

const int ContentDelay = 30;
const int ContentConcernDelay = 20;
const int ConcernDelay = 10;
const int ConcernWarningDelay = 5;
const int WarningDelay = 1;
const int WarningContentDelay = 20;

int RBrightness = 0;
int GBrightness = 0;
int BBrightness = 0;

int Responce = 0;
int Distance = 0;



void setup() {
   Serial.begin(9600);
  pinMode (ROne, OUTPUT);
  pinMode (GOne, OUTPUT);
  pinMode (BOne, OUTPUT);
  pinMode (RTwo, OUTPUT);
  pinMode (GTwo, OUTPUT);
  pinMode (BTwo, OUTPUT);
  pinMode (Trigger, OUTPUT);
  pinMode (Receiver, INPUT);
}

void loop() {
  
    digitalWrite(Trigger, LOW);
    delayMicroseconds(5);
    digitalWrite(Trigger, HIGH);
    delayMicroseconds(5);
    digitalWrite(Trigger, LOW);
    Responce = pulseIn(Receiver,HIGH);
    Distance = (Responce/58);
    Serial.print(Distance);
    Serial.print("cm");  
    
  if (Distance >75){
      //Content
      // Blue to Turquoise
    RBrightness = 255; 
    analogWrite(ROne, RBrightness);
    BBrightness = 0;
    analogWrite(BOne, BBrightness);
    
    for (GBrightness = 255; GBrightness > 0; GBrightness--) {
      analogWrite(GOne, GBrightness);
      delay(ContentDelay);
    }
    
    //Turquoise to Green  
    for (BBrightness = 0  ; BBrightness < 255; BBrightness++) {
      analogWrite(BOne, BBrightness);
      delay(ContentDelay);
    }
  
    // Green to Turquoise 
    for (BBrightness = 255  ; BBrightness > 0; BBrightness--) {
      analogWrite(BOne, BBrightness);
      delay(ContentDelay);
    }
  
    //Turquoise to Blue     
    for (GBrightness = 0; GBrightness < 255; GBrightness++) {
      analogWrite(GOne, GBrightness);
      delay(ContentDelay);
    }
  }
  
  else if(Distance > 25) {
    //Concern
    // Pale Yellow to Strong Yellow
    GBrightness = 0; 
    analogWrite(GOne, GBrightness);
    BBrightness = 255;
    analogWrite(BOne, BBrightness);
    
    for (RBrightness = 55; RBrightness > 0; RBrightness--) {
      analogWrite(ROne, RBrightness);
      delay(ConcernDelay);
    }
    
    //Strong Yellow to Amber  
    for (GBrightness = 0  ; GBrightness < 123; GBrightness++) {
        analogWrite(GOne, GBrightness);
        delay(ConcernDelay);
    }
    
     //Amber to Strong Yellow 
    for (GBrightness = 123  ; GBrightness > 0; GBrightness--) {
        analogWrite(GOne, GBrightness);
        delay(ConcernDelay);
    }
    
    //Strong Yellow to Pale Yellow   
    for (RBrightness = 0; RBrightness < 55; RBrightness++) {
        analogWrite(ROne, RBrightness);
        delay(ConcernDelay);
    }
    delay(500);
  }
  
  else if (Distance <25) {
     //Warning
     // Strong Orange to Red
      RBrightness = 0; 
      analogWrite(GOne, GBrightness);
      BBrightness = 255;
      analogWrite(BOne, BBrightness);
      
    for (GBrightness = 197; GBrightness < 255; GBrightness++) {
        analogWrite(GOne, GBrightness);
        delay(WarningDelay);
    }
      
    //Red to Warm Orange 
    for (GBrightness = 255  ; GBrightness > 142; GBrightness--) {
        analogWrite(GOne, GBrightness);
        delay(WarningDelay);
    }
    
      //Warm Orange to Fluorescent Pink 
    for (GBrightness = 142  ; GBrightness < 255; GBrightness++) {
        analogWrite(GOne, GBrightness);
        delay(WarningDelay);
    }
    for (BBrightness = 255  ; BBrightness > 146; BBrightness--) {
        analogWrite(BOne, BBrightness);
        delay(WarningDelay);
    }
    
    //Fluorescent Pink  to  Strong Orange
    for (GBrightness = 255  ; GBrightness > 197; GBrightness--) {
        analogWrite(GOne, GBrightness);
        delay(WarningDelay);
    }
    for (BBrightness = 146  ; BBrightness < 255; BBrightness++) {
        analogWrite(BOne, BBrightness);
        delay(WarningDelay);
    }
  }

}

My next step is to have it fade neatly between 2 moods, which I think I will be ok with.

My problem is I'm getting a delay whilst the code finishes its cycle before going back to check the Distance.

So my questions are:

  1. Whats the best way to read the ultrasonic sensor in real time, so its not waiting for the fade cycle to finish?
  2. Also, I would like the LEDs to go up and down in brightness as they switch between colours.Can anyone think of a cleaner/easier way to dim the LEDs whilst it is fading? Obviously I can drop the R/G/BBrightness values, but that could end up a very long code. Is there anyway to say, every .5 seconds divide R/G/BBrightness value by 10 for a couple of hundred milliseconds, then knock it back up again?
    3: Finally, any tips on cleaning up the above/better ways to do this?

Many thanks!
David

  1. Whats the best way to read the ultrasonic sensor in real time, so its not waiting for the fade cycle to finish?

The best way is to do your fading based on the passage of time, not based on step-and-delay. This will require major changes to the code. For each of the fade loops you now have you will need a 'state' pointer. This is usually just an integer to keep track of which loop you are currently in. When the distance range changes (you'll need to keep track os which range you were in) you set the state to the first fade for that range. At the end of each fade you need to change the state to the next fade. After the last fade in the range, go back to the first fade.

  1. Also, I would like the LEDs to go up and down in brightness as they switch between colours.Can anyone think of a cleaner/easier way to dim the LEDs whilst it is fading? Obviously I can drop the R/G/BBrightness values, but that could end up a very long code. Is there anyway to say, every .5 seconds divide R/G/BBrightness value by 10 for a couple of hundred milliseconds, then knock it back up again?

If the brightness is controlled separately from color you might want to switch from the RGB model to the HLS (Hue, Lightness, Saturation) You can fade between hues (colors) and pulse the Lightness. Each step you translate HLS to RGB and update the outputs.

3: Finally, any tips on cleaning up the above/better ways to do this?

See the answer to question 1.

Hey dude, thanks for your reply.
Ok I found the blink without delay tutorial so I now understand what you are talking about. Though I still have some questions. From what I have read it isnt possible to get an arduino to do 2 things at once. (I seem to remember this being easy with a picaxe?) so I cant have it constantly checking the sensor whilst it is fading the leds? I always have to wait for it to finish a cycle?
I get what you mean with 'state' but for changing between would you recommend the goto command?

So do you suggest I break the cycles to become much smaller, as in have more intervals? so it checks more often?

I worked out I can dim the leds by having the common anode 5V input to the LED coming from another PWM pin that says mostly high. I am running out of PWM pins though! even the mega only has 14, so thats only enough for 3 RGBs. How easy is it to "join" 2 unos? Do you know of a tutorial anywhere?

Thanks again!

Sciguy on youtube has some great tutorials.

Zakari:
Hey dude, thanks for your reply.
Ok I found the blink without delay tutorial so I now understand what you are talking about. Though I still have some questions. From what I have read it isnt possible to get an arduino to do 2 things at once. (I seem to remember this being easy with a picaxe?) so I cant have it constantly checking the sensor whilst it is fading the leds? I always have to wait for it to finish a cycle?

Exactly at the same time? No, but it can do it fast enough for the human eye to not know the difference.

so I cant have it constantly checking the sensor whilst it is fading the leds?

Yes you can, it is all in the blink without delay example.

So do you suggest I break the cycles to become much smaller, as in have more intervals? so it checks more often?

Ditch the idea of using delay and fading by using a loop. The whole thing becomes on continuous loop saying
{
what time is it now?
is it time to check the sensor -> ping it
is it time to look at the result of the ping
is it time to change the LED brightness a notch
}

Hello

Grumpy_Mike:
Ditch the idea of using delay and fading by using a loop. The whole thing becomes on continuous loop saying
{
what time is it now?
is it time to check the sensor -> ping it
is it time to look at the result of the ping
is it time to change the LED brightness a notch
}

This helped alot! Thankyou

I had a think and a play and came up with this simple test, which works grand :slight_smile:

const int Trigger = 8;
const int Receiver = 7;
const int ROne = 11;
const int GOne = 10;
const int BOne = 9;

unsigned long CurrentMillis = 0;
long PreviousMillis = 0;
int SonicPeriod = 250;
int ContentPeriod = 30;

int LEDBrightness = 0;

int RBrightness = 0;
int GBrightness = 0;
int BBrightness = 0;

int Distance = 0;
int Responce = 0;

void setup () {
  Serial.begin(9600);
  pinMode (Trigger, OUTPUT);
  pinMode (Receiver, INPUT);
  pinMode (ROne, OUTPUT);
  pinMode (GOne, OUTPUT);
  pinMode (BOne, OUTPUT);
  
}

void loop () {
  CurrentMillis = millis();
  
  if (CurrentMillis - PreviousMillis > SonicPeriod) {
     PreviousMillis = CurrentMillis; 
    digitalWrite(Trigger, LOW);
    delayMicroseconds(5);
    digitalWrite(Trigger, HIGH);
    delayMicroseconds(5);
    digitalWrite(Trigger, LOW);
    
    Responce = pulseIn(Receiver,HIGH);
    Distance = (Responce/29);
    Serial.print(Distance);
    Serial.print("cm");  
    
  } 
  
  
    if (Distance > 100) {
      digitalWrite (ROne, HIGH);
      digitalWrite (GOne, LOW);
      digitalWrite (BOne, HIGH);
    }
    
    else if (Distance > 50) {
      digitalWrite (ROne, HIGH);
      digitalWrite (GOne, HIGH);
      digitalWrite (BOne, LOW);
    }
    
    else if (Distance <= 50) {
      digitalWrite (ROne, LOW);
      digitalWrite (GOne, HIGH);
      digitalWrite (BOne, HIGH);
    }
    
 
}

Until I tried to introduce a second millis check:

const int Trigger = 8;
const int Receiver = 7;
const int ROne = 11;
const int GOne = 10;
const int BOne = 9;
const int Brightness = 3;

unsigned long CurrentMillis = 0;
long PreviousMillis = 0;
int SonicPeriod = 250;
int ContentPeriod = 30;
int ContentBrightnessPeriod = 50;

int LEDBrightness = 255;
int FadeAmount = 1;

int RBrightness = 0;
int GBrightness = 0;
int BBrightness = 0;

int Distance = 0;
int Responce = 0;

void setup () {
  Serial.begin(9600);
  pinMode (Trigger, OUTPUT);
  pinMode (Receiver, INPUT);
  pinMode (ROne, OUTPUT);
  pinMode (GOne, OUTPUT);
  pinMode (BOne, OUTPUT);
  pinMode (Brightness, OUTPUT);
  
}

void loop () {
  CurrentMillis = millis();
  
  if (CurrentMillis - PreviousMillis > SonicPeriod) {
    PreviousMillis = CurrentMillis; 
    digitalWrite(Trigger, LOW);
    delayMicroseconds(5);
    digitalWrite(Trigger, HIGH);
    delayMicroseconds(5);
    digitalWrite(Trigger, LOW);
    
    Responce = pulseIn(Receiver,HIGH);
    Distance = (Responce/29);
    Serial.print(Distance);
    Serial.print("cm");  
    
  } 
  
  if (CurrentMillis - PreviousMillis > ContentBrightnessPeriod) {
        
    analogWrite (Brightness, LEDBrightness);
    LEDBrightness = LEDBrightness + FadeAmount;
    
    if (LEDBrightness == 220 || LEDBrightness == 255) {
      FadeAmount = -FadeAmount;
    }
  }  
  

  
  
    if (Distance > 100) {
      digitalWrite (ROne, HIGH);
      digitalWrite (GOne, LOW);
      digitalWrite (BOne, HIGH);
    }
    
    else if (Distance > 50) {
      digitalWrite (ROne, HIGH);
      digitalWrite (GOne, HIGH);
      digitalWrite (BOne, LOW);
    }
    
    else if (Distance <= 50) {
      digitalWrite (ROne, LOW);
      digitalWrite (GOne, HIGH);
      digitalWrite (BOne, HIGH);
    }
    
 
}

The issue is I dont know where best to put the PreviousMillis = CurrentMillis; line. If I put it with the most frequent check, then the sonic will never get up a long enough interval to be checked, and if I put it with the sonic, the Brightness will only get check every 500milliseconds, instead of 30. Do I need two seperate intergers for each Current/Previous Millis?

Cheers guys :slight_smile:

Until I tried to introduce a second millis check:

You need to have two variables that give you the previous time, one for each task. Like PreviousMillisTask1 and PreviousMillisTask2.
Then you can control how often each task gets done.