Star Wars Time: Lightsaber Code

Hi there,
I am currently working on the electronics for lightsaber and this is my first Arduino/Electronics project. While I think, I am on the right track I currently experience some issues with the sound output, for which I would really appreciate your help.

The general idea is not to have pre-recorded audio samples, but to generate the sound in the Arduino so that it can later be modified for example by adding an accelerometer (the swings should increase the pitch of the sound). Therefore any approach using a stored sound file or a sine wave register with predefined values are out of the question. The waveform I currently use is a simple overlay of the sine waves with 95Hz and 125Hz (each contributing 50% to the overall amplitude) to get some nice interference sounds we all love from lightsabers.

My current approach is to use time interrupts ( = sampling rate) to generate the waveform values for the specific sample. The amplitude of the sine wave is then forwarded to the output. While I started with a PWM-output, which generates really audible artifacts, I now switched to a D/A converter using the digital pins of my Arduino Pro Mini (3.3V, 8 Mhz) with the hope to produce sound with less distortion. I went for a 5-pin R2R ladder which now serves as my D/A converter. However, when I output sound using this method, the result is just as distorted using my code as with using the PWM method. Yet, when I tried some code not using interrupts and live calculations it sounded really good. Thus, probably the issue is related to my code.

I would really appreciate your help!

#include <avr/interrupt.h>

volatile unsigned long millisecond = 1;
volatile float amplitude;
volatile int frequency1 = 95;
volatile int frequency2 = 125;
volatile float frequencyscaler = 1.0;
volatile const float pi = 3.14159265359;
volatile const float timescalefactor = 0.001;
volatile const int ledconstant = 512;

void setup() {
  //initialize D-Ports
  DDRD = 255;
  
  // put your setup code here, to run once:
  // initialize Timer1
    cli();             // disable global interrupts
  TCCR1A = 0;        // set entire TCCR1A register to 0
  TCCR1B = 0;

  // Timer Prescaling
  OCR1A = 7811.5 * timescalefactor;

  // activate Clear Timer on Compare Match
  TCCR1B |= (1 << WGM12);
  
  // Timer1: set T0IE1-bit = 1 --> overflow interrupt:
  TIMSK1 = (1 << TOIE1);
  // Set CS10 bit so Timer1B runs at clock speed:
  TCCR1B |= (1 << CS10);
  TCCR1B |= (1 << CS12);

  // enable timer compare interrupt:
  TIMSK1 |= (1 << OCIE1A);
  
  // enable global interrupts:
  sei();
}

void loop() {
  // put your main code here, to run repeatedly:

}

ISR(TIMER1_COMPA_vect)
{
  if (millisecond % 1 == 0) {
  amplitude = 0.5 * sin(2 * pi * frequencyscaler * frequency1 * millisecond * timescalefactor) + 0.5 * sin(2 * pi * frequencyscaler * frequency2 * millisecond * timescalefactor);
  PORTD = amplitude*255;
  analogWrite(8, ledconstant + amplitude * (1023 - ledconstant));
  }
  
  millisecond ++;
}

How do you plan to store a float in the OCR1A register?

Oha! So this is the Problem? In another thread I saw that someone used an unsigned char - would this be the correct way to go?

I don’t know if that’s the only problem but it is certainly a mistake.

No, in this case unsigned char isn’t right either. How many bits in OCR1A? What data type is that same size?

In this youtube video where I got the code from, the guy used the command to set the digital pins 1-8, thus 0-255.

That's the DDRD = 255 line. I'm talking about where you are trying to put a floating point number in the OCR1A register. Completely different line of code.

This line:

OCR1A = 7811.5 * timescalefactor;

Oh, wrong line ...

I had a look on other code examples and the value appears to range between 7k and 32k. So I should probably take an integer value like this:

volatile const int timescalefactor = 1000;

OCR1A = 7812 / timescalefactor;

An integer would be best since the OCR1A register is 16bits wide and that's how wide an integer is. Even better would be an unsigned int. That's where I was trying to get you to go in reply #3.

Thank you for your feedback! I changed it to an unsigned integer as proposed above (even tried a fixed value), but the sound remained the same. I suppose that there is something very strange going on. My next step would probably be to get my hands on an oscilloscope and see what's going on with the waveform, but that will take a while, because I ordered one of those cheap DIY Kits (SainSmart DSO138). I'll get back once I have more information.

On another note: I checked the synthesized 95Hz pitch against my guitar and the tone appears to be in very similar to Gb2, which is supposed to sit at roughly 92Hz. The created sound just has a whole lot of additional high frequency noise and humming.

Yeah, like I said that might not be the issue with your sound, but it was definitely an error that needed to be addressed.

analogWrite(8, ledconstant + amplitude * (1023 - ledconstant));

I’m unsure about your math here. Does this range from 0 to 1023? analogWrite only takes values from 0 to 255. If this somehow end up mapping to 0 to 255 then ignore, but I have a feeling this maps to 0 to 1023.