My build progress

Tricky! Carry on then…

6 Likes

I have actually done 75% of this but guess what I am waiting on to arrive…yup a Nano.

I need to order a 8 segment display as well actually, I coded against a OLED as I had one but not a LED.

I re-wrote the “calculations” part to give Note + cents, though it’s using floats now. The way that LEDs are lit would be better done using bit masking I just haven’t gotten round to it as I want to test the tuner code first. The tuner is in a cpp/h file combo can can be used by what ever you want to.

Obviously the result will only be as good as the frequency you give it and octaves 0-3 having integer frequencies will affect the result more and more as the note gets lower.

4 Likes

Here it is…
Confirmed working on a '168 nano by @ChristianBloch .

It is designed for the original PCB.
If you change some of the outputs so all the 7seg+dot are on one port, you can obviously output the whole 8 bits in a single instruction.
Same for the 3 ‘accuracy’ LEDs.

// https://www.instructables.com/member/amandaghassaei/

// Changelog
// Code origin Sam / LookMumNoComputer and Amandaghassaei
// 9 Jan 2020: Jos Bouten aka Zaphod B: 
// - put frequencies in a table and simplified controlling the led display
// - put strings in flash memory to use less program memory space.

// 18 Jan 2020
// Added test of clipping led.

// 29 Febr 2020
// Added a switch mechanism to the code to make it easy to use either a common anode or common
// cathode LED-display.
// Set the const LED_DISPLAY_TYPE to the type of LED display you use (COMMON_ANODE or COMMON_CATHODE).

// 26 Jan 2021 EricM (ematecki@hotmail.com)
// clean up :
// - new note table
// - use dichotomy
// - replaced strings with bits

// 28 Mai 2022 EricM (ematecki@hotmail.com)
// fixed some hexadecimal numbers which should have been binary (0x -> 0b) oops

/*
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
*/

//
// uncomment the one matching your 7seg display
//
#define  COMMON_ANODE
//#define  COMMON_CATHODE

// Uncomment the following #define if you want to check the 7-segment display
// 3 signalling LEDs and clipping LED continuously for testing purposes.
// Leave it commented out if you want to run the tuner.

//#define  LED_TEST

// DO NOT CHANGE ANYTHING BELOW THIS LINE
//-------------------------------------------------------------------------------------------
// Accuracy LEDs
const int  LED3 = 18;
const int  LED4 = 19;
const int  LED5 = 17;

const int  ACC_LOW    = 0b100;
const int  ACC_SPOTON = 0b010;
const int  ACC_HIGH   = 0b001;


// 7 segment display output pins;

const int  LEDA = 8;
const int  LEDB = 9;
const int  LEDC = 4;
const int  LEDD = 3;
const int  LEDE = 2;
const int  LEDF = 7;
const int  LEDG = 6;
const int  LEDDP = 5;

const int  CLIPPING_LED = 13;


//=============================================================== frequency determination code
// Data storage variables.
byte  newData = 0;
byte  prevData = 0;

// Freq variables.
unsigned int  period;

#define  HALF_SAMPLE_VALUE  127
#define  TIMER_RATE         38462
#define  TIMER_RATE_10      (TIMER_RATE * 10)

// Data storage variables.
unsigned int  time = 0;   // Keeps time and sends values to store in timer[] occasionally.
#define  BUFFER_SIZE  10
int  timer[BUFFER_SIZE];  // Sstorage for timing of events.
int  slope[BUFFER_SIZE];  // Storage for slope of events.
unsigned int  totalTimer; // Used to calculate period.
byte  index = 0;   // Current storage index.
int  maxSlope = 0; // Used to calculate max slope as trigger point.
int  newSlope;     // Storage for incoming slope data.

// Variables for decided whether you have a match.
#define  MAX_NO_MATCH_VALUE  9
byte  noMatch = 0;  // Counts how many non-matches you've received to reset variables if it's been too long.
byte  slopeTol = 3; // Slope tolerance - adjust this if you need.
int  timerTol = 10; // Timer tolerance - adjust this if you need.

// Variables for amp detection.
unsigned int  ampTimer = 0;
byte  maxAmp = 0;
byte  checkMaxAmp;
byte  ampThreshold = 30; // Raise if you have a very noisy signal.
long  clippingTimer = 0;

