Chladnie wave with arduino


I hope this is a right place to my question. I am newbie at arduino programing but like to build things like this chladnie waves. Basicly i use a signal generator and a rotary encoder with a pre written code. It makes a wave wich goes to an amplifier and to a speaker wich resonate with a plate. The code and all stuff working fine. I would ask for a little change in the code.

Its starts, you can chose any frequency from 100-1200Hz (its enough i guess for human ears) with the potentiometer and you can hear whatever you chose and also hear while rotating and of course you can see the waves on the plate.

After you are bored to “play” with this it would be so anoying if its just plays forever the last frequency (example 800-900Hz) so after nobody playing it should stop.
If (3sec passed, and the rotary at the same position) is should write frequency to 0HZ or switch off signal generator. if you rotate again the potenciometer it works again like before from 100hz or from last stored frequency its not a point.

Main code by Richard Visokey AD7C -
Revision 2.0 - November 6th, 2013

// Include the library code
#include <LiquidCrystal.h>
#include <rotary.h>
#include <EEPROM.h>

//Setup some items
#define W_CLK 8   // Pin 8 - connect to AD9850 module word load clock pin (CLK)
#define FQ_UD 9   // Pin 9 - connect to freq update pin (FQ)
#define DATA 11   // Pin 11 - connect to serial data load pin (DATA)
#define RESET 10  // Pin 10 - connect to reset pin (RST) 
#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }
Rotary r = Rotary(2,3); // sets the pins the rotary encoder uses.  Must be interrupt pins.
LiquidCrystal lcd(12, 13, 7, 6, 5, 4); // I used an odd pin combination because I need pin 2 and 3 for the interrupts.
int_fast32_t rx=100; // Base (starting) frequency of VFO.  This only loads once.  To force load again see ForceFreq variable below.
int_fast32_t rx2=1; // variable to hold the updated frequency
int_fast32_t increment = 10; // starting VFO update increment in HZ.
int_fast32_t iffreq = 0000000; // Intermedite Frequency - Amount to subtract (-) from base frequency. ********************************************
int buttonstate = 0;
int buttonstate2 = 0;
int GoIF = 1;
String hertz = "10 Hz";
int  hertzPosition = 5;
byte ones,tens,hundreds,thousands,tenthousands,hundredthousands,millions ;  //Placeholders
String freq; // string to hold the frequency
int_fast32_t timepassed = millis(); // int to hold the arduino miilis since startup
int memstatus = 1;  // value to notify if memory is current or old. 0=old, 1=current.

int ForceFreq = 1;  // Change this to 0 after you upload and run a working sketch to activate the EEPROM memory.  YOU MUST PUT THIS BACK TO 0 AND UPLOAD THE SKETCH AGAIN AFTER STARTING FREQUENCY IS SET!

