PWM light intensity adaption based on ultrasonic distance

goodafternoon,

for a home project I would like to make outdoor lightning, whereby the intensity/brightness will be higher when someone comes closer to that specific light point. ( apr 20-30 light points in total). My basic idea was to have a ultrasonic sensor in place to measure distance between the light and that person, and let the Arduino change the intensity of the led inside the light pole by means of PWM. Has something similar been done yet/ does it seems feasible to do something like this?

thank you guys very much in advance,

kind regards from a struggling basic Arduino enthusiast,

Bas

Yes it is perfectly feasible. Get the ultrasonic bit working first, lots of examples for that. Then use the map function to turn the distance into a number you can set thw PWM value of a pin that is driving an LED.

The cheap (less than $2) HC-SR04 ultrasonic rangefinder modules are probably not weatherproof. You may need to spend more or find a way to weatherproof them while retaining sufficient range.

okay thanks in advance for the help! I did some tutorials
1: ultransonic sensor works, great
2: PWM automatically set to fade works, great!
3: combining both with a map function to transform the distance input from the ultrasonic sensor to 0-255 from the PWM works too, great!
one thing now, if I set for example with the map function (0, 10, 255, 0) as in, getting closer to the sensor, the light should be brighter. this works I think, but when I m further away than 10 cm, the LED starts lighting up again on full brightness. Does anyone has a solution to this? something like if >10, then PWM -> 0

thank you! Bas

code:

/*
  Analog input, analog output, serial output

  Reads an analog input pin, maps the result to a range from 0 to 255 and uses
  the result to set the pulse width modulation (PWM) of an output pin.
  Also prints the results to the Serial Monitor.

  The circuit:
  - potentiometer connected to analog pin 0.
    Center pin of the potentiometer goes to the analog pin.
    side pins of the potentiometer go to +5V and ground
  - LED connected from digital pin 9 to ground

  created 29 Dec. 2008
  modified 9 Apr 2012
  by Tom Igoe

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/AnalogInOutSerial
*/
#define echoPin 2 // attach pin D2 Arduino to pin Echo of HC-SR04
#define trigPin 3 //attach pin D3 Arduino to pin Trig of HC-SR04

// defines variables
long duration; // variable for the duration of sound wave travel
int distance; // variable for the distance measurement

// These constants won't change. They're used to give names to the pins used:
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 6; // Analog output pin that the LED is attached to

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

void setup() {
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode(echoPin, INPUT); // Sets the echoPin as an INPUT
  Serial.begin(9600); // // Serial Communication is starting with 9600 of baudrate speed
  Serial.println("Ultrasonic Sensor HC-SR04 Test"); // print some text in Serial Monitor
  Serial.println("with Arduino UNO R3");
}

void loop() {
   // Clears the trigPin condition
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin HIGH (ACTIVE) for 10 microseconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
  // Displays the distance on the Serial Monitor
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");
  // map it to the range of the analog out:
  outputValue = map(distance, 0, 1000, 255, 0);
  // change the analog out value:
  analogWrite(analogOutPin, outputValue);

  // print the results to the Serial Monitor:
  Serial.print("sensor = ");
  Serial.print(sensorValue);
  Serial.print("\t output = ");
  Serial.println(outputValue);

  // wait 2 milliseconds before the next loop for the analog-to-digital
  // converter to settle after the last reading:
  delay(2);
}

one thing I forgot, since the sensor has to measure the distance every 10 ms, the led blinkers a bit caused (I guess) by the processing of the distance. is there something to prevent this? like, staying at the brightness of the previously measured value?

WARNING: If no echo is received, either because the sensor is blocked or there is nothing close enough to sense, pulseIn() will return 0 which results in a distance of 0. DON'T treat a distance of 0 like you sensed something very close. That is why your LED is going to full brightness when no echo is sensed.

The pulseIn() function has an optional timeout. If you use the default it will wait for up to 1000000 microseconds. That is WAY beyond the range of the sensor. A timeout of 30000ul ('unsigned long' constant) will cover the range of the sensor:

  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH, 30000ul);

You probably won't get echoes beyond 5 meters (500 cm). Since you set the input range on map() to 1000cm you will never go lower than half brightness. You should constrain the input range and use the constrained range:

  // map it to the range of the analog out:
  outputValue = map(constrain(distance, 3, 500), 3, 500, 255, 0);

You should probably change the delay at the end of loop() to 20 milliseconds. Much lower than that and you will sometimes be reading an echo from the PREVIOUS trigger and the distance will be lower than actual.

A little further! I got time to try your solution and it did work!!

But now, I want to try to light up every led to "follow" the person, by means of the distance measured in with the ultrasonic sensor. Therefore, I purchased a IS31FL3731 driver, to be able to control multiple lights/leds (+/-40). I soldered a charlieplexed "matrix" i.e. the amount of lights I want to be lighten up, and tried the swirl example that comes with the IS31FL3731 library. This did work, so the matrix seems fine. Now I want to combine the ultrasonic distance with the led controller.

Therefore my question, can anyone help me with manually controlling individual lights, i.e. lighting up one led (I read something about allocating leds on the grid by the x and y, but I do not understand the example (added below)). When I'm able to control each led, I can combine the distance with map() with each led, was my reasoning.

