map returns negative values even with "constrain" applied

Hi,

I've searched through the forum and found this post where someone else had the same issue, map function returning negative values.

I've read the constrain documentation and came up with the following code:

  sensorValue = analogRead(sensorPin);
  Serial.println(sensorValue);

//here I define the range of my constraints
  int potMin = 0;
  int potMax = 1024;

//here I apply the constrain on my defined range  
sensorValue = constrain(sensorValue, potMin, potMax);

  Serial.println(sensorValue);

//here I define the map applied to my values
  sensorValue = map(sensorValue, potMin, potMax,500, 5000);

Despite this I still get negative values and I really can't understand the reason why.

Thanks for your help

what has the constraint to do with map? it is applied before. is the value negative before constraint?
what is the value of the sensorPin and what is the data type of sensorValue?

Please post ALL the code, using code tags. It is required to know how you have defined "sensorValue", among other things.

Note that analogRead will never return values outside the range of 0 to 1023, so the constrain function, as you are using it, does nothing.

How is "sensorValue" declared?

Also post the results of those Serial.prints showing the input and output values.

Steve

Juraj:
what has the constraint to do with map? it is applied before. is the value negative before constraint?
what is the value of the sensorPin and what is the data type of sensorValue?

Hi Juraj,

In a previous post I've read it's better to use constrain so you don't have outliers being fed into the map function.

Before applying constrain values aren't negative but I wanted to make sure the quality of data coming in was good.

SensorPin is equal to

const int sensorPin = A0;

sensorValue is an in

int sensorValue = 0

jremington:
Please post ALL the code, using code tags. It is required to know how you have defined "sensorValue", among other things.

Note that analogRead will never return values outside the range of 0 to 1023, so the constrain function, as you are using it, does nothing.

Hi,

Thanks for your feedback, understood your point about the constrain function not being helpful in this situation, thank you very much.

As requested, please find my full code below

//libraries for timer
#include <TimeLib.h>
#include <TimeAlarms.h>
#include <i2cdetect.h>
#include <Wire.h>
#include <RTClib.h>
#include <hd44780.h>                       // main hd44780 header 
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header


const int buttonPin = 2;     // the number of the pushbutton pin
const int maxView = 2; // beyond this value, lcd screen will loop back to first screen

int buttonState = 0;         // variable for reading the pushbutton status
int viewnumber = 0; //default value to be used by default screen


//necessary for timer to work
AlarmId id;

// declare lcd object: auto locate & auto config expander chip
hd44780_I2Cexp lcd; 

//for rtc board
RTC_DS1307 rtc;

// LCD geometry
const int LCD_COLS = 20;
const int LCD_ROWS = 4;

//key parameters for water pump
const byte waterPumpRelay1Pin = 10;
const int wateringTimeMilliseconds = 1500;

// select the input pin for the potentiometer
const int sensorPin = A0;
int sensorValue = 0;  // variable to store the value coming from the sensor

void setup() {
  //used for debugging
  Serial.begin(115200);
  while (!Serial) ; // wait for Arduino Serial Monitor
   //here I set the relay1 default state
   //HIGH turns the relay OFF
  digitalWrite(waterPumpRelay1Pin, HIGH);
  //here I define my pin as an output
  pinMode(waterPumpRelay1Pin, OUTPUT);

  //this value is taken from the potentiometer and multipled so we can convert to days
  int timerValue = 0;

    // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  Serial.println(sensorValue);
  
  sensorValue = map (sensorValue, 21, 1017,86400, 1286000);
  Serial.println(sensorValue);


  //timer 7 days = 604800
  //second argument is the function to be called
  //timer value is in seconds
  Alarm.timerRepeat(sensorValue*1200, Repeats);           // timer 



  // initialize LCD with number of columns and rows: 
  // hd44780 returns a status from begin() that can be used
  // to determine if initalization failed.
  // the actual status codes are defined in <hd44780.h>
  // See the values RV_XXXX
  //
  // looking at the return status from begin() is optional
  // it is being done here to provide feedback should there be an issue
  //
  // note:
  //  begin() will automatically turn on the backlight
  //
  int status;
  status = lcd.begin(LCD_COLS, LCD_ROWS);
  if(status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }

  // initalization was successful, the backlight should be on now

  //LCD start-up message
  lcd.setCursor(0,0); //Defining positon to write from first row,first column .
  lcd.print("Automatic Watering"); //You can write 16 Characters per line .
  lcd.setCursor(0,1);  //Defining positon to write from second row,first column .
  lcd.print("by  ...");
  Alarm.delay(1000); 
  lcd.clear();//Clean the screen

  DateTime now = rtc.now();
  setTime(now.hour(),now.minute(),now.second(),now.day(),now.month(),now.year());

  
}