// Clipping indicator variables.
boolean  clipping = true;
#define  CLIPPING_TIME  (5 * TIMER_RATE) // This should amount to 2 seconds.


void
reset()   // Clear out some variables.
{
  index = 0;    // Reset index.
  noMatch = 0;  // Reset match counter.
  maxSlope = 0; // Reset slope.
}


void
SetupISR()
{
  cli(); // Disable interrupts.

  // Set up continuous sampling of analog pin 0.

  // Clear ADCSRA and ADCSRB registers.
  ADCSRA = 0;
  ADCSRB = 0;

  ADMUX |= (1 << REFS0); // Set reference voltage.
  ADMUX |= (1 << ADLAR); // Left align the ADC value- so we can read highest 8 bits from ADCH register only

  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // Set ADC clock with 32 prescaler -> 16mHz / 32 = 500kHz.
  ADCSRA |= (1 << ADATE); // Enable auto trigger.
  ADCSRA |= (1 << ADIE);  // Enable interrupts when measurement complete.
  ADCSRA |= (1 << ADEN);  // Enable ADC.
  ADCSRA |= (1 << ADSC);  // Start ADC measurements.

  sei(); // Enable interrupts.
}


ISR( ADC_vect )       // When new ADC value ready.
{
  PORTB &= B11101111; // Set pin 12 low.
  prevData = newData; // Store previous value.
  newData = ADCH;     // Get value from A0.
  if( prevData < HALF_SAMPLE_VALUE  &&  newData >= HALF_SAMPLE_VALUE ) // if increasing and crossing midpoint
  {
    newSlope = newData - prevData; // Calculate slope
    if( abs(newSlope - maxSlope) < slopeTol ) // If slopes are ==
    {
      // Record new data and reset time.
      slope[index] = newSlope;
      timer[index] = time;
      time = 0;
      if( index == 0 ) // New max slope just reset.
      {
        PORTB |= B00010000; // Set pin 12 high.
        noMatch = 0;
        index++; // Increment index.
      }
      else if( abs(timer[0] - timer[index]) < timerTol  &&  abs(slope[0] - newSlope) < slopeTol ) //if timer duration and slopes match
      {
        // Sum timer values.
        totalTimer = 0;
        for( byte  i = 0; i < index; ++i )
        {
          totalTimer += timer[i];
        }
        period = totalTimer; // Set period.
        // Reset new zero index values to compare with.
        timer[0] = timer[index];
        slope[0] = slope[index];
        index = 1; // Set index to 1.
        PORTB |= B00010000; // Set pin 12 high.
        noMatch = 0;
      }
      else
      { // Crossing midpoint but not match.
        index++; // Increment index.
        if( index > BUFFER_SIZE - 1 )
        {
          reset();
        }
      }
    }
    else if( newSlope > maxSlope ) // If new slope is much larger than max slope.
    {
      maxSlope = newSlope;
      time = 0; // Reset clock.
      noMatch = 0;
      index = 0; // Reset index.
    }
    else // Slope not steep enough.
    {
      noMatch++; // Increment no match counter.
      if( noMatch > MAX_NO_MATCH_VALUE )
      {
        reset();
      }
    }
  }

  if( newData == 0  ||  newData == 1023 ) // If clipping
  {
    PORTB |= B00100000; // set pin 13 high, i.e. turn on clipping indicator led.
    clipping = true; // Currently clipping.
  }

  time++; // Increment timer at rate of 38.5kHz
  clippingTimer++;
  if( clippingTimer > CLIPPING_TIME )
  {
    PORTB &= B11011111; // Set pin 13 low, i.e. turn off clipping indicator led.
    clipping = false;   // Currently not clipping.
    clippingTimer = 0;
  }

  ampTimer++; // Increment amplitude timer.
  if( abs(HALF_SAMPLE_VALUE - ADCH) > maxAmp )
  {
    maxAmp = abs(HALF_SAMPLE_VALUE-ADCH);
  }
  if( ampTimer == 1000 )
  {
    ampTimer = 0;
    checkMaxAmp = maxAmp;
    maxAmp = 0;
  }
}


//============================================================================================ user interface


