janux
Published © GPL3+

JX Wave Generator

Use Arduino, an OLED display and an AD9833 DDS module to create a useful wave generator for your lab.

IntermediateFull instructions provided1 hour6,479
JX Wave Generator

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
I haven't tried it but presumably you can use any Arduino model
×1
Display SH1106 I2C Oled 128x64 pixel (4 pin)
×1
AD9833 DDS Module
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
With this you need the soldering iron
×1
Rotary Encoder Module
For breadboard mounting this is better
×1
5V Relay Module (optional)
Optional, if there is no need to change the AC/DC output coupling mode. For breadboard mounting this is better
×1
Relay Reed 5V 500 Ohm
This would be best for a small signal but requires the soldering iron. With a 500 Ohm coil it can be connected directly to the Arduino digital pins (10 mA).
×1
Capacitor 10 µF
Capacitor 10 µF
×1
Capacitor 10 nF
Capacitor 10 nF
Polyester capacitor
×1
AC-DC 100-220V to 5V Step-Down Power Supply module
Optional, for powering the circuit when not connected via USB
×1

Software apps and online services

image2cpp
To convert your custom icons to hexadecimal format for PROGMEM

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
It is not essential if you want to test the project on a breadboard

Story

Read more

Schematics

EAGLE Schematics

Breadboard wiring

Display type updated

Code

JX_Wave_Generator_8.7.7.ino

C/C++
Version 8.7.7. Minor revision on 1/16/2021
/*
  Copyright (c) 2020 Janux
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.

  The configuration saving-loading code is a development of an original idea of the article
  "How to Load and Save Configurations on an Arduino" by Ragnar Ranyen Homb on the Norwegian Creation website.

*/

#include "JXWG_Defs.h"
#include "JXWG_Graphics.h"

void setup()   {

  //if you are using a simple encoder and 3x10K pullup resistors, apply this settings below
  pinMode(PinA, INPUT);
  pinMode(PinB, INPUT);
  pinMode(PinS, INPUT);

  //if you are using a simple encoder whitout 3x10K resistors use next tree row
  //pinMode(PinA, INPUT_PULLUP);
  //pinMode(PinB, INPUT_PULLUP);
  //pinMode(PinS, INPUT_PULLUP);

  //MOST PCB WELDED ENCODERS ALREADY HAVE PULLUP RESISTORS ON PIN A AND B BUT NOT ON SWITCH PIN
  //then use the settings below
  //pinMode(PinA, INPUT);
  //pinMode(PinB, INPUT);
  //pinMode(PinS, INPUT_PULLUP);

  digitalWrite(PinA, HIGH);
  digitalWrite(PinB, HIGH);
  digitalWrite(PinS, HIGH);

  pinMode(PinCoupling, OUTPUT);                  //Coupling Mode

  Encoder.setDebounceDelay(5);

  display.begin(SH1106_SWITCHCAPVCC, 0x3C);     //initialize with the I2C addr 0x3C (for the 128x64)
  display.clearDisplay();
  //display.setRotation(2);                     //uncomment this line if you want to mount the display upside down

  Wire.begin();                                 // join i2c bus as master
  TWBR = 5;                                     // freq=615kHz period=1.625uS

  //Assigns encoder switch push event to interrupt 1 Pin 3 of Arduino
  attachInterrupt(digitalPinToInterrupt(PinS), encoderSwitch, FALLING);

  DDS_Init();                                   //Initialize the DDS module;
  setConfig();                                  //Load config and set startup values

} //---> end setup()

void loop() {
  JX_WaveGenerator_MAIN();
}

