We are hosting Hackster Cafe: Open Hardware Summit. Watch the stream live on Friday!We are hosting Hackster Cafe: Open Hardware Summit. Stream on Friday!
dxb_
Published © GPL3+

LInterp – A linear-interpolation PROGMEM array generator

Generate large interpolation/translation/lookup arrays in PROGMEM just using an include file on *any* Arduino board.

IntermediateFull instructions provided429
LInterp – A linear-interpolation PROGMEM array generator

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
SparkFun Rotary Potentiometer - 100k Ohm, Logarithmic (Panel Mount)
×1

Story

Read more

Schematics

Logarithmic potentiometer

Produces a non-linear voltage increase with rotation angle attached to an analog input

Code

PotUnLog.ino

Arduino
Example Arduino program using LInterp.h to linearise part of the range of a logarithmic potentiometer
/* PotUnLog.ino                         <<c) dxb 2022 */
/* Demo program for LInterp PROGMEM array generator   */
/* assumes Arduino IDE serial monitor is active       */

/* if the following library is omitted, LInterp will  */
/* declare and initialise the array in RAM instead    */
#include <avr/pgmspace.h> /* declare array in PROGMEM */

/* Example: linear transform for part of the range of */
/* a logarithmic potentiometer attached to an analog  */
/* input of (any) Arduino board. The ends of the pot  */
/* should be connected to (analog) +V and GND with    */
/* the pot sweep terminal connected to the input pin. */

/* first, define our desired pot output specs:        */
#define LogPotLinFrom 3.0   /* (arb units) */
#define LogPotLinTo   7.0   /* (arb units) */
#define LogPotScale   10.0  /* (arb units) */
/* define the array specs we need to retain */
#define LogPotLinOfs  1330L /* (mV) */
#define LogPotLinEnd  3500L /* (mV) */
#define LogPotRes     5     /* (ADC levels) */
/* define our particular Arduino card ADC specs:      */
#define PotChan       A0    /* (analog channel ID) */
#define AnalogRange   5000  /* (mV) (NOMINAL!) */
#define ADCLevels     1024  /* (ADC levels) */

/* next, give LInterp our pot specs for the array def */
/* all LI_.. defs will be deleted after the array dec */
#define LI_ARR  LParray     /* our xfrom array name   */
#define LI_CAL  ADCLevels   /* arr ordinate scale (L) */
#define LI_RNG  AnalogRange /* input map pt scale (L) */
#define LI_SCL  ( LogPotLinTo - LogPotLinFrom )
#define LI_OFS  LogPotLinFrom
#define LI_INT  LogPotRes   /* interpolation width    */

#define LI_P0   1330        /* (mV) pot voltage map   */
#define LI_P1   1570        /* assume regular spacing */
#define LI_P2   1810        /* by angular increments  */
#define LI_P3   2100        /* of pot rotation sweep  */
#define LI_P4   2370
#define LI_P5   2705
#define LI_P6   2955
#define LI_P7   3230        /* min of 4 points ...    */
#define LI_P8   3500        /* ... max of 32 points   */

#include "LInterp.h"        /* declare and init array */
/* ... repeat the declaration list for another array  */

/* define a macro to hide the PROGMEM array funtion   */
#define LogPotLinPart( i ) pgm_read_float( LParray + i )

float ReadPot() {      /* read pot level and scale it */
float interp_start, interp_end, pot_value;
int samps, index;
/* these are declared long to prevent int overflows:  */
long pot_mV, pot_ADClevel = 0;
/* take an average of several analog channel readings */
for ( samps = 0; samps < 20; samps++ ) {
  pot_ADClevel += analogRead( PotChan );
  delay( 10 );
  }
pot_ADClevel /= samps;
/* uncorrected log pot voltage from analog channel    */
pot_mV = pot_ADClevel * AnalogRange / ADCLevels;
/* check bounds of our linear part and scale outside  */
if ( pot_mV < LogPotLinOfs )   /* bottom end of range */
  pot_value = LogPotLinFrom * pot_mV / LogPotLinOfs;
else if ( pot_mV >= LogPotLinEnd )/* top end of range */
  pot_value = ( LogPotScale - LogPotLinTo ) *
    pot_mV / ( AnalogRange - LogPotLinEnd );
/* otherwise pot is within our transform array range  */
else {     /* subtract the array offset in ADC levels */
  pot_ADClevel -= LogPotLinOfs * ADCLevels / AnalogRange;
  /* divide by the interp size to gat the array index */
  index = pot_ADClevel / LogPotRes;
  /* get the array elt containing this pot ADC level  */
  interp_start = LogPotLinPart( index );
  /* get the next array elt to interpolate in-between */
  interp_end = LogPotLinPart( index + 1 );
  /* linear interpolation b/w bounding array entries  */
  pot_value = interp_start + ( interp_end - interp_start ) *
    ( pot_ADClevel % LogPotRes ) / LogPotRes;
  }
/* return the pot voltage encoded as a knob-position  */
return( pot_value );
}