struct  cFreq4
{
    /// the four frequencies of the range (in Hz*10)
    uint16_t   mLowest, mLow, mHigh, mHighest;
    char  mNote;    ///< C c D d E F f G g A a B  lower case denotes #
    char  mOctave;  ///< number of the octave (not used 'coz we only have one digit display...)
};

static  const cFreq4  sgNotes[] PROGMEM =
{
  {    159,    162,    164,    167, 'C', 0 }, //    16.352 Hz
  {    168,    172,    174,    177, 'c', 0 }, //    17.324 Hz
  {    178,    182,    184,    188, 'D', 0 }, //    18.354 Hz
  {    189,    193,    195,    199, 'd', 0 }, //    19.445 Hz
  {    200,    204,    207,    211, 'E', 0 }, //    20.602 Hz
  {    212,    216,    219,    224, 'F', 0 }, //    21.827 Hz
  {    225,    229,    232,    237, 'f', 0 }, //    23.125 Hz
  {    238,    243,    246,    251, 'G', 0 }, //    24.500 Hz
  {    252,    257,    261,    266, 'g', 0 }, //    25.957 Hz
  {    267,    272,    277,    282, 'A', 0 }, //    27.500 Hz
  {    283,    289,    293,    299, 'a', 0 }, //    29.135 Hz
  {    300,    306,    311,    317, 'B', 0 }, //    30.868 Hz
  {    318,    324,    329,    336, 'C', 1 }, //    32.703 Hz
  {    337,    343,    349,    356, 'c', 1 }, //    34.648 Hz
  {    357,    364,    370,    377, 'D', 1 }, //    36.708 Hz
  {    378,    385,    392,    399, 'd', 1 }, //    38.891 Hz
  {    400,    408,    415,    423, 'E', 1 }, //    41.203 Hz
  {    424,    432,    440,    448, 'F', 1 }, //    43.654 Hz
  {    449,    458,    466,    475, 'f', 1 }, //    46.249 Hz
  {    476,    485,    494,    503, 'G', 1 }, //    48.999 Hz
  {    504,    514,    523,    533, 'g', 1 }, //    51.913 Hz
  {    534,    545,    554,    565, 'A', 1 }, //    55.000 Hz
  {    566,    577,    587,    599, 'a', 1 }, //    58.270 Hz
  {    600,    611,    622,    634, 'B', 1 }, //    61.735 Hz
  {    635,    648,    659,    672, 'C', 2 }, //    65.406 Hz
  {    673,    686,    699,    712, 'c', 2 }, //    69.296 Hz
  {    713,    727,    740,    755, 'D', 2 }, //    73.416 Hz
  {    756,    770,    784,    800, 'd', 2 }, //    77.782 Hz
  {    801,    816,    831,    847, 'E', 2 }, //    82.407 Hz
  {    848,    865,    881,    898, 'F', 2 }, //    87.307 Hz
  {    899,    916,    933,    951, 'f', 2 }, //    92.499 Hz
  {    952,    971,    988,   1008, 'G', 2 }, //    97.999 Hz
  {   1009,   1028,   1047,   1068, 'g', 2 }, //   103.826 Hz
  {   1069,   1089,   1110,   1131, 'A', 2 }, //   110.000 Hz
  {   1132,   1154,   1176,   1199, 'a', 2 }, //   116.541 Hz
  {   1200,   1223,   1246,   1270, 'B', 2 }, //   123.471 Hz
  {   1271,   1296,   1320,   1345, 'C', 3 }, //   130.813 Hz
  {   1346,   1373,   1398,   1426, 'c', 3 }, //   138.591 Hz
  {   1427,   1454,   1482,   1510, 'D', 3 }, //   146.832 Hz
  {   1511,   1541,   1570,   1600, 'd', 3 }, //   155.563 Hz
  {   1601,   1632,   1663,   1695, 'E', 3 }, //   164.814 Hz
  {   1696,   1729,   1762,   1796, 'F', 3 }, //   174.614 Hz
  {   1797,   1832,   1867,   1903, 'f', 3 }, //   184.997 Hz
  {   1904,   1941,   1978,   2016, 'G', 3 }, //   195.998 Hz
  {   2017,   2057,   2096,   2136, 'g', 3 }, //   207.652 Hz
  {   2137,   2179,   2220,   2263, 'A', 3 }, //   220.000 Hz
  {   2264,   2308,   2352,   2398, 'a', 3 }, //   233.082 Hz
  {   2399,   2446,   2492,   2541, 'B', 3 }, //   246.942 Hz
  {   2542,   2591,   2641,   2692, 'C', 4 }, //   261.626 Hz
  {   2693,   2745,   2798,   2852, 'c', 4 }, //   277.183 Hz
  {   2853,   2909,   2964,   3022, 'D', 4 }, //   293.665 Hz
  {   3023,   3081,   3140,   3201, 'd', 4 }, //   311.127 Hz
  {   3202,   3265,   3327,   3392, 'E', 4 }, //   329.628 Hz
  {   3393,   3459,   3525,   3594, 'F', 4 }, //   349.228 Hz
  {   3595,   3664,   3735,   3807, 'f', 4 }, //   369.994 Hz
  {   3808,   3882,   3957,   4034, 'G', 4 }, //   391.995 Hz
  {   4035,   4113,   4192,   4274, 'g', 4 }, //   415.305 Hz
  {   4275,   4358,   4442,   4528, 'A', 4 }, //   440.000 Hz
  {   4529,   4617,   4706,   4797, 'a', 4 }, //   466.164 Hz
  {   4798,   4892,   4986,   5083, 'B', 4 }, //   493.883 Hz
  {   5084,   5182,   5282,   5385, 'C', 5 }, //   523.251 Hz
  {   5386,   5491,   5596,   5705, 'c', 5 }, //   554.365 Hz
  {   5706,   5817,   5929,   6044, 'D', 5 }, //   587.330 Hz
  {   6045,   6163,   6282,   6404, 'd', 5 }, //   622.254 Hz
  {   6405,   6529,   6655,   6785, 'E', 5 }, //   659.255 Hz
  {   6786,   6918,   7051,   7188, 'F', 5 }, //   698.456 Hz
  {   7189,   7329,   7470,   7616, 'f', 5 }, //   739.989 Hz
  {   7617,   7765,   7915,   8069, 'G', 5 }, //   783.991 Hz
  {   8070,   8227,   8385,   8548, 'g', 5 }, //   830.609 Hz
  {   8549,   8716,   8884,   9057, 'A', 5 }, //   880.000 Hz
  {   9058,   9234,   9412,   9595, 'a', 5 }, //   932.328 Hz
  {   9596,   9783,   9972,  10166, 'B', 5 }, //   987.767 Hz
#if 0 // frqeuncy determination code cannot handle this high a frequency ?
  {  10167,  10365,  10565,  10771, 'C', 6 }, //  1046.502 Hz
  {  10772,  10981,  11194,  11411, 'c', 6 }, //  1108.731 Hz
  {  11412,  11634,  11859,  12090, 'D', 6 }, //  1174.659 Hz
  {  12091,  12326,  12564,  12809, 'd', 6 }, //  1244.508 Hz
  {  12810,  13059,  13312,  13570, 'E', 6 }, //  1318.510 Hz
  {  13571,  13835,  14103,  14377, 'F', 6 }, //  1396.913 Hz
  {  14378,  14658,  14942,  15232, 'f', 6 }, //  1479.978 Hz
  {  15233,  15530,  15830,  16138, 'G', 6 }, //  1567.982 Hz
  {  16139,  16453,  16772,  17098, 'g', 6 }, //  1661.219 Hz
  {  17099,  17431,  17769,  18115, 'A', 6 }, //  1760.000 Hz
  {  18116,  18468,  18826,  19192, 'a', 6 }, //  1864.655 Hz
  {  19193,  19566,  19945,  20333, 'B', 6 }, //  1975.533 Hz
  {  20334,  20730,  21132,  21542, 'C', 7 }, //  2093.005 Hz
  {  21543,  21962,  22388,  22823, 'c', 7 }, //  2217.461 Hz
  {  22824,  23268,  23719,  24181, 'D', 7 }, //  2349.318 Hz
  {  24182,  24652,  25130,  25618, 'd', 7 }, //  2489.016 Hz
  {  25619,  26118,  26624,  27142, 'E', 7 }, //  2637.020 Hz
  {  27143,  27671,  28208,  28756, 'F', 7 }, //  2793.826 Hz
  {  28757,  29316,  29885,  30466, 'f', 7 }, //  2959.955 Hz
  {  30467,  31059,  31662,  32278, 'G', 7 }, //  3135.963 Hz
  {  32279,  32906,  33545,  34197, 'g', 7 }, //  3322.438 Hz
  {  34198,  34863,  35540,  36230, 'A', 7 }, //  3520.000 Hz
  {  36231,  36936,  37653,  38385, 'a', 7 }, //  3729.310 Hz
  {  38386,  39132,  39892,  40667, 'B', 7 }, //  3951.066 Hz
  {  40668,  41459,  42264,  43086, 'C', 8 }, //  4186.009 Hz
  {  43087,  43924,  44777,  45648, 'c', 8 }, //  4434.922 Hz
  {  45649,  46536,  47440,  48362, 'D', 8 }, //  4698.636 Hz
  {  48363,  49303,  50261,  51238, 'd', 8 }, //  4978.032 Hz
  {  51239,  52235,  53250,  54285, 'E', 8 }, //  5274.041 Hz
  {  54286,  55341,  56416,  57513, 'F', 8 }, //  5587.652 Hz
  {  57514,  58632,  59771,  60933, 'f', 8 }, //  5919.911 Hz
  {  60934,  62118,  63325,  64556, 'G', 8 }, //  6271.927 Hz
// uint16_t => overflow !
  {  64557,  65812,  67091,  68395, 'g', 8 }, //  6644.875 Hz
  {  68396,  69726,  71080,  72462, 'A', 8 }, //  7040.000 Hz
  {  72463,  73872,  75307,  76771, 'a', 8 }, //  7458.620 Hz
  {  76772,  78264,  79785,  81336, 'B', 8 }, //  7902.133 Hz
  {  81337,  82918,  84529,  86172, 'C', 9 }, //  8372.018 Hz
  {  86173,  87849,  89555,  91297, 'c', 9 }, //  8869.844 Hz
  {  91298,  93072,  94881,  96725, 'D', 9 }, //  9397.273 Hz
  {  96726,  98607, 100523, 102477, 'd', 9 }, //  9956.063 Hz
  { 102478, 104470, 106500, 108571, 'E', 9 }, // 10548.082 Hz
  { 108572, 110682, 112833, 115027, 'F', 9 }, // 11175.303 Hz
  { 115028, 117264, 119543, 121867, 'f', 9 }, // 11839.822 Hz
  { 121868, 124237, 126651, 129113, 'G', 9 }, // 12543.854 Hz
  { 129114, 131624, 134182, 136791, 'g', 9 }, // 13289.750 Hz
  { 136792, 139451, 142161, 144925, 'A', 9 }, // 14080.000 Hz
  { 144926, 147743, 150614, 153542, 'a', 9 }, // 14917.240 Hz
  { 153543, 156528, 159570, 162673, 'B', 9 }, // 15804.266 Hz
#endif
};

