Smoothing values sent to digital pot?

Hello people,

I am sending values from a pot between two XBee’s via serial communication. So far, I can not seem to send a stream of values that are sequential. The transmission becomes latent unless I reduce the number of values sent over the air thus reducing the resolution.

The problem I am having is audible stepping when printing these values to a DAC or a digital pot. I need to fill in the missing values between what has been read in real-time. I see that this is something that is common with MIDI controller readings.

What I have is
<0>
<4>
<13>
<27>
etc

and what I need to send to the digital pot/DAC is

<0>
<1>
<2>
<3>
<4>
<5>
etc.

It would like to use a 10 bit pot but 8 bit would work. I’m not sure if smoothing mitigates this. ?

Thanks in advance if you get a minute to look at this. All help appreciated!

Here is my complete code.

#include <SPI.h>
#include <DAC_MCP49x1.h>
#define SS_PIN 10               // The Arduino pin used for the slave select / chip select

// Set up the DAC.
// First argument: model (MCP4901, MCP4911, MCP4921)
// Second argument: SS pin (10 is preferred)
// (The third argument, the LDAC pin, can be left out if not used)
DAC_MCP49x1 dac(DAC_MCP49x1::MCP4911, SS_PIN);

//Constants
const int comPin = 3;          //Led to Arduino pin 3 (PWM) Shows Pedal Value
const int buttonPin = 2;       // the number of the pushbutton pin
const int redPin =  4;         // the number of the LED pin
const int relayPin = 7;        //signal out to relay
const int xbstatePin = 9;      //copy of toe switch LED for XBee line passing pin state

//byte switchPin = 5;           //Code Toggle - not used here
//boolean switchState;

//Variables
int ledState = LOW;           // the current state of the output pin
int buttonState;              // the current reading from the input pin
int lastButtonState = LOW;    // the previous reading from the input pin
                              // the following variables are unsigned longs because the time, measured in
                              // milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
bool started = false;         //True: Message is started
bool ended  = false;          //True: Message is finished
char incomingByte ;           //Variable to store the incoming byte
char msg[3];                  //Message - array from 0 to 2 (3 values - PWM - e.g. 240)  4 places now!!!
byte index;                   //Index of array

void setup() {
                                        //Start the serial communication
  Serial.begin(115200);                  //Baud rate must be the same as is on xBee module
  pinMode(comPin, OUTPUT);              // Yellow LED
  pinMode(redPin, OUTPUT);              // Switch LED OUTPUT
  pinMode(xbstatePin, OUTPUT);          //same as redPin
  pinMode(relayPin, OUTPUT);            // RELAY OUT 
  pinMode(buttonPin, INPUT);
  //pinMode(switchPin, INPUT_PULLUP);
  dac.output(0);

  // Set the SPI frequency to 1 MHz (on 16 MHz Arduinos), to be safe.
  // DIV2 = 8 MHz works for me, though, even on a breadboard.
  // This is not strictly required, as there is a default setting.
  //dac.setSPIDivider(SPI_CLOCK_DIV2);
  // Use "port writes", see the manual page. In short, if you use pin 10 for
  // SS (and pin 7 for LDAC, if used), this is much faster.
  // Also not strictly required (no setup() code is needed at all).
  dac.setPortWrite(true);
  
}



void loop(){
  while (Serial.available()>0) {
                                            //Read the incoming byte
    incomingByte = Serial.read();
                                            //Start the message when the '<' symbol is received
    if (incomingByte == '<')
    {
      started = true;
      index = 0;
      msg[index] = '\0';                    // Throw away any incomplete packet
    }
                                            //End the message when the '>' symbol is received
    else if (incomingByte == '>')
    {
      ended = true;
      break;                                // Done reading - exit from while loop!
    }
                                            //Read the message!
    else
    {
      if (index < 4)                        // Make sure there is room
      {
        msg[index] = incomingByte;          // Add char to array
        index++;
        msg[index] = '\0';                  // Add NULL to end
      }
    }
  }

  if (started && ended)
  {
    int value = atoi(msg);
    //int value;
    analogWrite(comPin, value);
    index = 0;
    msg[index] = '\0';
    started = false;
    ended = false;
    Serial.print('<');
    Serial.print(value);                    
    Serial.println('>');
    Serial.flush();
    
    dac.output(value);                        // Send 0-255 to DAC
   }
 {
                                              // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);
                                              // check to see if you just pressed the button
                                              // (i.e. the input went from LOW to HIGH), and you've waited long enough
                                              // since the last press to ignore any noise:

                                              // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
                                              // reset the debouncing timer
    lastDebounceTime = millis();
  }
   if ((millis() - lastDebounceTime) > debounceDelay) {
                                              // whatever the reading is at, it's been there for longer than the debounce
                                              // delay, so take it as the actual current state:

                                              // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

                                              // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
     }
    }
 }
  {
    buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    digitalWrite (relayPin, HIGH);
    digitalWrite (redPin, HIGH);
    digitalWrite (xbstatePin, HIGH);
    }
    else 
    {
      digitalWrite (relayPin, LOW);
      digitalWrite (redPin, LOW);
      digitalWrite (xbstatePin, LOW);
      }
  }
}

The regular smoothing algorithm is a moving average and it's expecting continuous data. i.e. It will average-out to "5" after sending several 5's in a row or after sending a series of values that averages-out to 5.

...You could modify the algorithm (or write a different algorithm) it you want to "slowly-drift" toward the last value sent.

Thank you DVDdoug. You are correct. I am trying to fill in the missing or in-between values.

On the TX side, I am mapping from 10 bit to 8 bit (255). Perhaps there is a technique when I do the Serial.read on the receiving side that would make the values climb or descend sequentially. Maths are not a skill of mine. :slight_smile:

Z

You need a loop with fixed timing driving the DAC so it updates regularly and quickly enough.

Use a float value to drive this, and this needs to be low pass filtered version of the incoming
values.

A simple 1st order digital filter is often used for this, generically looking like:

float filter (float input)
{
  static float output_value = 0 ;
  output_value += k *  (input - output_value) ;
  return output_value ;
}

Where the parameter k sets the low pass filter time constant.

The serial code just has to update some variable to the latest incoming value,
and the DAC output loop uses that, passes to the filter, and uses the filtered
result.

Thanks for your reply, MarkT.

I saw your reply but this hasn't sunk in yet. I've been working on an unrelated circuit in the mean time. >:(

Z

You need a loop with fixed timing driving the DAC so it updates regularly and quickly enough.

I would disagree.
I would use a variable time, set by the time between successive numbers received.
So when you get say 26 and then 30 within X seconds, then on receipt of the 30, I would set a process going that increments the number every X / (30 - 26 ) seconds. Thus moving the number sent smoothly.

This would be a little tricky to implement because you have to have a cut off where the time between numbers being received is too long to represent the same movement. Also the code would have to cope with descending as well as ascending values.

In practice its easier just to update continuously at a fixed rate, so the exponential averaging time-constant
is fixed, leading to consistent glissando / volume change or whatever, and simpler code. All DSP is simpler with fixed sample rates, I’d suggest.