Navigation

Visualizing waveforms using the Serial Monitor

We are now in a position to generate sine waves for the theremin, but before proceeding we will look at a way of visualizing the waveform without an oscilloscope. Why do this? Well, the Arduino is meant to be a hobbist device (though it can be used for research projects too!). And most of us do not have an oscilloscope at home, so here we will see how we can sample the DAC waveforms without one!

Visualising a waveform using the Serial Monitor

So far, we have used the serial monitor to display the wave amplitude using the Arduino's ADC. That's what we did in the ramp code. This is not very iluminating and here we will convert the amplitude into a waveform. Here is the ramp example again:

Ramp code for the Serial Monitor

/*
 * Ramp out : Waveform visualised on the Serial Monitor
 * A. J. Misquitta based on code by Amanda Ghassaei
 */

void setup(){
  //set digital pins 0-7 as outputs
  for (int i=0;i<8;i++){
    pinMode(i,OUTPUT);
  }
  pinMode(A0,INPUT);    // Sample the wave using the ADC on pin A0
  Serial.begin(115200); // Output to Serial
  while (! Serial);     // Wait untilSerial is ready
  Serial.println("Ramp waveform generation!");
}

void loop(){
  for (int a=0; a<256; a++){
    PORTD = a;  //send out ramp to digital pins 0-7
    delay(20);  //wait 20ms if using a multimeter/serial port
    // sample the wave using the ADC on pin A0:
    int DACout = analogRead(A0); 
   
    if (Serial.available())
    {
      /* Code to visualise the wave form
       * -------------------------------
       * Print a * at a position proportional to the
       * amplitude. To do this we first print spaces
       * and then the *.
       */
      for(int i = 0; i<((DACout / 8)); i++)
      {
        Serial.print(' ');
      }
      Serial.println('*');
    }
  }
}

Load the code and start the serial monitor to see the waveform.

Here is code to visualise a sine wave. This code also calculates the period of the wave in ms. However, as the comments in the code state, the period is only indicative and not real as the time spent in sampling the DAC output by the ADC (at pin A0) and the code used to print to the Serial Monitor all take up time. This significantly effects the period of the wave. Nevertheless, this code gives us a nice way of visually debugging our circuits (and codes).

Sine waveform on the Serial Monitor

/*
 * Sine wave : On Serial Monitor
 * A. J. Misquitta based on code by  Amanda Ghassaei
 * Visualizes the waveform and calculates a "period"
 */

/*
 * This program 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.
 *
*/

byte sine[] = {127, 134, 142, 150, 158, 166, 173,
181, 188, 195, 201, 207, 213, 219, 224, 229, 234,
238, 241, 245, 247, 250, 251, 252, 253, 254, 253,
252, 251, 250, 247, 245, 241, 238, 234, 229, 224,
219, 213, 207, 201, 195, 188, 181, 173, 166, 158,
150, 142, 134, 127, 119, 111, 103, 95, 87, 80, 72,
65, 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6,
3, 2, 1, 0, 0, 0, 1, 2, 3, 6, 8, 12, 15, 19, 24,
29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119,};

int samples = 100; // These are the number of samples in sine[]

void setup(){
  //set digital pins 0-7 as outputs
  for (int i=0;i<8;i++){
    pinMode(i,OUTPUT);
  }
  pinMode(A0,INPUT);    // Use ADC on pin A0 for sampling
  Serial.begin(115200); // Output to Serial
  while (! Serial);     // Wait untilSerial is ready
  Serial.println("Sine waveform generation!");
}

void loop(){
  boolean up = true;
  unsigned long time_up = 0;
  unsigned long time_down = 0;
  
  int crossover = 512; // timers activate at this value
  
  for (int t=0; t < samples; t++){
    /* There are two ways we could generate the sine wave:
     * 1: using the sine[] array, and
     * 2: using the sin() function. 
     * The latter (2) is slower, but this doesn't matter here
     * because the rest of this function (the ADC sampling and the
     * printing to the Serial Monitor) are so slow that the delay
     * introduced by (2) is insignificant by comparison!
     */
     
    PORTD = sine[t];  // Use this for fast sampling
    //PORTD = 127+127*sin(2*3.1415926*t/100); // and this for slow sampling 
    
    /* The period of the wave can be controlled using the delay. 
     * But remember that other functions in the loop also effect
     * the period!
     */
    delay(10);  //wait 10ms
    
    // Read the output of the DAC using the ADC at pin A0:
    int DACout = analogRead(A0); 
    
    // Code to calculate period in ms:
    if (up){
      if (DACout > crossover){
        time_up = millis();
        up = false;
      }
    } else {
      if (DACout < crossover){
        time_down = millis();
        if (Serial.available()){
          unsigned long period = 2 * (time_down - time_up);
          Serial.println(period);
        }
        up = true;
      }
    }
    
    // Code to visualize the waveform:
    if (Serial.available())
    {
      for(int i = 0; i<((DACout / 8)); i++)
      {
        Serial.print(' ');
      }
      Serial.print('*');
      Serial.println(DACout); 
    }
  }
}