static  const int  sgNoteCount = sizeof sgNotes/sizeof sgNotes[0];
static  uint16_t  sgLowestFrequency = 0;
static  uint16_t  sgHighestFrequency = 65535;

void
setup()
{
  pinMode( LED3, OUTPUT );
  pinMode( LED4, OUTPUT );
  pinMode( LED5, OUTPUT );

  pinMode( LEDA, OUTPUT );
  pinMode( LEDB, OUTPUT );
  pinMode( LEDC, OUTPUT );
  pinMode( LEDD, OUTPUT );
  pinMode( LEDE, OUTPUT );
  pinMode( LEDF, OUTPUT );
  pinMode( LEDG, OUTPUT );
  pinMode( LEDDP, OUTPUT );

  Serial.begin( 9600 );

  sgLowestFrequency = pgm_read_word_near( &sgNotes[0].mLowest );
  sgHighestFrequency = pgm_read_word_near( &sgNotes[sgNoteCount-1].mHighest );

  SetupISR();

  // Run a test of all leds.
#ifndef LED_TEST
  TestLedsIndividually( 50 );
  TestMusicalChars( 50 );
  TestLedsIndividually( 50 );
  TestMusicalChars( 50 );
#endif
}


#ifdef LED_TEST
void
loop()//EM:
{  
  TestLedsIndividually( 500 );
  TestMusicalChars( 500 );
}
#else
void
loop()//EM:
{
  uint16_t  frequency = TIMER_RATE_10 / period; // Timer rate with an extra zero/period.

// check for out of bounds freq
  if( frequency < sgLowestFrequency )
  {
    ShowCharacter( '<' );
    return;
  }
  if( frequency > sgHighestFrequency )
  {
    ShowCharacter( '>' );
    return;
  }

// dichotomy to find note
  int  begin = 0;
  int  end = sgNoteCount-1;

  while( begin != end )
  {
    int  middle = (begin + end) >> 1;
    if( middle == begin )
    {
    // we found the note, now test its accuracy
      if( frequency < pgm_read_word_near( &sgNotes[middle].mLow ) )
        SetAccuracyLeds( ACC_LOW );
      else if( frequency > pgm_read_word_near( &sgNotes[middle].mHigh ) )
        SetAccuracyLeds( ACC_HIGH );
      else
        SetAccuracyLeds( ACC_SPOTON );

      ShowCharacter( pgm_read_byte_near( &sgNotes[middle].mNote ) );
      break;
    }
    if( frequency < pgm_read_word_near( &sgNotes[middle].mLowest ) )
      end = middle;
    else
      begin = middle;
  }

  delay( 70 );
//  Serial.print(frequency / 10);
//  Serial.println(F("Hz"));
}
#endif