Thank you all very much for the help!!

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_IS31FL3731.h>

// If you're using the full breakout...
Adafruit_IS31FL3731 ledmatrix = Adafruit_IS31FL3731();
// If you're using the FeatherWing version
//Adafruit_IS31FL3731_Wing ledmatrix = Adafruit_IS31FL3731_Wing();


// The lookup table to make the brightness changes be more visible
uint8_t sweep[] = {1, 2, 3, 4, 6, 8, 10, 15, 20, 30, 40, 60, 60, 40, 30, 20, 15, 10, 8, 6, 4, 3, 2, 1};

void setup() {
  Serial.begin(9600);
  Serial.println("ISSI swirl test");

  if (! ledmatrix.begin()) {
    Serial.println("IS31 not found");
    while (1);
  }
  Serial.println("IS31 found!");
}

void loop() {
  
int i = 0;
for (uint8_t x=0; x<16; x++) {
  for (uint8_t y=0; y<9; y++) {
    ledmatrix.drawPixel(x, y, i++);
  }
}
}

Did you get a 16x9 matrix to go with the IS31FL3731 driver board? It looks like you can set the intensity of each of the 144 LEDs.

no I just have the driver itself, and I welded a matrix myself afterwards ( only 6x7)

I created a new sketch where I added the "draw pixel" sentence, but I cannot find out whether it works since I burned my ultrasonic 10 mins ago when trying😂 :

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_IS31FL3731.h>

// If you're using the full breakout...
Adafruit_IS31FL3731 matrix = Adafruit_IS31FL3731();

#define echoPin 2 // attach pin D2 Arduino to pin Echo of HC-SR04
#define trigPin 3 //attach pin D3 Arduino to pin Trig of HC-SR04

// defines variables
long duration; // variable for the duration of sound wave travel
int distance; // variable for the distance measurement

// These constants won't change. They're used to give names to the pins used:
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

void setup() {
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode(echoPin, INPUT); // Sets the echoPin as an INPUT
  Serial.begin(9600); // // Serial Communication is starting with 9600 of baudrate speed
  Serial.println("Ultrasonic Sensor HC-SR04 Test"); // print some text in Serial Monitor
  Serial.println("with Arduino UNO R3");
  Serial.println("ISSI manual animation test");
  
}

void loop() {
   // Clears the trigPin condition
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin HIGH (ACTIVE) for 10 microseconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH, 30000ul);
  // Calculating the distance
  distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
  // Displays the distance on the Serial Monitor
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");
  // map it to the range of the analog out:
  outputValue = map(constrain(distance, 3, 500), 3, 500, 255, 0);
  matrix.drawPixel(0,0,outputValue);

  // print the results to the Serial Monitor:
  Serial.print("sensor = ");
  Serial.print(sensorValue);
  Serial.print("\t output = ");
  Serial.println(outputValue);

  // wait 2 milliseconds before the next loop for the analog-to-digital
  // converter to settle after the last reading:
  delay(20);
}

I tried some coding again, does this seem plausible to you? so, for led 1 (0,0 in the matrix) , it needs to glow up from 53-50cm, and go out from 50-47 cm

outputValuepl1 = map(constrain(distance, 500, 530), 500, 530, 0, 255);
  outputValuemn1 = map(constrain(distance, 470, 500), 470, 500, 0, 255);
  matrix.drawPixel(0,0,outputValuepl1);
  matrix.drawPixel(0,0,outputValuemn1);```

I have read this tutorial on PWM and found it really interesting so thought of sharing .... Introduction to PWM (Pulse Width Modulation) - The Engineering Projects ....

No. You are calculating two brightness values and assigning both to the same pixel. The first one will apply for only a small fraction of a second.

You need to combine the two values. I think adding them together would work except '500' is in both ranges and both map it to 255 so you would get 510 when you add them together. You could remove 500 from one of the ranges and add the two results together.

okay, that sounds logical, but I want it to react 'parabolic'. Say the led is placed on 50 cm from the sensor, it must go on when you approach it from 5 cm away, but it has to go down, too, until you are 5 cm past it. therefore I made two separate 'linear' values, one for going on, and one for dying out.

I tried something but still no success: ` if(distance <18 && distance > 13){

outputValuepl38 = map(constrain(distance, 13, 18), 13, 18, 255, 0);
matrix.drawPixel(1,1,outputValuepl38);
}
else{
outputValueminl38 = map(constrain(distance, 12.9, 8), 12.9, 8, 255, 0);
matrix.drawPixel(1,1,outputValueminl38);
}`

Perhaps it would be better to write a function to return the brightness value, since I suspect you will be using it more than once.

// From targetDistance-5 to targetDistance 
// the brightness gets higher.  From 
// targetDistance to targetDistance+5 the 
// brightness gets lower
byte brightness(const int distance, const int targetDistance)
{
  int delta = distance - targetDistance;
  if (delta < 0) delta = -delta;  // Absolute Value
  if (delta > 5)
    return 0;
  return map(delta, 0, 5, 255, 0);
}
1 Like

wow your the master of arduino!!! awesome, it solved my issue completely!! thank you:)

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.