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.
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.
Figure 1: The actual audio data stored in .WAV files with each block representing one byte
There are also two FIFO(First-In-First-Out) cyclic buffers for both channels and the size of these cyclic buffers is 2000. The cyclic buffers are used to ensure smooth audio playback and I managed two indices ReadIndex and WriteIndex to indicate the positions where to retrieve the audio data sent to the DAC and where to store the new audio data read from the SD card. Besides, considering the yield time for this reading thread is 10 milliseconds, it would be appropriate to read more than 0.01 * sample rate audio data into the cyclic buffer at the first time. And there are also two circumstances shown below to indicate how much data should be read from the SD card. When the ReadIndex is larger than the WriteIndex, the total number of data can be stored in the cyclic buffer is given by Buffer Size - (ReadIndex - WriteIndex). Otherwise, the total number is given as (WriteIndex - ReadIndex).
Figure 2: Two general circumstances to indicate how much data should be read from the SD card
At a high level, our project can be divided into 6 components shown in Figure 3. The PIC32 is the heart of our project and it is used to control different sections. When the user send requests from the interface on the website, the runtime server Node.js would take over the command and send it to the serial port that is connected to one Bluetooth module. This information is communicated in real-time by the means of Bluetooth transmission, the PIC32 can receive that command through the serial communication with the other Bluetooth module and based on the command transmitted from the PC, it will perform different functions by controling different sections including generating PWM signals to run motors, reading audio data in the SD card, displaying different patterns on the TFT display or sending audio data to the DAC.
For example, when the user sets our mutil-functional car working at driving mode and the user presses 'W' consecutively, the JavaScript will detect 'W' whose keyCode is 87 has been being pressed, then it will send 'W' to the server. At that time the web server Node.js receives the command from the website, it will send the command to the serial port. Through the Bluetooth communication, the PIC32 can finally get the same 'W' command from the other Bluetooth module and it will generate PWM signals to the motor driver, as well as output digital signals as the control signal to control the direction of motors' rotations. On the other hand, if the user sets our car working at speaker mode and the PIC32 receives a command to play music, it will split the information transmitted from the PC into the format of "command" + "data" and then it will use "data" as the file name to open the specific audio file in the SD card, turn on the timer with given sample rate, read audio data into cyclic buffers from the file and write the audio data to the DAC. Also, If the PIC32 receives the command of changing to the piano mode, the PIC32 will draw the piano interfece on the TFT display, wait for certain keys to be pressed on the keyboard, and open the corresponding audio files saved in the SD card as well as output the audio data to the DAC.
Figure 3: High-level block diagram of our final project
Hardware and Software Trade-offs
Both hardware and software significantly contributed to the success of the project. However, we had to make some trade-offs between hardware and software functionalities. For example, one of the design decisions we made is to use HM-10 as the Bluetooth module to implement the wireless communication between PIC32 and PC because it is quite reliable, but the downside of the HM-10 module is that we need to manually enter AT command to bind the two Bluetooth modules every time we turn on the whole circuit, which is not what we expected. But we successfully solved this in software and each time while starting the Node.js web server, it will automatically send the AT command to bind the Bluetooth modules first. Although there are some other Bluetooth modules that can store the bounded addresses and automatically bind with each other, the HM-10 is the most reliable module among all the Bluetooth modules we have ever tried. When we used HC-05, there are even some errors when sending commands to the PIC32 and we tested that by displaying sent commands on the TFT. For example, there was one time when we tested our HC-05 Bluetooth module and we transmit a character 'w' through serial monitor, but the PIC32 receives 't' instead for unknown reasons.
Another design decision is that we decided to take the TFT off the Big Board. In our project, the TFT display and the MicroSD adapter shares the same SPI 1 channel. As shown in the course website, it is easy to discover that the MISO of TFT is left unconnected and the MOSI is connected to RB11 on the PIC. However, due to the hardware limitation of the Peripheral Pin Select(PPS): RB11 can be both SDO1 and SDI1 but RB13 can only be SDO1. So in order to ensure they can work properly sharing the same SPI channel, we took the TFT off the Big Board to make it share the SCK, MOSI, and MISO with the MicroSD adapter and also modified the tft library. At the same time, since the MicroSD adapter shares the same SPI channel with the TFT display, there is not much time left to draw complicated patterns on the TFT display and the SD card adapter will take up the SPI 1 channel for the most time, for which reason it only displayed some simple patterns on the TFT.
The Bluetooth standard (IEEE 802.15.1) is commonly used with devices that involve Bluetooth transmission of data. This standard states that Bluetooth should be used in a range of 100m with 1-3 Mbps data transmission, a bandwidth of 2.14 GHz, a power consumption from 2.5 to 100 mW and uses that include short range control and/or monitoring [3].There are no patents, copyrights, or trademark considerations relevant to this project.