void
TestLedsIndividually( int  iDelay )//EM:
{
  // test 7-seg LEDs
  int  bits = 0b10000000;
  for( int  i = 0; i < 8; ++i )
  {
    Set7Segment( bits );
    delay( iDelay );
    bits >>= 1;
  }
  Set7Segment( 0 );

  // test accuracy LEDs
  SetAccuracyLeds( ACC_LOW );
  delay( iDelay );
  SetAccuracyLeds( ACC_SPOTON );
  delay( iDelay );
  SetAccuracyLeds( ACC_HIGH );
  delay( iDelay );
  SetAccuracyLeds( 0 );
}


void
TestMusicalChars( int  iDelay )//EM:
{
  const char*  iChars = "C c D d E F f G g A a B ";
  int  n = strlen( iChars );
  for( int  i = 0; i < n; ++i )
  {
    ShowCharacter( iChars[i] );
    delay( iDelay );
  }
}


void
SetAccuracyLeds( int  iAccuracy )//EM:
{
  //TODO: decoding is wrong because I haven't yet decided what it should be ! ============================================
  // Decode led pattern and switch on/off the leds.
#ifdef COMMON_ANODE  //TODO: just be sure it should not be !defined()  ===================================================
  iAccuracy = ~iAccuracy;
#endif
  digitalWrite( LED3, (iAccuracy & ACC_LOW   ) ? HIGH : LOW );
  digitalWrite( LED4, (iAccuracy & ACC_SPOTON) ? HIGH : LOW );
  digitalWrite( LED5, (iAccuracy & ACC_HIGH  ) ? HIGH : LOW );
//  digitalWrite( CLIPPING_LED, (iBits & 0b00010000) ? HIGH : LOW );
}