//-----------------------------------------------------------------------------------------------------------------------------------------
// JX WaveGenerator MAIN function
//-----------------------------------------------------------------------------------------------------------------------------------------
void JX_WaveGenerator_MAIN() {

  byte encoderSpin = Encoder.rotate();                                             //Encoder rotation direction 1=CW, 2=CCW
  byte encoderLongPush = Encoder.pushLong(1000);                                   //encoder long push event
  long lStep = 0;                                                                  //current frequency step value
  long wTime = 600000; //10 min

  if (encoderPush) delay(250);

  if (encoderSpin) {
    cTime = millis();
  } else {
    if (millis() - cTime > wTime) {
      ScreenSaver();
    }
  }

  switch (mode) {

    case LOGARITHMIC: //0
      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode LOGARITHMIC: Encoder rotation change frequency in logaritmic step 1,10,100,1000,10000,100000 Hz
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {

        if (lFreq >= 1) {
          lStep = AutoStep(lFreq, encoderSpin);                             //Calculate logaritmic step
        } else if (_CouplingMode == OFF) {                                  //if coupling mode is set to OFF
          resetCouplingMode();                                              //set default coupling mode when Frequency is not 0
          encoderSpin = 0;                                                  //skip first spin
        }

        if (encoderSpin == CW && lFreq <= 999999 - lStep) {                 //spin CW increment the frequency
          lFreq += lStep;
        }
        if (encoderSpin == CCW && lFreq >= lStep + 1) {                     //spin CCW decrement the frequency
          lFreq -= lStep;
        }

        DDS_FrequencySet(lFreq, Wave[_WaveType]);                           //send the frequency value to DDS module
        displayFrequency(lFreq);                                            //send formatted freq to display
        lLastFreq = lFreq;                                                  //save current freq
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // workmode LOGARITHMIC: Encoder push switch to OPTIONS mode
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;                                                //Clear push flag
        drawSymbol(1);                                                      //draw arrow symbol
        selectIcon(0, WHITE);                                               //draw a border around first icon
        idx = 0; idy = 0;                                                   //reset pointers var
        mode = OPTIONS;                                                     //go to mode OPTIONS
      }

      break; //end mode LOGARITHMIC

    case SINGLEDIGIT: //1
      //-------------------------------------------------------------------------------------------------------------
      // submode SINGLEDIGIT: Encoder rotation move cursor left and right
      //-------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idx < MAXDIGIT + 2) idx++;            //clockwise increase pointer
        if (encoderSpin == CCW && idx > 0) idx--;                      //counterclockwise decrease pointer

        //------------------------------------------------------------------------------------------------------------
        // when idx is from 0 to 5 select frequency digits
        //------------------------------------------------------------------------------------------------------------
        if (idx >= 0 && idx < MAXDIGIT) {                               //if the current position is within the digits
          drawSymbol(0);                                                //draw up arrow
          selectDigit(idx);                                             //show cursor at current position and delete previous
          if (idx == 5) selectIcon(0, BLACK);                           //hide first icon selection
        }

        //------------------------------------------------------------------------------------------------------------
        // when idx is from 6 to 8 select icons
        //------------------------------------------------------------------------------------------------------------
        if (idx >= MAXDIGIT && idx <= MAXDIGIT + 2) {                   //if the current position is beyond the digits
          hideCursor(MAXDIGIT - 1);                                     //hide cursor at last digit
          drawSymbol(1);                                                //draw dn arrow
          selectIcon(idx - MAXDIGIT, WHITE);                            //select icon
        }
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // submode SINGLEDIGIT: Encoder push event
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;

        //-------------------------------------------------------------------------------------------------------------
        // if a digit from 0 to 5 are selected go to mode DIGITCHANGE
        //-------------------------------------------------------------------------------------------------------------
        if (idx <= MAXDIGIT - 1) {
          hideCursor(idx);                                                              //flash cursor
          delay(250);                                                                   //for
          selectDigit(idx);                                                             //visual confirmation
          drawSymbol(2);                                                                //draw turn icon
          mode = DIGITCHANGE;                                                           //change mode
        }
        //-------------------------------------------------------------------------------------------------------------
        // otherwise there is an icon selected then go to OPTIONS
        //-------------------------------------------------------------------------------------------------------------
        else {
          if (idx >= MAXDIGIT && idx <= MAXDIGIT + 2) {
            idy = idx - MAXDIGIT;
            selectOption(idy);
            idy = options[idy];
          }
        }
      }
      break; //end mode SINGLEDIGIT


    case SWEEP://2
      //----------------------------------------------------------------------------------------------------------------
      // workmode SWEEP: Encoder rotation move cursor left and right for option selection
      //----------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      if (encoderPush) {
        //-----------------------------------------------------------------------------------------------------------------------------------
        // workmode SWEEP: Encoder push go to OPTIONS, SWEEP OPTIONS or START/STOP sweep
        //-----------------------------------------------------------------------------------------------------------------------------------
        encoderPush = false;

        switch (idy) {
          case 0:
            lFreq = atol(Freq);
            selectOption(idy);
            idy = options[idy];
            delay(100);
            if (_WorkMode != 2) displayFrequency(lLastFreq);
            SweepReset();
            break;

          case 1:
            lFreq = atol(Freq);
            drawSymbol(9);
            drawSymbol(1);
            displaySweepIcons();
            selectIcon(0, WHITE);
            idy = 0;
            displayFrequency(_Sweep(idy));
            delay(100);
            SweepReset();
            mode = OPTSWEEP;
            break;

          case 2:
            //** sweepStatus: STILL 0 (never started), 1 BREAK, 2 PAUSE **
            if (sweepStatus == STILL || sweepStatus == PAUSE) {
              drawSymbol(3);                            //draw pause icon
              selectIcon(2, WHITE);                     //select icon
              FrequencySweep();                         //run sweep
            }
            else {                                      //if paused
              drawSymbol(4);                            //draw play icon
              sweepStatus = PAUSE;
            }
            break;
        }
      }
      if (sweepStatus == PAUSE) flashIcon(250);           //flashing pause text
      break; //end mode SWEEP

    case OPTIONS://3
      if (encoderLongPush) reset();

      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTIONS: Encoder spin select option to change (workmode, wavetype, couplingmode)
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTIONS: Encoder push switch to relative mode
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        //selectIcon(idy, BLACK);
        selectOption(idy);
        hideCursor(0);
        idy = options[idy];
      }
      break; //end mode OPTIONS


    case OPTMODE://4
      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTMODE: Encoder spin select mode icons (logarithmic, singledigit, sweep)
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTMODE: Encoder push select workmode to set (stored in option[0])
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        byte pMode = _WorkMode;
        switch (idy) {
          case 0:
            hideCursor(0);
            drawSymbol(0);
            lFreq = lLastFreq;
            displayFrequency(lFreq);
            _setWorkMode(LOGARITHMIC);
            break;

          case 1:
            drawSymbol(0);
            selectDigit(0);
            lFreq = lLastFreq;
            displayFrequency(lFreq);
            _setWorkMode(SINGLEDIGIT);
            break;

          case 2:
            lLastFreq = lFreq;
            displayFrequency(_SweepMin); //ready to start
            _setWorkMode(SWEEP);
            break;
        }
        mode = _WorkMode;
        if (pMode != _WorkMode) saveConfig();
        if (_CouplingMode == OFF) resetCouplingMode();
        idx = 0;
        drawAllIcons();
      }
      break; //end mode OPTMODE


    case OPTWAVE://5
      //----------------------------------------------------------------------------------------------------------------
      // mode OPTWAVE: Encoder rotation move cursor left and right for wave selection (sqr, sin, tri)
      //----------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //--------------------------------------------------------------------------------------------------------------------
      // mode OPTWAVE: Encoder push set new wave type
      //--------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        if (_WaveType != idy) {
          _setWaveType(idy);
          saveConfig();
        }
        UpdateFrequency(); //updates wavetype
        drawAllIcons();
        idx = 0;
        mode = _WorkMode;

      }
      break; //end mode OPTWAVE


    case OPTCOUP://6
      //----------------------------------------------------------------------------------------------------------------
      // mode OPTCOUP: Encoder rotation move cursor left and right for coupling mode selection
      //----------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //--------------------------------------------------------------------------------------------------------------------
      // mode OPTCOUP: Encoder push select current coupling mode
      //--------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        setCouplingMode(idy);
        drawAllIcons();
        idx = 0;
        mode = _WorkMode;
      }
      break; //end mode OPTCOUP



    case OPTSWEEP://7
      //--------------------------------------------------------------------------------------------------------------------
      // mode OPTSWEEP: Encoder spin select sweep values to edit
      //--------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;                                          //clockwise increase pointer
        if (encoderSpin == CCW && idy > 0) idy--;                                         //counterclockwise decrease pointer
        selectIcon(idy, WHITE);                                                           //select first icon
        displayFrequency(_Sweep(idy));                                                    //display current sweep value
      }


      if (encoderPush) {
        encoderPush = false;
        //--------------------------------------------------------------------------------------------------------------------
        // mode OPTSWEEP: Encoder push confirm sweep values to edit
        //--------------------------------------------------------------------------------------------------------------------
        drawSymbol(0);
        selectDigit(0);
        selectIcon(idy, WHITE);
        idx = 0;
        displayFrequency(_Sweep(idy));
        mode = SWEEPEDIT;
      }
      break; //end mode OPTSWEEP


    case SWEEPEDIT://8

      //--------------------------------------------------------------------------------------------------------------
      // mode SWEEPEDIT: Encoder spin select digit to change
      //--------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idx < MAXDIGIT - 1) idx++;                        //clockwise increase pointer
        if (encoderSpin == CCW && idx > 0) idx--;                                  //counterclockwise decrease pointer
        selectDigit(idx);
      }

      //--------------------------------------------------------------------------------------------------------------
      // mode SWEEPEDIT: Encoder longpush exit from edit and return to SWEEP
      //--------------------------------------------------------------------------------------------------------------
      if (encoderLongPush == LONGPUSH) {
        encoderPush = false;
        drawAllIcons();
        displayFrequency(_SweepMin);
        hideCursor(idx);
        _setWorkMode(SWEEP);
        SweepReset();
        mode = _WorkMode;
        delay(250);
      }

      if (encoderPush) {
        encoderPush = false;
        //-------------------------------------------------------------------------------------------------------------
        // mode SWEEPEDIT: Encoder push go to mode DIGITCHANGE
        //-------------------------------------------------------------------------------------------------------------
        hideCursor(idx);                                                              //flash cursor
        delay(250);                                                                   //for
        selectDigit(idx);                                                             //visual confirmation
        drawSymbol(2);                                                                //draw turn icon
        mode = DIGITCHANGE;                                                           //change mode
      }
      break;//end mode SWEEPEDIT

    case DIGITCHANGE://9
      //---------------------------------------------------------------------------------------------------------
      // mode DIGITCHANGE: Encoder longpush exit from DIGITCHANGE when in SWEEP mode
      //---------------------------------------------------------------------------------------------------------
      if (encoderLongPush == LONGPUSH && _WorkMode == SWEEP) {
        encoderPush = false;
        hideCursor(idx);
        drawSymbol(1);        
        mode = OPTSWEEP;
        delay(250);
      }

      //---------------------------------------------------------------------------------------------------------
      // mode DIGITCHANGE: Encoder rotation change digit value (0 -> 9 ->0 and so on)
      //---------------------------------------------------------------------------------------------------------
      if (encoderSpin) {                                                  //encoder rotation
        if (encoderSpin == CW) {                                          //clockwise direction
          Freq[idx]++; if (Freq[idx] > '9') Freq[idx] = '0';
        } else {                                                          //counter clockwise direction
          Freq[idx]--; if (Freq[idx] < '0') Freq[idx] = '9';
        }
        updateDigit(idx, Freq[idx]);                                      //update digit on display
      }

      //---------------------------------------------------------------------------------------------------------
      // mode DIGITCHANGE: Encoder push return to mode SINGLEDIGIT or SWEEPEDIT
      //---------------------------------------------------------------------------------------------------------
      if (encoderPush) {                                                  //encoder push flag set by interrupt
        encoderPush = false;                                              //reset event flag
        hideCursor(idx);                                                  //flash cursor
        delay(250);                                                       //for
        selectDigit(idx);                                                 //visual confirmation
        drawSymbol(0);

        if (_WorkMode == SWEEP) {
          long ltemp = _Sweep(idy) ;                                       //save value
          _setSweep(idy, atol(Freq));                                      //convert new value from array to long
          if (_SweepMax > 0 && _SweepMax > _SweepMin && _SweepStep > 0) {  //check congruency of the new sweep value
            if (_Sweep(idy) != ltemp) saveConfig();                        //if value has changed write new value in EEPROM
            displayFrequency(_Sweep(idy));
            mode = SWEEPEDIT;                                              //change mode
          } else {            
            _displayErrMsg;                                                //display error message stored in flash mem
            delay(1000);
            _setSweep(idy, ltemp);                                         //restore saved value
            displayFrequency(_Sweep(idy));                                 //redisplay value
            drawSymbol(2);                                                 //redraw turn icon
          }

        } else {                                                           //if not in sweep mode
          if (_CouplingMode == OFF) {                                      //if coupling mode if OFF
            lLastFreq = atol(Freq);                                        //save current frequency
            resetCouplingMode();                                           //set default coupling mode
          }
          UpdateFrequency();                                               //send frequency to DDS Module
          mode = SINGLEDIGIT;                                              //change mode
        }
      }
      break; //end mode DIGITCHANGE

    default:
      break;
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------
// Push encoder event - Called by interrupt
//-----------------------------------------------------------------------------------------------------------------------------------
void encoderSwitch(void) {
  encoderPush = true;
}