void setup() {          /* check: print array values  */
int i = 0;
Serial.begin( 9600 );
Serial.println( "Press any key to start" );
while ( !Serial.available() ) delay( 100 );
while ( Serial.available() ) Serial.read();
Serial.print( "const float LParray[] PROGMEM = {\n" );
/* this is how to get the array size, in elements     */
while ( i < sizeof( LParray ) / sizeof( float ) ){
  Serial.print( LogPotLinPart( i ), 2 );
  /* print values in blocks of 15 */
  if ( ( i + 1 ) % 15 ) Serial.print( "\t" );
  else Serial.print( "\n" );
  i++;
  };
Serial.print( "}\nArray size: " );
Serial.println( i );
Serial.println( "press any key to continue" );
while ( !Serial.available() ) delay( 100 );
while ( Serial.available() ) Serial.read();
}

void loop() {
Serial.println( ReadPot(), 2 );
delay( 300 );
}

LInDev.h

C Header File
Generic outline header to virtualise a non-linear analog device as a linear function using the LInterp.h PROGMEM interpolation array generator
/* LInDev.h                             <<c) dxb 2022 */
/* prototype codes to produce linear output from a    */
/* non-linear analog device using the LInterp array   */
/* generator within the Arduino hardware platform     */

/* replace all occurrences of "DEV" in this file with */
/* a unique device identifier and change the filename */
/* to match. Comment out or delete definitions / code */
/* not relevant to your device definition or specs.   */

/* if the following library is omitted, LInterp will  */
/* declare and initialise the array in RAM instead    */
#include <avr/pgmspace.h> /* declare array in PROGMEM */

/* define our particular Arduino card ADC specs:      */
#define DEV_CHAN      A0    /* (analog channel) */
#define ANALOG_RNG    5000  /* (mV) */
#define ADC_LEV_RNG   1024  /* (ADC levels) */

/* analog device INPUT range definitions */
#define DEV_IN_MIN    0L    /* (mV) */
#define DEV_IN_MAX    5000L /* (mV) */

/* Analog device OUTPUT range definitions */
#define DEV_OUT_MIN   0.0   /* (arb units) */
#define DEV_OUT_MAX   1.0   /* (arb units) */
#define DEV_OUT_SCL   1.0   /* (arb units) */

/* retained LInterp array definitions */
#define DEV_INTERP    5     /* (ADC levels) */

/* temporary LInterp array definitions */
#define LI_ARR  DEV_LI_ARR  /* device array name */
#define LI_CAL  ADC_LEV_RNG
#define LI_RNG  ANALOG_RNG
#define LI_SCL  ( DEV_OUT_MAX - DEV_OUT_MIN )
#define LI_OFS  DEV_OUT_MIN
#define LI_INT  DEV_INTERP

/* optional processing definitions */
//#define LI_TYP  int   /* array (integer) elt type */
//#define LI_RAM        /* declare array in RAM */
//#define LI_VAR        /* make (RAM) array modifiable */

/* device ordinate map */
#define LI_P0   1000        /* (mV) */
#define LI_P1   1000
#define LI_P2   1000
#define LI_P3   1000
#define LI_P4   1000
#define LI_P5   1000
#define LI_P6   1000
#define LI_P7   1000
#define LI_P8   1000
#define LI_P9   1000
#define LI_P10  1000
#define LI_P11  1000
#define LI_P12  1000
#define LI_P13  1000
#define LI_P14  1000
#define LI_P15  1000
#define LI_P16  1000
#define LI_P17  1000
#define LI_P18  1000
#define LI_P19  1000
#define LI_P20  1000
#define LI_P21  1000
#define LI_P22  1000
#define LI_P23  1000
#define LI_P24  1000
#define LI_P25  1000
#define LI_P26  1000
#define LI_P27  1000
#define LI_P28  1000
#define LI_P29  1000
#define LI_P30  1000
#define LI_P31  1000

