Voice Controlled Signal Generator

Project Description

Function generators are often used as inputs to circuits in order to test their behavior. We envisioned a function generator that would be able to set it's output given a voice command. This project was an exploration into signal generation, SPI, C# and RPC, and the Google Assistant SDK.

Function generation was achieved using the SparkFun MiniGen, a breakout board containing the AD9837 signal generation IC. The existing Arduino library was rewritten for the Mbed! The SparkFun MiniGen was connected to the Mbed via an SPI bus, carefully considering that the AD9837 is an SPI Mode 2 device (positive clock polarity, and clock phase 0).

The Raspberry Pi ran a modified program from the Google Assistant SDK, allowing us to parse voice commands and send custom serial data to the Mbed. The Mbed would then translate the serial command into an SPI command to make the desired wave on the function generator. As the Raspberry Pi was intended to be "headless", there was a desire to have a debugging/status display. A C# GUI was thus designed to read the wave type and output frequency from the Mbed.


Libraries

Import librarySparkFun_MiniGen_AD9837

SPI driver for the Sparkfun MiniGen breakout board containing the AD9837 signal generator IC. Built using a similar Arduino library available on SparkFun's website.


Physical Setup

MBEDSparkfun MiniGen
pin 11SCLK
pin 12nc
pin 13SDATA
pin 8FSYNC
VOUTVIN
GNDGND
MBEDRaspberry Pi
pin 11TXD
pin 10RXD
GNDGND

Block Diagram

/media/uploads/nkorz/screenshot_from_2018-12-11_02-31-55.png


Mbed Program: Serial Communication, RPC, SPI

The below code successfully read in serial commands in the format "xy.1234567", where the first two characters refer to the wave type, and the digits after the period refer to the desired frequency. The wave type specifiers were as follows: "tr" for triangle wave, "si" for sine wave, and "sq" for square wave. The frequency was bounded at the upper limit of the AD9837, 3MHz.

#include "mbed.h"
#include "mbed_rpc.h"
#include "mbed.h"
#include "SparkFun_MiniGen.h"
#include <stdlib.h>
#include <string> 
#include "uLCD_4DGL.h"
#include <ctype.h>
#include "rpc.h"

MiniGen gen(p11, p12, p13, p8); // function generator chip (MOSI, MISO, SCK, CS)
DigitalOut led(LED1);
Serial Pi(p9,p10);
Serial pc(USBTX,USBRX);
uLCD_4DGL uLCD(p28,p27,p30);
Ticker on_indicator;
int signal_gen_MODE = -1;
float desired_frequency = 0.0;
int RPC_freq = 100;

// RPC variables
RPCVariable<int> RPC_MODE(&signal_gen_MODE, "MODE");
RPCVariable<int> RPC_FREQ(&RPC_freq, "FREQ");

void flip() { led = !led; } // simple on-indicator with LED1

int main() {
         // On indicator ticker at 500ms
	on_indicator.attach(&flip, 0.5);

         // Set up LCD and serial input from Raspberry Pi
	pc.baud(9600);
	Pi.baud(115200);
	uLCD.baudrate(3000000);
	uLCD.cls();
	uLCD.background_color(BLACK);

         // Reset Signal Generator, default behavior is 100Hz sine wave
	gen.reset();
	gen.setFreqAdjustMode(MiniGen::FULL);
	desired_frequency = 100;

         // RPC Buffers
	char buf[256], outbuf[256];
        // Accept serial input from Raspberry Pi, which changes the frequency
	char Buffer[11], fBuffer[7], type[2];
	int n = 0;
	char r;
	while(1) {
		if(Pi.readable()){	// reach command from the raspberry pi in the form "si.4000"
			r = Pi.getc();	// two-letter signal type, followed by a period, followed by the frequency
			uLCD.putc(r);
			Buffer[n] = r;
			n = n+1;
			while(r != '\n'){
				if(Pi.readable()){
					r = Pi.getc();
					uLCD.putc(r);
					Buffer[n] = r;   
					n = n+1;
				}
			}
			n = n-4;
		}
		if(n>0){
			for (int i = 0; i < n;i++){			// parse input from the raspberry pi
				fBuffer[7-n+i] = Buffer[i+3];
			}

			type[0] = Buffer[0];
			type[1] = Buffer[1];

			switch(type[1]){	// set signal generator mode
				case 'r':
				gen.setMode(gen.TRI);
				signal_gen_MODE = -3;
				break;
				case 'q':
				gen.setMode(gen.SQUARE);
				signal_gen_MODE = -2;
				break;
				case 'i':
				gen.setMode(gen.SINE);
				signal_gen_MODE = -1;
				break;
			}

			if(n != 0){
				// parse command frequency
				desired_frequency = atof(&fBuffer[7-n]);
	        	        // software multiplier function to get the correct frequency
				unsigned long freqReg = gen.freqCalc(desired_frequency);
	        	        // Send SPI command to adjust frequency
				gen.adjustFreq(gen.FREQ0, gen.FULL, (uint32_t)freqReg);
			}
			n = 0;
		}

		RPC_freq = int(desired_frequency);	// update RPC variable
		if (pc.readable()) {				// small while loop to communicate RPC
			while(1) {
				pc.gets(buf, 256);
				RPC::call(buf, outbuf); 
				pc.printf("%s\n", outbuf);
				if (!pc.readable()) {
					break;
				}
			}
		}

	}
}