//-----------------------------------------------------------------------------------------------------------------------------------
// Utility functions
//-----------------------------------------------------------------------------------------------------------------------------------
// Draw graphics interface
//----------------------------------------------------------------------------------------------------------------
void drawInterface() {

  display.clearDisplay();
  display.display();
  delay(1000);

  display.drawRoundRect(0, 0, 128, 64, 3, WHITE);                                       //draw external frame
  display.fillRect(1, 1, 126, 14, WHITE);                                               //draw caption frame

  displayText(12, 4, strFromFlash(0), BLACK, WHITE, SMALL);                             //print caption title
  delay(1000);

  if (cTime == 1) {                                                                     //only on power on 
    displayText(XPOS - 6, YPOS + 10, strFromFlash(1), WHITE, BLACK, BIG);               //show Welcom message
    delay(1000);
    display.fillRect(2, 16, display.width() - 3, 35, BLACK);                            //clear Welcome message
    cTime = 0;
  }

  display.display();

  displayText(XPOS + 84, YPOS + 4, strFromFlash(2), WHITE, BLACK, SMALL);              //print "Hz"

  sprintf(Freq, "%06li", lFreq);                                                       //put frequency value into char array with template "000000"
  for (int i = MAXDIGIT - 1; i >= 0; i--) {
    display.drawChar(XPOS + 2 + i * DELTAX, YPOS, Freq[i] , WHITE, BLACK, BIG);        //Display with animation effect from right to left
    display.display();
  }

}//end drawInterface()