The above code not only lets us see the wave form, but it also calculates the period of the wave using a trigger technique: the timers start and stop when the wave crosses the 512 reading (2.5 V) on the ADC input.

sine_wave_serial_display_1.png

Sample output. The number //472// on the left/bottom is the initial guess at the period of the wave. The first value should be discarded. Observe that the period estimates quickly settle down to a near-constant value. The *s indicate the waveform and the adjacent numbers the ADC input values. The fact that these never get to 1024 is because the DAC output voltage never gets to 5V. By default the ADC converts the range $[0,5]$ Volts into an output range $[0,1024]$. It is a 10 bit ADC.

Important

Baud Rate

If you are getting gibberish on your serial monitor you probably have the wrong baud rate. This is set at the bottom, right of the serial monitor. It should match the setting in the program. In the above example it should be 115200 Bd. The default is 9200 Bd.

See the Wikipedia page for more info about the Baud.

Things to note:

Note

The serial display is a great way to probe the circuit. Particularly if you don't have an oscilloscope at hand. The jumper attached to analogue pin A0 can be moved around to different parts of the circuit, allowing you to see what's going on in much the same way as you'd probe it using a multimeter, only the A0 probe gives you voltages as a function of time.

Waveform visualizing with two probes

It is also possible to sample the waveforms at two (or even more) points in the circuit. You will need to do this in the next part of the experiment. Here's the code to do this:

Waveform sampling with two probes

/* 
 * Sine wave : Two probes on the Serial Monitor
 * A. J. Misquitta based on code by  Amanda Ghassaei
 *
 * This version allows 2 analogue inputs displayed on the serial port
 * Useful for comparing DAC signal at various stages. 
 */

/*
 * This program 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.
 *
*/

void setup(){
  //set digital pins 0-7 as outputs
  for (int i=0;i<8;i++){
    pinMode(i,OUTPUT);
  }
  pinMode(A0,INPUT);    // Probe 1
  pinMode(A1,INPUT);    // probe 2
  Serial.begin(115200); // Output to Serial
  while (! Serial);     // Wait untilSerial is ready
  Serial.println("Waveform generation! Two probes");
}

void loop(){
  boolean up = true;
  unsigned long time_up = 0;
  unsigned long time_down = 0;
  
  int crossover = 512; // timers activate at this value
  int samples = 50;    // number of samples to use
  
  for (int t=0; t < samples; t++){
    //send sine wave to DAC, centered around (127/255)*5 = 2.5V
    PORTD = 127+127*sin(2*3.1415926*t/samples);
    
    // The period of the wave can be controlled using the delay. 
    // But other functions in the loop also effect the period!
    // The delay should be linked to the samples in a sensible way:
    //delayMicroseconds(5000/samples);
    delay(100); //wait 100ms if using a multimeter/serial port
    
    // Read the output of the DAC:
    int DACout = analogRead(A0); 
    int AnaOut1 = analogRead(A1);
    
    // Code to calculate period in ms
    if (up){
      if (DACout > crossover){
        time_up = millis();
        up = false;
      }
    } else {
      if (DACout < crossover){
        time_down = millis();
        if (Serial.available()){
          unsigned long period = 2 * (time_down - time_up);
          Serial.println(period);
        }
        up = true;
      }
    }
    
    // Print out the wave:
    if (Serial.available())
    {
      if (DACout >= AnaOut1)
      {
        for(int i = 0; i < (AnaOut1/8); i++)
        {
          Serial.print(' ');
        }
        Serial.print('.');
        for(int i = 0; i < ((DACout-AnaOut1)/8); i++)
        {
          Serial.print(' ');
        }
        Serial.println('*');
      }
      else
      {
        for(int i = 0; i < (DACout/8); i++)
        {
          Serial.print(' ');
        }
        Serial.print('*');
        for(int i = 0; i < ((AnaOut1-DACout)/8); i++)
        {
          Serial.print(' ');
        }
        Serial.println('.');
      }
    }
  }
}

To use this place one probe at the DAC output and the other at either ground or the 5V pin on the Arduino. Start the serial display. What do you see? We will use this code next.

Important

Tasks:

Upload these codes to your Arduino. Get the waveforms on the serial monitor. Are they what you expect them to be?

Get comfortable using the serial monitor to visualise waveforms as we will use this in subsequent parts of the experiment. Also, it is a very good way for debugging circuits!

Bonus: Can you modify the above sine code to produce and visualise a ramp or triangular wave?

Assessment: Include a screen-shot of the sine wave visualised using the serial monitor. Do not include the program but describe (in words) how we have use the Arduino to visualise a waveform.

AJMPublic/teaching/arduino-pi/projects/arduino/dac-audio/serial-monitor (last edited 2021-04-14 13:17:02 by apw109)