Direct Port Manipulation Pins

I need to change the pins PIND5 and PIND6
to PIND7 and PINB0
but the following code freezes when I do it,
is there anything else in the following code I need to change to use these pins?

void send_algo()
{
  // Copy FV-1 algorithm from PROGMEM to ram buffer.
  // Progmem is too slow in the send loop.
  // Since we will anyway have to copy from PROGMEM,
  // it makes sense to compress the algorithms and get
  // space savings (almost 2x).

  uint8_t algo = patch_data[current_patch].algorithm;
  decompress(algo_buffer, (prog_uchar *)pgm_read_word(&(algodata[algo].prog_addr)));

  // Set up everything to be ready
  const uint8_t sda_mask = (1 << PIND7);   // Was D5
  const uint8_t clk_mask = (1 << PINB0);     // Was D6
  uint8_t prev_clk = clk_mask;

  DDRD &= ~(clk_mask);   // clk pin as input
  DDRD &= ~(sda_mask);   // sda pin as input
  PORTD &= ~(sda_mask);  // SDA pin is pulled low or floated up
  // -> we need to set pin low only once.
  //    Toggling is done by setting pin to
  //    input (let float high) or output (pull low).
  //    FV-1 internal pullups are ok (tested).

  uint16_t pos = 0;
  uint8_t curr_byte = algo_buffer[pos];
  uint8_t bit_mask = 0b10000000;
  uint8_t clk_count = 0;

  // Undivided attention for FV-1 requests
  noInterrupts();

  // Notify FV-1 of patch change by toggling the notify pin
  PIND = _BV(4);   // Toggle pin 4

  while (clk_count < 37)             // Handle the header
  {
    uint8_t clk = PIND & clk_mask;

    if (!clk && prev_clk) {          // scl went down

      switch (clk_count)
      {
        case 8:
        case 17:
        case 26:
        case 36:
          DDRD  |= sda_mask;         // send ACK - pull sda pin low
          break;
        default:
          DDRD &= ~(sda_mask);       // Release
          break;
      }
      clk_count++;
    }
    prev_clk = clk;
  }

  clk_count = 0;
  while (pos < 512)                  // Send the data
  {
    uint8_t clk = PIND & clk_mask;

    if (!clk && prev_clk) {          // scl went down

      if (clk_count != 8) {          // Sending byte

        if (curr_byte & bit_mask) {
          DDRD &= ~(sda_mask);       // Send 1 = Let High
        } else {
          DDRD  |= sda_mask;         // Send 0 = Pull Low
        }
        bit_mask >>= 1;
        clk_count++;

      } else {                       // Let reciever ACK

        DDRD &= ~(sda_mask);         // Release
        clk_count = 0;
        bit_mask = 0b10000000;
        pos++;
        curr_byte = algo_buffer[pos];
      }
    }
    prev_clk = clk;
  }

Imagine. Pretend that you know nothing about your project, the hardware, the software, the application or anything else but what you've posted. Is it enough?

No info about board, circuit and what it is supposed to do.. Oh, that's tough.. My guess is that you are using code designed to run on pin 5 and 6 (PORTD) and run it on pin 7 and 8 (PORTD & PORTB) without reprogramming it to use PORTB. That is... If this is about an ATmega328 based board..

Oh, and you might want to ask your question here also..

why do you need to guess when that info is written in post 1?

No useful info about anything. You are posting code taken from github and expecting others to mend it for you. The code is not designed to run on the pins you want it to, so you need to rewrite it.

pretty sure "clk pin as input" needs changing to DDRB because I've moved port for that pin

You have not changed anything else than the clk pin-bit from PIND6 to PINB0. All work is still done on PORTD, so that is your problem.

Tried changing

uint8_t clk = PIND & clk_mask;

to

uint8_t clk = PINB & clk_mask;

Still freezing

you have to change line 42 also!

@Leetut: if you want support,
post the working FULL COMPILEABLE SKETCH without the changes and
the FULL COMPILEABLE SKETCH with your breaking modifications.
If the sketch is not from yourself - post a link to the source also.
Helpers need links to all dependencies also - meaning to each external library which is included to the sketch

The working sketch using pins D5 & D6


/*  Copyright 2020 Perttu Haimi

    This file is part of FVduino.

    FVduino 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.

    FVduino is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FVduino.  If not, see <https://www.gnu.org/licenses/>.

  My Changes

  1. Swapped fast / select buttons, short press encoder = select
  2. Set fast for pots 1-3
  3. Long press encoder for save
  4. Made select button more responsive
  5. Removed fast
  6. NeoPixel for Presets
  7. Turn off NoePixel if bypass fsw on tiny85 is off
  8. Replaced OLED with IPS TFT 1.54" 240x240
  ******328p Breadboard Version******
*/

#include <Arduino.h>
//#include <Bounce2.h>
#include <EEPROM.h>
#include <ButtonEvents.h>
#include  <avr/power.h>

typedef unsigned char PROGMEM prog_uchar;

#include "HW_I2CMaster.h"
#include "font.h"

const uint16_t MARKED = 0x3000; //0x0020
const uint16_t NONE   = 0xFFFF; // this is used for rectangle inner color when not used

#define BypassFSW A0 // (Bypass FSW)

////////////////////////////////////////////////////////////////////////////////////////////////
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Arduino_ST7789_Fast.h>
#define TFT_DC  A2 // (TFT DC)
#define TFT_RST 1 // (TFT Reset)
#define SCR_WD   240
#define SCR_HT   240
Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST);
////////////////////////////////////////////////////////////////////////////////////////////////

#undef min
#define min(a,b) ((a)<(b)?(a):(b))

// ----------------------------------------NEOPIXEL---------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN     7 // (Led Pin)
#define PIXEL_COUNT   1

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);


// Yellow Preset 1
int r0 = 170;
int g0 = 170;
int b0 = 0;

// Green Preset 2
int r1 = 0;
int g1 = 170;
int b1 = 0;

// Red Preset 3
int r2 = 170;
int g2 = 0;
int b2 = 0;

// Grey Preset 4
int r3 = 86;
int g3 = 86;
int b3 = 86;

// Blue Preset 5
int r4 = 0;
int g4 = 0;
int b4 = 170;

// Magenta Preset 6
int r5 = 170;
int g5 = 0;
int b5 = 170;

// Orange Preset 7
int r6 = 154;
int g6 = 54;
int b6 = 0;

// Cyan Preset 8
int r7 = 0;
int g7 = 170;
int b7 = 170;

// ----------------------------------------NEOPIXEL---------------------------------------------


// Pin configuration

// Digital potentiometer uses hardware I2C (TWI), pins A4 and A5
const uint8_t POT_ENABLE_PIN = A3; // (Pot Enable)
//const uint8_t POT_SDA_PIN = A4; (Pot SDA)
//const uint8_t POT_SCL_PIN = A5; (Pot SCL)

// Three PWM outputs
const uint8_t PAR1_PWM_PIN = 3; // (PWM 1) OC2B
const uint8_t PAR2_PWM_PIN = 9; // (PWM 2) OC1A
const uint8_t PAR3_PWM_PIN = 10; // (PWM 3) OC1B

// Two buttons.
const uint8_t PresetFSW = 8; // (Preset Footswitch)
const uint8_t buttonPin = 0;   // Select/Save Encoder Button (Encoder SW)

//ButtonEvents myButton; // create an instance of the ButtonEvents class to attach to our button

// Encoder
const uint8_t ENC1_PIN = 2; // (Encoder Pin 1)
const uint8_t ENC2_PIN = A1; // (Encoder Pin 2)

// EEPROM emulation and FV-1 program switching
const uint8_t PC_NOTIFY_PIN = 4; // (Notify FV1 s0)
const uint8_t I2C_EEPROM_EMUL_SDA_PIN = 5; // (FV1 SDA)
const uint8_t I2C_EEPROM_EMUL_SCL_PIN = 6; // (FV1 SCL)
const uint8_t EEPROM_EMUL_ADDR = 0x50;

// OLED display commununication
// For reference here, oled code uses port and bit banging
//const uint8_t OLED_DATA_PIN = 13;
//const uint8_t OLED_CS_PIN   = 12;
//const uint8_t OLED_CLK_PIN  = 11;

// Digipot address
const uint8_t DS1881_BASE_I2C_ADDR = 0x28;
const uint8_t DS1881_write_address = DS1881_BASE_I2C_ADDR << 1;
const uint8_t DS1881_read_address = (DS1881_BASE_I2C_ADDR << 1) | _BV(0);


/*********** FV-1 algorithm data structures ***********/

// The eeprom data is in 16 * 32 byte banks (total 512 bytes)
// They are compressed by LZ algorithm (compress.py) to approx.
// half the size. Include the algorithm data blocks here.

#include "spinsemi.h"



// Strings for describing the algorithm and parameters
typedef struct {
  char algoname [14];
  char par1name [10];
  char par2name [10];
  char par3name [10];
} Description;

// Sensible default settings for each algorithm
typedef struct {
  uint8_t vol;
  uint8_t mix;
  uint8_t par1;
  uint8_t par2;
  uint8_t par3;
} Default;

// Keep everything together in a single array
typedef struct {
  Description desc;
  Default def;
  uint8_t* const prog_addr;
} AlgoDatum;

const AlgoDatum algodata [] PROGMEM = {

  {{"Hall Reverb",   "Pre-delay", "Time", "Dampen" }, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_0_Hall},
  {{"Room Reverb",   "Pre-delay", "Time", "Dampen" }, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_1_Room},
  {{"Plate Reverb",  "Pre-delay", "Time", "Dampen" }, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_2_PLATE},
  {{"Rep Echo/Rev",  "Delay", "Repeats",   "Reverb"}, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_4_ECHO_REV},
  {{"Chor Reverb",   "Width", "Sweep", "Reverb"}, {15, 20, 128, 128, 128}, (uint8_t *)K3_V1_6_CHOR_REV},
  {{"FlangerVerb",   "Reverb", "Rate",  "Depth"}, {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_FLANGE},
  {{"Phaser Verb",    "Reverb", "Rate",  "Width"}, {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_PHASE},
  {{"Vibrato Verb",   "Reverb", "Speed", "Depth"}, {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_VIBRATO},
  {{"Filter Verb",   "Reverb", "Sens", "Depth"},  {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_WAH},
  {{"Tremolo+Rev",   "Reverb", "Rate",  "Tremolo" }, {15, 10, 128, 128, 128}, (uint8_t *)rom_trem_rev},
 
  {{"Spring+Trem",   "Reverb", "Rate", "Depth"},  {15, 10, 128, 128, 128}, (uint8_t *)spring_verb},
  {{"Reverse Dly",   "Bit", "Feedback", "Delay"}, {15, 10, 128, 128, 128}, (uint8_t *)afx},
  {{"Abbey Verb",    "Reverb", "Feedback", "Damp"}, {15, 20, 128, 128, 128}, (uint8_t *)abbeyverb},
  {{"Choirsaw",      "Delay", "Width", "Tremolo"},  {15, 10, 128, 128, 128}, (uint8_t *)choirsaw},
  {{"Binson",        "Mix", "Prog", "Feedback"}, {15, 10, 128, 128, 128}, (uint8_t *)bluenebula},
  {{"Shimmer",       "Dwell", "Pitch", "Mix"},   {15, 10, 128, 128, 128}, (uint8_t *)glimmer},
  {{"Flanger",       "Speed", "Depth", "Feedback"}, {15, 10, 128, 128, 128}, (uint8_t *)flanger},
  {{"Dual Pitch",     "Pitch1", "Mix", "Pitch2"},      {15, 10, 128, 128, 128}, (uint8_t *)dualpitch},
  {{"Daydream",       "Time", "Feedback", "Filter"},   {15, 10, 128, 128, 128}, (uint8_t *)daydream},
  {{"Glitch Dly",     "Speed", "Time", "Random"},      {15, 10, 128, 128, 128}, (uint8_t *)greenwood},
 
  {{"Bit Crusher",    "Crush", "Filter", "Detune"},    {15, 10, 128, 128, 128}, (uint8_t *)crusher},
  {{"Chorus Dual",    "Rate1", "Rate2", "Depth"},      {15, 10, 128, 128, 128}, (uint8_t *)chorusdualrate},
  {{"Space Delay",      "Time", "Filter", "Feedback"},   {15, 10, 128, 128, 128}, (uint8_t *)spacedelay},
  {{"Sample&Hold",            "Time", "Feedback", "Mod"},      {15, 10, 128, 128, 128}, (uint8_t *)sh},
  {{"Ring Pitch",     "Rate", "Blend", "Pitch"},       {15, 10, 128, 128, 128}, (uint8_t *)ringpitchulator},
  {{"ReverseVerb",   "Pre-delay", "Decay", "Damp"},     {15, 10, 128, 128, 128}, (uint8_t *)RevRev},
  {{"Dual Gl3",       "Speed", "Reson", "Feedback"},   {15, 10, 128, 128, 128}, (uint8_t *)dualglitch3},
  {{"Starfield",      "Time", "Feedback", "Phaser"},   {15, 10, 128, 128, 128}, (uint8_t *)starfield2},
  {{"Filter Trem",    "Speed", "Reson", "Gain"},       {15, 10, 128, 128, 128}, (uint8_t *)bass6},
  {{"Trem Dly",       "Pitch", "Speed", "Regen"},      {15, 10, 128, 128, 128}, (uint8_t *)bass7},

  {{"Dr.Bohm",        "Reverb", "Feedback", "Speed"},  {17, 20, 128, 128, 128}, (uint8_t *)Cathedral},
  {{"Rotor",          "Reverb", "Depth", "Speed"},     {17, 20, 128, 128, 128}, (uint8_t *)Rotor},
  {{"Solina",         "Reverb", "Blend", "Treble"},    {17, 20, 128, 128, 128}, (uint8_t *)Ensemble},
  {{"Fading",         "D-mix", "Depth", "Reson"},      {17, 20, 128, 128, 128}, (uint8_t *)Fading},
  {{"PolySixChor",    "Reverb", "Blend", "Treble"},    {17, 20, 128, 128, 128}, (uint8_t *)PolySixChor},
  {{"Rototrem",       "Reverb", "Warp", "Speed"},      {17, 20, 128, 128, 128}, (uint8_t *)Tremolo},
  {{"Leslie",         "Reverb", "Filter", "Speed"},    {17, 20, 128, 128, 128}, (uint8_t *)Leslie},
  {{"EQ/Width",       "Bass", "Treble", "Width"},      {17, 20, 128, 128, 128}, (uint8_t *)EQstereoWidth},
  {{"Para EQ",        "Freq", "Q-Band", "Q-Peak"},     {15, 10, 128, 128, 128}, (uint8_t *)para_eq},
  {{"Parallax",       "Time", "Feedback", "Trem"},     {15, 10, 128, 128, 128}, (uint8_t *)parallax},
};

// The space for printing algo number on screen is two characters
// In practice, about 80 algorithms is the max that fits on atmega328 32k memory
const uint8_t NALGO = min(99, sizeof(algodata) / sizeof(AlgoDatum));
// Algorithm will be copied to RAM before sending because PROGMEM is too slow
uint8_t algo_buffer[512];

// The strings of the currently visible algorithm are copied to ram
Description current_algo_strings;

/************** Patch data structures *****************/

typedef struct {
  uint8_t algorithm; // index
  uint8_t vol;
  uint8_t mix;
  uint8_t par1;
  uint8_t par2;
  uint8_t par3;
} Patch;

// 5 RGB 565 colours for each patch, from dark to light
const uint16_t patch_colors [][5] = {
  {0x5940, 0xD1A0, 0xFC20, 0xFD80, 0xFE85},   // Amber
  {0x0220, 0x0C80, 0x3640, 0x4F29, 0xA7D4},   // Green
  {0x7820, 0xE060, 0xFA8A, 0xFBCF, 0xFDD7},   // Red
  {0x31E8, 0x4ACC, 0x638F, 0xA577, 0xC639},   // Gray
  {0x00C9, 0x0230, 0x0457, 0x965B, 0xDF5E},   // Blue
  {0x3807, 0x8811, 0xF81F, 0xFC5F, 0xFEDF},   // Mag
  {0x8285, 0x9307, 0xBBE9, 0xD52D, 0xFFFF},   // Brown
  {0x1112, 0x73FD, 0xBD5F, 0x36BC, 0xAFDF},   // Cyan
};

const uint8_t NPATCH = sizeof(patch_colors) / sizeof(patch_colors[0]);

Patch patch_data[NPATCH]; // Keep all patch data in memory, store and restore in EEPROM

const uint16_t P_IN_USE_EE_ADDR = 0;
const uint16_t PATCH_EE_ADDR = 1;
const uint16_t ALGO_EE_ADDR = 512;

uint8_t changed[NPATCH];        // to keep track of patch edit

// State machine for user interface
enum EditState {None, Select, Change};

EditState es = None;
uint8_t selection = 0;            // default start from line 5
uint8_t patches_in_use = 2;
uint8_t current_patch = 0;
uint16_t bg_col = BLACK;          // bg_col will be different if patch is edited but not saved


/************** Digipot code ****************/


/* Calculate actual pot positions from vol and mix values

   vol goes from 0 to 20
   17 is unity gain (corresponding to pot volume 60)

   mix goes from 0 to 20
   10 is 1:1 mix max wet - max dry
   below 10 is less wet - max dry
   above 10 is max wet  - less dry
*/
uint8_t dry_from(uint8_t vol, uint8_t mix)
{
  if (mix > 20)
    mix = 20;

  if (vol > 20)
    vol = 20;

  if (vol == 0 || mix == 20)
    return 0;  // dry is muted

  uint8_t nominal_vol = 43 + vol; // Max 63

  if (mix <= 10)
    return nominal_vol; // dry is maxed

  uint8_t vol_adjustment = mix - 10;
  if (vol_adjustment > nominal_vol)
    return 0;           // dry is muted

  return nominal_vol - vol_adjustment;
}

uint8_t wet_from(uint8_t vol, uint8_t mix)
{

  if (mix > 20)
    mix = 20;

  if (vol > 20)
    vol = 20;

  if (vol == 0 || mix == 0)
    return 0;  // wet is muted

  uint8_t nominal_vol = 43 + vol; // Max 63

  if (mix >= 10)
    return nominal_vol; // wet is maxed

  uint8_t vol_adjustment = 10 - mix;
  if (vol_adjustment > nominal_vol)
    return 0;           // wet is muted

  return nominal_vol - vol_adjustment;
}


/* Run only once during factory default setup.

*/
bool pot_init()
{
  digitalWrite(POT_ENABLE_PIN, LOW); // enable pot communication

  if (!i2c_start(DS1881_write_address))
    return false; // no reply from device

  i2c_write(0b10000010);       // Nonvolatile writes: Zero crossing, 63 values + mute
  i2c_stop();
  //delay(20); // wait for EEPROM write to finish
  delay(10); // half delay when clocking nano to 8 MHz

  i2c_start(DS1881_write_address);
  i2c_write(0b10000110);       // Go back to volatile writes
  i2c_stop();
  //delay(20); // wait for EEPROM write to finish
  delay(10); // half delay when clocking nano to 8 MHz

  digitalWrite(POT_ENABLE_PIN, HIGH);

  return true;
}

/* Set the digipot to given value.
   Min volume 0: -80db attenuation,
   Max volume 63:  0db attenuation
*/
bool pot_set(uint8_t pot, uint8_t val)
{
  val &= 0b00111111; // only the low 6 bits matter

  val = 63 - val;    // invert the value.
  // pot acts as attenuator: 0 - no attenuation (max volume)
  // we want larger number to mean louder

  if (pot)
    val |= 0b01000000; // set the pot bit;

  digitalWrite(POT_ENABLE_PIN, LOW); // enable pot communication

  if (!i2c_start(DS1881_write_address))
    return false;  // no reply from device

  bool res = i2c_write(val);
  i2c_stop();

  digitalWrite(POT_ENABLE_PIN, HIGH);

  return res;
}


/*************** User Interface code **********************/
using Bounce2::Button;

Button footswitch = Button();

ButtonEvents myButton; // create an instance of the ButtonEvents class to attach to our button

void draw_selection(const uint16_t* colors, uint8_t pos, bool emph)
{
  uint8_t y = pos * 35;
  uint16_t col = emph ? colors[4] : colors[2];

  draw_rect(0 , y, SCR_WD , 30, GREEN, NONE, false); // Selection box, was col
}


void clear_selection(uint8_t pos)
{
  uint8_t y = pos * 35;

  draw_rect(0 , y, SCR_WD , 30, bg_col, NONE, false);
}


void draw_patch ()
{
  tft.fillRect(1, 1, SCR_WD, 27, bg_col);
  //  tft.fillRect(1, 210, SCR_WD, 25, bg_col); // clear algorithm name area with background color
  char buff[3];              // 3 chars + \0
  buff[0] =  ' ';            // put spaces in tens and hundreds position

  byte algo = patch_data[current_patch].algorithm + 1;
  btoa(algo, buff + 3); // give pointer to just after buffer (end)

  // Print algo number

  //tft.drawFastHLine(3, 27, 237, WHITE);

  tft.setCursor(214, 2);
  //  tft.setCursor(200, 6 * 35 + 1);
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print(buff); // Algo number

  // Print algo name

  tft.setCursor(2, 2);
  //  tft.setCursor(2, 6 * 35 + 1);
  tft.setTextColor(WHITE);
  tft.setTextSize(3);
  tft.print(current_algo_strings.algoname); // Algo name

  const uint16_t * palette = patch_colors[current_patch];

  clear_selection(1);
  draw_volume(palette, bg_col, 1, patch_data[current_patch].vol);
  clear_selection(2);
  draw_mix(palette, bg_col, 2, patch_data[current_patch].mix);
  clear_selection(3);
  draw_parameter(palette, bg_col, current_algo_strings.par1name, 3, patch_data[current_patch].par1);
  clear_selection(4);
  draw_parameter(palette, bg_col, current_algo_strings.par2name, 4, patch_data[current_patch].par2);
  clear_selection(5);
  draw_parameter(palette, bg_col, current_algo_strings.par3name, 5, patch_data[current_patch].par3);
}

void draw_panel()
{
  uint8_t boxlen = SCR_WD / NPATCH; // Preset boxes

  //tft.drawRect(1, 6 * 35 + 1, 238, 25, bg_col);
  tft.fillRect(1, 6 * 35 + 1, 238, 25, bg_col);
  //  tft.drawRect(1, 1, 238, 25, bg_col);
  //  tft.fillRect(1, 1, 238, 25, bg_col);

  for (uint8_t i = 0; i < patches_in_use; ++i) {
    if (i == current_patch) {
      //tft.drawRect(boxlen * i , 6 * 35 + 1, boxlen - 2, 25, patch_colors[i][2]);
      tft.fillRect(boxlen * i + 1, 6 * 35 + 1, boxlen - 3, 25, patch_colors[i][3]);
      //      tft.drawRect(boxlen * i , 1, boxlen - 2, 25, patch_colors[i][2]);
      //      tft.fillRect(boxlen * i , 1, boxlen - 2, 25, patch_colors[i][3]);
    } else {
      //tft.drawRect(boxlen * i , 6 * 35 + 1, boxlen - 2, 25, bg_col);
      tft.fillRect(boxlen * i + 1, 6 * 35 + 10, boxlen - 3, 15, patch_colors[i][3]);
      //      tft.drawRect(boxlen * i , 1, boxlen - 2, 25, bg_col);
      //      tft.fillRect(boxlen * i , 1, boxlen - 2, 25, patch_colors[i][0]);
    }
  }
}


void change_selection(const uint16_t * palette, int8_t dir)
{
  if (dir < -1) dir = -1;
  if (dir > 1) dir = 1;

  uint8_t oldsel = selection;
  selection += dir + 7;
  selection %= 7;
  clear_selection(oldsel);
  draw_selection(palette, selection, false);
}

void set_patch()
{
  // Turn digipots
  uint8_t dry = dry_from(patch_data[current_patch].vol, patch_data[current_patch].mix);
  uint8_t wet = wet_from(patch_data[current_patch].vol, patch_data[current_patch].mix);

  pot_set(0, dry);
  pot_set(1, wet);

  // Set analog signal to FV-1
  analogWrite(PAR1_PWM_PIN, patch_data[current_patch].par1);
  analogWrite(PAR2_PWM_PIN, patch_data[current_patch].par2);
  analogWrite(PAR3_PWM_PIN, patch_data[current_patch].par3);
}

void change_patch(int8_t dir)
{
  current_patch += dir + patches_in_use;
  current_patch %= patches_in_use;

  bg_col = changed[current_patch] ? MARKED : BLACK;

  // Save active patch to EEPROM?
  // 100000 writes promised -> 1000 days 100 patch changes per day
  // Maybe not worth it...

  send_algo();
  set_patch();

  // copy current patch strings for ui
  uint8_t algo = patch_data[current_patch].algorithm;
  memcpy_P(&current_algo_strings, &(algodata[algo].desc), sizeof current_algo_strings);

  // print changed gui
  draw_panel();
  draw_patch();

}

// Valid values for dir are -1 and 1
void edit_algo(int8_t dir)
{

  patch_data[current_patch].algorithm += dir + NALGO;
  patch_data[current_patch].algorithm %= NALGO;

  uint8_t algo = patch_data[current_patch].algorithm;

  // Copy current patch strings for ui
  memcpy_P(&current_algo_strings, &(algodata[algo].desc), sizeof current_algo_strings);

  // Set params to default values. Copy 5 bytes from EEPROM
  Default tmp;
  EEPROM.get(ALGO_EE_ADDR + algo * sizeof(Default),  tmp);

  patch_data[current_patch].vol  = tmp.vol;
  patch_data[current_patch].mix  = tmp.mix;
  patch_data[current_patch].par1 = tmp.par1;
  patch_data[current_patch].par2 = tmp.par2;
  patch_data[current_patch].par3 = tmp.par3;

  changed[current_patch] = true;

  send_algo();
  set_patch();

  // print updated elements
  draw_patch();

  const uint16_t * palette = patch_colors[current_patch];
  draw_selection(palette, selection, true);
}


void edit_vol(int8_t dir, const uint8_t pos)
{
  uint8_t vol = patch_data[current_patch].vol;

  if (vol == 0 && dir < 0)
    return; // At minimum
  if (vol == 20 && dir > 0)
    return; // Maxed out

  vol += dir;

  // Change the patch
  patch_data[current_patch].vol = vol;
  changed[current_patch] = true;

  // Turn the digipots.
  uint8_t dry = dry_from(vol, patch_data[current_patch].mix);
  uint8_t wet = wet_from(vol, patch_data[current_patch].mix);
  pot_set(0, dry);
  pot_set(1, wet);

  // Draw the change
  const uint16_t * palette = patch_colors[current_patch];
  draw_volume(palette, bg_col, pos, vol);
}

void edit_mix(int8_t dir, const uint8_t pos)
{
  uint8_t mix = patch_data[current_patch].mix;

  if (mix ==  0 && dir < 0)
    return;  // At minimum
  if (mix == 20 && dir > 0)
    return;  // At maximum

  mix += dir;

  // Change the patch
  patch_data[current_patch].mix = mix;
  changed[current_patch] = true;

  // Turn the digipots.
  uint8_t dry = dry_from(patch_data[current_patch].vol, mix);
  uint8_t wet = wet_from(patch_data[current_patch].vol, mix);
  pot_set(0, dry);
  pot_set(1, wet);

  // Draw the change
  const uint16_t * palette = patch_colors[current_patch];
  draw_mix(palette, bg_col, pos, mix);
}

// Valid values for dir are -1 and 1
//void edit_par(int8_t dir, bool fast, uint8_t pin, uint8_t &val, const char *parname, const uint8_t pos)
void edit_par(int8_t dir, uint8_t pin, uint8_t &val, const char *parname, const uint8_t pos)
{
  // if (fast)
  dir *= 10;

  if (dir < 0 && val < -dir)
    val = 0;
  else if (dir > 0 && (255 - val) < dir)
    val = 255;
  else
    val += dir;

  const uint16_t * palette = patch_colors[current_patch];

  changed[current_patch] = true;

  analogWrite(pin, val);
  draw_parameter(palette, bg_col, parname, pos, val);
}

void edit_menubar(int8_t dir)
{
  if (dir < -1) dir = -1;
  if (dir > 1) dir = 1;

  // patches_in_use must be in [2, NPATCH]
  if (patches_in_use <= 2 && dir < 0)
    return;
  if (patches_in_use >= NPATCH && dir > 0)
    return;

  const uint16_t * palette = patch_colors[current_patch];

  patches_in_use += dir;

  if (current_patch >= patches_in_use) {
    change_patch(-1);  // move to previous patch since current is not in use
    draw_selection(palette, selection, true);
  } else {
    draw_panel();
    draw_selection(palette, selection, true);
  }

  // write number of patches in use to eeprom
  EEPROM.put(P_IN_USE_EE_ADDR, patches_in_use);
}

//void edit_patch(uint8_t sel, int8_t dir, bool fast)
void edit_patch(uint8_t sel, int8_t dir)
{
  switch (sel) {
    case 0 : edit_algo(-dir);     break;  // Changed dir
    case 1 : edit_vol(-dir, sel); break;
    case 2 : edit_mix(-dir, sel); break;
    case 3 : edit_par(-dir, PAR1_PWM_PIN, patch_data[current_patch].par1, current_algo_strings.par1name, sel); break;
    case 4 : edit_par(-dir, PAR2_PWM_PIN, patch_data[current_patch].par2, current_algo_strings.par2name, sel); break;
    case 5 : edit_par(-dir, PAR3_PWM_PIN, patch_data[current_patch].par3, current_algo_strings.par3name, sel); break;
    case 6 : edit_menubar(-dir); break;
  }
}

void save_current_patch()
{
  uint16_t eeAddress = PATCH_EE_ADDR + current_patch * sizeof(Patch);
  Patch curr = patch_data[current_patch];

  EEPROM.put(eeAddress, curr);

  // Save to algo defaults also so that next time they can be used

  eeAddress = ALGO_EE_ADDR + curr.algorithm * sizeof(Default);
  Default tmp = {curr.vol, curr.mix, curr.par1, curr.par2, curr.par3};

  EEPROM.put(eeAddress, tmp);

  changed[current_patch] = false;
  bg_col = BLACK;
  draw_panel();
  draw_patch();             // Redraw the whole screen
}



/********** 24LC32A EEPROM emulation */

/* Compression algorithm based on
   http://excamera.com/sphinx/article-compression.html

   With small modifications.
*/
class Flashbits {
  public:

    void begin(prog_uchar *s)
    {
      src = s;
      mask = curr_byte = 0x00;
    }

    uint8_t get1(void)
    {
      if (!mask) {
        mask = 0x80;
        curr_byte = pgm_read_byte_near(src++);
      }

      uint8_t r = (curr_byte & mask) != 0;
      mask >>= 1;

      return r;
    }

    uint16_t getn(uint8_t n)
    {
      uint16_t r = 0;
      while (n--) {
        r <<= 1;
        r |= get1();
      }
      return r;
    }

  private:
    prog_uchar *src;
    uint8_t mask;
    uint8_t curr_byte;
};

Flashbits bits;

void decompress(uint8_t *dst, prog_uchar *src)
{
  bits.begin(src);
  uint8_t O = bits.getn(4);
  uint8_t L = bits.getn(4);
  uint8_t M = bits.getn(2);

  uint8_t *end = dst + bits.getn(16);

  while (dst != end) {
    if (bits.get1() == 0) {
      *dst++ = bits.getn(8);
    } else {
      int offset = -bits.getn(O) - 1;
      int length = bits.getn(L) + M;
      while (length--) {
        *dst = *(dst + offset);
        dst++;
      }
    }
  }
}

void send_algo()
{
  // Copy FV-1 algorithm from PROGMEM to ram buffer.
  // Progmem is too slow in the send loop.
  // Since we will anyway have to copy from PROGMEM,
  // it makes sense to compress the algorithms and get
  // space savings (almost 2x).

  uint8_t algo = patch_data[current_patch].algorithm;
  decompress(algo_buffer, (prog_uchar *)pgm_read_word(&(algodata[algo].prog_addr)));

  // Set up everything to be ready
  const uint8_t sda_mask = (1 << PIND5);
  const uint8_t clk_mask = (1 << PIND6);
  uint8_t prev_clk = clk_mask;

  DDRD &= ~(clk_mask);   // clk pin as input
  DDRD &= ~(sda_mask);   // sda pin as input
  PORTD &= ~(sda_mask);  // SDA pin is pulled low or floated up
  // -> we need to set pin low only once.
  //    Toggling is done by setting pin to
  //    input (let float high) or output (pull low).
  //    FV-1 internal pullups are ok (tested).

  uint16_t pos = 0;
  uint8_t curr_byte = algo_buffer[pos];
  uint8_t bit_mask = 0b10000000;
  uint8_t clk_count = 0;

  // Undivided attention for FV-1 requests
  noInterrupts();

  // Notify FV-1 of patch change by toggling the notify pin
  PIND = _BV(4);   // Toggle pin 4

  while (clk_count < 37)             // Handle the header
  {
    uint8_t clk = PIND & clk_mask;

    if (!clk && prev_clk) {          // scl went down

      switch (clk_count)
      {
        case 8:
        case 17:
        case 26:
        case 36:
          DDRD  |= sda_mask;         // send ACK - pull sda pin low
          break;
        default:
          DDRD &= ~(sda_mask);       // Release
          break;
      }
      clk_count++;
    }
    prev_clk = clk;
  }

  clk_count = 0;
  while (pos < 512)                  // Send the data
  {
    uint8_t clk = PIND & clk_mask;

    if (!clk && prev_clk) {          // scl went down

      if (clk_count != 8) {          // Sending byte

        if (curr_byte & bit_mask) {
          DDRD &= ~(sda_mask);       // Send 1 = Let High
        } else {
          DDRD  |= sda_mask;         // Send 0 = Pull Low
        }
        bit_mask >>= 1;
        clk_count++;

      } else {                       // Let reciever ACK

        DDRD &= ~(sda_mask);         // Release
        clk_count = 0;
        bit_mask = 0b10000000;
        pos++;
        curr_byte = algo_buffer[pos];
      }
    }
    prev_clk = clk;
  }

  interrupts();

}


void setup()
{

  // ----------------------------------------NEOPIXEL---------------------------------------------
  pixels.begin();
  pixels.setPixelColor(0, 255, 255, 0);
  pixels.show();
  // ----------------------------------------NEOPIXEL---------------------------------------------

  // set up pins

  // Use high frequency PWM
  TCCR1A = (TCCR1A & 0b11111100) | 0b00000001;    // set timer 1 to 8 bit mode
  TCCR1B = (TCCR1B & 0b11100000) | 0b00001001;    // set timer 1 to fast PWM, divisor to 1 for PWM frequency of 31372.55 Hz

  TCCR2A = (TCCR2A & 0b11111100) | 0b00000011;    // set timer 2 to fast PWM
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000001;    // set timer 2 divisor to 1 for PWM frequency of 31372.55 Hz

  // PWM outputs
  pinMode(PAR1_PWM_PIN, OUTPUT);
  pinMode(PAR2_PWM_PIN, OUTPUT);
  pinMode(PAR3_PWM_PIN, OUTPUT);

  // 24LC32 EEPROM emulation
  pinMode(PC_NOTIFY_PIN, OUTPUT);
  pinMode(I2C_EEPROM_EMUL_SDA_PIN, INPUT);
  pinMode(I2C_EEPROM_EMUL_SCL_PIN, INPUT);



  // Buttons and encoder

  pinMode(buttonPin, INPUT_PULLUP);
  myButton.attach(buttonPin);
  myButton.activeHigh();
  myButton.debounceTime(15); //apply 5ms debounce
  myButton.holdTime(750); // require button to be held for 1000ms before triggering a hold event
  footswitch.attach(PresetFSW, INPUT);
  footswitch.interval(5);

  pinMode(ENC1_PIN, INPUT_PULLUP);
  pinMode(ENC2_PIN, INPUT_PULLUP);

  pinMode(BypassFSW, INPUT_PULLUP);

  // SoftI2C is used for digipot control
  pinMode(POT_ENABLE_PIN, OUTPUT);
  i2c_init(); // Uses hardware I2C (pins A4 and A5)


  // Write factory defaults to EEPROM if requested
  // Footswitch pressed at boot for 1 sec

  unsigned long startTime = millis();
  unsigned long heldTime = 0;

  while (digitalRead(PresetFSW) == HIGH)

  {
    heldTime = millis() - startTime;
    if (heldTime > 1000)
      break;
  }

  if (heldTime > 1000) {

    // Digipot initial settings, saved in its EEPROM.
    // Depends on i2c_init(). Hangs setup if no pot present...
    pot_init();

    // Write algorithm default settings
    Default tmp_d;
    for (uint8_t i = 0; i < NALGO; ++i) {
      memcpy_P(&tmp_d, &(algodata[i].def), sizeof(Default));
      EEPROM.put(ALGO_EE_ADDR + i * sizeof(Default), tmp_d);
      delay(10);
    }

    // Write empty patches using the first algorithm and default settings
    memcpy_P(&tmp_d, &(algodata[0].def), sizeof(Default));
    Patch tmp_p = {0, tmp_d.vol, tmp_d.mix, tmp_d.par1, tmp_d.par2, tmp_d.par3};

    for (uint8_t i = 0; i < NPATCH; ++i) {
      EEPROM.put(PATCH_EE_ADDR + i * sizeof(Patch), tmp_p);
      delay(10);
    }

    // Set 8 patches as default
    EEPROM.put(P_IN_USE_EE_ADDR, (uint8_t)8);

    rainbow(2);
    pixels.show();

  }

  // load patch settings from EEPROM
  uint16_t eeAdress = PATCH_EE_ADDR;
  for (uint8_t i = 0; i < NPATCH; ++i) {
    EEPROM.get(eeAdress, patch_data[i]);
    if (patch_data[i].algorithm >= NALGO) {
      patch_data[i].algorithm = 0;   // something wrong with algo index, set to zero
      changed[i] = true;             // user will have to edit and save
    }
    if (patch_data[i].vol > 20)
      patch_data[i].vol = 20;
    if (patch_data[i].mix > 100)
      patch_data[i].mix = 100;
    eeAdress += sizeof(Patch);
  }

  current_patch = 0;

  // get patches_in_use from EEPROM
  EEPROM.get(P_IN_USE_EE_ADDR, patches_in_use);
  if (patches_in_use < 2)
    patches_in_use = 2;
  if (patches_in_use >= NPATCH)
    patches_in_use = NPATCH;

  // copy current patch strings for ui
  uint8_t algo = patch_data[current_patch].algorithm;
  memcpy_P(&current_algo_strings, &(algodata[algo].desc), sizeof current_algo_strings);

  // send current patch algorithm to FV-1
  send_algo();
  set_patch();

  // TFT Display setup
  delay(200);
  tft.init();
  tft.setRotation(1);
  //tft.setRotation(0);  // Pins at bottom
  tft.fillScreen(BLACK); // Black
  delay(200);

  // print initial gui
  draw_panel();
  draw_patch();

}


/* Rotary encoder handling code from:
   https://www.best-microcontroller-projects.com/rotary-encoder.html
*/
uint8_t prevNextCode = 0;
uint16_t store = 0;

// A valid CW or CCW move returns 1 or -1, invalid returns 0.
int8_t read_rotary() {
  const int8_t rot_enc_table[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};

  prevNextCode <<= 2;
  if (digitalRead(ENC1_PIN)) prevNextCode |= 0x02;
  if (digitalRead(ENC2_PIN)) prevNextCode |= 0x01;
  prevNextCode &= 0x0f;

  // If valid then store as 16 bit data.
  if  (rot_enc_table[prevNextCode] ) {
    store <<= 4;
    store |= prevNextCode;
    if ((store & 0xff) == 0x2b) return -1;
    if ((store & 0xff) == 0x17) return 1;
  }
  return 0;

}


void loop()
{
  if (digitalRead(BypassFSW) == HIGH)
  {
    if (current_patch == 0) {
      pixels.setPixelColor(0, r0, g0, b0); // Yellow
      pixels.show();
    }
    if (current_patch == 1) {
      pixels.setPixelColor(0, r1, g1, b1); // Green
      pixels.show();
    }
    if (current_patch == 2) {
      pixels.setPixelColor(0, r2, g2, b2);  // Red
      pixels.show();
    }
    if (current_patch == 3) {
      pixels.setPixelColor(0, r3, g3, b3); // Grey
      pixels.show();
    }
    if (current_patch == 4) {
      pixels.setPixelColor(0, r4, g4, b4); // Blue
      pixels.show();
    }
    if (current_patch == 5) {
      pixels.setPixelColor(0, r5, g5, b5); // Magenta
      pixels.show();
    }
    if (current_patch == 6) {
      pixels.setPixelColor(0, r6, g6, b6); // Orange
      pixels.show();
    }
    if (current_patch == 7) {
      pixels.setPixelColor(0, r7, g7, b7); // Cyan
      pixels.show();
    }
  }
  if (digitalRead(BypassFSW) == LOW)
  {
    pixels.setPixelColor(0, 0, 0, 0); // Off
    pixels.show();

  }
  const uint16_t * palette = patch_colors[current_patch];
  if (myButton.rose() == true) {
    if (es == None) {
      es = Select;
      draw_selection(palette, selection, false);
    } else if (es == Select) {
      es = Change;
      // emphasize bar that is being edited
      draw_selection(palette, selection, true);
    } else if (es == Change) {
      es = None;
      if (changed[current_patch]) {
        bg_col = MARKED;
        draw_panel();
        draw_patch();             // Redraw the whole screen
      }
      clear_selection(selection);
    }
  }



  int8_t dir = read_rotary();

  if (myButton.update() == true) {
    switch (myButton.event()) {

      // things to do if the button was held (Hold = Save)
      case (hold) : {
          if (changed[current_patch]) {
            save_current_patch();
            es = None;                               // Go to default mode
            clear_selection(selection);

            rainbow(1);
            pixels.show();
          }
          break;
        }
    }
  }

  footswitch.update();

  // Encoder turned
  if (dir) {
    if (es == None) { // first movement activates Select mode
      es = Select;
      draw_selection(palette, selection, false);
    } else if (es == Select) {
      change_selection(palette, dir);  // Move selection
    } else if (es == Change) {
      //  bool fast = (fast_button.read() == HIGH);
      edit_patch(selection, -dir);       // Change parameter
    }
  }

  // Footswitch pressed
  if (footswitch.pressed()) {
    if (es == Select) {
      clear_selection(selection);
      es = None;

    }
    if (es != Change) {  // Patch change disabled during editing
      change_patch(1);

    }
  }
}

void rainbow(int wait) {
  for (long firstPixelHue = 0; firstPixelHue < 1 * 65536; firstPixelHue += 256) {
    for (int i = 0; i < pixels.numPixels(); i++) {
      int pixelHue = firstPixelHue + (i * 65536L / pixels.numPixels());
      pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue)));
    }
    pixels.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
    pixels.setPixelColor(0, 0, 0, 0);
    pixels.show(); // Update strip with new contents
  }
}

// - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / -



// Draw a rectangle in foreground colour optionally filled another colour
void draw_rect (uint8_t x, uint8_t y, uint8_t w, uint8_t h,
                uint16_t edgecol, uint16_t fillcol, boolean filled)
{
  if (w == 0 || h == 0)
    return;

  uint8_t e_red   = (edgecol & 0xf800) >> 10;
  uint8_t e_green = (edgecol & 0x07e0) >> 5;
  uint8_t e_blue  = (edgecol & 0x001f) << 1;

  uint8_t f_red   = (fillcol & 0xf800) >> 10;
  uint8_t f_green = (fillcol & 0x07e0) >> 5;
  uint8_t f_blue  = (fillcol & 0x001f) << 1;

  tft.drawRect(x, y, w, h, edgecol);

  if (filled)                             // filled rectangles take time to draw
    tft.fillRect(x, y, w, h, edgecol);
}

// Graphics **********************************************

// Clear display
void clear_display()
{
  tft.clearScreen();
  //  PINB = 1 << cs;                         // cs low
  //  Send(0x25);                             // Clear Window
  //  Send(0); Send(0); Send(95); Send(63);
  //  PINB = 1 << cs;                         // cs high
  //  delayMicroseconds(900);
  //delayMicroseconds(450);                 // half delay when clocking nano to 8 MHz
}

// Draw line to (x,y) in given colour
void draw_line (uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t col)
{
  uint8_t red   = (col & 0xf800) >> 10;
  uint8_t green = (col & 0x07e0) >> 5;
  uint8_t blue  = (col & 0x001f) << 1;

  tft.drawLine(x0, y0, x1, y1, col);
  //  PINB = 1 << cs;                         // cs low
  //  Send(0x21);                             // Draw Line
  //  Send(x0); Send(y0); Send(x1); Send(y1);
  //  Send(red); Send(green); Send(blue);
  //  PINB = 1 << cs;                         // cs high
}

// Plot a point at (x,y)
void plot_point (uint8_t x, uint8_t y, uint16_t col)
{
  draw_line(x, y, x, y, col);
}



// Plot character in given colour
void plot_char (uint8_t x, uint8_t y, uint8_t ch, uint16_t col, uint8_t scale)
{
  uint8_t red   = (col & 0xf800) >> 10;
  uint8_t green = (col & 0x07e0) >> 5;
  uint8_t blue  = (col & 0x001f) << 1;

  //  PINB = 1 << cs;                         // cs low
  for (uint8_t c = 0 ; c < 5; c++) {      // Column range
    uint8_t bits = pgm_read_byte(&font[ch - 32][c]);
    uint8_t r = 0;
    while (bits) {
      while ((bits & 1) == 0) {
        r++;
        bits = bits >> 1;
      }
      uint8_t on = (7 - r) * scale;
      while ((bits & 1) != 0) {
        r++;
        bits = bits >> 1;
      }
      uint8_t off = (7 - r) * scale + 1;
      for (int i = 0; i < scale; i++) {
        uint8_t h = x + c * scale + i;
        //        Send(0x21);                         // Draw line
        //        Send(h); Send(y + on); Send(h); Send(y + off);
        //        Send(red); Send(green); Send(blue);
      }
    }
  }
  //  PINB = 1 << cs;                           // cs high
}

// Plot text
void plot_text(uint8_t x, uint8_t y, const char* text, uint16_t col, uint8_t scale)
{
  while (1) {
    char c = *(text++);
    if (c == 0) return;
    plot_char(x, y, c, col, scale);
    x += 6;
  }
}

// Higher level functions for drawing ui elements

// convert unsigned byte to char array
// *s points to end() of buffer
char *btoa(uint8_t x, char *s)
{
  *--s = 0;
  if (!x) *--s = '0';
  for (; x; x /= 10)
    * --s = '0' + x % 10;
  return s;
}


// Master volume: -10 db - +4 db
// Mix: 0 - 50% - 100% (Not all values are possible, map to pot values)
// Valid values for dir are -1 and 1


// Draw volume -15 ------ 0 --- +4db / Mute
// Values given are 0-20.
void draw_volume(const uint16_t* palette, const uint16_t bg, const uint8_t pos, const uint8_t val)
{
  uint8_t start = pos * 35 + 1;
  uint8_t len = map(val, 0, 20, 0, 238);
  uint16_t barcol  = palette[3];
  uint16_t textcol = palette[3];

  tft.fillRect(1 , start + 18, 238, 8, bg);       // Erase old bar
  tft.fillRect(1 , start + 18, len, 8, barcol);   // Draw new bar

  tft.fillRect(178 , start, 34, 14, bg);        // Erase old value

  tft.setCursor(2, start);
  tft.setTextColor(textcol);
  tft.setTextSize(2);
  tft.print("Volume");

  // The code for constructing these on the fly
  // takes as much space, but the array is simpler.
  static const char db[21][4] = {"-18",
                                 "-16", "-15", "-14", "-13",
                                 "-12", "-11", "-10", " -9",
                                 " -8", " -7", " -6", " -5",
                                 " -4", " -3", " -2", " -1",
                                 " +0", " +1", " +2", " +3"
                                };


  tft.setCursor(178, start);
  tft.print(db[val]);

  tft.setCursor(215, start);
  tft.print("db");
}

// Draw mix as Dry ---  50% ---- Wet
// Values given are 0-20.
void draw_mix(const uint16_t* palette, const uint16_t bg, const uint8_t pos, const uint8_t val)
{
  uint8_t start = pos * 35 + 1;
  uint8_t len = map(val, 0, 20, 0, 238);
  uint16_t barcol  = palette[3];
  uint16_t textcol = palette[3];

  tft.fillRect(1 , start + 18, 238, 8, bg);        // Erase old bar
  tft.fillRect(1 , start + 18, len, 8, barcol);   // Draw new bar

  tft.setCursor(2, start);
  tft.setTextColor(textcol);
  tft.setTextSize(2);
  tft.print("Mix");

  char buff[4];                  // 3 chars + \0
  buff[0] = buff[1] = ' ';       // put spaces in tens and hundreds position

  btoa(val * 5, buff + 4); // give pointer to just after buffer (end)


  tft.fillRect(203 , start, 34, 14, bg);        // Erase old value
  tft.setCursor(203, start);
  tft.print(buff);
}


//  Draws bar with name and value
// Parameter values shown as 0-100%
void draw_parameter(const uint16_t* palette, const uint16_t bg, const char* text, const uint8_t pos, const uint8_t val)
{
  uint8_t start = pos * 35 + 1;
  uint8_t len = map(val, 0, 255, 0, 238);
  uint8_t perc = map(val, 0, 255, 0, 100);
  uint16_t barcol  = palette[3];
  uint16_t textcol = palette[3];

  tft.fillRect(1 , start + 18, 238, 8, bg);        // Erase old bar
  tft.fillRect(1 , start + 18, len, 8, barcol);   // Draw new bar
  tft.fillRect(2 , start, 110, 16, bg);        // Erase old part names
  tft.setCursor(2, start);
  tft.setTextColor(textcol);
  tft.setTextSize(2);
  tft.print(text);

  char buff[4];                  // 3 chars + \0 // add percent mark?
  buff[0] = buff[1] = ' ';       // put spaces in tens and hundreds position

  btoa(perc, buff + 4); // give pointer to just after buffer (end)

  // we want right justified, give buff instead of res
  tft.fillRect(203 , start, 34, 14, bg);        // Erase old value
  tft.setCursor(203, start);
  tft.print(buff);
}

The freezing sketch after changing pins to D7 & B0,
Blue led never lights up showing where the freeze happens


/*  Copyright 2020 Perttu Haimi

    This file is part of FVduino.

    FVduino 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.

    FVduino is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FVduino.  If not, see <https://www.gnu.org/licenses/>.

  My Changes

  1. Swapped fast / select buttons, short press encoder = select
  2. Set fast for pots 1-3
  3. Long press encoder for save
  4. Made select button more responsive
  5. Removed fast
  6. NeoPixel for Presets
  7. Turn off NoePixel if bypass fsw on tiny85 is off
  8. Replaced OLED with IPS TFT 1.54" 240x240
  ******328p IPS PCB v1.3******
*/

#include <Arduino.h>
//#include <Bounce2.h>
#include <EEPROM.h>
#include <ButtonEvents.h>
#include  <avr/power.h>

typedef unsigned char PROGMEM prog_uchar;

#include "HW_I2CMaster.h"
#include "font.h"

const uint16_t MARKED = 0x3000; //0x0020
const uint16_t NONE   = 0xFFFF; // this is used for rectangle inner color when not used

#define BypassFSW 0 // (Bypass FSW)

////////////////////////////////////////////////////////////////////////////////////////////////
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Arduino_ST7789_Fast.h>
#define TFT_DC  2 // (TFT DC)
#define TFT_RST 3 // (TFT Reset)
#define SCR_WD   240
#define SCR_HT   240
Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST);
////////////////////////////////////////////////////////////////////////////////////////////////

#undef min
#define min(a,b) ((a)<(b)?(a):(b))

// ----------------------------------------NEOPIXEL---------------------------------------------
#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN     A2 // (Led Pin)
#define PIXEL_COUNT   1

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);


// Yellow Preset 1
int r0 = 170;
int g0 = 170;
int b0 = 0;

// Green Preset 2
int r1 = 0;
int g1 = 170;
int b1 = 0;

// Red Preset 3
int r2 = 170;
int g2 = 0;
int b2 = 0;

// Grey Preset 4
int r3 = 86;
int g3 = 86;
int b3 = 86;

// Blue Preset 5
int r4 = 0;
int g4 = 0;
int b4 = 170;

// Magenta Preset 6
int r5 = 170;
int g5 = 0;
int b5 = 170;

// Orange Preset 7
int r6 = 154;
int g6 = 54;
int b6 = 0;

// Cyan Preset 8
int r7 = 0;
int g7 = 170;
int b7 = 170;

// ----------------------------------------NEOPIXEL---------------------------------------------


// Pin configuration

// Digital potentiometer uses hardware I2C (TWI), pins A4 and A5
const uint8_t POT_ENABLE_PIN = A3; // (Pot Enable)
//const uint8_t POT_SDA_PIN = A4; (Pot SDA)
//const uint8_t POT_SCL_PIN = A5; (Pot SCL)

// Three PWM outputs
const uint8_t PAR1_PWM_PIN = 9; // (PWM 1) OC1A
const uint8_t PAR2_PWM_PIN = 6; // (PWM 2) OC0A
const uint8_t PAR3_PWM_PIN = 5; // (PWM 3) OC0B

// Two buttons.
const uint8_t PresetFSW = 1; // (Preset Footswitch)
const uint8_t buttonPin = A1;   // Select/Save Encoder Button (Encoder SW)

//ButtonEvents myButton; // create an instance of the ButtonEvents class to attach to our button

// Encoder
const uint8_t ENC1_PIN = A0; // (Encoder Pin 1)
const uint8_t ENC2_PIN = 10; // (Encoder Pin 2)

// EEPROM emulation and FV-1 program switching
const uint8_t PC_NOTIFY_PIN = 4; // (Notify FV1 s0)
const uint8_t I2C_EEPROM_EMUL_SDA_PIN = 7; // (FV1 SDA) Was Pin 5
const uint8_t I2C_EEPROM_EMUL_SCL_PIN = 8; // (FV1 SCL) Was Pin 6
const uint8_t EEPROM_EMUL_ADDR = 0x50;

// OLED display commununication
// For reference here, oled code uses port and bit banging
//const uint8_t OLED_DATA_PIN = 13;
//const uint8_t OLED_CS_PIN   = 12;
//const uint8_t OLED_CLK_PIN  = 11;

// Digipot address
const uint8_t DS1881_BASE_I2C_ADDR = 0x28;
const uint8_t DS1881_write_address = DS1881_BASE_I2C_ADDR << 1;
const uint8_t DS1881_read_address = (DS1881_BASE_I2C_ADDR << 1) | _BV(0);


/*********** FV-1 algorithm data structures ***********/

// The eeprom data is in 16 * 32 byte banks (total 512 bytes)
// They are compressed by LZ algorithm (compress.py) to approx.
// half the size. Include the algorithm data blocks here.

#include "spinsemi.h"



// Strings for describing the algorithm and parameters
typedef struct {
  char algoname [14];
  char par1name [10];
  char par2name [10];
  char par3name [10];
} Description;

// Sensible default settings for each algorithm
typedef struct {
  uint8_t vol;
  uint8_t mix;
  uint8_t par1;
  uint8_t par2;
  uint8_t par3;
} Default;

// Keep everything together in a single array
typedef struct {
  Description desc;
  Default def;
  uint8_t* const prog_addr;
} AlgoDatum;

const AlgoDatum algodata [] PROGMEM = {

  {{"Hall Reverb",   "Pre-delay", "Time", "Dampen" }, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_0_Hall},
  {{"Room Reverb",   "Pre-delay", "Time", "Dampen" }, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_1_Room},
  {{"Plate Reverb",  "Pre-delay", "Time", "Dampen" }, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_2_PLATE},
  {{"Rep Echo/Rev",  "Delay", "Repeats",   "Reverb"}, {15, 10, 128, 128, 128}, (uint8_t *)K3_V1_4_ECHO_REV},
  {{"Chor Reverb",   "Width", "Sweep", "Reverb"}, {15, 20, 128, 128, 128}, (uint8_t *)K3_V1_6_CHOR_REV},
  {{"FlangerVerb",   "Reverb", "Rate",  "Depth"}, {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_FLANGE},
  {{"Phaser Verb",    "Reverb", "Rate",  "Width"}, {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_PHASE},
  {{"Vibrato Verb",   "Reverb", "Speed", "Depth"}, {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_VIBRATO},
  {{"Filter Verb",   "Reverb", "Sens", "Depth"},  {15, 20, 128, 128, 128}, (uint8_t *)GA_DEMO_WAH},
  {{"Tremolo+Rev",   "Reverb", "Rate",  "Tremolo" }, {15, 10, 128, 128, 128}, (uint8_t *)rom_trem_rev},

  {{"Spring+Trem",   "Reverb", "Rate", "Depth"},  {15, 10, 128, 128, 128}, (uint8_t *)spring_verb},
  {{"Reverse Dly",   "Bit", "Feedback", "Delay"}, {15, 10, 128, 128, 128}, (uint8_t *)afx},
  {{"Abbey Verb",    "Reverb", "Feedback", "Damp"}, {15, 20, 128, 128, 128}, (uint8_t *)abbeyverb},
  {{"Choirsaw",      "Delay", "Width", "Tremolo"},  {15, 10, 128, 128, 128}, (uint8_t *)choirsaw},
  {{"Binson",        "Mix", "Prog", "Feedback"}, {15, 10, 128, 128, 128}, (uint8_t *)bluenebula},
  {{"Shimmer",       "Dwell", "Pitch", "Mix"},   {15, 10, 128, 128, 128}, (uint8_t *)glimmer},
  {{"Flanger",       "Speed", "Depth", "Feedback"}, {15, 10, 128, 128, 128}, (uint8_t *)flanger},
  {{"Dual Pitch",     "Pitch1", "Mix", "Pitch2"},      {15, 10, 128, 128, 128}, (uint8_t *)dualpitch},
  {{"Daydream",       "Time", "Feedback", "Filter"},   {15, 10, 128, 128, 128}, (uint8_t *)daydream},
  {{"Glitch Dly",     "Speed", "Time", "Random"},      {15, 10, 128, 128, 128}, (uint8_t *)greenwood},

  {{"Bit Crusher",    "Crush", "Filter", "Detune"},    {15, 10, 128, 128, 128}, (uint8_t *)crusher},
  {{"Chorus Dual",    "Rate1", "Rate2", "Depth"},      {15, 10, 128, 128, 128}, (uint8_t *)chorusdualrate},
  {{"Space Delay",      "Time", "Filter", "Feedback"},   {15, 10, 128, 128, 128}, (uint8_t *)spacedelay},
  {{"Sample&Hold",            "Time", "Feedback", "Mod"},      {15, 10, 128, 128, 128}, (uint8_t *)sh},
  {{"Ring Pitch",     "Rate", "Blend", "Pitch"},       {15, 10, 128, 128, 128}, (uint8_t *)ringpitchulator},
  {{"ReverseVerb",   "Pre-delay", "Decay", "Damp"},     {15, 10, 128, 128, 128}, (uint8_t *)RevRev},
  {{"Dual Gl3",       "Speed", "Reson", "Feedback"},   {15, 10, 128, 128, 128}, (uint8_t *)dualglitch3},
  {{"Starfield",      "Time", "Feedback", "Phaser"},   {15, 10, 128, 128, 128}, (uint8_t *)starfield2},
  {{"Filter Trem",    "Speed", "Reson", "Gain"},       {15, 10, 128, 128, 128}, (uint8_t *)bass6},
  {{"Trem Dly",       "Pitch", "Speed", "Regen"},      {15, 10, 128, 128, 128}, (uint8_t *)bass7},

  {{"Dr.Bohm",        "Reverb", "Feedback", "Speed"},  {17, 20, 128, 128, 128}, (uint8_t *)Cathedral},
  {{"Rotor",          "Reverb", "Depth", "Speed"},     {17, 20, 128, 128, 128}, (uint8_t *)Rotor},
  {{"Solina",         "Reverb", "Blend", "Treble"},    {17, 20, 128, 128, 128}, (uint8_t *)Ensemble},
  {{"Fading",         "D-mix", "Depth", "Reson"},      {17, 20, 128, 128, 128}, (uint8_t *)Fading},
  {{"PolySixChor",    "Reverb", "Blend", "Treble"},    {17, 20, 128, 128, 128}, (uint8_t *)PolySixChor},
  {{"Rototrem",       "Reverb", "Warp", "Speed"},      {17, 20, 128, 128, 128}, (uint8_t *)Tremolo},
  {{"Leslie",         "Reverb", "Filter", "Speed"},    {17, 20, 128, 128, 128}, (uint8_t *)Leslie},
  {{"EQ/Width",       "Bass", "Treble", "Width"},      {17, 20, 128, 128, 128}, (uint8_t *)EQstereoWidth},
  {{"Para EQ",        "Freq", "Q-Band", "Q-Peak"},     {15, 10, 128, 128, 128}, (uint8_t *)para_eq},
  {{"Parallax",       "Time", "Feedback", "Trem"},     {15, 10, 128, 128, 128}, (uint8_t *)parallax},
};

// The space for printing algo number on screen is two characters
// In practice, about 80 algorithms is the max that fits on atmega328 32k memory
const uint8_t NALGO = min(99, sizeof(algodata) / sizeof(AlgoDatum));
// Algorithm will be copied to RAM before sending because PROGMEM is too slow
uint8_t algo_buffer[512];

// The strings of the currently visible algorithm are copied to ram
Description current_algo_strings;

/************** Patch data structures *****************/

typedef struct {
  uint8_t algorithm; // index
  uint8_t vol;
  uint8_t mix;
  uint8_t par1;
  uint8_t par2;
  uint8_t par3;
} Patch;

// 5 RGB 565 colours for each patch, from dark to light
const uint16_t patch_colors [][5] = {
  {0x5940, 0xD1A0, 0xFC20, 0xFD80, 0xFE85},   // Amber
  {0x0220, 0x0C80, 0x3640, 0x4F29, 0xA7D4},   // Green
  {0x7820, 0xE060, 0xFA8A, 0xFBCF, 0xFDD7},   // Red
  {0x31E8, 0x4ACC, 0x638F, 0xA577, 0xC639},   // Gray
  {0x00C9, 0x0230, 0x0457, 0x965B, 0xDF5E},   // Blue
  {0x3807, 0x8811, 0xF81F, 0xFC5F, 0xFEDF},   // Mag
  {0x8285, 0x9307, 0xBBE9, 0xD52D, 0xFFFF},   // Brown
  {0x1112, 0x73FD, 0xBD5F, 0x36BC, 0xAFDF},   // Cyan
};

const uint8_t NPATCH = sizeof(patch_colors) / sizeof(patch_colors[0]);

Patch patch_data[NPATCH]; // Keep all patch data in memory, store and restore in EEPROM

const uint16_t P_IN_USE_EE_ADDR = 0;
const uint16_t PATCH_EE_ADDR = 1;
const uint16_t ALGO_EE_ADDR = 512;

uint8_t changed[NPATCH];        // to keep track of patch edit

// State machine for user interface
enum EditState {None, Select, Change};

EditState es = None;
uint8_t selection = 0;            // default start from line 5
uint8_t patches_in_use = 2;
uint8_t current_patch = 0;
uint16_t bg_col = BLACK;          // bg_col will be different if patch is edited but not saved


/************** Digipot code ****************/


/* Calculate actual pot positions from vol and mix values

   vol goes from 0 to 20
   17 is unity gain (corresponding to pot volume 60)

   mix goes from 0 to 20
   10 is 1:1 mix max wet - max dry
   below 10 is less wet - max dry
   above 10 is max wet  - less dry
*/
uint8_t dry_from(uint8_t vol, uint8_t mix)
{
  if (mix > 20)
    mix = 20;

  if (vol > 20)
    vol = 20;

  if (vol == 0 || mix == 20)
    return 0;  // dry is muted

  uint8_t nominal_vol = 43 + vol; // Max 63

  if (mix <= 10)
    return nominal_vol; // dry is maxed

  uint8_t vol_adjustment = mix - 10;
  if (vol_adjustment > nominal_vol)
    return 0;           // dry is muted

  return nominal_vol - vol_adjustment;
}

uint8_t wet_from(uint8_t vol, uint8_t mix)
{

  if (mix > 20)
    mix = 20;

  if (vol > 20)
    vol = 20;

  if (vol == 0 || mix == 0)
    return 0;  // wet is muted

  uint8_t nominal_vol = 43 + vol; // Max 63

  if (mix >= 10)
    return nominal_vol; // wet is maxed

  uint8_t vol_adjustment = 10 - mix;
  if (vol_adjustment > nominal_vol)
    return 0;           // wet is muted

  return nominal_vol - vol_adjustment;
}


/* Run only once during factory default setup.

*/
bool pot_init()
{
  digitalWrite(POT_ENABLE_PIN, LOW); // enable pot communication

  if (!i2c_start(DS1881_write_address))
    return false; // no reply from device

  i2c_write(0b10000010);       // Nonvolatile writes: Zero crossing, 63 values + mute
  i2c_stop();
  //delay(20); // wait for EEPROM write to finish
  delay(10); // half delay when clocking nano to 8 MHz

  i2c_start(DS1881_write_address);
  i2c_write(0b10000110);       // Go back to volatile writes
  i2c_stop();
  //delay(20); // wait for EEPROM write to finish
  delay(10); // half delay when clocking nano to 8 MHz

  digitalWrite(POT_ENABLE_PIN, HIGH);

  return true;
}

/* Set the digipot to given value.
   Min volume 0: -80db attenuation,
   Max volume 63:  0db attenuation
*/
bool pot_set(uint8_t pot, uint8_t val)
{
  val &= 0b00111111; // only the low 6 bits matter

  val = 63 - val;    // invert the value.
  // pot acts as attenuator: 0 - no attenuation (max volume)
  // we want larger number to mean louder

  if (pot)
    val |= 0b01000000; // set the pot bit;

  digitalWrite(POT_ENABLE_PIN, LOW); // enable pot communication

  if (!i2c_start(DS1881_write_address))
    return false;  // no reply from device

  bool res = i2c_write(val);
  i2c_stop();

  digitalWrite(POT_ENABLE_PIN, HIGH);

  return res;
}


/*************** User Interface code **********************/
using Bounce2::Button;

Button footswitch = Button();

ButtonEvents myButton; // create an instance of the ButtonEvents class to attach to our button

void draw_selection(const uint16_t* colors, uint8_t pos, bool emph)
{
  uint8_t y = pos * 35;
  uint16_t col = emph ? colors[4] : colors[2];

  draw_rect(0 , y, SCR_WD , 30, GREEN, NONE, false); // Selection box, was col
}


void clear_selection(uint8_t pos)
{
  uint8_t y = pos * 35;

  draw_rect(0 , y, SCR_WD , 30, bg_col, NONE, false);
}


void draw_patch ()
{
  tft.fillRect(1, 1, SCR_WD, 27, bg_col);
  //  tft.fillRect(1, 210, SCR_WD, 25, bg_col); // clear algorithm name area with background color
  char buff[3];              // 3 chars + \0
  buff[0] =  ' ';            // put spaces in tens and hundreds position

  byte algo = patch_data[current_patch].algorithm + 1;
  btoa(algo, buff + 3); // give pointer to just after buffer (end)

  // Print algo number

  //tft.drawFastHLine(3, 27, 237, WHITE);

  tft.setCursor(214, 2);
  //  tft.setCursor(200, 6 * 35 + 1);
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.print(buff); // Algo number

  // Print algo name

  tft.setCursor(2, 2);
  //  tft.setCursor(2, 6 * 35 + 1);
  tft.setTextColor(WHITE);
  tft.setTextSize(3);
  tft.print(current_algo_strings.algoname); // Algo name

  const uint16_t * palette = patch_colors[current_patch];

  clear_selection(1);
  draw_volume(palette, bg_col, 1, patch_data[current_patch].vol);
  clear_selection(2);
  draw_mix(palette, bg_col, 2, patch_data[current_patch].mix);
  clear_selection(3);
  draw_parameter(palette, bg_col, current_algo_strings.par1name, 3, patch_data[current_patch].par1);
  clear_selection(4);
  draw_parameter(palette, bg_col, current_algo_strings.par2name, 4, patch_data[current_patch].par2);
  clear_selection(5);
  draw_parameter(palette, bg_col, current_algo_strings.par3name, 5, patch_data[current_patch].par3);
}

void draw_panel()
{
  uint8_t boxlen = SCR_WD / NPATCH; // Preset boxes

  //tft.drawRect(1, 6 * 35 + 1, 238, 25, bg_col);
  tft.fillRect(1, 6 * 35 + 1, 238, 25, bg_col);
  //  tft.drawRect(1, 1, 238, 25, bg_col);
  //  tft.fillRect(1, 1, 238, 25, bg_col);

  for (uint8_t i = 0; i < patches_in_use; ++i) {
    if (i == current_patch) {
      //tft.drawRect(boxlen * i , 6 * 35 + 1, boxlen - 2, 25, patch_colors[i][2]);
      tft.fillRect(boxlen * i + 1, 6 * 35 + 1, boxlen - 3, 25, patch_colors[i][3]);
      //      tft.drawRect(boxlen * i , 1, boxlen - 2, 25, patch_colors[i][2]);
      //      tft.fillRect(boxlen * i , 1, boxlen - 2, 25, patch_colors[i][3]);
    } else {
      //tft.drawRect(boxlen * i , 6 * 35 + 1, boxlen - 2, 25, bg_col);
      tft.fillRect(boxlen * i + 1, 6 * 35 + 10, boxlen - 3, 15, patch_colors[i][3]);
      //      tft.drawRect(boxlen * i , 1, boxlen - 2, 25, bg_col);
      //      tft.fillRect(boxlen * i , 1, boxlen - 2, 25, patch_colors[i][0]);
    }
  }
}


void change_selection(const uint16_t * palette, int8_t dir)
{
  if (dir < -1) dir = -1;
  if (dir > 1) dir = 1;

  uint8_t oldsel = selection;
  selection += dir + 7;
  selection %= 7;
  clear_selection(oldsel);
  draw_selection(palette, selection, false);
}

void set_patch()
{
  // Turn digipots
  uint8_t dry = dry_from(patch_data[current_patch].vol, patch_data[current_patch].mix);
  uint8_t wet = wet_from(patch_data[current_patch].vol, patch_data[current_patch].mix);

  pot_set(0, dry);
  pot_set(1, wet);

  // Set analog signal to FV-1
  analogWrite(PAR1_PWM_PIN, patch_data[current_patch].par1);
  analogWrite(PAR2_PWM_PIN, patch_data[current_patch].par2);
  analogWrite(PAR3_PWM_PIN, patch_data[current_patch].par3);
}

void change_patch(int8_t dir)
{
  current_patch += dir + patches_in_use;
  current_patch %= patches_in_use;

  bg_col = changed[current_patch] ? MARKED : BLACK;

  // Save active patch to EEPROM?
  // 100000 writes promised -> 1000 days 100 patch changes per day
  // Maybe not worth it...

  send_algo();
  set_patch();

  // copy current patch strings for ui
  uint8_t algo = patch_data[current_patch].algorithm;
  memcpy_P(&current_algo_strings, &(algodata[algo].desc), sizeof current_algo_strings);

  // print changed gui
  draw_panel();
  draw_patch();

}

// Valid values for dir are -1 and 1
void edit_algo(int8_t dir)
{

  patch_data[current_patch].algorithm += dir + NALGO;
  patch_data[current_patch].algorithm %= NALGO;

  uint8_t algo = patch_data[current_patch].algorithm;

  // Copy current patch strings for ui
  memcpy_P(&current_algo_strings, &(algodata[algo].desc), sizeof current_algo_strings);

  // Set params to default values. Copy 5 bytes from EEPROM
  Default tmp;
  EEPROM.get(ALGO_EE_ADDR + algo * sizeof(Default),  tmp);

  patch_data[current_patch].vol  = tmp.vol;
  patch_data[current_patch].mix  = tmp.mix;
  patch_data[current_patch].par1 = tmp.par1;
  patch_data[current_patch].par2 = tmp.par2;
  patch_data[current_patch].par3 = tmp.par3;

  changed[current_patch] = true;

  send_algo();
  set_patch();

  // print updated elements
  draw_patch();

  const uint16_t * palette = patch_colors[current_patch];
  draw_selection(palette, selection, true);
}


void edit_vol(int8_t dir, const uint8_t pos)
{
  uint8_t vol = patch_data[current_patch].vol;

  if (vol == 0 && dir < 0)
    return; // At minimum
  if (vol == 20 && dir > 0)
    return; // Maxed out

  vol += dir;

  // Change the patch
  patch_data[current_patch].vol = vol;
  changed[current_patch] = true;

  // Turn the digipots.
  uint8_t dry = dry_from(vol, patch_data[current_patch].mix);
  uint8_t wet = wet_from(vol, patch_data[current_patch].mix);
  pot_set(0, dry);
  pot_set(1, wet);

  // Draw the change
  const uint16_t * palette = patch_colors[current_patch];
  draw_volume(palette, bg_col, pos, vol);
}

void edit_mix(int8_t dir, const uint8_t pos)
{
  uint8_t mix = patch_data[current_patch].mix;

  if (mix ==  0 && dir < 0)
    return;  // At minimum
  if (mix == 20 && dir > 0)
    return;  // At maximum

  mix += dir;

  // Change the patch
  patch_data[current_patch].mix = mix;
  changed[current_patch] = true;

  // Turn the digipots.
  uint8_t dry = dry_from(patch_data[current_patch].vol, mix);
  uint8_t wet = wet_from(patch_data[current_patch].vol, mix);
  pot_set(0, dry);
  pot_set(1, wet);

  // Draw the change
  const uint16_t * palette = patch_colors[current_patch];
  draw_mix(palette, bg_col, pos, mix);
}

// Valid values for dir are -1 and 1
//void edit_par(int8_t dir, bool fast, uint8_t pin, uint8_t &val, const char *parname, const uint8_t pos)
void edit_par(int8_t dir, uint8_t pin, uint8_t &val, const char *parname, const uint8_t pos)
{
  // if (fast)
  dir *= 10;

  if (dir < 0 && val < -dir)
    val = 0;
  else if (dir > 0 && (255 - val) < dir)
    val = 255;
  else
    val += dir;

  const uint16_t * palette = patch_colors[current_patch];

  changed[current_patch] = true;

  analogWrite(pin, val);
  draw_parameter(palette, bg_col, parname, pos, val);
}

void edit_menubar(int8_t dir)
{
  if (dir < -1) dir = -1;
  if (dir > 1) dir = 1;

  // patches_in_use must be in [2, NPATCH]
  if (patches_in_use <= 2 && dir < 0)
    return;
  if (patches_in_use >= NPATCH && dir > 0)
    return;

  const uint16_t * palette = patch_colors[current_patch];

  patches_in_use += dir;

  if (current_patch >= patches_in_use) {
    change_patch(-1);  // move to previous patch since current is not in use
    draw_selection(palette, selection, true);
  } else {
    draw_panel();
    draw_selection(palette, selection, true);
  }

  // write number of patches in use to eeprom
  EEPROM.put(P_IN_USE_EE_ADDR, patches_in_use);
}

//void edit_patch(uint8_t sel, int8_t dir, bool fast)
void edit_patch(uint8_t sel, int8_t dir)
{
  switch (sel) {
    case 0 : edit_algo(-dir);     break;  // Changed dir
    case 1 : edit_vol(-dir, sel); break;
    case 2 : edit_mix(-dir, sel); break;
    case 3 : edit_par(-dir, PAR1_PWM_PIN, patch_data[current_patch].par1, current_algo_strings.par1name, sel); break;
    case 4 : edit_par(-dir, PAR2_PWM_PIN, patch_data[current_patch].par2, current_algo_strings.par2name, sel); break;
    case 5 : edit_par(-dir, PAR3_PWM_PIN, patch_data[current_patch].par3, current_algo_strings.par3name, sel); break;
    case 6 : edit_menubar(-dir); break;
  }
}

void save_current_patch()
{
  uint16_t eeAddress = PATCH_EE_ADDR + current_patch * sizeof(Patch);
  Patch curr = patch_data[current_patch];

  EEPROM.put(eeAddress, curr);

  // Save to algo defaults also so that next time they can be used

  eeAddress = ALGO_EE_ADDR + curr.algorithm * sizeof(Default);
  Default tmp = {curr.vol, curr.mix, curr.par1, curr.par2, curr.par3};

  EEPROM.put(eeAddress, tmp);

  changed[current_patch] = false;
  bg_col = BLACK;
  draw_panel();
  draw_patch();             // Redraw the whole screen
}



/********** 24LC32A EEPROM emulation */

/* Compression algorithm based on
   http://excamera.com/sphinx/article-compression.html

   With small modifications.
*/
class Flashbits {
  public:

    void begin(prog_uchar *s)
    {
      src = s;
      mask = curr_byte = 0x00;
    }

    uint8_t get1(void)
    {
      if (!mask) {
        mask = 0x80;
        curr_byte = pgm_read_byte_near(src++);
      }

      uint8_t r = (curr_byte & mask) != 0;
      mask >>= 1;

      return r;
    }

    uint16_t getn(uint8_t n)
    {
      uint16_t r = 0;
      while (n--) {
        r <<= 1;
        r |= get1();
      }
      return r;
    }

  private:
    prog_uchar *src;
    uint8_t mask;
    uint8_t curr_byte;
};

Flashbits bits;

void decompress(uint8_t *dst, prog_uchar *src)
{
  bits.begin(src);
  uint8_t O = bits.getn(4);
  uint8_t L = bits.getn(4);
  uint8_t M = bits.getn(2);

  uint8_t *end = dst + bits.getn(16);

  while (dst != end) {
    if (bits.get1() == 0) {
      *dst++ = bits.getn(8);
    } else {
      int offset = -bits.getn(O) - 1;
      int length = bits.getn(L) + M;
      while (length--) {
        *dst = *(dst + offset);
        dst++;
      }
    }
  }
}

void send_algo()
{
  // Copy FV-1 algorithm from PROGMEM to ram buffer.
  // Progmem is too slow in the send loop.
  // Since we will anyway have to copy from PROGMEM,
  // it makes sense to compress the algorithms and get
  // space savings (almost 2x).

  uint8_t algo = patch_data[current_patch].algorithm;
  decompress(algo_buffer, (prog_uchar *)pgm_read_word(&(algodata[algo].prog_addr)));

  // Set up everything to be ready
  const uint8_t sda_mask = (1 << PIND7); // Pin11 (was D5 Pin9)
  const uint8_t clk_mask = (1 << PINB0); // Pin12 (was D6 Pin10)
  uint8_t prev_clk = clk_mask;

  DDRB &= ~(clk_mask);   // clk pin as input  (was DDRD)
  DDRD &= ~(sda_mask);   // sda pin as input
  PORTD &= ~(sda_mask);  // SDA pin is pulled low or floated up
  // -> we need to set pin low only once.
  //    Toggling is done by setting pin to
  //    input (let float high) or output (pull low).
  //    FV-1 internal pullups are ok (tested).

  uint16_t pos = 0;
  uint8_t curr_byte = algo_buffer[pos];
  uint8_t bit_mask = 0b10000000;
  uint8_t clk_count = 0;

  // Undivided attention for FV-1 requests
  noInterrupts();

  // Notify FV-1 of patch change by toggling the notify pin
  PIND = _BV(4);   // Toggle pin 4
 
  while (clk_count < 37)             // Handle the header
  {
    uint8_t clk = PIND & clk_mask;

    if (!clk && prev_clk) {          // scl went down

      switch (clk_count)
      {
        case 8:
        case 17:
        case 26:
        case 36:
          DDRD  |= sda_mask;         // send ACK - pull sda pin low
          break;
        default:
          DDRD &= ~(sda_mask);       // Release
          break;
      }
      clk_count++;
    }
    prev_clk = clk;
  }
  
  ////Freezes Here////
  
  //// Blue Test Led //// Blue Test Led //// Blue Test Led ////
  pixels.begin();
  pixels.setPixelColor(0, 0, 0, 50); // BLUE
  pixels.show(); 
  //// Blue Test Led //// Blue Test Led //// Blue Test Led ////
  
  clk_count = 0;
  while (pos < 512)                  // Send the data
  {
    uint8_t clk = PIND & clk_mask;

    if (!clk && prev_clk) {          // scl went down

      if (clk_count != 8) {          // Sending byte

        if (curr_byte & bit_mask) {
          DDRD &= ~(sda_mask);       // Send 1 = Let High
        } else {
          DDRD  |= sda_mask;         // Send 0 = Pull Low
        }
        bit_mask >>= 1;
        clk_count++;

      } else {                       // Let reciever ACK

        DDRD &= ~(sda_mask);         // Release
        clk_count = 0;
        bit_mask = 0b10000000;
        pos++;
        curr_byte = algo_buffer[pos];
      }
    }
    prev_clk = clk;
  }

  interrupts();

}


void setup()
{

  // ----------------------------------------NEOPIXEL---------------------------------------------
  pixels.begin();
  pixels.setPixelColor(0, 255, 255, 0);
  pixels.show();
  // ----------------------------------------NEOPIXEL---------------------------------------------

  // TFT Display setup
  //delay(200);
  tft.init();
  tft.setRotation(0);
  //tft.setRotation(0);  // Pins at bottom
  tft.fillScreen(BLACK); // Black
  //delay(200);

  // print initial gui
  //  draw_panel();
  //  draw_patch();

  // set up pins

  // Use high frequency PWM
  TCCR1A = (TCCR1A & 0b11111100) | 0b00000001;    // set timer 1 to 8 bit mode
  TCCR1B = (TCCR1B & 0b11100000) | 0b00001001;    // set timer 1 to fast PWM, divisor to 1 for PWM frequency of 31372.55 Hz

  TCCR2A = (TCCR2A & 0b11111100) | 0b00000011;    // set timer 2 to fast PWM
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000001;    // set timer 2 divisor to 1 for PWM frequency of 31372.55 Hz

  // PWM outputs
  pinMode(PAR1_PWM_PIN, OUTPUT);
  pinMode(PAR2_PWM_PIN, OUTPUT);
  pinMode(PAR3_PWM_PIN, OUTPUT);

  // 24LC32 EEPROM emulation
  pinMode(PC_NOTIFY_PIN, OUTPUT);
  pinMode(I2C_EEPROM_EMUL_SDA_PIN, INPUT);
  pinMode(I2C_EEPROM_EMUL_SCL_PIN, INPUT);



  // Buttons and encoder

  pinMode(buttonPin, INPUT_PULLUP);
  myButton.attach(buttonPin);
  myButton.activeHigh();
  myButton.debounceTime(15); //apply 5ms debounce
  myButton.holdTime(750); // require button to be held for 1000ms before triggering a hold event
  footswitch.attach(PresetFSW, INPUT);
  footswitch.interval(5);

  pinMode(ENC1_PIN, INPUT_PULLUP);
  pinMode(ENC2_PIN, INPUT_PULLUP);

  pinMode(BypassFSW, INPUT_PULLUP);

  // SoftI2C is used for digipot control
  pinMode(POT_ENABLE_PIN, OUTPUT);

  i2c_init(); // Uses hardware I2C (pins A4 and A5)

  //  pixels.setPixelColor(0, 0, 255, 0);
  //  pixels.show();

  // Write factory defaults to EEPROM if requested
  // Footswitch pressed at boot for 1 sec

  unsigned long startTime = millis();
  unsigned long heldTime = 0;

  while (digitalRead(PresetFSW) == HIGH)

  {
    heldTime = millis() - startTime;
    if (heldTime > 1000)
      break;
  }

  if (heldTime > 1000) {

    // Digipot initial settings, saved in its EEPROM.
    // Depends on i2c_init(). Hangs setup if no pot present...
    pot_init();

    // Write algorithm default settings
    Default tmp_d;
    for (uint8_t i = 0; i < NALGO; ++i) {
      memcpy_P(&tmp_d, &(algodata[i].def), sizeof(Default));
      EEPROM.put(ALGO_EE_ADDR + i * sizeof(Default), tmp_d);
      delay(10);
    }

    // Write empty patches using the first algorithm and default settings
    memcpy_P(&tmp_d, &(algodata[0].def), sizeof(Default));
    Patch tmp_p = {0, tmp_d.vol, tmp_d.mix, tmp_d.par1, tmp_d.par2, tmp_d.par3};

    for (uint8_t i = 0; i < NPATCH; ++i) {
      EEPROM.put(PATCH_EE_ADDR + i * sizeof(Patch), tmp_p);
      delay(10);
    }

    // Set 8 patches as default
    EEPROM.put(P_IN_USE_EE_ADDR, (uint8_t)8);

    rainbow(2);
    pixels.show();

  }


  // load patch settings from EEPROM
  uint16_t eeAdress = PATCH_EE_ADDR;
  for (uint8_t i = 0; i < NPATCH; ++i) {
    EEPROM.get(eeAdress, patch_data[i]);
    if (patch_data[i].algorithm >= NALGO) {
      patch_data[i].algorithm = 0;   // something wrong with algo index, set to zero
      changed[i] = true;             // user will have to edit and save
    }
    if (patch_data[i].vol > 20)
      patch_data[i].vol = 20;
    if (patch_data[i].mix > 100)
      patch_data[i].mix = 100;
    eeAdress += sizeof(Patch);
  }

  current_patch = 0;

  // get patches_in_use from EEPROM
  EEPROM.get(P_IN_USE_EE_ADDR, patches_in_use);
  if (patches_in_use < 2)
    patches_in_use = 2;
  if (patches_in_use >= NPATCH)
    patches_in_use = NPATCH;

  // copy current patch strings for ui
  uint8_t algo = patch_data[current_patch].algorithm;
  memcpy_P(&current_algo_strings, &(algodata[algo].desc), sizeof current_algo_strings);

//  draw_panel();
//  draw_patch();

  //////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST
  pixels.setPixelColor(0, 0, 55, 0); // GREEN
  pixels.show(); // This works
  //////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST//////TEST



  /////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE
  // send current patch algorithm to FV-1
  send_algo();
  /////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE/////FREEZES//HERE
  set_patch();
  
  pixels.setPixelColor(0, 55, 0, 0); // RED
  pixels.show();

  //  // TFT Display setup
  //  //delay(200);
  //  tft.init();
  //  tft.setRotation(1);
  //  //tft.setRotation(0);  // Pins at bottom
  //  tft.fillScreen(BLACK); // Black
  //  //delay(200);
  //
  //  // print initial gui
    draw_panel();
    draw_patch();

}


/* Rotary encoder handling code from:
   https://www.best-microcontroller-projects.com/rotary-encoder.html
*/
uint8_t prevNextCode = 0;
uint16_t store = 0;

// A valid CW or CCW move returns 1 or -1, invalid returns 0.
int8_t read_rotary() {
  const int8_t rot_enc_table[] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};

  prevNextCode <<= 2;
  if (digitalRead(ENC1_PIN)) prevNextCode |= 0x02;
  if (digitalRead(ENC2_PIN)) prevNextCode |= 0x01;
  prevNextCode &= 0x0f;

  // If valid then store as 16 bit data.
  if  (rot_enc_table[prevNextCode] ) {
    store <<= 4;
    store |= prevNextCode;
    if ((store & 0xff) == 0x2b) return -1;
    if ((store & 0xff) == 0x17) return 1;
  }
  return 0;

}


void loop()
{
  if (digitalRead(BypassFSW) == HIGH)
  {
    if (current_patch == 0) {
      pixels.setPixelColor(0, r0, g0, b0); // Yellow
      pixels.show();
    }
    if (current_patch == 1) {
      pixels.setPixelColor(0, r1, g1, b1); // Green
      pixels.show();
    }
    if (current_patch == 2) {
      pixels.setPixelColor(0, r2, g2, b2);  // Red
      pixels.show();
    }
    if (current_patch == 3) {
      pixels.setPixelColor(0, r3, g3, b3); // Grey
      pixels.show();
    }
    if (current_patch == 4) {
      pixels.setPixelColor(0, r4, g4, b4); // Blue
      pixels.show();
    }
    if (current_patch == 5) {
      pixels.setPixelColor(0, r5, g5, b5); // Magenta
      pixels.show();
    }
    if (current_patch == 6) {
      pixels.setPixelColor(0, r6, g6, b6); // Orange
      pixels.show();
    }
    if (current_patch == 7) {
      pixels.setPixelColor(0, r7, g7, b7); // Cyan
      pixels.show();
    }
  }
  if (digitalRead(BypassFSW) == LOW)
  {
    pixels.setPixelColor(0, 0, 0, 0); // Off
    pixels.show();

  }
  const uint16_t * palette = patch_colors[current_patch];
  if (myButton.rose() == true) {
    if (es == None) {
      es = Select;
      draw_selection(palette, selection, false);
    } else if (es == Select) {
      es = Change;
      // emphasize bar that is being edited
      draw_selection(palette, selection, true);
    } else if (es == Change) {
      es = None;
      if (changed[current_patch]) {
        bg_col = MARKED;
        draw_panel();
        draw_patch();             // Redraw the whole screen
      }
      clear_selection(selection);
    }
  }



  int8_t dir = read_rotary();

  if (myButton.update() == true) {
    switch (myButton.event()) {

      // things to do if the button was held (Hold = Save)
      case (hold) : {
          if (changed[current_patch]) {
            save_current_patch();
            es = None;                               // Go to default mode
            clear_selection(selection);

            rainbow(1);
            pixels.show();
          }
          break;
        }
    }
  }

  footswitch.update();

  // Encoder turned
  if (dir) {
    if (es == None) { // first movement activates Select mode
      es = Select;
      draw_selection(palette, selection, false);
    } else if (es == Select) {
      change_selection(palette, dir);  // Move selection
    } else if (es == Change) {
      //  bool fast = (fast_button.read() == HIGH);
      edit_patch(selection, -dir);       // Change parameter
    }
  }

  // Footswitch pressed
  if (footswitch.pressed()) {
    if (es == Select) {
      clear_selection(selection);
      es = None;

    }
    if (es != Change) {  // Patch change disabled during editing
      change_patch(1);

    }
  }
}

void rainbow(int wait) {
  for (long firstPixelHue = 0; firstPixelHue < 1 * 65536; firstPixelHue += 256) {
    for (int i = 0; i < pixels.numPixels(); i++) {
      int pixelHue = firstPixelHue + (i * 65536L / pixels.numPixels());
      pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue)));
    }
    pixels.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
    pixels.setPixelColor(0, 0, 0, 0);
    pixels.show(); // Update strip with new contents
  }
}

// - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / - / -



// Draw a rectangle in foreground colour optionally filled another colour
void draw_rect (uint8_t x, uint8_t y, uint8_t w, uint8_t h,
                uint16_t edgecol, uint16_t fillcol, boolean filled)
{
  if (w == 0 || h == 0)
    return;

  uint8_t e_red   = (edgecol & 0xf800) >> 10;
  uint8_t e_green = (edgecol & 0x07e0) >> 5;
  uint8_t e_blue  = (edgecol & 0x001f) << 1;

  uint8_t f_red   = (fillcol & 0xf800) >> 10;
  uint8_t f_green = (fillcol & 0x07e0) >> 5;
  uint8_t f_blue  = (fillcol & 0x001f) << 1;

  tft.drawRect(x, y, w, h, edgecol);

  if (filled)                             // filled rectangles take time to draw
    tft.fillRect(x, y, w, h, edgecol);
}

// Graphics **********************************************

// Clear display
void clear_display()
{
  tft.clearScreen();
  //  PINB = 1 << cs;                         // cs low
  //  Send(0x25);                             // Clear Window
  //  Send(0); Send(0); Send(95); Send(63);
  //  PINB = 1 << cs;                         // cs high
  //  delayMicroseconds(900);
  //delayMicroseconds(450);                 // half delay when clocking nano to 8 MHz
}

// Draw line to (x,y) in given colour
void draw_line (uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t col)
{
  uint8_t red   = (col & 0xf800) >> 10;
  uint8_t green = (col & 0x07e0) >> 5;
  uint8_t blue  = (col & 0x001f) << 1;

  tft.drawLine(x0, y0, x1, y1, col);
  //  PINB = 1 << cs;                         // cs low
  //  Send(0x21);                             // Draw Line
  //  Send(x0); Send(y0); Send(x1); Send(y1);
  //  Send(red); Send(green); Send(blue);
  //  PINB = 1 << cs;                         // cs high
}

// Plot a point at (x,y)
void plot_point (uint8_t x, uint8_t y, uint16_t col)
{
  draw_line(x, y, x, y, col);
}



// Plot character in given colour
void plot_char (uint8_t x, uint8_t y, uint8_t ch, uint16_t col, uint8_t scale)
{
  uint8_t red   = (col & 0xf800) >> 10;
  uint8_t green = (col & 0x07e0) >> 5;
  uint8_t blue  = (col & 0x001f) << 1;

  //  PINB = 1 << cs;                         // cs low
  for (uint8_t c = 0 ; c < 5; c++) {      // Column range
    uint8_t bits = pgm_read_byte(&font[ch - 32][c]);
    uint8_t r = 0;
    while (bits) {
      while ((bits & 1) == 0) {
        r++;
        bits = bits >> 1;
      }
      uint8_t on = (7 - r) * scale;
      while ((bits & 1) != 0) {
        r++;
        bits = bits >> 1;
      }
      uint8_t off = (7 - r) * scale + 1;
      for (int i = 0; i < scale; i++) {
        uint8_t h = x + c * scale + i;
        //        Send(0x21);                         // Draw line
        //        Send(h); Send(y + on); Send(h); Send(y + off);
        //        Send(red); Send(green); Send(blue);
      }
    }
  }
  //  PINB = 1 << cs;                           // cs high
}

// Plot text
void plot_text(uint8_t x, uint8_t y, const char* text, uint16_t col, uint8_t scale)
{
  while (1) {
    char c = *(text++);
    if (c == 0) return;
    plot_char(x, y, c, col, scale);
    x += 6;
  }
}

// Higher level functions for drawing ui elements

// convert unsigned byte to char array
// *s points to end() of buffer
char *btoa(uint8_t x, char *s)
{
  *--s = 0;
  if (!x) *--s = '0';
  for (; x; x /= 10)
    * --s = '0' + x % 10;
  return s;
}


// Master volume: -10 db - +4 db
// Mix: 0 - 50% - 100% (Not all values are possible, map to pot values)
// Valid values for dir are -1 and 1


// Draw volume -15 ------ 0 --- +4db / Mute
// Values given are 0-20.
void draw_volume(const uint16_t* palette, const uint16_t bg, const uint8_t pos, const uint8_t val)
{
  uint8_t start = pos * 35 + 1;
  uint8_t len = map(val, 0, 20, 0, 238);
  uint16_t barcol  = palette[3];
  uint16_t textcol = palette[3];

  tft.fillRect(1 , start + 18, 238, 8, bg);       // Erase old bar
  tft.fillRect(1 , start + 18, len, 8, barcol);   // Draw new bar

  tft.fillRect(178 , start, 34, 14, bg);        // Erase old value

  tft.setCursor(2, start);
  tft.setTextColor(textcol);
  tft.setTextSize(2);
  tft.print("Volume");

  // The code for constructing these on the fly
  // takes as much space, but the array is simpler.
  static const char db[21][4] = {"-18",
                                 "-16", "-15", "-14", "-13",
                                 "-12", "-11", "-10", " -9",
                                 " -8", " -7", " -6", " -5",
                                 " -4", " -3", " -2", " -1",
                                 " +0", " +1", " +2", " +3"
                                };


  tft.setCursor(178, start);
  tft.print(db[val]);

  tft.setCursor(215, start);
  tft.print("db");
}

// Draw mix as Dry ---  50% ---- Wet
// Values given are 0-20.
void draw_mix(const uint16_t* palette, const uint16_t bg, const uint8_t pos, const uint8_t val)
{
  uint8_t start = pos * 35 + 1;
  uint8_t len = map(val, 0, 20, 0, 238);
  uint16_t barcol  = palette[3];
  uint16_t textcol = palette[3];

  tft.fillRect(1 , start + 18, 238, 8, bg);        // Erase old bar
  tft.fillRect(1 , start + 18, len, 8, barcol);   // Draw new bar

  tft.setCursor(2, start);
  tft.setTextColor(textcol);
  tft.setTextSize(2);
  tft.print("Mix");

  char buff[4];                  // 3 chars + \0
  buff[0] = buff[1] = ' ';       // put spaces in tens and hundreds position

  btoa(val * 5, buff + 4); // give pointer to just after buffer (end)


  tft.fillRect(203 , start, 34, 14, bg);        // Erase old value
  tft.setCursor(203, start);
  tft.print(buff);
}


//  Draws bar with name and value
// Parameter values shown as 0-100%
void draw_parameter(const uint16_t* palette, const uint16_t bg, const char* text, const uint8_t pos, const uint8_t val)
{
  uint8_t start = pos * 35 + 1;
  uint8_t len = map(val, 0, 255, 0, 238);
  uint8_t perc = map(val, 0, 255, 0, 100);
  uint16_t barcol  = palette[3];
  uint16_t textcol = palette[3];

  tft.fillRect(1 , start + 18, 238, 8, bg);        // Erase old bar
  tft.fillRect(1 , start + 18, len, 8, barcol);   // Draw new bar
  tft.fillRect(2 , start, 110, 16, bg);        // Erase old part names
  tft.setCursor(2, start);
  tft.setTextColor(textcol);
  tft.setTextSize(2);
  tft.print(text);

  char buff[4];                  // 3 chars + \0 // add percent mark?
  buff[0] = buff[1] = ' ';       // put spaces in tens and hundreds position

  btoa(perc, buff + 4); // give pointer to just after buffer (end)

  // we want right justified, give buff instead of res
  tft.fillRect(203 , start, 34, 14, bg);        // Erase old value
  tft.setCursor(203, start);
  tft.print(buff);
}

void send_algo uses 3 pins, shown at the start of the sketch as:

// EEPROM emulation and FV-1 program switching
const uint8_t PC_NOTIFY_PIN = 4; // (Notify FV1 s0)
const uint8_t I2C_EEPROM_EMUL_SDA_PIN = 7; // (FV1 SDA) Was Pin 5
const uint8_t I2C_EEPROM_EMUL_SCL_PIN = 8; // (FV1 SCL) Was Pin 6
const uint8_t EEPROM_EMUL_ADDR = 0x50;

and in setup:

  // 24LC32 EEPROM emulation
  pinMode(PC_NOTIFY_PIN, OUTPUT);
  pinMode(I2C_EEPROM_EMUL_SDA_PIN, INPUT);
  pinMode(I2C_EEPROM_EMUL_SCL_PIN, INPUT);

I haven't changed pin4, so don't think I need to change PIND = _BV(4); // Toggle pin 4

The code you are posting is a 1-to-1 copy from github. If you cannot mend it yourself, either pay someone to do it for you or ask the copyright holder of the code to help you. If neither suits you, use the designated pins or find the three places where send_algo() has to be modified.

Or... Kindly ask experts on a free forum like this one for help about where send_algo() has to be modified to be able to change pins, like I did in post 1

Tried adding
PINB = _BV(8);
PIND = _BV(4) && _BV(8);
DDRB &= ~(clk_mask); // clk pin as input
uint8_t clk = PIND & PINB & clk_mask;
still freezing

PINB = _BV(0);
PORTB &= ~(clk_mask);
uint8_t clk = PINB & clk_mask;
Still freezing

Still freezing......

Very chilly

PINB = clk_mask;
still freezing

Yes Lee, but I can't tell you because you're not worthy