//----------------------------------------------------------------------------------------------------------------
// Print string in x,y pos with specified colors and size
//----------------------------------------------------------------------------------------------------------------
void displayText(byte x, byte y, const char *str, byte foreColor, byte backColor, byte textSize) {
  display.setTextSize(textSize);                                  //textsize: SMALL or BIG global const
  display.setTextColor(foreColor, backColor);                     //colors WHITE or BLACK global const of the library
  display.setCursor(x, y);                                        //set the cursor position
  display.print(str);                                             //str is the pointer to the string of chars
  display.display();                                              //update display
}

//----------------------------------------------------------------------------------------------------------------
// Copies element [i] of the string_table array from flash memory to the ram buffer and returns the pointer to the buffer
//----------------------------------------------------------------------------------------------------------------
char* strFromFlash(byte i) {
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i])));
  return (char*)buffer;
}

//----------------------------------------------------------------------------------------------------------------
// Draw or clear a border around selected icon after clearing border of the previous one
//----------------------------------------------------------------------------------------------------------------
void selectIcon(byte icon, byte color) {
  static byte prevIcon;
  display.drawRect(XPOS - 10 + prevIcon * 32, YPOS + 19, 29, 20, BLACK);
  display.drawRect(XPOS - 10 + icon * 32, YPOS + 19, 29, 20, color);
  display.display();
  prevIcon = icon;
}

