Output logarithmic curve

I'm using Arduino's analogWrite() function to control volume.

By pressing an up or down button I hope to adjust the volume incrementally....it kinda works, but the values are linear so the volume isn't smooth.

so......
I've been looking at using Arduino Playground - Fscale but I can't seem to get it working.

Cheers in advance :slight_smile:

//////////////
// Melodies //
//////////////

const unsigned long melody[8] = {c5,d5,e5,f5,g5,a5,b5,c6};
const unsigned long melody2[8] = {c6,as5,gs5,g5,f5,ds5,d5,c5};
const unsigned long melody3[6] = {c4,d4,e4,g4,a4,c5};
const unsigned long melody4[7] = {c5,as4,g4,fs4,f4,ds4,c4};

////////////////////
// Pin Assignment //
////////////////////

const byte dataPin = 4;
const byte latchPin = 5;
const byte clockPin = 6; 

const byte button1 = 0; 
const byte button2 = 2; 
const byte button3 = 12; 
const byte button4 = 10; 
 
const byte upButton = 7;
const byte selectButton = 8; 
const byte downButton = 9; 

const byte xAxis = 0; 
const byte yAxis = 1; 
const byte zAxis = 2; 

/////////////////////////////
// Temporary Memory Stores //
/////////////////////////////

byte sr1 = 0; 
byte sr2 = 0; 

byte t = 0;

unsigned int mainVOLUME = 0;
unsigned int VOLUME = 0; 
boolean state = false;
int note = 0;

int mult = 8;
int attack = 1;
int sustain = 0;
int release = 4;
int velocity = 200;
int gap = 10;

///////////
// Setup //
///////////

void setup() 
{
/////////////////////////////////////////////////////////////////////////////////////
// Configure PWM on pins 3 and 11 to run at maximum speed, rather than the default //
/////////////////////////////////////////////////////////////////////////////////////

pinMode(3,OUTPUT); // speaker on pin 3
pinMode(11,OUTPUT); // amp on pin 11

cli(); // disable interrupts while registers are configured

bitSet(TCCR2A, WGM20);
bitSet(TCCR2A, WGM21); // set Timer2 to fast PWM mode (doubles PWM frequency on pins 3 & 11)
bitSet(TCCR2B, CS20);
bitClear(TCCR2B, CS21);
bitClear(TCCR2B, CS22);

sei(); // enable interrupts now that registers have been set
/////////////////////////////////////////////////////////////////////////////////////

  // LED Shift Registers 
  pinMode(latchPin, OUTPUT); 
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  
  // Main Buttons
  pinMode(button1, INPUT); 
  pinMode(button2, INPUT); 
  pinMode(button3, INPUT); 
  pinMode(button4, INPUT); 

  // Menu Buttons
  pinMode(upButton, INPUT); // Up Button
  pinMode(selectButton, INPUT); // Select Button
  pinMode(downButton, INPUT); // Down Button
  
  // Set All Button Pins High 
  digitalWrite(button1, HIGH); 
  digitalWrite(button2, HIGH);
  digitalWrite(button3, HIGH);
  digitalWrite(button4, HIGH);
  
  digitalWrite(upButton, HIGH); 
  digitalWrite(selectButton, HIGH);
  digitalWrite(downButton, HIGH);
 
  Serial.begin(9600);
  setupLCD(); 
  
  analogWrite(11, mainVOLUME);
  Serial.print("Volume = ");
  Serial.print(mainVOLUME / 2.55);  
  playMelody();
}
 
/////////////// 
// Main Loop //
///////////////