//bits ABCDEFG.
void
Set7Segment( int  iBits )//EM:
{
  // Decode 7-seg pattern and switch on/off the leds.
#ifdef COMMON_ANODE  //TODO: just be sure it should not be !defined()  ===================================================
  iBits = ~iBits;
#endif
  digitalWrite( LEDA,  (iBits & 0b10000000) ? HIGH : LOW );
  digitalWrite( LEDB,  (iBits & 0b01000000) ? HIGH : LOW );
  digitalWrite( LEDC,  (iBits & 0b00100000) ? HIGH : LOW );
  digitalWrite( LEDD,  (iBits & 0b00010000) ? HIGH : LOW );
  digitalWrite( LEDE,  (iBits & 0b00001000) ? HIGH : LOW );
  digitalWrite( LEDF,  (iBits & 0b00000100) ? HIGH : LOW );
  digitalWrite( LEDG,  (iBits & 0b00000010) ? HIGH : LOW );
  digitalWrite( LEDDP, (iBits & 0b00000001) ? HIGH : LOW );
}


//   AAAA
//  F    B
//  F    B
//  F    B
//   GGGG
//  E    C
//  E    C
//  E    C
//   DDDD
//        DP
void
ShowCharacter( char  iChar )//EM:
{
  switch( iChar )
  {
                            //ABCDEFG.
    case ' ':  Set7Segment( 0b00000000 );  return; // off
    case 'A':  Set7Segment( 0b11101110 );  return;
    case 'a':  Set7Segment( 0b11101111 );  return;
    case 'B':  Set7Segment( 0b00111110 );  return;
    case 'C':  Set7Segment( 0b10011100 );  return;
    case 'c':  Set7Segment( 0b10011101 );  return;
    case 'D':  Set7Segment( 0b01111010 );  return;
    case 'd':  Set7Segment( 0b01111011 );  return;
    case 'E':  Set7Segment( 0b10011110 );  return;
    case 'F':  Set7Segment( 0b10001110 );  return;
    case 'f':  Set7Segment( 0b10001111 );  return;
    case 'G':  Set7Segment( 0b10111110 );  return;
    case 'g':  Set7Segment( 0b10111111 );  return;
    case '<':  Set7Segment( 0b00001110 );  return; // freq too low
    case '>':  Set7Segment( 0b01100010 );  return; // freq too high
    default:   Set7Segment( 0b00000001 );  return;
  }
}

// test pattern : (E-G-A-G-D-C-D-E)
4 Likes

Yes, this is a life saver! Are you going to put it on GitHub?

1 Like

Yes it is planned… but my plans always take too long to become reality :frowning:
At least, now it is here so everybody can pick it up until I post it on github (or rather gitlab where I already have an account).

1 Like

If you don’t like the way the characters are displayed, just change the bits in the ShowCharacter() function.
I know @ChristianBloch did change some of them.

1 Like

Reading wondering round Aldi and that looks nice. The common anode handling is a nice use of binary stuff which is something I’m having to re learn after years of .net!

Could use the DP to show a sharp which is how my old tuner did it.

2 Likes


some finished projects . some pcb and face plate sets I got during their last sale .

12 Likes

Does the Berlin School have a built in clock or does it need an external one?

2 Likes

I bodged up a test setup for the maths+ OLED tuner in my lunch. iPad puts out 2v peak to peak into a non inverting op amp to get to 4.7v ish then it’s offset to centre on 2.5v so the arduino is happy (all in the original instructable).

It works but it’s accuracy is not perfect. The result based on the input is right but the calculated frequency is a tad off.

I’m feeding it a 440 hz A4 triangle here but you can see the measured frequency in the top corner is off by the amount of cents on the 2nd line.

In tune is ± 5 cents which would draw a box round the note. I seem to detect at about -11.58 or +8.21 which I am guessing is the resolution of the timer somewhere.

This is rendering at 10hz on the screen, but it will happily render way faster if unbound but that doesn’t make for easy viewing in a demo as it’s too twitchy.

When I get a chance I’ll swap to the table method Eric posted above and see if that’s more reliable.

Shame as I was hopeful on this :disappointed:

6 Likes

good question I really don’t know haven’t used it yet . I didn’t really buy it to use as a clock .

hard to tell from the description but I would say no , not that it really matters for how I plan on using it.

2 Likes

More I’ve looked at it more than once but I don’t have a clock to feed it! Getting started is the hardest part it seems.

3 Likes

Have an LFO with a square output? An oscillator that can be tuned really low? An audio output from somewhere that can be turned up a bit? But also, yeah, get or build some kind of clock module.

5 Likes

A 555 lfo makes an excellent clock

3 Likes


a pair of analog output joy sticks .

21 Likes

This is so awesome :rofl:

3 Likes

Built @jessecakeindustries glide Verified Stripboard Layouts! - #422 by jessecakeindustries - testing tomorrow… curious what it will do to audio signals as well as cv

5 Likes

Are they true “analog outputs” ?
I mean, you have CV ins and the hands move accordingy :stuck_out_tongue:

4 Likes

Allways a small original detail ! excellent :slight_smile:

1 Like

Just tested Eric’s method above and it’s far more stable. Though I seem to have picked up an off by one somewhere as it it’s showing an a (A#) not an A. I’ll dig into why, hopefully a copy paste error somewhere, but this seems to be the way forward.

The frequency here is x10 as per the original code though it would be easy to display as a decimal if desired.

3 Likes