void loop() {



    // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  
  Serial.println(sensorValue);

  int potMin = 0;
  int potMax = 3;

  sensorValue = constrain(sensorValue, potMin, potMax);

  Serial.println(sensorValue);
  
  sensorValue = map(sensorValue, potMin, potMax,500, 5000);

  int sensorMultiplier = 1000;

  Serial.println(sensorValue);

  Serial.println("Times 1000");
  sensorValue = sensorValue * sensorMultiplier;
  
  Serial.println(sensorValue);


  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
     Serial.println("ON");
     viewnumber++;
     Serial.println(viewnumber);
     if (viewnumber == maxView){
      viewnumber = 0;
     }
     
  } 


 
  
  

    //this is epoch for scheduling
    //unsigned long time = now.unixtime();

    //this is for debugging only
    //write to serial monitor
    DateTime now = rtc.now();
    Serial.print(now.day());
    Serial.print('/');
    Serial.print(now.month());
    Serial.print('/');
    Serial.println(now.year());
    Serial.print(now.hour());
    Serial.print(":");
    Serial.print(now.minute());
    Serial.print(":");
    Serial.println(now.second());
    lcd.clear();//Clean the screen


    

    if (viewnumber == 0){
    //this is for fancy display only
    lcd.setCursor(0,0); //Defining positon to write from first row,first column .
    lcd.print("Date :"); //You can write 20 Characters per line .
    lcd.setCursor(0,1); //Defining positon to write from first row,first column .
    String full_date = String(now.day()) + "  " + String(now.month())+ "  " + String(now.year());
    lcd.print(full_date);
    lcd.setCursor(0,2); 
    lcd.print("Time :");
    String full_time =  String(now.hour())+ "  " + String(now.minute())+ "  " + String(now.second());
    lcd.setCursor(0,3);
    lcd.print(full_time);
    
    Alarm.delay(1000);
    }

    if (viewnumber == 1){
     lcd.setCursor(0,2); //Defining positon to write from first row,first column .
  lcd.print("Next trigger time :"); //You can write 20 Characters per line .
  lcd.setCursor(0,3); //Defining positon to write from first row,first column .
  String jour2 = String(day(Alarm.getNextTrigger()));
  String moi2 = String(month(Alarm.getNextTrigger()));
  String annee2 = String(year(Alarm.getNextTrigger()));
  String heure2 = String(hour(Alarm.getNextTrigger()));
  String minut2 = String(minute(Alarm.getNextTrigger()));
  String seconde2 = String(second(Alarm.getNextTrigger()));
  lcd.print(jour2+ " " + moi2 + " " +annee2 + " " + heure2 + " " + minut2 + " " + seconde2); //You can write 20 Characters per line .
Alarm.delay(1000);
    }



      
  
    
  

 
    
}

//control water pump and log status
void waterPump(const byte relayPin, bool state){
  digitalWrite(relayPin, state);
  Serial.print("Water pump at pin ");
  Serial.print(relayPin);
  Serial.print(" is ");
  Serial.println(state);
  Serial.println(" ");
 
}



void Repeats() {
    Serial.println("Timer");
    Serial.println ("Let's turn ON this relay");
    waterPump(waterPumpRelay1Pin,LOW);
    Serial.println ("Now I wait");
    Alarm.delay(wateringTimeMilliseconds);
    Serial.println ("Now I turn the relay OFF");
    waterPump(waterPumpRelay1Pin,HIGH);
}

void digitalClockDisplay() {
  // digital clock display of the time
  Serial.print(day());
  Serial.print("/");
  Serial.print(month());
  Serial.print("/");
  Serial.println(year());
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println();
  
}

void triggerTimeDisplay() {
  Serial.print(day(Alarm.getNextTrigger()));
  Serial.print("/");
  Serial.print(month(Alarm.getNextTrigger()));
  Serial.print("/");
  Serial.println(year(Alarm.getNextTrigger()));
  
  Serial.print(hour(Alarm.getNextTrigger()));
  printDigits(minute(Alarm.getNextTrigger()));
  printDigits(second(Alarm.getNextTrigger()));
  Serial.println();
}

void printDigits(int digits) {
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

Hi,

I've made a simplified version of my code in order to try to isolate the issue.

With the code below it works like a charm....

int sensorValue = 0;  // variable to store the value coming from the sensor
const int sensorPin = A0;

void setup() {

Serial.begin(115200);
while (!Serial) ; // wait for Arduino Serial Monitor
}

void loop() {
   // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  Serial.println(sensorValue);
  sensorValue = map (sensorValue, 0, 1023,2000, 3000);
  Serial.println(sensorValue);
  delay(1000);
}

I guess the issue is coming from another part of my code because now I believe I can be sure my potentiometer works as expected and the wiring is correct as well.

 sensorValue = map (sensorValue, 21, 1017,86400, 1286000);

the map result is too big for int. it is not the case for 5000 in your snippet.

change type of sensorValue to long

  sensorValue = map (sensorValue, 21, 1017,86400, 1286000);

86400 and 1286000 are too big to fit in an int.

Hi Guys,

You were all spot on, my data type wasn't appropriate and resulted in weird looking values.

It's now solved by using

unsigned long int sensorValue = 0;

ayawatson:
Hi Guys,

You were all spot on, my data type wasn't appropriate and resulted in weird looking values.

It's now solved by using

unsigned long int sensorValue = 0;

return of map() is long

This issue is a bug within the map function calculation. Per the documentation, the map function is:

long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

For large ranges, the (x - in_min) * (out_max - out_min) can create a number greater than 2.1M which is the max value of a long variable. This large value causes the negative number to be returned.

I submitted a bug request for this, but in the mean time, I have created a new map function for my code that wraps the division in a parentheses and used that. So, my newMap is:

long newMap(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * ((out_max - out_min) / (in_max - in_min)) + out_min;
}

This avoids the large number within the calculation.

1 Like

rjp119:
For large ranges, the (x - in_min) * (out_max - out_min) can create a number greater than 2.1M G which is the max value of a long variable. This large value causes the negative number to be returned.