//----------------------------------------------------------------------------------------------------------------
// Display all workmode icons
//----------------------------------------------------------------------------------------------------------------
void displayWorkModeIcons(void) {
  byte const *bitmap[3] = {imgLog, imgDigit, imgSweep};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Display all wavetype icons
//----------------------------------------------------------------------------------------------------------------
void displayWaveTypeIcons(void) {
  byte const *bitmap[3] = {imgSqr, imgSin, imgTri};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Display all coupling mode icons
//----------------------------------------------------------------------------------------------------------------
void displayCouplingModeIcons(void) {
  byte const *bitmap[3] = {imgCoAc, imgCoDc, imgCoOff};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Display all sweep icons
//----------------------------------------------------------------------------------------------------------------
void displaySweepIcons(void) {
  byte const *bitmap[3] = {imgSwMax, imgSwMin, imgSwStep};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Draw all icons
//----------------------------------------------------------------------------------------------------------------
void drawAllIcons(void) {
  _clearIconsArea;
  drawModeIcon();

  if (_WorkMode == SWEEP || _WorkMode == SWEEPEDIT ) {
    display.drawBitmap(XPOS + 24, YPOS + 21, imgSwOpt, 25, 16, WHITE);
    display.drawBitmap(XPOS + 56, YPOS + 21, imgSwStart, 25, 16, WHITE);
    drawSymbol(1);
    idy = 2;
    selectIcon(idy, WHITE); //ready to sweep
    drawSmallWaveIcon();
    drawSmallCouplingIcon();
  }
  else {
    drawWaveIcon();
    drawCouplingIcon();
    drawSymbol(0);
    if (_WorkMode == SINGLEDIGIT) selectDigit(0);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Draws the icon based on the value of relative option
//----------------------------------------------------------------------------------------------------------------
void drawModeIcon(void) {
  byte x = XPOS - 8, y = YPOS + 21;
  byte const *bitmap[3] = {imgLog, imgDigit, imgSweep};
  display.fillRect(x, y, 25, 16, BLACK);
  display.drawBitmap(x, y, bitmap[_WorkMode], 25, 16, WHITE);
  display.display();
}

void drawWaveIcon(void) {
  byte x = XPOS + 24, y = YPOS + 21;
  const byte *bitmap[3] = {imgSqr, imgSin, imgTri};
  display.fillRect(x, y, 25, 16, BLACK);
  display.drawBitmap(x, y, bitmap[_WaveType], 25, 16, WHITE);
  display.display();
  drawSmallWaveIcon();
}

void drawCouplingIcon(void) {
  byte x = XPOS + 56, y = YPOS + 21;
  const byte *bitmap[3] = {imgCoAc, imgCoDc, imgCoOff};
  display.fillRect(x, y, 25, 16, BLACK);
  display.drawBitmap(x, y, bitmap[_CouplingMode], 25, 16, WHITE);
  display.display();
  drawSmallCouplingIcon();
}


//----------------------------------------------------------------------------------------------------------------
// Draws small wave icon based on the value of relative option
//----------------------------------------------------------------------------------------------------------------
void drawSmallWaveIcon(void) {
  byte x = 114, y = 41;
  const byte *bitmap[3] = {imgSqrSmall, imgSinSmall, imgTriSmall};
  display.fillRect(x, y, 9, 8, BLACK);
  display.drawBitmap(x, y, bitmap[_WaveType], 9, 8, WHITE);
  display.display();
}


//----------------------------------------------------------------------------------------------------------------
// Draws small coupling icon based on the value of relative option
//----------------------------------------------------------------------------------------------------------------
void drawSmallCouplingIcon(void) {
  byte x = 114, y = 50;
  const byte *bitmap[3] = {imgAcSmall, imgDcSmall, imgOffSmall};
  display.fillRect(x, y, 9, 8, BLACK);
  display.drawBitmap(x, y, bitmap[_CouplingMode], 9, 8, WHITE);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Show cursor at x position
//----------------------------------------------------------------------------------------------------------------
void showCursor(byte x) {
  display.drawChar(XPOS + 2 + x * DELTAX, YPOS + DELTAY, CURSOR, WHITE, WHITE, BIG);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Hide cursor at x position
//----------------------------------------------------------------------------------------------------------------
void hideCursor(byte x) {
  display.drawChar(XPOS + 2 + x * DELTAX, YPOS + DELTAY, CURSOR, BLACK, BLACK, BIG);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Show cursor at x position after hiding previous one
//----------------------------------------------------------------------------------------------------------------
void selectDigit(byte x) {
  static byte lastDigit;
  hideCursor(lastDigit);
  display.drawChar(XPOS + 2 + x * DELTAX, YPOS + DELTAY, CURSOR, WHITE, WHITE, BIG);
  display.display();
  lastDigit = x;
}

//----------------------------------------------------------------------------------------------------------------
// Update single digit frequency to chr value
//----------------------------------------------------------------------------------------------------------------
void updateDigit(byte digit, char chr) {
  display.drawChar(XPOS + 2 + digit * DELTAX, YPOS, chr , WHITE, BLACK, BIG);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Drwaw or clear some symbols/icons
//----------------------------------------------------------------------------------------------------------------
void drawSymbol(byte symbol) {

  switch (symbol) {
    case 0: //Top arrow
      display.fillRect(2, 20, 25, 16, BLACK);
      display.fillRect(2, 43, 14, 16, BLACK);
      display.drawChar(XPOS - 20 , YPOS + 4, ARROW, WHITE, BLACK, SMALL);  //draw top arrow top
      break;

    case 1: //Bottom arrow
      display.fillRect(2, 20, 25, 16, BLACK);
      display.fillRect(2, 43, 14, 16, BLACK);
      display.drawChar(XPOS - 20 , YPOS + 25, ARROW, WHITE, BLACK, SMALL); //draw bottom arrow
      break;

    case 2: //Turn icon
      display.fillRect(2, 20, 25, 16, BLACK);
      display.drawBitmap(XPOS - 21, YPOS + 1, imgTurn, 13, 13, WHITE);      //draw turn icon
      break;

    case 3: //Play icons
      display.fillRect(4, 23, 23, 11, BLACK);                              //clear pause icon
      display.drawBitmap(4, 23, imgSwRun, 23, 11, WHITE);                  //draw sweep icon
      display.fillRect(XPOS + 56, YPOS + 21, 25, 16, BLACK);               //clear icon area
      display.drawBitmap(XPOS + 56, YPOS + 21, imgSwPause, 25, 16, WHITE); //drwaw sweep play symbol icon
      break;

    case 4: //Pause icons
      display.fillRect(4, 23, 23, 11, BLACK);                              //clear sweep icon
      display.drawBitmap(4, 23, imgSwPsd, 23, 11, WHITE);                  //draw pause icon
      display.fillRect(XPOS + 56, YPOS + 21, 25, 16, BLACK);               //clear icon area
      display.drawBitmap(XPOS + 56, YPOS + 21, imgSwStart, 25, 16, WHITE); //draw sweep pause symbol icon
      break;

    case 9: //Simply clear symbol area
      display.fillRect(2, 20, 25, 16, BLACK);                              //clear top symbol area
      display.fillRect(2, 43, 14, 16, BLACK);                              //clear bottom symbol area
      break;

    default:
      break;
  }
  display.display();
}

//---------------------------------------------------------------------------------------------------------------
// Set current frequency in DDS module, if frequency is 0 it will be set to 1
//---------------------------------------------------------------------------------------------------------------
void UpdateFrequency(void) {
  lFreq = atol(Freq);                                         //convert char array to long
  if (lFreq < 1) {                                            //the frequency at zero makes no sense
    ++Freq[MAXDIGIT - 1];                                     //increase the right most digit
    lFreq = 1;                                                //set frequency to 1
  }
  displayFrequency(lFreq);                                    //update the display
  DDS_FrequencySet(lFreq, Wave[_WaveType]);                   //send the frequency value to DDS module
  lLastFreq = lFreq;                                          //save current freq
}

//---------------------------------------------------------------------------------------------------------------
// Display the frequency with the six-zero template
//---------------------------------------------------------------------------------------------------------------
void displayFrequency(long f) {
  sprintf(Freq, "%06li", f);                                       //convert long to char with template '000000'
  displayText(XPOS + 2, YPOS, Freq , WHITE, BLACK, BIG);           //print frequency on display
  display.display();                                               //refresh display
}

//---------------------------------------------------------------------------------------------------------------
// Reset coupling mode to default
//---------------------------------------------------------------------------------------------------------------
void resetCouplingMode(void) {
  if (lFreq == 0 && _CouplingMode == OFF) {
    setCouplingMode(AC);
    drawCouplingIcon();
  }
}

//---------------------------------------------------------------------------------------------------------------
// Set a specific coupling mode
//---------------------------------------------------------------------------------------------------------------
void setCouplingMode(byte cMode) {
  byte pMode = _CouplingMode;
  switch (cMode) {
    case 0:
      if (lLastFreq > 0) lFreq = lLastFreq;
      digitalWrite(PinCoupling, LOW);
      _setCouplingMode(AC);
      break;

    case 1:
      if (lLastFreq > 0) lFreq = lLastFreq;
      digitalWrite(PinCoupling, HIGH);
      _setCouplingMode(DC);
      break;

    case 2:
      lLastFreq = lFreq;
      lFreq = 0;
      digitalWrite(PinCoupling, LOW);
      _setCouplingMode(OFF);
      break;
  }
  DDS_FrequencySet(lFreq, Wave[_WaveType]);
  displayFrequency(lFreq);
  if (cMode != pMode) saveConfig();
}


//---------------------------------------------------------------------------------------------------------------
// Select options
//---------------------------------------------------------------------------------------------------------------
void selectOption(byte opt) {

  selectIcon(opt, BLACK);

  switch (opt) {
    case 0:                           //workMode;
      displayWorkModeIcons();
      selectIcon(_WorkMode, WHITE);
      mode = OPTMODE;
      break;

    case 1:                           //waveType;
      displayWaveTypeIcons();
      selectIcon(_WaveType, WHITE);
      mode = OPTWAVE;
      break;

    case 2:                          //couplingMode;
      displayCouplingModeIcons();
      selectIcon(_CouplingMode, WHITE);
      mode = OPTCOUP;
      break;
  }
}

//----------------------------------------------------------------------------------------------------------------
// Calculate logarithmic steps of the frequency
//----------------------------------------------------------------------------------------------------------------
long AutoStep(long value, byte spin) {
  if (spin == CW) {
    if (value >= 100000) return 100000;
    if (value >= 10000) return 10000;
    if (value >= 1000) return 1000;
    if (value >= 100) return 100;
    if (value >= 10) return 10;
    if (value >= 1) return 1;
    return 0;  // Invalid value
  }
  else {
    if (value <= 10) return 1;
    if (value <= 100) return 10;
    if (value <= 1000) return 100;
    if (value <= 10000) return 1000;
    if (value <= 100000) return 10000;
    if (value <= 1000000) return 100000;
    return 0;  // Invalid value
  }
}

//-------------------------------------------------------------------------------------------
// Start Sweep or restart it from where it came from before the pause
//-------------------------------------------------------------------------------------------
void FrequencySweep() {
  do {
    if (sweepDnPausedVal == 0) {                       //if sweepDown has not been stopped
      if (sweepUpPausedVal > 0) {                      //and sweepUp has been stopped
        sweepUpPausedVal = SweepUp(sweepUpPausedVal);  //continues from current value
      }
      else {
        sweepUpPausedVal = SweepUp(_SweepMin);         //else start from min
      }
    }
    if (sweepStatus != BREAK) {                        //if sweep has been stopped
      if (sweepDnPausedVal > 0) {                      //and sweepDn has been stopped
        sweepDnPausedVal = SweepDn(sweepDnPausedVal);  //continues from current value
      }
      else {
        sweepDnPausedVal = SweepDn(_SweepMax);         //else start from max
      }
    }
  } while (sweepStatus != BREAK);                      //continues sweep until stopped
}

//-----------------------------------------------------------------------------------------
// Sweep Up from sweepmin push encoder to pause
//-----------------------------------------------------------------------------------------
long SweepUp(long sweepmin) {
  long f;

  for (f = sweepmin ; f < _SweepMax ; f += _SweepStep) {
    DDS_FrequencySet(f, Wave[_WaveType]);
    displayFrequency(f);
    if (encoderPush) {
      sweepStatus = BREAK;
      break;
    }
  }
  if (sweepStatus == BREAK) return f;
  return 0;
}

//-----------------------------------------------------------------------------------------
// Sweep down from sweepmax push encoder to pause
//-----------------------------------------------------------------------------------------
long SweepDn(long sweepmax) {
  long f;

  for (f = sweepmax; f > _SweepMin ; f -= _SweepStep) {
    DDS_FrequencySet(f, Wave[_WaveType]);
    displayFrequency(f);
    if (encoderPush) {
      sweepStatus = BREAK;
      break;
    }
  }
  if (sweepStatus == BREAK)return f;
  return 0;
}

//-----------------------------------------------------------------------------------------
// Clear global sweep vars and restore display
//-----------------------------------------------------------------------------------------
void SweepReset(void) {
  sweepStatus = STILL;
  sweepUpPausedVal = 0;
  sweepDnPausedVal = 0;
  display.fillRect(4, 23, 23, 11, BLACK);              //clear sweep text
  display.display();
}

//-----------------------------------------------------------------------------------------
// Flash sweep pause icon
//-----------------------------------------------------------------------------------------
void flashIcon(int interval) {
  static long previousMillis;
  static boolean picShow;
  if (millis() - previousMillis >= interval) {
    previousMillis = millis();
    picShow = !picShow;
    if (picShow) {
      display.drawBitmap(4, 23, imgSwPsd, 23, 11, WHITE);      //drwaw sweep pause icon
      display.display();
    } else {
      display.fillRect(4, 23, 23, 11, BLACK);                 //clear sweep pause icon
      display.display();
    }
  }
}

//-----------------------------------------------------------------------------------------
// Arduino software reset
//-----------------------------------------------------------------------------------------
void reset(void) {
  display.fillRect(1, 16, 125, 46, BLACK);
  display.display();
  displayText(30, 30, strFromFlash(4), WHITE, BLACK, BIG);
  char str[2];
  for (byte i = 3; i > 0; i--) {
    sprintf(str, "%d", i);
    displayText(95, 30, str, WHITE, BLACK, BIG);
...

This file has been truncated, please download it to see its full contents.

JXWG_Defs.h

C/C++
Declarections section
// This file is an integral part of the JX_WaveGenerator.ino and must be
// distributed together with the main file to allow it to function correctly.
// The same license of the main file applies to this file.
// Janux 01/04/2021 on Turin, Italy.

#ifndef JXWG_Defs
#define JXWG_Defs

#include <Wire.h>
#include <EEPROM.h>

//Encoder library, see https://www.arduino.cc/reference/en/libraries/simplerotary/
#include <SimpleRotary.h> // 

//adaptation of the library for SSD1306 to the SH1106 display, see https://github.com/wonho-maker/Adafruit_SH1106
#include <Adafruit_SH1106.h>

#define DEBUG 0
#define OLED_RESET -1
Adafruit_SH1106 display(OLED_RESET);

#define PinA          5 //Encoder pin A
#define PinB          4 //Encoder pin B 
#define PinS          3 //Encoder pin Switch
SimpleRotary Encoder(PinA, PinB, PinS);

//list of loop mode
#define LOGARITHMIC   0 //+workmode
#define SINGLEDIGIT   1 //+workmode
#define SWEEP         2 //+workmode

#define OPTIONS       3 //-submode of LOGARITHMIC and SINGLEDIGIT
#define OPTMODE       4 //-submode of OPTIONS
#define OPTWAVE       5 //-submode of OPTIONS
#define OPTCOUP       6 //-submode of OPTIONS
#define OPTSWEEP      7 //-submode of SWEEP
#define SWEEPEDIT     8 //-submode of OPTSWEEP
#define DIGITCHANGE   9 //-submode of SINGLEDIGIT and SWEEPEDIT

#define PinCoupling   7 //Coupling mode pin (relay pin)

//constants 
#define XPOS         28
#define YPOS         21
#define DELTAX       12
#define DELTAY        4
#define SMALL         1
#define BIG           2
#define CW            1
#define CCW           2
#define PUSH          1
#define LONGPUSH      1

//Num Freq digit
#define MAXDIGIT      6

//Wave type
#define SQUARE        0
#define SINE          1
#define TRIANGLE      2

//Coupling mode
#define AC            0
#define DC            1
#define OFF           2

//Sweep status
#define STILL        0
#define BREAK        1
#define PAUSE        2

//Symbols chars
#define CURSOR        0x5F
#define ARROW         0x10

//AD9833 module Pin connection
#define DDS_FSY       9
#define DDS_CLK      10
#define DDS_DAT      11

//AD9833 Wave Type const
#define wSquare     0x28
#define wSine       0x00
#define wTriangle   0x02

//-----------------------------------------------------------------------------
// Variables declarections 
//-----------------------------------------------------------------------------

//Strings constants placed in flash memory save ram space
const char str1[] PROGMEM = "JX WAVE GENERATOR"; // 18 byte
const char str2[] PROGMEM = "WELCOME";           //  8 byte
const char str3[] PROGMEM = "Hz";                //  3 byte
const char str4[] PROGMEM = "ERROR!";            //  7 byte
const char str5[] PROGMEM = "RESET";             //  6 byte //42 byte total
const char* const string_table[] PROGMEM = {str1, str2, str3, str4, str5};
char buffer[18]; //local buffer for string, make sure this is large enough for the largest string it must hold

long lFreq             = 1000;                                       //main frequency variable
long lLastFreq         = 1000;                                       //used to save the current freq value in some situations
long sweepUpPausedVal  = 0;                                          //value of sweep when pused
long sweepDnPausedVal  = 0;                                          //value of sweep when pused
long lSweep[3]         = {20000, 0, 100};                            //Sweep Hz default value MAX, MIN, STEP;
byte sweepStatus       = STILL;                                      //current status of the sweep process
const byte Wave[]      = {wSquare, wSine, wTriangle};                //array for WaveType
volatile boolean encoderPush = false;                                //var used in the routine called by interrupt
char Freq[MAXDIGIT + 1];                                             //array for display frequency in 6 digit template "000000"
byte mode = 0;                                                       //current loop mode
byte idx  = 0;                                                       //pointer to digit index (0 to 5)
byte idy  = 0;                                                       //same of idx in submode
long cTime = 1;                                                      //screensaver counter

//default startup preferences
byte options[3] = {LOGARITHMIC, SINE, DC};                          //mode, wavetype, couplingmode

//define others macros
#define _WorkMode           options[0]
#define _setWorkMode(x)     options[0]=x
#define _WaveType           options[1]
#define _setWaveType(x)     options[1]=x
#define _CouplingMode       options[2]
#define _setCouplingMode(x) options[2]=x
#define _reservedbyte       0xFF
#define _SweepMax           lSweep[0]
#define _SweepMin           lSweep[1]
#define _SweepStep          lSweep[2]
#define _Sweep(x)           lSweep[x]
#define _setSweep(x,f)      lSweep[x]=f
#define _setSweepMax(x)     lSweep[0]=x
#define _setSweepMin(x)     lSweep[1]=x
#define _setSweepStep(x)    lSweep[2]=x

//define short functions macros
#define _clearIconsArea display.fillRect(XPOS - 11, YPOS + 18, 94, 24, BLACK)
#define _displayErrMsg  displayText(XPOS + 2, YPOS, strFromFlash(3), WHITE, BLACK, BIG);

//define CONFIG consts & vars 
#define CONFIG_START 32           //EEPROM Memory start location
#define CONFIG_VERSION "JXWG1"    //Config version ID

//define custom type struct
typedef struct {
  char version[6];
  byte workmode;
  byte wavetype;
  byte couplingmode;
  byte reservedbyte;  
  long sweepmax;  
  long sweepmin;  
  long sweepstep;
} config_type;

//create new struct and load it with default value
config_type CONFIG = {
  CONFIG_VERSION,
  _WorkMode,
  _WaveType,
  _CouplingMode,
  _reservedbyte,
  _SweepMax,
  _SweepMin,
  _SweepStep,
};

//define processor reset function
void(*ATmegaReset)(void) = 0;

#endif

JXWG_Graphics.h

C/C++
Icon resource data file
// This file is an integral part of the JX_WaveGenerator.ino and must be
// distributed together with the main file to allow it to function correctly.
// The same license of the main file applies to this file.
// Janux 01/04/2021 on Turin, Italy.

#ifndef JXWG_Graphics
#define JXWG_Graphics

//----------------------------------------------------------------------------------------------
// Plain b&w bitmaps PROGMEM icons data
//----------------------------------------------------------------------------------------------

const byte imgLog[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x88, 0x00, 0x40, 0x80, 0x9c, 0x00, 0x40, 0x80,
  0x88, 0x00, 0x80, 0x80, 0x88, 0x00, 0x80, 0x80, 0x88, 0x01, 0x00, 0x80, 0x88, 0x02, 0x00, 0x80,
  0x88, 0x0c, 0x00, 0x80, 0x88, 0x30, 0x00, 0x80, 0x89, 0xc0, 0x00, 0x80, 0x8e, 0x00, 0x10, 0x80,
  0x9f, 0xff, 0xf8, 0x80, 0x88, 0x00, 0x10, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgDigit[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x9c, 0x47, 0x3e, 0x80, 0xa2, 0xc8, 0x82, 0x80, 0xa6, 0x40, 0x84, 0x80, 0xaa, 0x47, 0x0c, 0x80,
  0xb2, 0x48, 0x02, 0x80, 0xa2, 0x48, 0x22, 0x80, 0x9c, 0xef, 0x9c, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x3e, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSweep[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x8f, 0xcf, 0x38, 0x80,
  0x88, 0x49, 0x28, 0x80, 0x88, 0x49, 0x28, 0x80, 0x88, 0x49, 0x28, 0x80, 0x88, 0x49, 0x28, 0x80,
  0x88, 0x49, 0x28, 0x80, 0x88, 0x49, 0x28, 0x80, 0x88, 0x49, 0x28, 0x80, 0x88, 0x49, 0x28, 0x80,
  0x88, 0x49, 0x28, 0x80, 0xb8, 0x79, 0xee, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSqr[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x82, 0x08, 0x20, 0x80, 0x80, 0xff, 0x80, 0x80, 0x82, 0x88, 0xa0, 0x80,
  0x80, 0x80, 0x80, 0x80, 0x82, 0x88, 0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, 0xaa, 0xaa, 0xaa, 0x80,
  0x80, 0x80, 0x80, 0x80, 0x82, 0x88, 0xa0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x82, 0x88, 0xa0, 0x80,
  0xff, 0x80, 0xff, 0x80, 0x82, 0x08, 0x20, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSin[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x82, 0x08, 0x20, 0x80, 0x80, 0x1c, 0x00, 0x80, 0x82, 0x2a, 0x20, 0x80,
  0x80, 0x41, 0x00, 0x80, 0x82, 0x49, 0x20, 0x80, 0x80, 0x80, 0x80, 0x80, 0xaa, 0xaa, 0xaa, 0x80,
  0x80, 0x80, 0x80, 0x80, 0xc3, 0x08, 0x61, 0x80, 0xc1, 0x00, 0x41, 0x80, 0xa2, 0x08, 0x22, 0x80,
  0x9c, 0x00, 0x1c, 0x80, 0x82, 0x08, 0x20, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgTri[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x82, 0x08, 0x20, 0x80, 0x82, 0x00, 0x20, 0x80, 0x87, 0x08, 0x70, 0x80,
  0x85, 0x00, 0x50, 0x80, 0x8a, 0x88, 0xa8, 0x80, 0x88, 0x80, 0x88, 0x80, 0xba, 0xeb, 0xae, 0x80,
  0x90, 0x41, 0x04, 0x80, 0xa2, 0x2a, 0x22, 0x80, 0xa0, 0x22, 0x02, 0x80, 0xc2, 0x1c, 0x21, 0x80,
  0xc0, 0x14, 0x01, 0x80, 0x82, 0x08, 0x20, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgCoAc[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x9c, 0x70, 0x00, 0x80, 0xa2, 0x88, 0x00, 0x80, 0xa2, 0x80, 0xc0, 0x80, 0xa2, 0x81, 0x24, 0x80,
  0xbe, 0x81, 0x24, 0x80, 0xa2, 0x88, 0x18, 0x80, 0xa2, 0x70, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgCoDc[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0xbc, 0x70, 0x00, 0x80, 0xa2, 0x88, 0x00, 0x80, 0xa2, 0x81, 0x54, 0x80, 0xa2, 0x80, 0x00, 0x80,
  0xa2, 0x81, 0xfc, 0x80, 0xa2, 0x88, 0x00, 0x80, 0xbc, 0x70, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgCoOff[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x9c, 0xf7, 0x80, 0x80, 0xa2, 0x84, 0x22, 0x80, 0xa2, 0x84, 0x14, 0x80, 0xa2, 0xe7, 0x08, 0x80,
  0xa2, 0x84, 0x14, 0x80, 0xa2, 0x84, 0x22, 0x80, 0x9c, 0x84, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSwMax[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x81, 0x00, 0x00, 0x80, 0x82, 0x00, 0x00, 0x80, 0x87, 0x01, 0x00, 0x80, 0x82, 0x03, 0x80, 0x80,
  0x82, 0x07, 0xc0, 0x80, 0x82, 0x0f, 0xe0, 0x80, 0x82, 0x1f, 0xf0, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSwMin[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x81, 0x00, 0x00, 0x80, 0x82, 0x00, 0x00, 0x80, 0x87, 0x1f, 0xf0, 0x80, 0x82, 0x0f, 0xe0, 0x80,
  0x82, 0x07, 0xc0, 0x80, 0x82, 0x03, 0x80, 0x80, 0x82, 0x01, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSwOpt[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x84, 0x08, 0x10, 0x80, 0x88, 0x1c, 0x38, 0x80, 0x9c, 0x3e, 0x10, 0x80, 0x88, 0x00, 0x00, 0x80,
  0x88, 0x00, 0x00, 0x80, 0x88, 0x3e, 0x00, 0x80, 0x88, 0x1c, 0x38, 0x80, 0x88, 0x08, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSwStep[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x84, 0x08, 0x04, 0x80, 0x88, 0x0c, 0x08, 0x80, 0x9c, 0x7e, 0x1c, 0x80, 0x88, 0x7f, 0x08, 0x80,
  0x88, 0x7e, 0x08, 0x80, 0x88, 0x0c, 0x08, 0x80, 0x88, 0x08, 0x08, 0x80, 0x80, 0x00, 0x00, 0x80,
  0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSwStart[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x00, 0x80,
  0x80, 0xf0, 0x00, 0x80, 0x80, 0xfc, 0x00, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0xff, 0xc0, 0x80,
  0x80, 0xff, 0xc0, 0x80, 0x80, 0xff, 0x00, 0x80, 0x80, 0xfc, 0x00, 0x80, 0x80, 0xf0, 0x00, 0x80,
  0x80, 0xc0, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgSwPause[] PROGMEM = {
  0xff, 0xff, 0xff, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x81, 0xe3, 0xc0, 0x80,
  0x81, 0xe3, 0xc0, 0x80, 0x81, 0xe3, 0xc0, 0x80, 0x81, 0xe3, 0xc0, 0x80, 0x81, 0xe3, 0xc0, 0x80,
  0x81, 0xe3, 0xc0, 0x80, 0x81, 0xe3, 0xc0, 0x80, 0x81, 0xe3, 0xc0, 0x80, 0x81, 0xe3, 0xc0, 0x80,
  0x81, 0xe3, 0xc0, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x80
};

const byte imgTurn[] PROGMEM = {
  0x0f, 0x80, 0x30, 0x60, 0x47, 0x10, 0x58, 0xd0, 0x90, 0x48, 0x80, 0xe8, 0x90, 0x48, 0xb8, 0x08,
  0x90, 0x48, 0x58, 0xd0, 0x47, 0x10, 0x30, 0x60, 0x0f, 0x80
};

const byte imgSwRun[] PROGMEM = {
  0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x9b, 0xa2, 0x22, 0x6b, 0xae, 0xec, 0x7b, 0xae, 0xec, 0x9a,
  0xa6, 0x62, 0xea, 0xae, 0xee, 0x6a, 0xae, 0xee, 0x9c, 0x62, 0x2e, 0xff, 0xff, 0xfe, 0x00, 0x00,
  0x00
};

const byte imgSwPsd[] PROGMEM = {
  0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x1c, 0xdb, 0x30, 0x6b, 0x5a, 0xd6, 0x6b, 0x5a, 0xf6, 0x18,
  0x5b, 0x32, 0x7b, 0x5b, 0xd6, 0x7b, 0x5a, 0xd6, 0x7b, 0x67, 0x30, 0xff, 0xff, 0xfe, 0x00, 0x00,
  0x00
};

//Small icons ---------------------------------------------------------------------------------
const byte imgSqrSmall[] PROGMEM = {
  0x00, 0x00, 0x3e, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0xe3, 0x80, 0x00, 0x00
};

const byte imgSinSmall[] PROGMEM = {
  0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x84, 0x80, 0x84, 0x80, 0x03, 0x00, 0x00, 0x00
};

const byte imgTriSmall[] PROGMEM = {
  0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x88, 0x80, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
};

const byte imgAcSmall[] PROGMEM = {
  0x00, 0x00, 0x30, 0x00, 0x48, 0x00, 0x48, 0x00, 0x84, 0x80, 0x84, 0x80, 0x03, 0x00, 0x00, 0x00
};

const byte imgDcSmall[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x80, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00
};

const byte imgOffSmall[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x14, 0x00, 0x08, 0x00, 0x14, 0x00, 0x22, 0x00, 0x00, 0x00
};

//Total program memory space used by icons data: 1148 byte


#endif

Credits

janux

janux

3 projects • 13 followers
I have always been a technology enthusiast, at 13 I started using a soldering iron and I already knew what a transistor was.

Comments