void setup() {
  pinMode(A0,INPUT); // Connect to a button that goes to GND on push
  pinMode(A5,INPUT); // IF sense **********************************************
  lcd.begin(16, 2);
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT); 
  pulseHigh(FQ_UD);  // this pulse enables serial mode on the AD9850 - Datasheet page 12.
  // Load the stored frequency  
  if (ForceFreq == 0) {
    freq = String(;
    rx = freq.toInt();  

void loop() {    
  // Update the display and frequency if the new Freq NEQ the old Freq  
  if (rx != rx2){   
        rx2 = rx;        
  // Rotate through the rate of tuning as you hold down the button
  buttonstate = digitalRead(A0);
  if(buttonstate == LOW) {
  // Check for PIN low to drive IF offset Freq
  buttonstate = digitalRead(A5);  
    if (buttonstate != buttonstate2){
        if(buttonstate == LOW) {       
              GoIF = 0; 
              buttonstate2 = buttonstate; 
            lcd.print(" ");
            GoIF = 1;
            buttonstate2 = buttonstate;
    // Write the frequency to memory if not stored and 2 seconds have passed since the last frequency change.
    if(memstatus == 0){   
      if(timepassed+2000 < millis()){
// Interrupt routine to catch the rotary encoder
ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result) {    
    if (result == DIR_CW){rx=rx+increment;}
    else {rx=rx-increment;};       
      if (rx >=1200){rx=rx2;}; // UPPER VFO LIMIT
      if (rx <=100){rx=rx2;}; // LOWER VFO LIMIT

// frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {  
  if (GoIF == 1){frequency=frequency-iffreq;}; //If pin = low, subtract the IF frequency.
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850.  You can make 'slight' tuning variations here by adjusting the clock frequency.
  for (int b=0; b<4; b++, freq>>=8) {
    tfr_byte(freq & 0xFF);
  tfr_byte(0x000);   // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);  // Done!  Should see output
// transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
void tfr_byte(byte data)
  for (int i=0; i<8; i++, data>>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high

void setincrement(){
  if(increment == 10){increment = 50; hertz = "50 Hz"; hertzPosition=5;}
  else if (increment == 50){increment = 100;  hertz = "100 Hz"; hertzPosition=4;}
  else if (increment == 100){increment = 1; hertz="1 Hz"; hertzPosition=6;}
  else if (increment == 500){increment = 1000; hertz="1 Khz"; hertzPosition=6;}
  //else if (increment == 1000){increment = 2500; hertz="2.5 Khz"; hertzPosition=4;}
  //else if (increment == 2500){increment = 5000; hertz="5 Khz"; hertzPosition=6;}
  //else if (increment == 5000){increment = 10000; hertz="10 Khz"; hertzPosition=5;}
  //else if (increment == 10000){increment = 100000; hertz="100 Khz"; hertzPosition=4;}
  //else if (increment == 100000){increment = 1000000; hertz="1 Mhz"; hertzPosition=6;}  
  else{increment = 10; hertz = "10 Hz"; hertzPosition=5;};  
   lcd.print("                      ");
   delay(250); // Adjust this delay to speed up/slow down the button menu scroll speed.

void showFreq(){
    millions = int(rx/1000000);
    hundredthousands = ((rx/100000)%10);
    tenthousands = ((rx/10000)%10);
    thousands = ((rx/1000)%10);
    hundreds = ((rx/100)%10);
    tens = ((rx/10)%10);
    ones = ((rx/1)%10);
    lcd.print("                ");
   if (millions > 9){lcd.setCursor(1,0);}
   else{lcd.setCursor(3,0);}             //hova írja ki a kijelzőn!
   // lcd.print(millions);
   // lcd.print(".");
   // lcd.print(tenthousands);
   // lcd.print(".");
    lcd.print(" Hz  ");
    timepassed = millis();
    memstatus = 0; // Trigger memory write

void storeMEM(){
  //Write each frequency section to a EPROM slot.  Yes, it's cheating but it works!
   memstatus = 1;  // Let program know memory has been written

For all timing the standard pattern should be followed, to be sure that overflows are handled properly:

unsigned long current_time, start_time;
 current_time = millis();
 if (current_time - start_time >= interval) ...

If you know that it's sufficient to play a tone for 3 seconds, set interval=3000 (ms).
Then at every change of the tone (or rotary encoder...), turn on the amplifier, play the tone and update the start_time. When the interval has elapsed, turn the amplifier off again.

Thank you for your help i really appreciate it. I understand your code but since i am noob at programing and this is not my code i can change the basic things like upper lower limit and change intervals of the rotary.

could you help me to understand which part is the real write of the frequency?

its here:

“frequency calc from datasheet page 8 = * /2^32?”

if (conditions is right: more than 3sec) set fequency to 0Hz

Sorry for my bad English.

As I understand it, the frequency is set in sendFrequency(). Here the start_time should be remembered.

Thank you for your help. I think for something like this:

current_time = millis();
start_time = ? (it should be the rx != rx2)

if (rx != rx2 && current_time - start_time <= 3000){
rx2 = rx;

how can i say that start time is that time when the rotary turns so rx not equal with rx2 ?

If you want to limit the audio output time, the start time is when the sound is started. Then you don't need to wait until the time has elapsed, instead every rotary knob change can change the output to the new frequency, and restart the time interval.

sound output is actice after yoo rotate the poteciometer and got any frequency beetween the limits. Sound is the sound of the actual sin frequency. So as i think somehow start time should be: that time in millis() when you make a difference between rx and rx2 but i dont know how to tell this in arduino language like:

stop_time = start_time + 3000;

if (rx != rx2){millis() == start_time};

if (rx != rx2 && millis()<stop_time){ // i dont sure if stop time is changeing or its fix ?
rx2 = rx;

sorry if i am not understand you correctly but i am not a native English speaker nor a programmer just a curious man in Hungary who like build

In reply #1 I've shown you the correct code for interval timing.

yes i saw and understand but i am not know how to definy start_time what will fix that if this is the start_time.

really appreciate your help

i am not know how to definy start_time

start_time = millis();


start_time = millis();

thinked before current_time = millis(),
start_time = a new time from somewhere

sorry i am a total new in programming know the basics but this timing is so confusing for me

than i need another time

millis() returns the current time, i.e. a new value whenever it's called.

Put the line

start_time = millis();

into sendFrequency(), to update start_time whenever the frequency has been changed.

You can call variables what ever you like but it is best to call them something meaningful, the line
current_time = millis(
Generates a variable that has a name that is not meaningful because the variable will only be the current time at the instant you execute the line. Any time after that although it is called current time it will only be the time you assigned the variable. That is it will not be the current time any more.