#include "LInterp.h"        /* declare and init array */

/* setup analog channel device */
/* CALL this function from setup() in the sketch */
void DEV_Setup( void ) {
/* ... your device setup code ... */
}

/* Array read macros */
#define DEV_ARRAY( i ) pgm_read_float( DEV_LI_ARR + i )
//#define DEV_ARRAY( i ) pgm_read_byte( DEV_LI_ARR + i )
//#define DEV_ARRAY( i ) pgm_read_int( DEV_LI_ARR + i )
//#define DEV_ARRAY( i ) pgm_read_long( DEV_LI_ARR + i )

/* Linearise supplied device output */
float DEV_Linear( long level ) {
float interp_start, interp_end;
long dev_mV, index;
dev_mV = level * ANALOG_RNG / ADC_LEV_RNG;
if ( dev_mV < DEV_IN_MIN )     /* bottom end of range */
  level = DEV_IN_MIN * ADC_LEV_RNG / ANALOG_RNG;
else if ( dev_mV >= DEV_IN_MAX )  /* top end of range */
  level = DEV_IN_MAX * ADC_LEV_RNG / ANALOG_RNG;
level -= (long) DEV_IN_MIN * ADC_LEV_RNG / ANALOG_RNG;
index = level / DEV_INTERP;
interp_start = DEV_ARRAY( index );
interp_end = DEV_ARRAY( index + 1 );
return ( interp_start + ( interp_end - interp_start ) *
    ( level % DEV_INTERP ) / DEV_INTERP );
}

/* read analog device and linearise output */
float DEV_Read() {
return( DEV_Linear( analogRead( DEV_CHAN ) ) );
}

/* read averaged analog device and linearise output */
float DEV_ReadAvg() {
int samps;
long ADClevel = 0;
/* take an average of several analog channel readings */
for ( samps = 0; samps < 20; samps++ ) {
  ADClevel += analogRead( DEV_CHAN );
  delay( 5 );
  }
return ( DEV_Linear( ADClevel / samps ) );
}

LInterp.h

C Header File
Arduino C-compiler pre-processor script to generate interpolation / translation / lookup arrays in PROGMEM
/* LInterp.h   Linear interpolation array generator     */
/* <<c) dxb 1982 - 2022                                 */

#if defined( LI_ELT )           /* generate array elt   */
#if LI_LEV < LI_BND && !defined( LI_EOI )
  #if ( LI_ELT <= LI_S( LI_LEV, LI_NXT ) + ( LI_LEV == LI_BND - 1 ) )
    ,(LI_TYP) ( LI_RND + LI_OFS + LI_SCL * ( LI_LEV +
      ( LI_ELT - LI_R( 0, LI_LEV ) + LI_P( 0, LI_LEV ) ) /
        LI_R( LI_LEV, LI_NXT ) ) / LI_BND )
  #else
    #define LI_EOI
  #endif
#endif
#undef LI_ELT

#elif defined( LI_LEV )         /* process intervals    */
#define LI_ELT 1
#include "LInterp.h"
#define LI_ELT 2
#include "LInterp.h"
#define LI_ELT 3
#include "LInterp.h"
#define LI_ELT 4
#include "LInterp.h"
#define LI_ELT 5
#include "LInterp.h"
#define LI_ELT 6
#include "LInterp.h"
#define LI_ELT 7
#include "LInterp.h"
#define LI_ELT 8
#include "LInterp.h"
#define LI_ELT 9
#include "LInterp.h"
#define LI_ELT 10
#include "LInterp.h"
#define LI_ELT 11
#include "LInterp.h"
#define LI_ELT 12
#include "LInterp.h"
#define LI_ELT 13
#include "LInterp.h"
#define LI_ELT 14
#include "LInterp.h"
#define LI_ELT 15
#include "LInterp.h"
#define LI_ELT 16
#include "LInterp.h"
#define LI_ELT 17
#include "LInterp.h"
#define LI_ELT 18
#include "LInterp.h"
#define LI_ELT 19
#include "LInterp.h"
#define LI_ELT 20
#include "LInterp.h"
#define LI_ELT 21
#include "LInterp.h"
#define LI_ELT 22
#include "LInterp.h"
#define LI_ELT 23
#include "LInterp.h"
#define LI_ELT 24
#include "LInterp.h"
#define LI_ELT 25
#include "LInterp.h"
#define LI_ELT 26
#include "LInterp.h"
#define LI_ELT 27
#include "LInterp.h"
#define LI_ELT 28
#include "LInterp.h"
#define LI_ELT 29
#include "LInterp.h"
#define LI_ELT 30
#include "LInterp.h"
#define LI_ELT 31
#include "LInterp.h"
#define LI_ELT 32     /* <- repeat these lines for more */
#include "LInterp.h"  /* <- interps, with inc elt idxs  */
/* ... */
#if LI_LEV < LI_BND && !defined( LI_EOI )
#error LInterp: Too many interps in one interval. Add mapping pts or decrease LI_INT
#endif
#undef LI_EOI
#undef LI_LEV
#undef LI_NXT

