You have two issues going on - noise pickup, and a potentiometer with imprecise stops at one or both ends.
The pot is the easiest to fix: What are the lowest low reading and highest high reading you can reliably get when you turn it to its stops? Use those in place of '0' and '1023' in your map()
function.
Improving the noise is only slightly more involved. An exponential average is an easy filter to write and quite effective at quieting noisy data, as long the signal to noise ratio is fairly low. The basic idea is you add only a fraction of each new value and keep (1 - that fraction) of the old average; so if your chosen fraction is 0.1, you only consider 10% each new datum, keeping 90% of the current average.
avg = f*avg + (1-f)*new_value
The output of this averaging filter can be slow to respond to changes if your fraction is too small, but less effective at quieting noise if your fraction is too large.
I usually use .25, or 25% of each new point and 75% of the current average, when I'm just trying to clean up minor noise, which is what you probably have.
Here is my 25% averaging filter:
int16_t xpavg(int16_t newdat, int16_t avg){
// (3 * avg + 1 * newdat) / 4
return( (((avg<<2) - avg + newdat) + 2) >> 2 );
}
As you can see, I do the arithmetic in Fixed Point - just like integer arithmetic, but with an assumed binary point 2 places to the left during the computation. And I choose a fraction that represents "nicely" in binary - 1/4 in this case - to keep the arithmetic faster (since multiplication and division can be replaced by shifting).
The function just multiplies the current average by 4 and subtracts the old average (effectively multiplying by 3), adds the new value, and divides the sum by 4. (That extra "+2" rounds the fractional bits just before we shift them away). The end result is:
(3*avg + newData)/4 (== 3/4*avg + 1/4*newData).
If you were to only sample the pot every second or two, you could probably observe the delay as the filter falls behind while you turn the pot and catches up when you stop. If you're sampling at 10x/sec or less, you shouldn't be able to notice.
Update:
You need to add the function xpavg()
to your code, and call it each time you read a new pot value (that is "newdat"), passing it the old average as well. It returns a new average pot value, now less affected by noise. You use that new average value instead of the raw number you just read from the pot, in your hue calculation. As Duncan offered, you can - and I do - initialize the average with a raw value from the pot. You could also start it at zero but it will take a few samples to converge on the current actual pot reading. The downside of using one raw sample to start it is that if that one value is terribly noisy, i.e. way different from the pot reading, it will still take a few samples to converge.
unsigned int AvgPot = analogRead(poti1); // initialize pot average
// Exponential averaging functions
int16_t xpavg(int16_t newdat, int16_t avg)
{
// (3 * avg + 1 * newdat) / 4
return( (((avg<<2) - avg + newdat) + 2) >> 2 );
}
void setup()
{
; // initializations go here
}
void loop()
{
int hue = analogRead(poti1);
hue = xpavg(poti1, PotAvg);
int mappedHue = map(hue, 0, 1023, 0, 65535);
neoPixelStrip.fill(neoPixelStrip.gamma32(neoPixelStrip.ColorHSV(mappedHue, 255, 255)));
neoPixelStrip.show();
}