Raspberry Pi: Voice Command Recognition and Custom Command to Mbed

Google Assistant

Voice recognition was accomplished using the Google Assistant SDK. This SDK exists in multiple languages, but for ease of use on the Raspberry Pi’s Linux environment, the Python 3 version of the SDK was installed. Highly detailed documentation is provided by Google for the setup of the SDK, and is provided in the following link.

Modification of Google Assistant Functionality

The Google Assistant SDK contains numerous sample codes for developers to edit. The "hotword" demo from the SDK was modified to include a custom device action that would control the Mbed and then the function generator. The procedure to create the custom device action is described in the following link.

The "hotword" sample code is likely the most recoginizable version of the Google Assistant: the user triggers the assistant using the "hotword", "Ok, Google", followed by the command. The standard repertoire of responses includes Google search results, results from Google Maps, weather, and many more. On top of this, the Google Assistant SDK can be modified to incorporate custom device actions. For the Voice Controlled Signal Generator, the custom device action was to send the Mbed a serial set of characters from the Raspberry Pi. The custom device action is described in the scripting language JSON and is registered using the the procedure detailed in the link above. This JSON code describes the trigger phrases for the custom device action, the standard output from Google Assistant, and output parameters from a given user voice command.

The code in the "hotword" demo itself (hotword.py) was modified to include the Python function below. On a successful trigger of the custom "com.example.commands.SignalGen" command, the program begins the series of if-statements parsing the command parameters corresponding to the wave type and the frequency. After a quick frequency bounds check (3MHz max as dictated by the AD9837), the Raspberry Pi opens the serial port and writes the serial command to the Mbed.

if event.type == EventType.ON_DEVICE_ACTION:
        for command, params in event.actions:
            print('Do command', command, 'with params', str(params))
            if command == "com.example.commands.SignalGen":
                if "sine" in params['wave_type'] or "sign" in params['wave_type']:
                	res = "si"
                elif "square" in params['wave_type']:
                    res = "sq"
                elif "triangle" in params['wave_type']:
                    res = "tr"
                if int(params['frequency']) < 3000000 :
	                res = res + "." + params['frequency']
	                print(res)
	                res = res.encode()
	                port.write(res[0:8])
	                res = res[8:]
	                time.sleep(0.03)
	                port.write(res)
	                port.write(nln)

AD9837 SPI Library

As previously mentioned, signal generation was accomplished using the SparkFun MiniGen, a breakout board containing the AD9837 signal generator IC. A driver for the Arduino suite of microcontrollers is given on SparkFun's website.

For the Voice Controlled Signal Generator, the Arduino driverwas ported to the Mbed, changing the appropriate Arduino library calls with those of the mbed. Extensive debugging was completed to ensure proper performance of the signal generator chip. The ported library is available for import at the following link.

Import librarySparkFun_MiniGen_AD9837

SPI driver for the Sparkfun MiniGen breakout board containing the AD9837 signal generator IC. Built using a similar Arduino library available on SparkFun's website.

C# GUI with Remote Procedure Call

Since the "hotword" Python script had to be initiated on the Raspberry Pi and since we did not want to have a dedicated monitor for the Raspberry Pi, a C# GUI was built as a status portal for the system. A putty terminal from a Windows 10 PC was used to initiate the modified "hotword" demo. The C# GUI was to complement the command line output from Google's server with a simple and visual system status interface.

The C# GUI communicates with the Mbed using the Mbed’s RPC library. RPC variables on Mbed corresponding to the operating frequency and signal type currently being output by the AD9837. The following GitHub repository contains the source code to the C# GUI, the Mbed main.cpp and the library files for the SparkFun MiniGen.

Performance Limitations and Future Directions

  • The AD9837 is limited in its frequency capability and its selection of signals. This chip can only produce square, sine, and triangle waves, at frequencies between 0-3MHz. In addition, the output voltage is biased at VCC/2 with a peak-to-peak amplitude of 1V. Additional circuitry can be added to change the wave amplitude and level-shift the bias voltage.
  • There is a plethora of performance improvements to be had on the voice-recognition component. The regular-expression-like trigger strings in the JSON script can be optimized to handle many more input commands. However, testing must be done to ensure that commands do not have overlapping trigger sequences (this could lead to incorrect command parameter parsing by Google). Additionally, future work could explore the "conversation" capacity within Google Assistant to make the user interaction more fluid and less robotic.

Final Results



Please log in to post comments.