Main Content

In our final project, we have built a multi-functional car controlled by a web application on the PC, which allows users to drive the car as well as play music from the interface on the website.

All the requests from the website are collected by JavaScript and the runtime environment Node.js serves to transmit the command via Bluetooth to the car, which enables the car to perform different functions based on the commands:

Driving Mode: Move toward one specific direction
Speaker Mode: Read .WAV files stored in the SD card and play it through speakers, as well as showcase the amplitude of the audio on the TFT display
Piano Mode: Play certain notes of one piano and display which key being pressed on the TFT
In general, we applied what we have learned in this course into this project and integrating many functions together was quite fun and a good learning experience.

Rationale
Node.js is an open source server environment and it runs single-threaded, non-blocking, and asynchronously, which is very memory efficient. In addition, Node.js has a built-in HTTP module that allows Node.js to transfer data over the Hyper Text Transfer Protocol(HTTP) and it can create an HTTP server that listens to server ports. The SerialPort object of Node.js is also quite useful to open a port and it allows reading or writing to the serial port at any time. So it is feasible to use the SerialPort object as the bridge between JavaScript and the microcontroller. Via Bluetooth transmission, we can send commands to the microcontroller through the website remotely.

Our group decided to pursue this subject as our final project because we can not only leverage what we have learned through this semester, such as generating PWM to run motors by output compare units, playing music through DAC, serial communication through UART, concurrent programming and etc, but also learn a lot of new concepts such as file system, operations on shared SPI channels, waveform audio file format and some modern front-end technologies.

Background
The audio format to be played is .WAV and it stores data in “chunks”. There are two sections in the .WAV files: the header and the data. The header is the beginning of a WAV(RIFF) file. The header is used to provide specifications on the file type, sample rate, sample size and bit size of the file, as well as its overall length. The header of a WAV(RIFF) file is 44 bytes long. The data section is the individual samples. An individual sample is the bit size times the number of channels.[1]

In the header section, for example, bytes from 40 to 43 give the actual filesize. So the filesize is given as

(byte43 << 24) | (byte42 << 16) | (byte41 << 8) | byte40
The Timer3 interrupt is enabled to transmit audio data to the DAC based on the sample rate of the music, and it is also easy to get the sample rate of the music given by

( byte24 | (byte25 << 8)) & (0xffff)
As for the data section, for 16-bit PCM files, the data is stored as 16-bit Signed samples, whereas for 8-bit PCM files, the data is stored as 8-bit Unsigned samples[2]. The way the audio data is origanized is shown below. Considering the MCP4822 has 12-bit resolution, the audio samples are converted to 12-bit values along with channel specifications for the DAC. For mono files, both channels play the same value. As for 8-bit samples, the 12-bit data is obtained by bit-shifting left 4 times. For 16-bit samples, the 12-bit data is obtained by combining the 8 bits of the higher byte with the 4 bits of the lower byte.”

Link to article