void loop() 
{    
    if(digitalRead(upButton) == LOW && mainVOLUME < 255) // If Up Button Is Pressed
    {
       mainVOLUME++;
       
       clearLCD();
       selectLineOne();
       Serial.print("Volume = ");
       Serial.print(mainVOLUME / 2.55);
       
       [glow]mainVOLUME = fscale(0, 255, 0.0, 255.0, mainVOLUME, -2.0);[/glow]
       analogWrite(11, mainVOLUME);
       
       selectLineTwo();
       Serial.print("Actual = ");         
       Serial.print(mainVOLUME);
       
       playTone(c8,5);
       delay(10);
    }  
    
    if(digitalRead(downButton) == LOW && mainVOLUME > 0) // If Down Button Is Pressed
    {     
       mainVOLUME--;
       
       clearLCD();
       selectLineOne();
       Serial.print("Volume = ");         
       Serial.print(mainVOLUME / 2.55);
       
       [glow]mainVOLUME = fscale(0, 255, 0.0, 255.0, mainVOLUME, -2.0);[/glow]
       analogWrite(11, mainVOLUME);
       
       selectLineTwo();
       Serial.print("Actual = ");         
       Serial.print(mainVOLUME);
       
       playTone(c8,5);
       delay(10);
    } 
  
    if(digitalRead(selectButton) == LOW) // If Select Button Is Pressed
    {  
       clearLCD();
       selectLineOne();
       Serial.print("Select");
       selectLineTwo();
       Serial.print("Button Pressed");
       playTone(c9,5);
    }  
    
    if(digitalRead(upButton) == HIGH && digitalRead(downButton) == HIGH && digitalRead(selectButton) == HIGH)
    {
    playMelody();
    } 
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////
// Shift Data To LEDs //
////////////////////////

void shift() 
{
digitalWrite(latchPin, LOW);     
shiftOut(dataPin, clockPin, MSBFIRST, sr2); 
shiftOut(dataPin, clockPin, MSBFIRST, sr1);                
digitalWrite(latchPin, HIGH);
}

/////////////////////////
// Functions For Sound //
/////////////////////////
        
void playTone(int tone, int duration) 
{
    for (long i = 0; i < duration * 1000L; i += tone * 2) 
    {
    analogWrite(3, VOLUME);
    delayMicroseconds(tone);
    analogWrite(3, 0);
    delayMicroseconds(tone);
    }
}

void playMelody()
{
    if(state == false) // Ramp Up Note (Attack)
    {
    VOLUME += mult;
    playTone(melody[note], attack); 
    }
  
    if(state == true) // Ramp Down Note (release)
    {    
    VOLUME -= mult;
    playTone(melody[note], release);  
    }
    
    if(VOLUME >= velocity)
    {     
    playTone(melody[note], sustain);   
    state = true;
    } 
  
    if(VOLUME <= 0) 
    {    
    delay(gap);
    note++;
    state = false;
    } 
       
    if (note >= 8) // If last note in sequence is reached then reset to the begining
    {
    note = 0; 
    }   
}

///////////////////////
// Functions for LCD //
///////////////////////
        
void clearLCD()
{
Serial.print(0xFE, BYTE); // Command Flag
Serial.print(0x01, BYTE); // Clear Command
}

void selectLineOne()
{ 
Serial.print(0xFE, BYTE); // Command Flag
Serial.print(128, BYTE);  // puts the cursor at line 0 char 0.
}

void selectLineTwo()
{  
Serial.print(0xFE, BYTE); // Command Flag
Serial.print(192, BYTE);  // puts the cursor at line 2 char 0.
}

void setupLCD()
{
// Serial.print(0x7C, BYTE);   // Command Flag
// Serial.print(0x09, BYTE);   // Splash Screen Toggle on/off

// Serial.print(0x7C, BYTE); // Command Flag
// Serial.print(0x0C, BYTE); // Display On

// Serial.print(0x7C, BYTE); // Command Flag
// Serial.print(0x0D, BYTE); // Baud Rate: 9600 

// Serial.print(0x7C, BYTE); // Command Flag
// Serial.print(157, BYTE); // Backlight On

Serial.print(0x07, BYTE); // Command Flag
Serial.print(18, BYTE);  // reset LCD

clearLCD(); 
selectLineOne();
}


[glow]float fscale( float originalMin, float originalMax, float newBegin, float
newEnd, float inputValue, float curve){

  float OriginalRange = 0;
  float NewRange = 0;
  float zeroRefCurVal = 0;
  float normalizedCurVal = 0;
  float rangedValue = 0;
  boolean invFlag = 0;

  // condition curve parameter
  // limit range

  if (curve > 10) curve = 10;
  if (curve < -10) curve = -10;

  curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output 
  curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function

  /*
   Serial.println(curve * 100, DEC);   // multply by 100 to preserve resolution  
   Serial.println(); 
   */

  // Check for out of range inputValues
  if (inputValue < originalMin) {
    inputValue = originalMin;
  }
  if (inputValue > originalMax) {
    inputValue = originalMax;
  }

  // Zero Refference the values
  OriginalRange = originalMax - originalMin;

  if (newEnd > newBegin){ 
    NewRange = newEnd - newBegin;
  }
  else
  {
    NewRange = newBegin - newEnd; 
    invFlag = 1;
  }

  zeroRefCurVal = inputValue - originalMin;
  normalizedCurVal  =  zeroRefCurVal / OriginalRange;   // normalize to 0 - 1 float

  /*
  Serial.print(OriginalRange, DEC);  
   Serial.print("   ");  
   Serial.print(NewRange, DEC);  
   Serial.print("   ");  
   Serial.println(zeroRefCurVal, DEC);  
   Serial.println();  
   */

  // Check for originalMin > originalMax  - the math for all other cases i.e. negative numbers seems to work out fine 
  if (originalMin > originalMax ) {
    return 0;
  }

  if (invFlag == 0){
    rangedValue =  (pow(normalizedCurVal, curve) * NewRange) + newBegin;

  }
  else     // invert the ranges
  {   
    rangedValue =  newBegin - (pow(normalizedCurVal, curve) * NewRange); 
  }

  return rangedValue;
}
[/glow]

but I can't seem to get it working.

Geez, that's a real shame.

Cheers in advance

Your welcome.

Now, if you would like to tell us what is not working, we might be able to help. What is the program doing that you do not want it to do, or not doing that you want it to do?

I've been looking at using http://www.arduino.cc/playground/Main/Fscale but I can't seem to get it working.

fscale (in particular) isn't working for me, the volume won't increment - if i press up it just won't budge the value.

I just want a logarithmic curve applied to the analogWrite function so the volume increases smoothly, if I just increase it in a linear fashion the volume leaps up too quickly and then hardly adjusts at all over the remaining steps.

mainVOLUME = fscale(0, 255, 0.0, 255.0, mainVOLUME, -2.0);

You are using an int where fscale expects a float (which is OK), and overwriting the input with the output (which is not OK).

If the input is 0, the output will be 0.00000, which will be truncated to 0.

If the input is 1, the output will be 0.045, which will be truncated to 0.

Create a new (local) float variable to hold the output, and pass that value to digitalWrite.

Cheers dude, thanks for looking through my code :sunglasses: