Arduino Frequentie generator en pulsbreedte


Nee helaas. Op dat gebied zijn deze Atmels wel wat beperkt. Er zit maar een enkele 16-bit timer in, met 2 compare units. Daarbij is CompareA de enige die de timer kan resetten, en die heb je nodig om de frequentie in te stellen. Dan blijft alleen CompareB over om het signaal te genereren, en die is hard gekoppeld aan OC1B (PB2).

Je ziet wel steeds meer processors waarbij je zelf een pin kunt koppelen, maar bij deze gaat dat niet.

Waar heb je PIN 10 nu voor nodig?

Ik wil wel een voorzetje maken om de beide stukken te combineren, dan krijg je de gewenste frequentie bereik gekoppeld aan de 2 rotaries.

De rotaries kunnen wel zonder probleem naar andere pinnen.

blackdog

Golden Member

Hi deKees,

Dit is een stukje uit de LCD driver, misschien kan ik de DC functie naar pin-13 zetten.
Maar zijn de andere pinnummers nog wel beschikbaar van die poort?

// It is assumed that the LCD module is connected to
// the following pins using a levelshifter to get the
// correct voltage to the module.
// SCK - Pin 8
// MOSI - Pin 9
// DC - Pin 10
// RST - Pin 11
// CS - Pin 12

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Zo te zien worden die pinnen softwarematig aangestuurd. Dus dan zou het geen probleem moeten zijn om ze te verplaatsen.

En ja, de andere pinnen van PORTB zijn nog vrij.

Je kunt zo even proberen om het lcd aan te sturen op
// SCK - Pin 8
// MOSI - Pin 9
--
// DC - Pin 13
// RST - Pin 11
// CS - Pin 12

blackdog

Golden Member

Hi deKees,

Ga ik morgen of woensdag even proberen.
Net wat testjes gedaan met een Arduino Pro mini, zelfde processor maar wat lager verbruik.
Dit is een 8MHZ 3.3V model.

Ik zal later in dit topic een blokschema laten zien hoe ik je software toepas.
Ik wil een stuk of 6 drukkopjes gebruiken die de frequentie en de duty cycle bepalen voor twee functies van dit apparaatje.

Het display laat de uitgangs frequentie en de duty cycle zien, of de signalen enable zijn en misschien de batterij spanning.

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Dat is het leuke van die controllertjes. Dan kun je het precies zo maken als je wilt.

Ik ben gisteravond ook nog wat aan het spelen geweest. Voortbordurend op de 2 rotaries.

Ik heb nu:
-- Frequentie bereik 1 Hz tot 100 kHz.
-- Logaritmische schaal, 35 kliks per decade.
-- DutyCycle 0 tot 100 %
-- Fijnregeling op frequentie en op duty-cycle.
-- Grotere sprongen bij snel draaien.
Was wel even een uitdaging. Leuk.

Hierbij de code:

code:



// ================================================================================================

/*
 * File         : RotaryEncoder.cpp
 * Created      : August 3, 2016
 * By           : CJ van der Hoeven
 *
 * Purpose      : Implementation of an interface to a rotary encoder
 *
 * Featuring    : Rotary encoder with push-button
 *                Detection of rotation and speed
 *                Detection of button clicks
 *                Supports multiple rotary encoders in a single system.
 */

#include <math.h>
#include <avr/interrupt.h>

static const uint16_t Delay_1 =   60;   // Duration for 'slow' rotations.
static const uint16_t Delay_2 =   40;   // Duration for 'fast' rotations

static const uint16_t Delay_3 =  1000;  // Minimum duration of 'Long press".
static const uint16_t Delay_4 = 10000;  // Max value of duration counter.

// We use a class definition so we can declare multiple rotary encoders.
// - Use one object per encoder.
class RotaryEncoder
{
public:
   RotaryEncoder()
   {  m_Armed     = false;
      m_PinStatus = 0x00;
      m_Debounce  = 100;
   }

   // Return true if the rotary button was pressed.
   bool Button();      // Short Press ( < 500 mS)
   bool LongButton();  // Long Press  ( > 500 mS)

   // Return NrClick rotation since last check.
   // Positive / Negative depending on direction.
   int16_t GetRotation();
   bool    GetRotation(int16_t &Delta);

   // Update 'Value' using rotary movement, but stay within range Min-Max
   // - No display updates. Just the value is updated.
   // - Return true when value was changed.
   bool UpdateValue (uint8_t  &Value, uint8_t  MinValue, uint8_t  MaxValue);
   bool UpdateInt   (uint16_t &Value, uint16_t MinValue, uint16_t MaxValue);
   bool UpdateDouble(double &Value, double MinValue, double MaxValue, double Delta);

   // Clear the status of the rotary encoder. Discard Buttons and movement.
   void Clear();

   // Count the rotation clicks.
   // - Positive for ClockWise rotation,
   // - Negative for CounterClockWise
   int16_t           m_Rotation;     //< Number of rotation clicks

   // Register current pin status from timer interrupt.
   // Bits are active low.
   // -  A = 0b00000001
   // -  B = 0b00000010
   // -  S = 0b00000100
   void Update(uint8_t PinStatus);

   // m_PinStatus retains the status of the pins for the rotary encoder
   // Are used to detect which pins have changed
   // - Can be used by application
   // -  A = 0b00000001
   // -  B = 0b00000010
   // -  S = 0b00000100
   uint8_t           m_PinStatus;

private:
   uint8_t           m_Armed;      //< State for Rotation
   uint16_t          m_Timer;      //< Timer for Rotation speed

   int8_t            m_Button;     //< Indicates that button was pressed. Code depends on Duration.
   int8_t            m_Debounce;   //< Debounce counter.
   uint16_t          m_Duration;   //< Count duration of button press.
};


// Handle Rotary status.
// - Pin status in lower 3 bits : SAB, Active Low.
// - Please note : Hardware is connected as SBA, but actual encoder produces SAB
//   - Perhaps these chinese encoders have pins A and B swapped!
void RotaryEncoder::Update(uint8_t PinStatus)
{  // Rotary movement:
   // - ClockWise    :  11 - 01 - 00 - 10 - 11 (with actual chinese encoders).
   // - CounterClock :  11 - 10 - 00 - 01 - 11 (with actual chinese encoders)
   uint8_t Rotary = PinStatus & 0b00000011;

   // Check Rotary AB
   if ((m_Armed == 0) && (Rotary == 0b0000000))
   {  // Both pins Low (contacts closed) indicates middle-of-click.
      m_Armed = 1;
   }

   if (m_Armed && (Rotary == 0b00000011))
   {  // Check previous value to determine direction.
      // - Both pins high indicates idle position.

      // Derive increment from duration.
      // - Faster movements have more effect (bigger steps).
      uint16_t Increment = 1;
      if(m_Timer > Delay_1)       // Slower than 10 clicks per second ?
      {  Increment = 1;           //
      }
      else if(m_Timer > Delay_2)  // Slower than 20 clicks per second ?
      {  Increment = 5;           // Big steps for fast movements
      }
      else
      {  Increment = 10;          // SuperBig steps for ultrafast movements
      }

      if((m_PinStatus & 0b00000011) == 0b00000010)
      {  // Moving forwards
         m_Rotation += Increment;
      }
      if((m_PinStatus & 0b00000011) == 0b00000001)
      {  // Moving backwards
         m_Rotation -= Increment;
      }
      m_Armed  = false;
      m_Timer  = 0;
   }
   m_PinStatus = Rotary; // Register new Pin Status

   // Also Check button
   // -- Button is bouncing quit a bit
   // -- Debouncing required.
   if(PinStatus & 0b00000100)  //< Check Switch
   {  // Pin High : Button switch open
      // Button-release stable: Allow new button-press.
      if(m_Debounce < 0)       // Counter started at -100
      {  m_Debounce += 1;      // Count interrupts with switch open
      }
      if(m_Debounce == 0)      // Counter started at -100
      {  if(m_Duration < Delay_3) // Less than 500 ms :
         {  m_Button = 1;      // Short Press
         }
         m_Debounce = 100;     // Count interrupts with switch open.
      }
      if(m_Debounce > 0)
      {  m_Debounce = 100;     // Restart counter when Switch is open.
      }
   }
   else
   {  // Pin Low : Button switch closed
      // Button active (=Low)
      if(m_Debounce > 0)       // Countdown started at 100
      {  m_Debounce -= 1;
      }
      if(m_Debounce == 0)      // Register Click after 100 interrupts (50 ms)
      {  m_Debounce = -100;
         m_Duration =  0;      // Start new Duration counter.
      }
      if(m_Debounce < 0)       // Keep it low as long as switch remains closed.
      {  m_Debounce = -100;

         if(m_Duration == Delay_3)// Notify user when the button is down long enough
         {  m_Button = 2;         // Long Press.
         }

         if(m_Duration < Delay_4) // Up to a Max.
         {  m_Duration += 1;      // Count duration of button Down
         }
      }
   }

   // Increment timer counter at every interrupt.
   // - So we get time between clicks (5000 increments per second)
   if (m_Timer < 1000)
   {  m_Timer += 1;
   }
}


// Interface functions to check if a button is pressed.
// Return true if the rotary button was pressed
bool RotaryEncoder::Button()
{  if(m_Button == 1)
   {  // Clear button, so it is used only once.
      // Please note: The surrounding if() is required here because clearing too often may
      //   clear the Button before it is used.
      //   I.E. when ISR writes a new button between ButtonRead and ButtonClear.
      m_Button = 0;
      return true;
   }
   else
   {  return false;
   }
}

// Interface functions to check if a button is pressed.
// Return true if the rotary button was pressed
bool RotaryEncoder::LongButton()
{  if(m_Button == 2)
   {  // Clear button, so it is used only once.
      // Please note: The surrounding if() is required here because clearing too often may
      //   clear the Button before it is used.
      //   I.E. when ISR writes a new button between ButtonRead and ButtonClear.
      m_Button = 0;
      return true;
   }
   else
   {  return false;
   }
}

// User function to check if the rotary is turned.
// Return signed Count
//    < 0   : Nr clicks, turned backwards
//    = 0   : No movement
//    > 0   : Nr clicks, turned forwards
int16_t RotaryEncoder::GetRotation()
{  if(m_Rotation)
   {  // Yes, Action:
      int16_t Rotation = m_Rotation; //< Register current value
      m_Rotation = 0;                //< Clear
      return Rotation;               //< Return current value to application
   }
   else
   {  return 0;  //< No movement
   }
}

bool RotaryEncoder::GetRotation(int16_t &Delta)
{  Delta = GetRotation();
   return (Delta != 0);
}

// Clear the status of the rotary encoder. Discard Buttons and movement.
void RotaryEncoder::Clear()
{  m_Rotation = 0;
   m_Button   = 0;
}

// ================================================================================================

/*
 * Main.cpp
 *
 * Created    : 2018-21-10
 * Author     : Kees
 * Purpose    : Firmware for Inductor tester.
 *              - Using rotary encoders:
 *                MyRotary1 : Set Pwm frequency.
 *                MyRotary2 : Set Pwm PulseWidth.
 *
 *              - AtTiny24
 *
 * Schematics : Projects\Kicad\InductorTester
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>

// ==================================================================
// PWM Output pin
// -- PB2, D10 : (PWM through OC1B), Active Low (using TC426 Mosfet driver).
// -- Variable Frequency and Duty cycle to control led power.
static const uint8_t PIN_PWM_OUT = 0b00000100;  // PB2

// ================================================================================================

struct TIMER_DSCR
{
   uint16_t    m_Started;

   void Start()
   {  m_Started = millis();
   }

   // Check if 'Duration'
   bool Sync(uint16_t Duration)
   {  if( (((uint16_t)millis()) - m_Started) >= Duration )
      {  m_Started += Duration;
         return true;
      }
      else
      {  return false;
      }
   }
};

TIMER_DSCR Timer;

// ================================================================================================

// Instantiate a couple Rotary encoder handlers
RotaryEncoder MyRotary1;  // To set Pwm Frequency
RotaryEncoder MyRotary2;  // To set Pwm PulseWidth

// We have a single interrupt handler for our 2 rotary encoders.
// - To be called from a timer interrupt at regular intervals.
//    - Should be fast enough to detect all rotary pulses.
// - This handler will check the encoder's I/O pins and derive rotation movements.
//
// This is not included in the rotary library function because:
// - Here we can define as many encoders as needed in an application..
// - And we provide actual link between processor pins and encoder switch.
void RotaryInterruptHandler()
{
   // Setup I/O port for Rotary encoder SW2 SW1
   DDRD   &=  ~0b11111100;  // Set Inputs      SW2:SBA SW1:SBA
   PORTD  |=   0b11111100;  // Enable Pullups

   MCUCR  &=  ~(1 << PUD);  // Enable all pull-up functions.

   // First read rotary encoder.
   uint8_t PinStatus = PIND & 0b11111100;  // PD.7 .. PD.2

   // SW1 :
   MyRotary1.Update(PinStatus >> 2);      // Rotary1 uses PD.4 = S, PD.3 = B, PD.2 = A

   // SW2 :
   MyRotary2.Update(PinStatus >> 5);      // Rotary2 uses PD.7 = S, PD.6 = B, PD.5 = A
}

// ================================================================================================
// We use a 16-bit timer 1 for the Pwm Engine.
// -- So we get a 16-bit frequency range.
//
// - Use OCR1A to set Frequency, Clear on Compare Match
//   -- WGM = 0100 : CTC, Fast PWM
// - Use OCR1B to generate PWM wave form
//   COM0B = 11 : Set at Compare Match, Clear at BOTTOM.
//                -- This mode allows Zero pulse output, I.E. Mosfet off.
//                -- Active Pulse width : OCR1A - OCR1B
void SetFrequency(float Frequency, float DutyCycle)
{
   uint32_t Count = 16000000ul / Frequency;
   uint16_t Prescaler;

   // Timer 1 has 16 bits so we need a count below 65536
   if( Count < 65536ul)
   {  // Ok, Prescaler can be 1
      Prescaler = ( 0b001 << CS10);
   }
   else if (Count < (8 * 65536ul))
   {  // We need a prescaler of 8
      Count /= 8;
      Prescaler = ( 0b010 << CS10);
   }
   else if (Count < (64 * 65536ul))
   {  // We need a prescaler of 64
      Count /= 64;
      Prescaler = ( 0b011 << CS10);
   }
   else
   {  // We need a prescaler of 256
      Count /= 256;
      Prescaler = ( 0b100 << CS10);
   }

   uint16_t Width = (Count * DutyCycle) / 100;

   TCCR1A = ( 0b00 << COM1A0 )  // Compare A is not used
          + ( 0b11 << COM1B0 )  // Set at compare match, Clear at Bottom
          + ( 0b11 << WGM10  ); // Clear timer on Compare A match

   TCCR1B = ( 0b0  << ICNC1  )
          + ( 0b0  << ICES1  )
          + ( 0b11 << WGM12  )
          + ( Prescaler      ); 

   TIMSK1 = (0 << TOIE1)       // Disable all interrupts on Timer 1
          + (0 << OCIE1A)
          + (0 << OCIE1B)
          + (0 << ICIE1);
          
   if(Count < OCR1A)
   {  // Setting Shorter period : Update frequency first.
      // - Update frequency first
      OCR1A = Count;
      OCR1B = Count - Width;
   }
   else
   {  // Setting longer period time:
      // - Update pulseWidth first
      OCR1B = Count - Width;
      OCR1A = Count;
   }

   DDRB |= PIN_PWM_OUT; // Enable driver for OC1B (PB2)

   Serial.print("f = "); 
   if(Frequency < 100.)
   {  Serial.print(Frequency, 3); Serial.print(" Hz, ");
   }  
   else if(Frequency < 1000.)
   {  Serial.print(Frequency, 2); Serial.print(" Hz, ");
   }  
   else if(Frequency < 10000.)
   {  Serial.print(Frequency, 1); Serial.print(" Hz, ");
   }  
   else
   {  Serial.print(Frequency / 1000, 3); Serial.print(" kHz, ");
   }  
   Serial.print(DutyCycle, 1); Serial.print(" %");
   Serial.println();
}

// =================================================================================================================

// To convert a linear scale to a logarithmic scale.
// To get 35 clicks per decade :
//  - 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.2 2.4 2.6 2.8 3.0 3.2 3.4 3.6 3.8 4.0 4.2 4.4 4.6 4.8 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0
// In other words:
//  - 0.1 from 1.0 to  2.0
//  - 0.2 from 2.0 to  5.0
//  - 0.5 from 5.0 to 10.0  


struct LookupTable
{  float Value;
   float Delta;
};

static LookupTable MyTable[]=
{  {      1,    0.1 },
   {      2,    0.2 },
   {      5,    0.5 },
   {     10,    1.  },
   {     20,    2.  },
   {     50,    5.  },
   {    100,   10.  },
   {    200,   20.  },
   {    500,   50.  },
   {   1000,  100.  },
   {   2000,  200.  },
   {   5000,  500.  },
   {  10000, 1000.  },
   {  20000, 2000.  },
   {  50000, 5000.  },
   { 500000, 5000.  },
};

float DoLogScale(unsigned int n, float &Delta)
{  
   int   Index    = 0;
   float NewValue = MyTable[Index].Value;

   Delta = MyTable[Index].Delta;
   Index += 1;
   float Limit = MyTable[Index].Value;

   while(n > 0)
   {  if( (NewValue +  0.05) > Limit)
      {  Delta = MyTable[Index].Delta;
         Index += 1; 
         Limit = MyTable[Index].Value;
      }

      NewValue += Delta;
      n -= 1;
   }
   return NewValue;
}

float Update(float Value, int Direction, float Delta, float MinValue, float MaxValue)
{  
   while(Direction < 0)  // Turning CounterClock-wise
   {  if(Value > (MinValue + Delta))
      {  Value -= Delta;
      }
      else
      {  Value = MinValue;
      }
      Direction += 1;
   }
   
   while(Direction > 0)  // Turning Clock-wise
   {  if( (Value + Delta) < MaxValue)
      {  Value += Delta;
      }
      else
      {  Value  = MaxValue;
      }
      Direction -= 1;
   }
   return Value;
}

// =================================================================================================================

void setup() 
{  Serial.begin(115200);
   Timer.Start();
}

void loop() 
{
   if(Timer.Sync(1))
   {  RotaryInterruptHandler();
   }

   static bool     SetFineFrequency  = false;

   static uint16_t FrequencyIndex    = 0;
   static float    FrequencyStepsize = 0.1;
   static int      FrequencyOffset   = 0;    // For Fine-Tuning
   static float    Frequency;                // Actual active frequency

   static float    DutyCycleDelta = 1.0;     // or 0.1 when FineTuning 
   static float    DutyCycle;                // Actual DutyCycle.
   
   // ==========================================================================================

   // Update frequency
   if(MyRotary1.Button())   // Rotary-Switch toggles between Fine and course
   {  SetFineFrequency = ! SetFineFrequency;
      Serial.println(SetFineFrequency ? "Freq fine" : "Freq coarse");

      FrequencyOffset = 0; // Clear Adjustmenst when switching mode.

      Frequency = DoLogScale(FrequencyIndex, FrequencyStepsize);
      SetFrequency(Frequency, DutyCycle);
   }

   int16_t Delta; 
   if(MyRotary1.GetRotation(Delta))  // Turn rotary-1 to set frequency
   {  
      if(SetFineFrequency)
      {  FrequencyOffset = Update(FrequencyOffset, Delta, 1, -1000, 1000);
      }
      else
      {  // Update frequencyIndex with rotary data
         FrequencyIndex = Update(FrequencyIndex, Delta, 1, 0, 175);
      }
   
      // Convert to Logarithmic scale 
      Frequency = DoLogScale(FrequencyIndex, FrequencyStepsize);
      Frequency += ( FrequencyOffset * (FrequencyStepsize / 100) );

      SetFrequency(Frequency, DutyCycle);
   }

   // ==========================================================================================
   // Rotary-2 to update DutyCycle
   if(MyRotary2.Button()) // Switch toggles between Fine/Coarse
   {  if(DutyCycleDelta < 0.5)
      {  DutyCycleDelta = 1.0;       //< Coarse
         Serial.println("D coarse");
      }
      else
      {  DutyCycleDelta = 0.1;       //< Fine
         Serial.println("D fine");
      }
   }

   if(MyRotary2.GetRotation(Delta))
   {  DutyCycle = Update(DutyCycle, Delta, DutyCycleDelta, 0.0 , 100.0);
      SetFrequency(Frequency, DutyCycle);
   }
}


blackdog

Golden Member

Hi deKees,

Mooi dat je het een uitdaging vond! ;)
En anderen er dan ook nog iets aan hebben, toppie!

Ik heb vanavond in de slow modus (gaar van het werken) een Nokia scherpje aan een Pro mini gehangen en in de LCD setup pin-10 nar pin-13 verplaatst en dat werkt.

Nu proberen de software te maken voor de druk knopjes en de opbouw van het display netjes doen.
Het schrijven naar het scherm lijkt de frequentie generatie niet te beinvloeden.
Ik test dit dus met je eerste stukje code waar je de frequentie en duty cycle ingeeft op eé'n regel.

Als ik morgen nog wat tijd heb, test ik je nieuwe code!
Dank er voor, het wordt gewardeerd!

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"
blackdog

Golden Member

Hi,

Ik ben wat code aan het samenvoegen, dat is de code van deKees.

c code:

void setup(){
unsigned long Frequency;
unsigned int DutyCycle;


Frequency = 1000;
DutyCycle = 50;
}

void loop() {
 
   unsigned long Count = 8000000ul / Frequency;
   unsigned char Prescaler;

   // Timer 1 has 16 bits so we need a count below 65536
   if( Count < 65536ul)
   {  // Ok, Prescaler can be 1
      Prescaler = ( 0b001 << CS10);
   }
   else if (Count < (8 * 65536ul))
   {  // We need a prescaler of 8
      Count /= 8;
      Prescaler = ( 0b010 << CS10);
   }
   else if (Count < (64 * 65536ul))
   {  // We need a prescaler of 64
      Count /= 64;
      Prescaler = ( 0b011 << CS10);
   }
   else if (Count < (256 * 65536ul))
   {  // We need a prescaler of 256
      Count /= 256;
      Prescaler = ( 0b100 << CS10);
   }

   TCCR1A = ( 0b00 << COM1A0 )  // Compare A is not used
          + ( 0b11 << COM1B0 )  // Set at compare match, Clear at Bottom
          + ( 0b01 << WGM10  ); // Clear timer on Compare A match
   TCCR1B = ( 0b0  << ICNC1  )
          + ( 0b0  << ICES1  )
          + ( 0b10 << WGM12  )
          + ( Prescaler      ); 

   OCR1B = Count * DutyCycle / 100;
   OCR1A = Count;

   DDRB |= 0b00000100; // Enable driver for OC1B (PB2)
}

En dat dus samen gevoegt zover ik dat kan, met de onderstaande code.
Deze code gebruikt een analoge pin en daar komen via een weerstand deler acht schakelaars aan te hangen via zeven 1K weerstanden vanaf de 5V
en dan 1K8 naar massa, simpel en effectief, de waarden per schakelaar liggen netjes uit elkaar en dat levert verder geen problemen op.

c code:

/*
AnalogButton_Combos
Version 0.1
Created By: Michael Pilcher
February 24, 2010
*/

int j = 1; // integer used in scanning the array designating column number
//2-dimensional array for asigning the buttons and there high and low values
int Button[9][3] = {{1, 1000, 1023}, // button 1
                    {2, 900, 910}, // button 2
                    {3, 782, 792}, // button 3
                    {4, 665, 675}, // button 4
                    {5, 548, 558}, // button 5
                    {6, 433, 445}, // button 6
                    {7, 316, 328}, // button 7
                    {8, 200, 214}}; // button 8                   

int analogpin = 5; // analog pin to read the buttons
int label = 0;  // for reporting the button label
int counter = 0; // how many times we have seen new value
long time = 0;  // the last time the output pin was sampled
int debounce_count = 50; // number of millis/samples to consider before declaring a debounced input
int current_state = 0;  // the debounced input value
int ButtonVal;


void setup()
{
 Serial.begin(9600);
unsigned long Frequency;
unsigned int DutyCycle;

Frequency = 400;
DutyCycle = 10;

}

void loop()
{
  // If we have gone on to the next millisecond
 if (millis() != time)
 {
   // check analog pin for the button value and save it to ButtonVal
   ButtonVal = analogRead(analogpin);
   if(ButtonVal == current_state && counter >0)
   {
     counter--;
   }
   if(ButtonVal != current_state)
   {
     counter++;
   }
   // If ButtonVal has shown the same value for long enough let's switch it
   if (counter >= debounce_count)
   {
     counter = 0;
     current_state = ButtonVal;
     //Checks which button or button combo has been pressed
     if (ButtonVal > 0)
     {
       ButtonCheck();
     }
   }
   time = millis();
 }


void ButtonCheck()
{
 // loop for scanning the button array.
 for(int i = 0; i <= 21; i++)
 {
   // checks the ButtonVal against the high and low vales in the array
   if(ButtonVal >= Button[i][j] && ButtonVal <= Button[i][j+1])
   {
     // stores the button number to a variable
     label = Button[i][0];
     Action();      
   }
 }
}

void Action()
{
 if(label == 1)
 {
Frequency = 400;
DutyCycle = 10;
 }
 if(label == 2)
 {
Frequency = 400;
DutyCycle = 20;
 }
 if(label == 3)
 {
  Frequency = 1000;
  DutyCycle = 10;  
 }
 if(label == 4)
 {
  Frequency = 1000;
  DutyCycle = 20;  
 }
 if(label == 5)
 {
   Frequency = 1;
   DutyCycle = 1;  
 }
 if(label == 6)
 {
    Frequency = 1;
    DutyCycle = 5;  
 }
 if(label == 7)
 {
   Frequency = 5;
   DutyCycle = 1;  
 }
 if(label == 8)
 {
   Frequency = 10;
   DutyCycle = 1;  
 }    
}
// Frequentie generatie geschreven door deKees, Circuits Online, Nederland

   unsigned long Count = 8000000ul / Frequency;
   unsigned char Prescaler;

   // Timer 1 has 16 bits so we need a count below 65536
   if( Count < 65536ul)
   {  // Ok, Prescaler can be 1
      Prescaler = ( 0b001 << CS10);
   }
   else if (Count < (8 * 65536ul))
   {  // We need a prescaler of 8
      Count /= 8;
      Prescaler = ( 0b010 << CS10);
   }
   else if (Count < (64 * 65536ul))
   {  // We need a prescaler of 64
      Count /= 64;
      Prescaler = ( 0b011 << CS10);
   }
   else if (Count < (256 * 65536ul))
   {  // We need a prescaler of 256
      Count /= 256;
      Prescaler = ( 0b100 << CS10);
   }

   TCCR1A = ( 0b00 << COM1A0 )  // Compare A is not used
          + ( 0b11 << COM1B0 )  // Set at compare match, Clear at Bottom
          + ( 0b01 << WGM10  ); // Clear timer on Compare A match
   TCCR1B = ( 0b0  << ICNC1  )
          + ( 0b0  << ICES1  )
          + ( 0b10 << WGM12  )
          + ( Prescaler      ); 

   OCR1B = Count * DutyCycle / 100;
   OCR1A = Count;

   DDRB |= 0b00000100; // Enable driver for OC1B (PB2)
 
}

Als ik dit dus aan elkaar knoopt krijg ik een aantal error bij het compilen en ik kan niet zien wat de fout is van deze comby code.
De button code alleen werkt zonder problemen net als de frequentie code van deKees.

Ik zal zondermeer ergens een of meerdere fouten maken, maar ik zie ze niet 8)7

Dit is de foutcode die ik te zien krijg:

c code:


C:\Users\master21\Documents\Arduino\Analog-Button-1e-Versie\Analog-Button-1e-Versie.ino: In function 'void loop()':

Analog-Button-1e-Versie:62:8: error: 'ButtonCheck' was not declared in this scope

        ButtonCheck();

        ^~~~~~~~~~~

C:\Users\master21\Documents\Arduino\Analog-Button-1e-Versie\Analog-Button-1e-Versie.ino:62:8: note: suggested alternative: 'ButtonVal'

        ButtonCheck();

        ^~~~~~~~~~~

        ButtonVal

Analog-Button-1e-Versie:70:1: error: a function-definition is not allowed here before '{' token

 {

 ^

Analog-Button-1e-Versie:85:1: error: a function-definition is not allowed here before '{' token

 {

 ^

Analog-Button-1e-Versie:129:38: error: 'Frequency' was not declared in this scope

    unsigned long Count = 8000000ul / Frequency;

                                      ^~~~~~~~~

Analog-Button-1e-Versie:161:20: error: 'DutyCycle' was not declared in this scope

    OCR1B = Count * DutyCycle / 100;

                    ^~~~~~~~~

exit status 1
'ButtonCheck' was not declared in this scope

Dus wie helpt om mij te verlossen van mijn blinde vlekken. :+

Dank en groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Paar kleine dingetjes:

code:


void setup(){
unsigned long Frequency;
unsigned int DutyCycle;


Frequency = 1000;
DutyCycle = 50;
}

de beide variabelen Frequency en DutyCycle bestaan niet meer zodra setup is afgelopen. Dus die moet je buiten de setup funktie declareren.

code:


void ButtonCheck()
{
 // loop for scanning the button array.
 for(int i = 0; i <= 21; i++)
 {
   // checks the ButtonVal against the high and low vales in the array
   if(ButtonVal >= Button[i][j] && ButtonVal <= Button[i][j+1])

De button array heeft maar 9 items. Dus dan moet je i binnen de limieten 0 - 8 houden. Dit geeft alleen een warning, geen foutmelding.

Analog-Button-1e-Versie:62:8: error: 'ButtonCheck' was not declared in this scope

De C++ compiler klaagt hier dat je een funktie gebruikt voordat die is gedefinieerd. Normaal is dat geen probleem omdat de arduino ide dat voor je oplost. Maar dat lukt hier niet omdat er een paar haakjes verkeerd staan.
- Geen sluithaakje aan het eind van loop(), dus voor de funktie ButtonCheck()
- en de code voor het instellen van de timer staat niet binnen een funktie. Die zou binnen de Action() moeten staan.

Probeer zo maar eens

code:


/*
AnalogButton_Combos
Version 0.1
Created By: Michael Pilcher
February 24, 2010
*/

//2-dimensional array for asigning the buttons and there high and low values
int Button[9][3] = {{1, 1000, 1023}, // button 1
                    {2, 900, 910}, // button 2
                    {3, 782, 792}, // button 3
                    {4, 665, 675}, // button 4
                    {5, 548, 558}, // button 5
                    {6, 433, 445}, // button 6
                    {7, 316, 328}, // button 7
                    {8, 200, 214}}; // button 8                   

int analogpin = 5; // analog pin to read the buttons
int label = 0;  // for reporting the button label
int counter = 0; // how many times we have seen new value
unsigned long time = 0;  // the last time the output pin was sampled
int debounce_count = 50; // number of millis/samples to consider before declaring a debounced input
int current_state = 0;   // the debounced input value
int ButtonVal;

void ButtonCheck();
void Action();


unsigned long Frequency;
unsigned int DutyCycle;

void setup()
{
   Serial.begin(9600);

   Frequency = 400;
   DutyCycle = 10;
}

void loop()
{
   // If we have gone on to the next millisecond
   if (millis() != time)
   {
      // check analog pin for the button value and save it to ButtonVal
      ButtonVal = analogRead(analogpin);
      if(ButtonVal == current_state && counter >0)
      {
         counter--;
      }
      if(ButtonVal != current_state)
      {
        counter++;
      }
      // If ButtonVal has shown the same value for long enough let's switch it
      if (counter >= debounce_count)
      {
         counter = 0;
         current_state = ButtonVal;
         //Checks which button or button combo has been pressed
         if (ButtonVal > 0)
         { 
             ButtonCheck();
         }
      }
      time = millis();
   }
}


void ButtonCheck()
{
   // loop for scanning the button array.
   for(int i = 0; i <= 8; i++)
   {
      // checks the ButtonVal against the high and low values in the array
      if(ButtonVal >= Button[i][1] && ButtonVal <= Button[i][2])
      {
        // stores the button number to a variable
        label = Button[i][0];
        Action();      
      }
   }
}

void Action()
{
   if(label == 1)
   {
      Frequency = 400;
      DutyCycle = 10;
   }
   if(label == 2)
   {
      Frequency = 400;
      DutyCycle = 20;
   }
   if(label == 3)
   {
      Frequency = 1000;
      DutyCycle = 10;  
   }
   if(label == 4)
   {
      Frequency = 1000;
      DutyCycle = 20;  
   }
   if(label == 5)
   {
      Frequency = 1;
      DutyCycle = 1;  
   }
   if(label == 6)
   {
      Frequency = 1;
      DutyCycle = 5;  
   }
   if(label == 7)
   {
      Frequency = 5;
      DutyCycle = 1;  
   }
   if(label == 8)
   {
      Frequency = 10;
      DutyCycle = 1;  
   }    

   // Frequentie generatie geschreven door deKees, Circuits Online, Nederland

   unsigned long Count = 8000000ul / Frequency;
   unsigned char Prescaler;

   // Timer 1 has 16 bits so we need a count below 65536
   if( Count < 65536ul)
   {  // Ok, Prescaler can be 1
      Prescaler = ( 0b001 << CS10);
   }
   else if (Count < (8 * 65536ul))
   {  // We need a prescaler of 8
      Count /= 8;
      Prescaler = ( 0b010 << CS10);
   }
   else if (Count < (64 * 65536ul))
   {  // We need a prescaler of 64
      Count /= 64;
      Prescaler = ( 0b011 << CS10);
   }
   else if (Count < (256 * 65536ul))
   {  // We need a prescaler of 256
      Count /= 256;
      Prescaler = ( 0b100 << CS10);
   }

   TCCR1A = ( 0b00 << COM1A0 )  // Compare A is not used
          + ( 0b11 << COM1B0 )  // Set at compare match, Clear at Bottom
          + ( 0b01 << WGM10  ); // Clear timer on Compare A match
   TCCR1B = ( 0b0  << ICNC1  )
          + ( 0b0  << ICES1  )
          + ( 0b10 << WGM12  )
          + ( Prescaler      ); 

   OCR1B = Count * DutyCycle / 100;
   OCR1A = Count;

   DDRB |= 0b00000100; // Enable driver for OC1B (PB2)
}

blackdog

Golden Member

Hi deKees, :-)

Deze:

c code:

 // loop for scanning the button array.
 for(int i = 0; i <= 21; i++)

Had ik over het hoofd gezien tijdens het aanpassen van de orginele code van de drukknoppen, 21 is wat veel voor dit meetinstrumentje.

En wat betreft dit

c code:

Analog-Button-1e-Versie:62:8: error: 'ButtonCheck' was not declared in this scope

Ik kan het niet vinden waar ik wat vergeten ben ook niet met de code van jou naast de code van "mij"

Dat de variabele in de setup niet meer werken in de loop was ik gewoon vergeten. :-)

Nu draait het geheel weer even op een Nano en als er wat tijd over is vandaag hang ik de Nokia display driver in de code.
Als dat goed gaat dan pas ik het weer aan naar de Pro Mini.

Ik weet nog niet of ik meetinstrumentje een eigen netvoeding geef of op een batterij laat draaien.
Dat wordt ook bepaald door het stuurniveau dat ik nodig heb voor mijn dummy loads, en hoe complex het wordt.

deKees als ik een extra drukknop wil toevoegen die de uitgang van jou genrator stil maakt (enable-disable),
dat als de uitgang in de disable stand staat deze "0" is waar kan ik dit dan het beste doen in jouw code?

Dank vast en gegroet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ik kan het niet vinden waar ik wat vergeten ben ook niet met de code van jou naast de code van "mij"

De indenting in je oorspronkelijke code is niet helemaal consequent doorgevoerd. Daardoor wordt het al snel veel moeilijker om te zien of de haakjes allemaal goed staan. Dat is altijd het eerste wat ik aanpas. Altijd 3 posities inspringen bij elk haakjes niveau.

code:


void loop()
{
  // If we have gone on to the next millisecond
 if (millis() != time)
 {
   // check analog pin for the button value and save it to ButtonVal
   ButtonVal = analogRead(analogpin);
   if(ButtonVal == current_state && counter >0)
   {
     counter--;
   }
   if(ButtonVal != current_state)
   {
     counter++;
   }
   // If ButtonVal has shown the same value for long enough let's switch it
   if (counter >= debounce_count)
   {
     counter = 0;
     current_state = ButtonVal;
     //Checks which button or button combo has been pressed
     if (ButtonVal > 0)
     {
       ButtonCheck();
     }
   }
   time = millis();
 }
   <<< ******* Hier is een sluithaakje te weinig *******

void ButtonCheck()
{
 // loop for scanning the button array.
 

code:


 if(label == 8)
 {
   Frequency = 10;
   DutyCycle = 1;  
 }    
}  <<< *************** Haakje te veel ****************
// Frequentie generatie geschreven door deKees, Circuits Online, Nederland

   unsigned long Count = 8000000ul / Frequency;
   unsigned char Prescaler;

   // Timer 1 has 16 bits so we need a count below 65536
   if( Count < 65536ul)
   {  // Ok, Prescaler can be 1
      Prescaler = ( 0b001 << CS10);
   }

Ik vind het wel een leuke methode om zoveel knopjes op een enkele pin in te lezen. Een ekstra knopje toevoegen kan nog wel met een extra 1K weerstand. Maar dan veranderen de getallen in de button tabel omdat alle spanningen dan opschuiven. Dus dan zou ik de compiler de nieuwe getallen laten uitrekenen.

Dat wordt dan zoiets:

code:


//2-dimensional array for assigning the buttons and their high and low values

#define RTOT   (1800 + (8 * 1000))
#define DELTA   5   
#define BUTTON_VOLTAGE(R) (((1024 * R) / RTOT) - DELTA),(((1024 * R) / RTOT) + DELTA)  

int Button[9][3] = {{1, BUTTON_VOLTAGE(9800)},  // button 1
                    {2, BUTTON_VOLTAGE(8800)},  // button 2
                    {3, BUTTON_VOLTAGE(7800)},  // button 3
                    {4, BUTTON_VOLTAGE(6800)},  // button 4
                    {5, BUTTON_VOLTAGE(5800)},  // button 5
                    {6, BUTTON_VOLTAGE(4800)},  // button 6
                    {7, BUTTON_VOLTAGE(3800)},  // button 7
                    {8, BUTTON_VOLTAGE(2800)},  // button 8
                    {9, BUTTON_VOLTAGE(1800)}}; // button 9                   

Dan kun je in de Action() een funktie toevoegen.

blackdog

Golden Member

Hi deKees,

Hier is zichtbaar hoe ik de schakelaars electrisch heb gekoppeld, links de 8 knops versie en rechts de 9 knops versie.
Met de 1K weerstanden is dat het maximaal aantal standen.
Natuurlijk weer ik dat er ook andere manieren mogelijk zijn zzoals het r-2r netwerkjes enz.
Maar het leuke van deze setup is dat ik hier nu ook een draai schakelaar kan gebruiken.

En ja met 470Ω weerstanden kom je aan het dubbele aantal standen, de marge wordt wel steeds kleiner en je weerstadns nauwkeurigheid gaat da nook meespelen net als de lineairietijd van de ADC.
Het mooi van deze setup en dan heb ik het over de software die de uitlezing doet, is dat de uitgang vast blijft en geen last van dender heeft.
Dit alles net als de code van deKees zonder gebruik te maken van library's.

De "5V" Out van de Arduino is natuurlijk geen 5V er zit altijd en diode tussen,
maar voor de dudielijkheid heb ik hier in het schema de gemeten waarden aangegeven

Bij de 8-schakelaar uitvoering staat links van de schakelaar de 10Bit waarde weergegeven, voor b.v. S5 is dat de waarde 553.
Voor de vergelijking heb ik ongeveer + en -5 bitjes aangehouden en ik denk dat voor 8 of 9 schakelaars + en - 10 bitjes wat meer zekerheid geeft.

De weerstadn van de schakelaar zelf is verder onbelangrijk, R10 geeft ongeveer 1% belasting in de ongunstigste stand.
De Ri van de hier getoonde spanningsdeler is de totale waarde van de weerstadns string gedeeld door vier.
Dit gaat ook hier op omdat de impedantie van de + aansluiting bovenaan zeer laag is t.o.v. de weerstands string, zeg maar een paar Ohm.
Dan krijgen we dus een maximale uitgangs impedantie van de sting op stand-5 van 7x 1K + 1K8 gedeeld door vier endat geeft dan een waarde van 2K2.

Ik weet dat de Arduino graag voor hoge frequenties een lage impedantie ziet aan zijn analoge ingang, de 1nF heeft een impedantie van ongeveer 160Ω bij 1MHz.
Volgens mijn metijgn voldoende laag voor mijn gbruikt, ik zoek bij deze toepassing niet naar optimaal lineair gedrag. :-)

http://www.bramcam.nl/Diversen/Arduino-Analog-Button-01.png

.
Dit is de testsetup met een Nano, een 1K8 weerstand en zeven stuks 1K naar de 5V uitgang van de Nano.
De LED zit via een weerstand op de uitgang zodat ik kan zien dat de frequentie en/of pulsbreedte veranderd.
http://www.bramcam.nl/Diversen/Arduino-Analog-Button-02.png

deKees
Twee dingen...
Als eerste het stukje code dat je laat zien om de toetsen uit te lezen, de "Delta" variabele heeft dit de zelfde functie als dit in de orginele code:

c code:

int Button[9][3] = {{1, 1000, 1023}, // button 1
                    {2, 900, 910}, // button 2
                    {3, 782, 792}, // button 3

Werkt dit twee kanten op net als in de orginele code?

Ik zal de code later even testen.

Verder zal ik beter letten op de haakjes in de code. :-)

Oja, de tweede vraag...
Hoe zet ik je frequentie generator uit en dat de pin-10 een "0" niveau heeft.

Kan het door dit stukje de 1 door een 0 te vervangen?

c code:

DDRB |= 0b00000100; // Enable driver for OC1B (PB2)

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ja, de delta werkt 2 kanten op.
De BUTTON_VOLTAGE macro levert 2 getallen op. Dat is de berekende middenwaarde, en dan min DELTA en Plus DELTA.

Ik was daarbij uitgegaan van 1K8 als onderste weerstand, en 8 keer 1K.
In jouw geval heb je de 1K afgetrokken van de onderste weerstand, Dan kunnen alle getallen in de tabel hetzelfde blijven, en toch een regel toevoegen. Dat kan ook door de waarde bij RTOT aan te passen naar (820 + (8 * 1000)). En eigenlijk moet je ook R19 (R9) nog meenemen in de berekening.

Overigens vind ik een DELTA van 5 wel erg klein. Dat geeft een marge van 0.5 % op de gemeten waarde dus dan heb je al goede weerstanden nodig. Dat kan best omhoog naar 50 volgens mij.

Even uitrekenen:
- Button 1 : 1019 - 1029 (1024 +/- 5)
- Button 2 : 914 - 924 ( 919 +/- 5)
enz

De output driver uitzetten gaat zo:

code:


   DDRB &= ~0b00000100; // Disable driver for OC1B (PB2)
of
   pinMode(10, INPUT);

Daarmee wordt PB2 een input en dus tri-state.

Maar waarschijnlijk wil je de driver aktief laag houden. Dan moet je de waveform generator uitschakelen (0b00 naar COM1B0):

code:


   TCCR1A = ( 0b00 << COM1A0 )  // Compare A is not used
          + ( 0b00 << COM1B0 )  // << Waveform generator uitschakelen
          + ( 0b01 << WGM10  ); // Clear timer on Compare A match

En de pin laag maken:

code:


   PORTB &= ~(0b00000100);
of
   digitalWrite(10,LOW);
blackdog

Golden Member

Hi deKees, :-)

Vraagje voor als ik iets wil uitbreiden met de analoge input schakelaars.
Niet direct voor dit meetinstrumentje maar ik zou voor iets anders b.v. drie groepjes van druk knopjes.

Zo de drie groepjes krijgen ieder een eigen analog ingang toebedeeld.
Kan ik b.v. alle variabelen die gebruikt worden voor de drukknopjes voorzien van _a, _b, _c, voor de drie groepjes schakelaars?
Kom ik dan niet in de problemen met time = millis(); voor die drie groepjes schakelaars?

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ja, dat kan. Maar dan wordt het al snel onoverzichtelijk, en je maakt gemakkelijk foutjes. Dan is het beter om de variabelen te bundelen in een class. En dan zou ik alle logica ook binnen die class bouwen.
Met zo een class kun je zoveel strings maken als je analoge poorten hebt.

Dat zou dan zo kunnen werken:

code:



class ANALOG_BUTTON
{
public:
   void setup(int PinNr)
   {  m_PinNr = PinNr;
   }

   bool check(int &Action)
   { 
      if(m_Timer != millis())
      {  m_Timer = millis();
      
         int NewValue = analogRead(m_PinNr);
         if(m_Counter < 0)
         {  if(near(NewValue,0))
            m_Counter += 1;
         }
         else
         {  if( near(NewValue, m_LastValue) )
            {  const int MaxDebounce = 50; 
               if( m_Counter < MaxDebounce)
               {  // Same value multiple times in a row
                  m_Counter += 1;
               }   
               else
               {  Action = decodeButton(m_LastValue);
                  if(Action != 0)
                  {  m_Counter = -50;
                     return true;
                  }
               }
            }
            else
            {  // Value has changed. Register new value and restart counter.
               m_LastValue = NewValue;
               m_Counter = 0;
            }
         }
      }
      return false;
   }

   bool near(int x1, int x2)
   {  int Margin = 20;
      return ((x1 > (x2 - Margin)) && (x1 < (x2 + Margin)) );  
   }

   int decodeButton(int AnalogValue)
   {  // - Assuming a resistor ladder of 9 equal resistors.  
      static const int Table[] = 
      {  1024 * 0 / 9, 
         1024 * 1 / 9, 
         1024 * 2 / 9, 
         1024 * 3 / 9, 
         1024 * 4 / 9, 
         1024 * 5 / 9, 
         1024 * 6 / 9, 
         1024 * 7 / 9, 
         1024 * 8 / 9, 
         1024 * 9 / 9, 
      };
      
      for(int i = 0; ; i++)
      {  if(i >= 10)
         {  return 0;
         }
         if(near(AnalogValue, Table[i]))
         {  return i;
         }
      }
   }

   int           m_PinNr;
   unsigned long m_Timer;
   int           m_LastValue;
   int           m_Counter;
};


ANALOG_BUTTON Button1;
ANALOG_BUTTON Button2;
ANALOG_BUTTON Button3;

void setup()
{
   Serial.begin(9600);
   
   Button1.setup(A1);  // Button1 uses analog pin A1
   Button2.setup(A2);  // Button2 uses analog pin A2
   Button3.setup(A3);  // Button3 uses analog pin A3
}

void loop() 
{
   int Action;
   if(Button1.check(Action))
   {  Serial.print("- Button 1, Action "); Serial.println(Action);
   
      if(Action == 1)
      {  // Code voor Button1 Action 1
      }
      if(Action == 2)
      {  // Code voor Button1 Action 2
      }
   }

   if(Button2.check(Action))
   {  Serial.print("- Button 2, Action "); Serial.println(Action);

      if(Action == 1)
      {  // Code voor Button2 Action 1
      }
      if(Action == 2)
      {  // Code voor Button2 Action 2
      }
   }

   if(Button3.check(Action))
   {  Serial.print("- Button 3, Action "); Serial.println(Action);

      if(Action == 1)
      {  // Code voor Button3 Action 1
      }
      if(Action == 2)
      {  // Code voor Button3 Action 2
      }
   }
}

[Bericht gewijzigd door deKees op 17 december 2019 01:25:35 (84%)]

blackdog

Golden Member

Hi deKees,

Ik heb vanochtend en vanavond het laatste stukje code doorgenomen en ik doorzie het niet goed of jij hebt mijn uitleg niet begrepen.
Laten we er vanuitgaan dat jij mij wel begrijpt en dat ik codeblind ben *grin*

Mooi, nogmaals mijn uitgangspunt, drie annaloge ingangen waar een x aantal drukknopjes aanhangen b.v. jouw voorstel van 9 gelijke weerstanden.
Deze drie groepjes werken geheel afzonderlijk, dus als ik van de eerste groep schakelaar-3 indruk dan wordt de taak die aan die knop hangt ge-latcht en uitgevoerd.
Deze actie beinvloed niet de toestand van een of beide andere twee groepjes.
De hoeveelheid schakelaars kan varieren, bij drie schakelaars in groep-a kan de rest van de weerstandswaarde van de deler
vervangen worden door een weerstand van de vervangende waarde.

Zoals de code er uitziet, de je als laatste van gisterenavond, kan ik dit gedrag niet herkennen zoals hier omschreven.
Nogmaals, dit is alleen voor later, niet voor dit meetinstrumentje, het is makkelijk dat ik wat code heb voor andere meetinstrumenten zodat ik microcontrolers wat vaker ga gebruiken voor bestuur functies.
Duuw er dus niet te veel tijd in..
Natuurlijk word je hulp zeer gewardeerd!

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Toch wel, hoe zal ik het uitleggen.

Het begint met een 'class' definitie. Daarmee kun je een nieuw soort variabele definiëren. Die klasse heet ANALOG_BUTTON.

code:


class ANALOG_BUTTON
{
   ...

   int           m_PinNr;
   unsigned long m_Timer;
   int           m_LastValue;
   int           m_Counter;   
};

Binnen de class wordt een viertal variabelen gedefinieerd. Elke variabele die met die class wordt aangemaakt krijgt een eigen set van die 4 variabelen.

Even later worden er 3 variabelen van dat type aangemaakt.

code:


ANALOG_BUTTON Button1;
ANALOG_BUTTON Button2;
ANALOG_BUTTON Button3;

Elk van die 3 variabelen hebben hun eigen kopie van de getallen die in de class zijn gedefiniëerd. Die Button1, Button2 en Button3 hebben elk hun eigen m_PinNr, m_Counter enz.
Je kunt hier nog meer ANALOG_BUTTON's toevoegen, zoveel je wilt.

In de arduino setup() worden er dan analog pin nummers gekoppeld. Elke ANALOG_BUTTON krijgt zijn eigen pin nr.

code:


void setup()
{
   Serial.begin(9600);
   
   Button1.setup(A1);  // Button1 uses analog pin A1
   Button2.setup(A2);  // Button2 uses analog pin A2
   Button3.setup(A3);  // Button3 uses analog pin A3
}

Vervolgens kun je dan in de loop() controleren or er een switch aktief is. Daarbij reageert Button1 op switches die zijn gekoppeld aan A1, omdat die in Button1.Setup(A1) aan elkaar gekoppeld zijn. Zo reageert Button2 op A2, en Button3 op A3.

Het testen van de switches doe je door de check() funktie aan te roepen, ook weer gekoppeld aan één van de Button variabelen.

code:


void loop()
{  
   int Action;
   
   if(Button1.check(Action))
   {  // Switch op A1 is ingedrukt.
   }
   else
   {  // Geen switch op A1 aktief.
   }
}

De check() funktie van Button1 gebruikt de variabelen van Button1. Die leest de analoge waarde van A1, en berekent of er een switch aktief is.

Zolang er geen switch gesloten is, geeft check false terug en gaat het verder in de else.

Is er wel een switch gesloten, dan berekent check() welke dat is en zet een getal in de Action variabele. En bovendien wordt er dan een true teruggegeven, zodat het programma nu wel het if() gedeelte induikt. Daar kun je dus testen welke switch is ingedrukt, en de bijbehorende aktie uitvoeren.

Ditzelfde kun je dan ook doen met de andere Button variabele.

blackdog

Golden Member

Hi deKees,

Dank voor de extra uitleg, maar...
Het kwartje valt nog niet.

Het kost veel inzet het te doorgronden door mijn probleem met taal.

Ik zie te veel Button in de code staan :-)
Is er een mogelijkheid om een roep b.v. die allen aan zeg A1 hangen te voorzien van zoiets als Groep_a1.
Zodat ik het onderscheid ga zien tussen groep functies en de variabelen die voor de echte drukknoppen worden gebruikt,
ik hoop dat je begrijpt wat ik bedoel.

Ik denk dat ik wel begrijp dat je een deel van de code steeds opnieuw gebruikt onafhankelijk van of ik nu twee annaloge ingangen gebruik, of zes stuks.

Ben wezen zoeken naar een goede uitleg van Classes, de meeste uitleg gaan direct veel te diep, starten niet met de LED knipper modus :-)
Jip en Janne begin dus, maar ben nu al te moe om het verder uit te zoeken en te begrijpen, morgen heb ik weer wat tijd hier voor.

Als laatste dacht ik laat ik eens iets ombouwen van jouw code om het duidelijk te maken wat ik bedoel, en dat doe ik met de code uit de loop.
Geen idee of dit kan of klopt maar om duidelijk te maken wat ik bedoel.

c code:


void loop() 
{
   int Action;
   if(Button1.check(Action))
   {  Serial.print("- Groep-A0, Action "); Serial.println(Action);
   
      if(Action == 1)
      {  // Code voor Groep-A0 Knop_1
      }
      if(Action == 2)
      {  // Code voor Groep-A0 Knop_2
      }
   }

   if(Button2.check(Action))
   {  Serial.print("- Groep-A1, Action "); Serial.println(Action);

      if(Action == 1)
      {  // Code voor Groep-A1 Knop_1
      }
      if(Action == 2)
      {  // Code voor Groep-A1 Knop_2
      }
   }

   if(Button3.check(Action))
   {  Serial.print("- Groep-A2, Action "); Serial.println(Action);

      if(Action == 1)
      {  // Code voor Groep-A2 Knop_1
      }
      if(Action == 2)
      {  // Code voor Groep-A2 Knop_2
      }
   }
}

Dit is voor jou misschien van weinig betekenins maar voor mij erg belangrijk om de code te doorzien.
De opmerking over de haakjes die niet goed staan in mijn code begrijp ik natuurlijk, dat is de norm,
maar voor mijn brein is die haakjes positie heel rommelig en raak daardoor snel de weg kwijt. :-)
Iedereen zijn eigen mix van abberaties...

Dank en groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ja dat kan zo. Het stukje onder de if is de applikatie waar de switches gekoppeld worden aan de gewenste akties.

Als je het eens opbouwt dan kun je in de arduino monitor zien wat er gebeurt.

En als er teveel 'button' instaat dan kun je die buttons ook een andere naam geven. En ook de 'Action' variabele kun je bijv SwitchNr noemen. En de class naam kun je ook aanpassen naar bijv ANALOG_BUTTON_GROUP. Dan krijg je zoiets als dit, exact hetzelfde programma met andere namen:

code:



class ANALOG_BUTTON_GROUP
{
public:
   void setup(int PinNr)
   {  m_PinNr = PinNr;
   }

   bool check(int &Action)
   { 
      if(m_Timer != millis())
      {  m_Timer = millis();
      
         int NewValue = analogRead(m_PinNr);
         if(m_Counter < 0)
         {  if(near(NewValue,0))
            m_Counter += 1;
         }
         else
         {  if( near(NewValue, m_LastValue) )
            {  const int MaxDebounce = 50; 
               if( m_Counter < MaxDebounce)
               {  // Same value multiple times in a row
                  m_Counter += 1;
               }   
               else
               {  Action = decodeButton(m_LastValue);
                  if(Action != 0)
                  {  m_Counter = -50;
                     return true;
                  }
               }
            }
            else
            {  // Value has changed. Register new value and restart counter.
               m_LastValue = NewValue;
               m_Counter = 0;
            }
         }
      }
      return false;
   }

   bool near(int x1, int x2)
   {  int Margin = 20;
      return ((x1 > (x2 - Margin)) && (x1 < (x2 + Margin)) );  
   }

   int decodeButton(int AnalogValue)
   {  // - Assuming a resistor ladder of 9 equal resistors.  
      static const int Table[] = 
      {  1024 * 0 / 9, 
         1024 * 1 / 9, 
         1024 * 2 / 9, 
         1024 * 3 / 9, 
         1024 * 4 / 9, 
         1024 * 5 / 9, 
         1024 * 6 / 9, 
         1024 * 7 / 9, 
         1024 * 8 / 9, 
         1024 * 9 / 9, 
      };
      
      for(int i = 0; ; i++)
      {  if(i >= 10)
         {  return 0;
         }
         if(near(AnalogValue, Table[i]))
         {  return i;
         }
      }
   }

   int           m_PinNr;
   unsigned long m_Timer;
   int           m_LastValue;
   int           m_Counter;
};


ANALOG_BUTTON_GROUP GroepA1;
ANALOG_BUTTON_GROUP GroepA2;
ANALOG_BUTTON_GROUP GroepA3;

void setup()
{
   Serial.begin(9600);
   
   GroepA1.setup(A1);  // GroepA1 gebruikt analog pin A1
   GroepA2.setup(A2);  // GroepA2 gebruikt analog pin A2
   GroepA3.setup(A3);  // GroepA3 gebruikt analog pin A3
}

void loop() 
{
   int SwitchNr;

   if(GroepA1.check(SwitchNr))
   {  Serial.print("- GroepA1, SwitchNr "); Serial.println(SwitchNr);
   
      if(SwitchNr == 1)
      {  // Code voor GroepA1 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA1 SwitchNr 2
      }
   }

   if(GroepA2.check(SwitchNr))
   {  Serial.print("- GroepA2, SwitchNr "); Serial.println(SwitchNr);

      if(SwitchNr == 1)
      {  // Code voor GroepA2, SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA2, SwitchNr 2
      }
   }

   if(GroepA3.check(SwitchNr))
   {  Serial.print("- GroepA3, SwitchNr "); Serial.println(SwitchNr);

      if(SwitchNr == 1)
      {  // Code voor GroepA3, SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA3, SwitchNr 2
      }
   }
}
blackdog

Golden Member

Hi deKees,

Vlak voor ik plat ga denk ik dat ik nu wat meer begrijp....
Hieronder mijn interpretatie van een deel van de opbouw.

class ANALOG_BUTTON_GROUP <= dit is de naam van jouw stukje code dat een class is

Hieronder wordt aangegeven dat deze code drie groepjes bevat en wel GroepA1, GroepA2 en GroepA3
ANALOG_BUTTON_GROUP GroepA1;
ANALOG_BUTTON_GROUP GroepA2;
ANALOG_BUTTON_GROUP GroepA3;

En hier wort aangegeven welke analoge ingangen doorde drie groepen gebruikt wordt.
GroepA1.setup(A1); // GroepA1 gebruikt analog pin A1
GroepA2.setup(A2); // GroepA2 gebruikt analog pin A2
GroepA3.setup(A3); // GroepA3 gebruikt analog pin A3

Van het volgende stukje snap ik tot nogtoe helemaal niets...
void setup(int PinNr)
{ m_PinNr = PinNr;
}

bool check(int &Action)
{
if(m_Timer != millis())
{ m_Timer = millis();

int NewValue = analogRead(m_PinNr);

En dan heb ik het over: m_PinNr = PinNr, hoe zit dit gekoppeld aan A1, A2 of A3 in de code?
Ik neem aan dit dat stukje code de desbetreffende analoge ingang uitleest, of zie ik dit verkeerd?

Morgen weer een dag!

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Interpretatie klopt helemaal.

code:


void setup(int PinNr)
{ m_PinNr = PinNr;
}

Dit is de definitie van een setup funktie (subroutine). Die funktie is een onderdeel van de class definitie. De funktie heeft een parameter (PinNr) die je meegeeft als je de funktie aanroept. Het enige wat de funktie hier doet is die parameter kopiëren naar de class variabele m_PinNr. Daarmee wordt de PinNr binnen de class opgeslagen.

Als je de funktie wilt uitvoeren dan moet je die koppelen aan een class variabele. Door die koppeling krijgt de funktie toegang tot de data binnen de class.

Dat doe je met :

code:


  GroepA1.setup(A1);

Hier gebruik je de setup() funktie. PinNr wordt gelijk aan A1 en wordt opgeslagen in de m_PinNr variabele van GroepA1.

Later wordt die variabele gebruikt door de check() funktie. Die doet:

code:


int NewValue = analogRead(m_PinNr);

Die doet analogRead met het PinNr uit m_PinNr. Voor GroepA1 is dat A1, voor GroepA2 is dat A2, omdat die getallen door setup() zijn vastgelegd.

Ook die check() funktie moet aan een class variabele gekoppeld worden als je hem gebruikt. Dus:

code:


   GroepA1.check( .. ); 
blackdog

Golden Member

Hi deKees,

Vanavond de weerstands string uitgebreid naar de negen stuks 1K en de analoge ingangen aangepast in de code naar de annaloge ingangsnummers die ik gebruikte.

Werkt! :D

Dank en groet.
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"
blackdog

Golden Member

Hi deKees, :-)

Ik heb de code nog een beetje aangepast wat het uitvoerende deel betreft en nog wat extra info er bij gezet.
Wil jij als je tijd hebt er een blik op werpen en aanpassen waar nodig.

Pas aan of gooi er uit wat bij de omschijving staat een het begin van de code, het is van jou, jij bepaald dat ;-)

c code:


/*  Multi group Analog button selector
 * 
 * Created      : December, 2019
 * By           : CJ van der Hoeven
 *
 * Purpose      : Reading out multiple switches on a single analog input of an Arduino Microcontroler (here as an example: 9x a switch per analog input in three groups)
 *              
 *              
 * Featuring    : Multiple customizable to more or less groups and also more or less switches per group 
 *                
 * The code has been adapted for my application.      
 * Blackdog, www.circuitsonline.net Netherlands
 * 
 * The code is owned by: CJ van der Hoeven
 *                
 */


class ANALOG_BUTTON_GROUP
{
public:
   void setup(int PinNr)
   {  m_PinNr = PinNr;
   }

   bool check(int &Action)
   { 
      if(m_Timer != millis())
      {  m_Timer = millis();
      
         int NewValue = analogRead(m_PinNr);
         if(m_Counter < 0)
         {  if(near(NewValue,0))
            m_Counter += 1;
         }
         else
         {  if( near(NewValue, m_LastValue) )
            {  const int MaxDebounce = 50; 
               if( m_Counter < MaxDebounce)
               {  // Same value multiple times in a row
                  m_Counter += 1;
               }   
               else
               {  Action = decodeButton(m_LastValue);
                  if(Action != 0)
                  {  m_Counter = -50;
                     return true;
                  }
               }
            }
            else
            {  // Value has changed. Register new value and restart counter.
               m_LastValue = NewValue;
               m_Counter = 0;
            }
         }
      }
      return false;
   }

   bool near(int x1, int x2)
   {  int Margin = 20;                          // this helps with the precision of the resistor divider and the inaccuracy of the ADC, the measured value of a switch can be between +20 and -20 LSB of the ADC.
      return ((x1 > (x2 - Margin)) && (x1 < (x2 + Margin)) );  
   }

   int decodeButton(int AnalogValue)
   {                                            // Assuming a resistor ladder of 9 equal resistors, use 9x 1K resistor selected 5% or normal 1% type
      static const int Table[] =                // Change this table for more or less switches, up to 20 switches is possible on an analog input with 1% resistors.
      {  1024 * 0 / 9,                           
         1024 * 1 / 9, 
         1024 * 2 / 9, 
         1024 * 3 / 9, 
         1024 * 4 / 9, 
         1024 * 5 / 9, 
         1024 * 6 / 9, 
         1024 * 7 / 9, 
         1024 * 8 / 9, 
         1024 * 9 / 9, 
      };
      
      for(int i = 0; ; i++)
      {  if(i >= 10)
         {  return 0;
         }
         if(near(AnalogValue, Table[i]))
         {  return i;
         }
      }
   }

   int           m_PinNr;
   unsigned long m_Timer;
   int           m_LastValue;
   int           m_Counter;
};


ANALOG_BUTTON_GROUP GroepA1;                    // As an example, three groups of switches have been created, which can be reduced or expanded as required
ANALOG_BUTTON_GROUP GroepA2;                    // Don't forget to create an equal amount of GroupAx.setup(Ax) in "void setup()"
ANALOG_BUTTON_GROUP GroepA3;                    // Also in the "void loop()" an button action piece is necessary for each buttongroup

void setup()
{
   Serial.begin(9600);
   
   GroepA1.setup(A3);                           // Swtich GroupA1 uses the analog input A3 in this case, can be changed to any analog input
   GroepA2.setup(A4);                           // Swtich GroupA2 uses the analog input A4 in this case, can be changed to any analog input
   GroepA3.setup(A5);                           // Swtich GroupA3 uses the analog input A5 in this case, can be changed to any analog input
}

void loop() 
{
   int SwitchNr;

   if(GroepA1.check(SwitchNr))
   {  Serial.print("- GroepA1, SwitchNr "); Serial.println(SwitchNr);

// -------------------------------------------------------------------
// Action for the first group of switches: ANALOG_BUTTON_GROUP GroepA1
// -------------------------------------------------------------------
   
      if(SwitchNr == 1)
      {  // Code voor GroepA1 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA1 SwitchNr 2
      }
      if(SwitchNr == 3)
      {  // Code voor GroepA1 SwitchNr 3
      }
      if(SwitchNr == 4)
      {  // Code voor GroepA1 SwitchNr 4
      }  
      if(SwitchNr == 5)
      {  // Code voor GroepA1 SwitchNr 5
      }
      if(SwitchNr == 6)
      {  // Code voor GroepA1 SwitchNr 6
      }
      if(SwitchNr == 7)
      {  // Code voor GroepA1 SwitchNr 7
      }
      if(SwitchNr == 8)
      {  // Code voor GroepA1 SwitchNr 8
      }  
      if(SwitchNr == 9)
      {  // Code voor GroepA1 SwitchNr 9
      }

// --------------------------------------------------------------------
// Action for the second group of switches: ANALOG_BUTTON_GROUP GroepA2
// --------------------------------------------------------------------

      if(SwitchNr == 1)
      {  // Code voor GroepA2 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA2 SwitchNr 2
      }
      if(SwitchNr == 3)
      {  // Code voor GroepA2 SwitchNr 3
      }
      if(SwitchNr == 4)
      {  // Code voor GroepA2 SwitchNr 4
      }  
      if(SwitchNr == 5)
      {  // Code voor GroepA2 SwitchNr 5
      }
      if(SwitchNr == 6)
      {  // Code voor GroepA2 SwitchNr 6
      }
      if(SwitchNr == 7)
      {  // Code voor GroepA2 SwitchNr 7
      }
      if(SwitchNr == 8)
      {  // Code voor GroepA2 SwitchNr 8
      }  
      if(SwitchNr == 9)
      {  // Code voor GroepA2 SwitchNr 9
      }

// --------------------------------------------------------------------
// Action for the second group of switches: ANALOG_BUTTON_GROUP GroepA3
// --------------------------------------------------------------------
   
      if(SwitchNr == 1)
      {  // Code voor GroepA3 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA3 SwitchNr 2
      }
      if(SwitchNr == 3)
      {  // Code voor GroepA3 SwitchNr 3
      }
      if(SwitchNr == 4)
      {  // Code voor GroepA3 SwitchNr 4
      }  
      if(SwitchNr == 5)
      {  // Code voor GroepA3 SwitchNr 5
      }
      if(SwitchNr == 6)
      {  // Code voor GroepA3 SwitchNr 6
      }
      if(SwitchNr == 7)
      {  // Code voor GroepA3 SwitchNr 7
      }
      if(SwitchNr == 8)
      {  // Code voor GroepA3 SwitchNr 8
      }  
      if(SwitchNr == 9)
      {  // Code voor GroepA3 SwitchNr 9
      }

  }

}

Gegroet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ja, dat was de bedoeling, om voor elke switch een if statement te hebben. Dan kun je daar nog code aan toevoegen om de gewenste aktie uit te voeren.

Maar je moet nog wel de analoge port inlezen en de procedure uitvoeren om te testen of er een toets is ingedrukt.

Dus bij

code:


// --------------------------------------------------------------------
// Action for the second group of switches: ANALOG_BUTTON_GROUP GroepA2
// --------------------------------------------------------------------

Moet je dan nog een stukje toevoegen:

code:


   }

   if(GroepA2.check(SwitchNr))
   {  Serial.print("- GroepA2, SwitchNr "); Serial.println(SwitchNr);

En dat dan ook voor GroepA3

blackdog

Golden Member

Hi,

Ik heb het schaklaar schema ook aangepast, er is maar één weerstands string nodig voor alle groepen, daar de weerstands string niet wordt belast.
Zoals ook al in de code staat, kan je de hoeveelheid strings gebruiken (analoge ingang groepen) die je nodig hebt.
Ook hoef je voor iedere groep geen negen schakelaars te gebruiken twee of zeven kan ook.

Let wel op met de analoge ingangen, als er geen schaklaar is ingedrukt, is de impedantie in de lage frequenties rond de 220K met 1nF parallel.
Dus bij lange bedrading kunnen de ingangs draden storingen oppikken, dus zoals altijd let op je bedradings techniek.

http://www.bramcam.nl/Diversen/Arduino-Analog-Button-03.png

.
De code heb ik ook aangepast zoals deKees al aangaf, zie hieronder de de laangepaste vrsie.

c code:


/*  Multi group Analog button selector
 * 
 * Created      : December, 2019
 * By           : CJ van der Hoeven
 *
 * Purpose      : Reading out multiple switches on a single analog input of an Arduino (here as an example 9x one switch per input in three groups)
 *              :
 *              :
 * Featuring    : Multiple customizable to more or less groups and also more or less switches per group 
 *                
 * The code has been adapted for my application.      
 * Blackdog, www.circuitsonline.net Netherlands
 * 
 * The code is owned by: CJ van der Hoeven
 *                
 */


class ANALOG_BUTTON_GROUP
{
public:
   void setup(int PinNr)
   {  m_PinNr = PinNr;
   }

   bool check(int &Action)
   { 
      if(m_Timer != millis())
      {  m_Timer = millis();
      
         int NewValue = analogRead(m_PinNr);
         if(m_Counter < 0)
         {  if(near(NewValue,0))
            m_Counter += 1;
         }
         else
         {  if( near(NewValue, m_LastValue) )
            {  const int MaxDebounce = 50; 
               if( m_Counter < MaxDebounce)
               {  // Same value multiple times in a row
                  m_Counter += 1;
               }   
               else
               {  Action = decodeButton(m_LastValue);
                  if(Action != 0)
                  {  m_Counter = -50;
                     return true;
                  }
               }
            }
            else
            {  // Value has changed. Register new value and restart counter.
               m_LastValue = NewValue;
               m_Counter = 0;
            }
         }
      }
      return false;
   }

   bool near(int x1, int x2)
   {  int Margin = 20;                          // this helps with the precision of the resistor divider and the inaccuracy of the ADC, the measured value of a switch can be between +20 and -20 LSB of the ADC.
      return ((x1 > (x2 - Margin)) && (x1 < (x2 + Margin)) );  
   }

   int decodeButton(int AnalogValue)
   {                                            // Assuming a resistor ladder of 9 equal resistors, use 9x 1K resistor selected 5% or normal 1% type
      static const int Table[] =                // Change this table for more or less switches, up to 20 switches is possible on an analog input with 1% resistors.
      {  1024 * 0 / 9,                           
         1024 * 1 / 9, 
         1024 * 2 / 9, 
         1024 * 3 / 9, 
         1024 * 4 / 9, 
         1024 * 5 / 9, 
         1024 * 6 / 9, 
         1024 * 7 / 9, 
         1024 * 8 / 9, 
         1024 * 9 / 9, 
      };
      
      for(int i = 0; ; i++)
      {  if(i >= 10)
         {  return 0;
         }
         if(near(AnalogValue, Table[i]))
         {  return i;
         }
      }
   }

   int           m_PinNr;
   unsigned long m_Timer;
   int           m_LastValue;
   int           m_Counter;
};


ANALOG_BUTTON_GROUP GroepA1;                    // As an example, three groups of switches have been created, which can be reduced or expanded as required
ANALOG_BUTTON_GROUP GroepA2;                    // Don't forget to create an equal amount of GroupAx.setup(Ax) in "void setup()"
ANALOG_BUTTON_GROUP GroepA3;                    // Also in the "void loop()" an button action piece is necessary for each buttongroup

void setup()
{
   Serial.begin(9600);
   
   GroepA1.setup(A3);                           // Swtich GroupA1 uses the analog input A3 in this case, can be changed to any analog input
   GroepA2.setup(A4);                           // Swtich GroupA2 uses the analog input A4 in this case, can be changed to any analog input
   GroepA3.setup(A5);                           // Swtich GroupA3 uses the analog input A5 in this case, can be changed to any analog input
}

void loop() 
{
   int SwitchNr;

   if(GroepA1.check(SwitchNr))
   {  Serial.print("- GroepA1, SwitchNr "); Serial.println(SwitchNr);
// -------------------------------------------------------------------
// Action for the first group of switches: ANALOG_BUTTON_GROUP GroepA1
// -------------------------------------------------------------------
   
      if(SwitchNr == 1)
      {  // Code voor GroepA1 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA1 SwitchNr 2
      }
      if(SwitchNr == 3)
      {  // Code voor GroepA1 SwitchNr 3
      }
      if(SwitchNr == 4)
      {  // Code voor GroepA1 SwitchNr 4
      }  
      if(SwitchNr == 5)
      {  // Code voor GroepA1 SwitchNr 5
      }
      if(SwitchNr == 6)
      {  // Code voor GroepA1 SwitchNr 6
      }
      if(SwitchNr == 7)
      {  // Code voor GroepA1 SwitchNr 7
      }
      if(SwitchNr == 8)
      {  // Code voor GroepA1 SwitchNr 8
      }  
      if(SwitchNr == 9)
      {  // Code voor GroepA1 SwitchNr 9
      }
   }
   
// --------------------------------------------------------------------
// Action for the second group of switches: ANALOG_BUTTON_GROUP GroepA2
// --------------------------------------------------------------------
   if(GroepA2.check(SwitchNr))
   {  Serial.print("- GroepA2, SwitchNr "); Serial.println(SwitchNr);
      
      if(SwitchNr == 1)
      {  // Code voor GroepA2 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA2 SwitchNr 2
      }
      if(SwitchNr == 3)
      {  // Code voor GroepA2 SwitchNr 3
      }
      if(SwitchNr == 4)
      {  // Code voor GroepA2 SwitchNr 4
      }  
      if(SwitchNr == 5)
      {  // Code voor GroepA2 SwitchNr 5
      }
      if(SwitchNr == 6)
      {  // Code voor GroepA2 SwitchNr 6
      }
      if(SwitchNr == 7)
      {  // Code voor GroepA2 SwitchNr 7
      }
      if(SwitchNr == 8)
      {  // Code voor GroepA2 SwitchNr 8
      }  
      if(SwitchNr == 9)
      {  // Code voor GroepA2 SwitchNr 9
      }
   }
   
// --------------------------------------------------------------------
// Action for the third group of switches: ANALOG_BUTTON_GROUP GroepA3
// --------------------------------------------------------------------
   if(GroepA3.check(SwitchNr))
   {  Serial.print("- GroepA3, SwitchNr "); Serial.println(SwitchNr);   

      if(SwitchNr == 1)
      {  // Code voor GroepA3 SwitchNr 1
      }
      if(SwitchNr == 2)
      {  // Code voor GroepA3 SwitchNr 2
      }
      if(SwitchNr == 3)
      {  // Code voor GroepA3 SwitchNr 3
      }
      if(SwitchNr == 4)
      {  // Code voor GroepA3 SwitchNr 4
      }  
      if(SwitchNr == 5)
      {  // Code voor GroepA3 SwitchNr 5
      }
      if(SwitchNr == 6)
      {  // Code voor GroepA3 SwitchNr 6
      }
      if(SwitchNr == 7)
      {  // Code voor GroepA3 SwitchNr 7
      }
      if(SwitchNr == 8)
      {  // Code voor GroepA3 SwitchNr 8
      }  
      if(SwitchNr == 9)
      {  // Code voor GroepA3 SwitchNr 9
      }
  }
}

Nu weer verder met de andere projectjes, niet te veel tijd door werkdruk. :-)

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ja, inderdaad. 1 string weerstanden is genoeg voor alle drie de groepen.

Ziet er goed uit zo.