#else                           /* define array params  */
#if !defined( LI_P0 ) || !defined( LI_P1 ) || !defined( LI_P2 ) || !defined( LI_P3 )
#error LIinterp: define at least 4 mapping values LI_P0 to LI_Pnn (nn < 32)
#endif
#if !defined( LI_TYP )
#define LI_TYP  float           /* default array type   */
#define LI_RND  0               /* no rounding          */
#else                           /* all integer types!   */
#define LI_RND  0.5             /* for float-int convs  */
#endif
#if !defined( LI_ARR )
#define LI_ARR  LInterp         /* default array name   */
#endif
#if !defined ( LI_RNG )         /* input ordinate range */
#error LInterp: Define an ordinate mapping scale LI_RNG
#endif
#if !defined( LI_CAL )          /* array indexing scale */
#error LInterp: Define an array-base indexing scale LI_CAL
#endif
#if !defined( LI_INT )
#define LI_INT  1               /* interp interval      */
#endif
#if !defined( LI_SCL )
#define LI_SCL  1               /* array value scale    */
#endif
#if !defined( LI_OFS )
#define LI_OFS  0               /* array value offset   */
#endif
#if !defined( LI_VAR )
#define LI_VAR  const           /* array value offset   */
#endif

#if !defined( LI_P4 )
#define LI_BND  3
#elif !defined( LI_P5 )
#define LI_BND  4
#elif !defined( LI_P6 )
#define LI_BND  5
#elif !defined( LI_P7 )
#define LI_BND  6
#elif !defined( LI_P8 )
#define LI_BND  7
#elif !defined( LI_P9 )
#define LI_BND  8
#elif !defined( LI_P10 )
#define LI_BND  9
#elif !defined( LI_P11 )
#define LI_BND  10
#elif !defined( LI_P12 )
#define LI_BND  11
#elif !defined( LI_P13 )
#define LI_BND  12
#elif !defined( LI_P14 )
#define LI_BND  13
#elif !defined( LI_P15 )
#define LI_BND  14
#elif !defined( LI_P16 )
#define LI_BND  15
#elif !defined( LI_P17 )
#define LI_BND  16
#elif !defined( LI_P18 )
#define LI_BND  17
#elif !defined( LI_P19 )
#define LI_BND  18
#elif !defined( LI_P20 )
#define LI_BND  19
#elif !defined( LI_P21 )
#define LI_BND  20
#elif !defined( LI_P22 )
#define LI_BND  21
#elif !defined( LI_P23 )
#define LI_BND  22
#elif !defined( LI_P24 )
#define LI_BND  23
#elif !defined( LI_P25 )
#define LI_BND  24
#elif !defined( LI_P26 )
#define LI_BND  25
#elif !defined( LI_P27 )
#define LI_BND  26
#elif !defined( LI_P28 )
#define LI_BND  27
#elif !defined( LI_P29 )
#define LI_BND  28
#elif !defined( LI_P30 )
#define LI_BND  29
#elif !defined( LI_P31 )
#define LI_BND  30
/* repeat following defn block for more mapping points  */
/* increasing iterators as per sequence above           */
/* also add matching interval inits at end of file      */
#elif !defined( LI_P32 )
#define LI_BND  31
/* ... */
#else
#error LInterp: Too many mapping points defined. Add extra point defs to LInterp.h
#endif

#define LI_V( i ) ( LI_P##i )           /* compile pass */
#define LI_Q( i ) ( LI_V( i ) * 1L * LI_CAL )
#define LI_P( i, j ) ( ( LI_Q( j ) - LI_Q( i ) ) / ( 1L * LI_INT * LI_RNG ) )
#define LI_R( i, j ) ( (float) ( LI_Q( j ) - LI_Q( i ) ) / ( LI_INT * LI_RNG ) )
#define LI_S( i, j ) ( LI_P( 0, j ) - LI_P( 0, i ) )

