Sensor Scaling ADC to DAC

Dear Forum

This is the first time that I am building a PI controller, it is for a water level sensor for an aquarium. The aquarium has a sump with a controlled return pump to keep the level correct in the main aquarium overflow tank (to reduce noise of the water rushing down the downpipe)… (Ok so it’s complicated )
the aim is to use a PI controller with an extremely slow response. Sample every 30 min and every minute if the error is larger than 4 cm.

The typical range of the water level is:

Max height => sensor measures 5 cm ( PI to reduce pump flow)
reference height => sensor measures 9 cm ( PI to maintain flow)
Minimum heght => sensor measures 13 cm ( PI to increase flow)

The return water pump is controlled by a 0-10 VDC signal, I create this signal by using the MCP4725 12 Bit DAC in series with a non-inverting amplifier (gain of 2)

I am using the HC SR04 (ultrasonic sensor) to measure the water distance (it returns a value in cm)

I am struggling to figure out how to create the code for the scaling of the PI controller.

I force the PI output signal to (-2048 to 2048) (is this incorrect ?). I do this as the error signal is very small typically ± 5 cm and I must somehow get a 0-4096 signal for the DAC…

Now my problem is that the DAC is 12 bit (0-4095), Can I simply level shift the output by adding 2048 to it ?

I fear that by doing this I am fighting the main principle of the PI controller, which is to be free to adjust as it pleases. In other words if the error is zero then it will still force an output of 50% to the pump.

On the flip side, If I don’t do it, every time the error goes negative the PI output will go straight to zero.

Can anyone assist me on this ?

Thanks in advance!

void loop() 

    unsigned long currentMillis = millis();

        if ((unsigned long)(currentMillis - previousMillis) >= interval)   // 1 second timer
              ctr = ctr + 1; // loop counter used to determine speed of control loop ( 1 minutes or 30 min)
                Error =  (Reference - sonar.ping_cm());

                  if (abs(Error) > 4.0)   // set speed of control loop
                    k = 60; // 1 minutes = 60 sec
                    k = 1800; // 30 min
                if (ctr >= k) // start PI loop   every x seconds
                  ctr = 0;
                  /// limit the error to +- 5    // is this correct ?
                  if(Error > 5.0)
                    Error = 5.0;
                  if(Error < -5.0)
                    Error = -5.0;

                  Error = Error *400;


                  Int_Sum += ki*Error;         // Int term

                   if(Int_Sum > 1000){Int_Sum = 500;}
                   if(Int_Sum < -1000){Int_Sum = -500;}  // limit windup
                    Output = (kp * Error + Int_Sum);   // PI output
                    Out_DAC = int(Output); // cast to int

                   Out_DAC =  Out_DAC + 2048 ;   // or should I use map(Out_DAC,-2048,2048,0,4095) ??
                  if(Out_DAC > Out_Max){Out_DAC = Out_Max;}
                  else if(Out_DAC < Out_Min) {Out_DAC = Out_Min;}

                  dac.setVoltage(Out_DAC, false);

                previousMillis = currentMillis;



I know the reason why you wrote pseudo code is probably to save us from unnecessary work, which is a noble intention, but that leads to more problems than it solves. So please show us your full code and use code-tags :slight_smile:

Also I'm not sure why you want to send an anlog signal to the pump. Usually pumps are switched on or off. I guess you are trying to keep some kind of tank at a contant level. Is that correct?

lg, couka

Do you want to pump water into or out from the aquarium? Because I don’t see why you are taking measurements on both sides of the wanted level, except you want to do both. Just limit the value to 0…2048 instead of -2048…2048.

lg, couka

Good day Couka

Thanks for your assistance, I have modified the main post to describe the problem a bit better.
apologies for the cross post.

I only do one measurement and based on that I control the duty cycle of the PI controller.

  1. sensor measures in cm
  2. output required 0-4095 (0-100% duty cycle)

I am not certain what you mean with the scaling of the 0-2048 ? can you please explain in further detail ?


So, just to make sure I got that right: The pump is always needed to keep a certain water level, but you want to adjust it to maintain the level constant. Is that correct?

If so, yes you can just add 2048 to the Output. You are right that is it going to force 50% Output, when the error is zero. But since that is probably causing the level to change, the error will increase or decrease and the PI-Controller will correct for that. That's what it is good for. Note that the error value can be negative.

lg, couka