Fun with Arduino 12 Analog Input, analogRead(), Change Range, map()

Now that the User Interface based on keyboard input is working (see video 11) we’ll now focus on the other solution we promised our customer, the Tech Company: a rotating knob on the switch panel. We have a couple of options … we can use an analog potentiometer, or a digital rotary encoder.

potmeter_rotaryenc

I made myself a little switch panel (click to enlarge the images) that has both available:

switch_box_small  switch_box_bottom_small

The potentiometer is the easiest option, we can simply connect it to one of the Arduino’s analog inputs and use the analogRead() instruction.

analog_in_320When connected to 5V and GND, the voltage on the middle pin of the potentiometer will vary between 0 and 5V. What resistor value to choose? Not too small, we don’t want to draw too much current. Also not too large, Arduino advises not to go higher than 10k. I only had R220 lying around, so I used that, although it draws a somewhat higher current.

Read on below the video …

 

Let’s check out the analogRead() command to get a feel for it. We can see it in action with this code:

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

void loop() {
  Serial.println(analogRead(A0));
  delay(250);
}

Note that unlike the digital I/O pins, which must be specified in setup(), we can use an analog pin just like that, without first specifying it.

When we rotate the knob, we notice that:

  • The values range from 0 – 1023
  • The readings show some noise, they vary +/- 2 ticks

What range do we want for our cycle time? As a first step we can use a range of 1-9 minutes (seconds in the test phase). We have to ‘map’ the range of 0-1023 onto a range of 1-9 … and the fun is … we do not have to do the math ourselves, the Arduino has the map() command for this:

map(number, from_min, from_max, to_min, to_max);

Let’s map our analog read values from 0-1023 to 1-9 and see what happens:

unsigned int cycle_time;

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

void loop() {
  cycle_time = map(analogRead(A0), 0, 1023, 1, 9); 
  Serial.println(cycle_time);
  delay(250);
}

Hmm … almost OK, but just not. It goes to 8 and only every now and then it barely touches 9. Apparently only the value 1023 becomes 9, while 1022 still converts to 8. We don’t want that. Let’s tweak the numbers and say 1024 has to become 10. Our input value never becomes 1024, so the outcome will never be 10, but a range of values up to and including 1023 will now become 9. It behaves much like the random() statement, where we also had to subtract 1 from the max value to get what we want.

unsigned int cycle_time;

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

void loop() {
  cycle_time = map(analogRead(A0), 0, 1024, 1, 10); 
  Serial.println(cycle_time);
  delay(250);
}

Yes, this looks fine. Notice how we also automatically got rid of the noise in the readings.

Let’s now take the code we had in video 11 and change the section where we read the keyboard input into analogRead(). Let’s include a print statement, with a new-old test such that it only prints if the value has changed. We introduce a variable cycle_time_old for this. In setup() we add an initial analog reading. And of course we add a #define for the potentiometer pin, to make it easy to modify when needed.

#define SWITCH_PIN 2 // input for toggle switch
#define POTM_PIN A0  // potmeter analog input 
#define LIGHTS_PIN 8 // output to FET or relay module
#define LED_PIN A5   // output to LED on switch panel
#define T_MIN 1      // minimum random time to add to cycle_time
#define T_MAX 5      // maximum random time to add to cycle_time

byte switch_new, switch_old;
unsigned int cycle_time, cycle_time_old;
unsigned int day_time, night_time;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  cycle_time = map(analogRead(POTM_PIN), 0, 1024, 1, 10); 
  cycle_time_old = cycle_time;  
  Serial.begin(9600);
  Serial.println();
  Serial.println("Day/Night cycle system is ready");
  Serial.println();
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
  Serial.println();
  Serial.println("To change the cycle time, rotate the knob");
  Serial.println();
}

void loop() {
  cycle_time = map(analogRead(POTM_PIN), 0, 1024, 1, 10);
  if(cycle_time != cycle_time_old) {
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" seconds");
    cycle_time_old = cycle_time;
  }
  switch_new = digitalRead(SWITCH_PIN);
  if (switch_new != switch_old) {
    if (switch_new == LOW) Serial.println("Cycle started");
    else Serial.println("Cycle stopped");
    switch_old = switch_new;
  }
  if (switch_new == LOW) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(LIGHTS_PIN, HIGH);
    night_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Night time = ");
    Serial.print(night_time);
    Serial.println(" seconds");
    delay((unsigned long)night_time * 1000);
    digitalWrite(LIGHTS_PIN, LOW);
    day_time = cycle_time + random(T_MIN, T_MAX);
    Serial.print("Day time   = ");
    Serial.print(day_time);
    Serial.println(" seconds");
    delay((unsigned long)day_time * 1000); 
  }
  else {
    digitalWrite(LED_PIN, LOW);
  }
}

Testing 123 … almost perfect … it’s just that we still suffer a bit from that analogRead() noise with the transitions from one value to another. A simple way of getting rid of that is to add a short delay. Also, if we like, we can map to another range. In stead of 1-9 we could map to say 1-31 and multiply by 10. This will get us a range from 10 – 300 seconds, with 10 seconds increments. It’s totally flexible, simply use your own preferences here.

...
void loop() {  
cycle_time = map(analogRead(POTM_PIN), 0, 1024, 1, 31) * 10;
  if(cycle_time != cycle_time_old) {
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" minutes");
    cycle_time_old = cycle_time;
    delay(100);
  }
...

Yes, this works fine, the screen output is not jumpy anymore and we now have a range from 10-300 with step size 10.

Like with the keyboard input in video 11, also here we have the issue that when we change the cycle time while the cycle is running, we do not get any direct feedback on screen as to what we are doing. This is caused by the delay() statement, which completely blocks the Arduino. In the next video we will replace delay() with something better … we will use the Arduino built in clock to decide when it is time for action.

— 0 —

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s