#if defined( PROGMEM ) && !defined( LI_RAM )
const LI_TYP LI_ARR[] PROGMEM = { LI_OFS
#else
LI_VAR LI_TYP LI_ARR[] = { LI_OFS
#endif
#define LI_LEV  0
#define LI_NXT  1
#include "LInterp.h"
#define LI_LEV  1
#define LI_NXT  2
#include "LInterp.h"
#undef  LI_P1
#define LI_LEV  2
#define LI_NXT  3
#include "LInterp.h"
#undef  LI_P2
#define LI_LEV  3
#define LI_NXT  4
#include "LInterp.h"
#undef  LI_P3
#define LI_LEV  4
#define LI_NXT  5
#include "LInterp.h"
#undef  LI_P4
#define LI_LEV  5
#define LI_NXT  6
#include "LInterp.h"
#undef  LI_P5
#define LI_LEV  6
#define LI_NXT  7
#include "LInterp.h"
#undef  LI_P6
#define LI_LEV  7
#define LI_NXT  8
#include "LInterp.h"
#undef  LI_P7
#define LI_LEV  8
#define LI_NXT  9
#include "LInterp.h"
#undef  LI_P8
#define LI_LEV  9
#define LI_NXT  10
#include "LInterp.h"
#undef  LI_P9
#define LI_LEV  10
#define LI_NXT  11
#include "LInterp.h"
#undef  LI_P10
#define LI_LEV  11
#define LI_NXT  12
#include "LInterp.h"
#undef  LI_P11
#define LI_LEV  12
#define LI_NXT  13
#include "LInterp.h"
#undef  LI_P12
#define LI_LEV  13
#define LI_NXT  14
#include "LInterp.h"
#undef  LI_P13
#define LI_LEV  14
#define LI_NXT  15
#include "LInterp.h"
#undef  LI_P14
#define LI_LEV  15
#define LI_NXT  16
#include "LInterp.h"
#undef  LI_P15
#define LI_LEV  16
#define LI_NXT  17
#include "LInterp.h"
#undef  LI_P16
#define LI_LEV  17
#define LI_NXT  18
#include "LInterp.h"
#undef  LI_P17
#define LI_LEV  18
#define LI_NXT  19
#include "LInterp.h"
#undef  LI_P18
#define LI_LEV  19
#define LI_NXT  20
#include "LInterp.h"
#undef  LI_P19
#define LI_LEV  20
#define LI_NXT  21
#include "LInterp.h"
#undef  LI_P20
#define LI_LEV  21
#define LI_NXT  22
#include "LInterp.h"
#undef  LI_P21
#define LI_LEV  22
#define LI_NXT  23
#include "LInterp.h"
#undef  LI_P22
#define LI_LEV  23
#define LI_NXT  24
#include "LInterp.h"
#undef  LI_P23
#define LI_LEV  24
#define LI_NXT  25
#include "LInterp.h"
#undef  LI_P24
#define LI_LEV  25
#define LI_NXT  26
#include "LInterp.h"
#undef  LI_P25
#define LI_LEV  26
#define LI_NXT  27
#include "LInterp.h"
#undef  LI_P26
#define LI_LEV  27
#define LI_NXT  28
#include "LInterp.h"
#undef  LI_P27
#define LI_LEV  28
#define LI_NXT  29
#include "LInterp.h"
#undef  LI_P28
#define LI_LEV  29
#define LI_NXT  30
#include "LInterp.h"
#undef  LI_P29
#define LI_LEV  30      /* <- repeat this set of defs   */
#define LI_NXT  31      /* <- with increasing indices   */
#include "LInterp.h"    /* <- for further map defn pts  */
#undef  LI_P30          /* <- */
/* ... */
#undef  LI_P31          /* change to match LI_BND defn  */
};

#undef LI_P0
#undef LI_ARR
#undef LI_SCL
#undef LI_TYP
#undef LI_RND
#undef LI_RNG
#undef LI_CAL
#undef LI_INT
#undef LI_SCL
#undef LI_OFS
#undef LI_BND
#undef LI_RAM
#undef LI_VAR
#undef LI_V
#undef LI_Q
#undef LI_P
#undef LI_R
#undef LI_S
#undef LI_N
#undef LI_W
#endif

/* EOF LInterp.h */

Credits

dxb_
0 projects • 0 followers
Contact

Comments

Please log in or sign up to comment.