I'm pretty new to microcontrollers and C++ in general and have been trying lots of little project ideas to learn as I go. For the most part I've made good progress but I need some guidance a particular feature I need. I'm not sure on the correct terminology and so finding it difficult to research.
I have a piece of commercial software that sends a stream of data based on user input. When the user moves a control, values in the range of 0 to 255 are received by my Arduino. If the control is at 0 and is moved to 255, all values between 0 and 255 are received. If the control is at 128 and is moved to 0, values between 128 and 0 are received. Etc etc.
I guess this is essentially like the input that would be received from a potentiometer connected to an analog pin. But in my case it's data being received over Wi-Fi based on a software potentiometer of sorts.
I would like to implement some kind of smoothing to the data, so that a variable is set based on the final value (presumably after a short delay) and all the other values received as the control was moved are ignored. Then when the control is moved again (after another delay), the variable is redefined as the new final value. Hopefully that makes sense.
Does this technique have a particular name so I can read about it?
Please tell us how much time this takes. from start to finish. Does it all come in a single message or is it broken up into multiple messages? Are there possibilities of message being missed or data corrupted?
are you trying to skip all the intermediate values and only report a values that remains unchanged for some period of time?
so if a value is received, a timestamp can be captured and if a new value received before a time has expired since the timestamp, not report that previous value
I'd think of it as somewhat like "debouncing" or "state change detection" on an analog signal.
This simulation doesn't blank out all the intermediate values:
code
// Analog StateChange Detection for
// https://wokwi.com/projects/391124361363049473
// for https://forum.arduino.cc/t/esp8266-analog-input-triggering-issue-for-one-shot/1229757/24
// and // and https://forum.arduino.cc/t/using-analog-input-to-both-turn-on-and-turn-off-a-digital-output/1292092/5
int lastButtonState = 0; // integer to remeber a range of distinct states
const int AnalogPin = A2;
const int SensorPin = A0;
int currentButtonState = 0;
int currentSensorState;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
void setup() {
Serial.begin(115200);
}
void loop() {
checkAndReportSliderA();
checkAndReportSensor2();
}
bool checkAndReportSensor2() {
bool changed = false;
static int lastSensorState = -1;
currentSensorState = analogRead(SensorPin);
if (currentSensorState != lastSensorState) {
changed = true;
lastSensorState = currentSensorState;
Serial.print(currentSensorState);
Serial.print(" ");
}
}
void checkAndReportSliderA() {
// This checks an analog input,
// bins it into categories,
// and prints a message when the category changes
const long NumCategories = 52;
const int MaxADC = 1023;
currentButtonState = analogRead(AnalogPin) * NumCategories / (MaxADC + 1); // transform into distinct ranges
if (currentButtonState != lastButtonState && millis() - lastDebounceTime >= debounceDelay) {
// The input has changed
lastDebounceTime = millis(); // record the time for rate limiting
Serial.print(num2azAZ(currentButtonState));
lastButtonState = currentButtonState;
}
}
char num2azAZ(int num) {
char ch = num < 26 ? char('a' + num) : char('A' + num - 26);
return ch;
}
...but how I would approach the masking of the intermediate values would depend on the noise and the precision of the signal. Given a certain delay and threshold, I'd do a level of change detection of amounts larger than the threshhold, while recording the time of the update, and then set a second filtered output variable from the value hasn't if it hasn't changed more than the threshold over the delay period.
The key trick is the State Change Detection on different sorts of variables.
Edit: Maybe something like this replacement for checkAndReportSensor2() in that simulation:
int lastButtonState = 0; // integer to remeber a range of distinct states
const int AnalogPin = A2;
const int SensorPin = A0;
int currentButtonState = 0;
int currentSensorState;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
void setup() {
Serial.begin(115200);
}
void loop() {
checkAndReportSliderA();
checkAndReportSensor2b();
}
bool checkAndReportSensor2b() {
bool changed = false;
static int lastSensorState = -1;
static uint32_t lastChangeMs = millis();
static int blankedValue = -1;
uint32_t now = millis();
currentSensorState = analogRead(SensorPin);
if (abs(currentSensorState - lastSensorState) > 5) {
changed = true;
lastSensorState = currentSensorState;
lastChangeMs = now;
Serial.print(currentSensorState);
Serial.print(" ");
}
if(blankedValue != lastSensorState && now - lastChangeMs > 500){
blankedValue = lastSensorState;
Serial.print("\nEndResult: ");
Serial.println(blankedValue);
}
}
bool checkAndReportSensor2() {
bool changed = false;
static int lastSensorState = -1;
currentSensorState = analogRead(SensorPin);
if (currentSensorState != lastSensorState) {
changed = true;
lastSensorState = currentSensorState;
Serial.print(currentSensorState);
Serial.print(" ");
}
}
void checkAndReportSliderA() {
// This checks an analog input,
// bins it into categories,
// and prints a message when the category changes
const long NumCategories = 52;
const int MaxADC = 1023;
currentButtonState = analogRead(AnalogPin) * NumCategories / (MaxADC + 1); // transform into distinct ranges
if (currentButtonState != lastButtonState && millis() - lastDebounceTime >= debounceDelay) {
// The input has changed
lastDebounceTime = millis(); // record the time for rate limiting
Serial.print(num2azAZ(currentButtonState));
lastButtonState = currentButtonState;
}
}
char num2azAZ(int num) {
char ch = num < 26 ? char('a' + num) : char('A' + num - 26);
return ch;
}
Oh ye of little faith. Same library also gives delta of the data range. When delta goes to zero for x readings. You are at the end and here is your value.
Thank you everyone! Sorry if I didn't explain what I'm trying to achieve very well but this is exactly what I was thinking:
I don't think I can use averaging. As if the value changes from 0 to 10 and stays at 10 for approx 1 second, I need the output to be 10, not 5 for example. I guess there may be more advanced averaging methods though.
I've tried a couple of debouncing libraries but from what I could see, they're aimed at smoothing a Boolean signal from a switch and I couldn't figure out a way to apply that to my situation with 256 potential values. But perhaps there's a way.
State change detection sounds promising, so I'll look into that and your other suggestions and come back when I've learned more. Thanks again.