ME218C

ME218C is a Stanford class where you learn about communication protocols and how to implement them on a microcontroller. This included writing assembly code on a PIC microcontroller to communicate with a computer over UART, and developing an audio tone based communication protocol to communicate with other students over the internet.

Due to the Covid-19 pandemic, this class was required to be taught in a virtual format. As such, our final project was to develop an audio tone based communication protocol, where frequencies/timing of a tone would translate to bits, and implement this onto a PIC microcontroller. Each microcontroller would then send audio over a voice channel to play a programmed game against other students. This project included the following features:

  • Servo motor to actuate a push-to-talk button on a keyboard
  • Student developed communication protocol that dictate the timing of when transmissions would be sent to ensure different teams were not “talking over each other”
  • Frequency decoder on the PIC microcontroller that used the built in analog to digital converter to read in data from a microphone and then calculated the frequency from this data
  • Tone generator that uses the digital to analog converter on the PIC to generate a sin wave at varying frequencies to send to a speaker

Website dedicated to the project

Hardware setup for the project Hardware setup

The most valuable aspect of this project was that we had to develop our own communication protocol, rather than implementing someone elses. With this, we learned a lot about what makes communication protocols effective. The two major deficits we found in ours through testing was that it was too slow, it would take 2 seconds to transmit a message, and that it had no way to acknowledge if the message heard was correctly heard. Sending audio messages over voice apps, especially with poor WiFi, had frequent errors and many messages would be lost in transit.

Electrical wiring for the project Wiring

Code excerpt that decoded the frequency of received audio tones; the ADC reads analog signals from a microphone into a 200 element array at a rate of 16.67 kHz

/****************************************************************************
 Function
    GetCurrentFrequency
 
 Returns
    uint16_t, the frequency of the tone
 
 Description
    Approximates the read frequency by counting the number of values above the high and low threshold
****************************************************************************/
uint16_t GetCurrentFrequency(void){    
    //Loop through the data and find the number of zero crossings
    uint8_t AverageReading = 128;
    uint16_t ZeroCrossings = 0;
    //Value to check if the reading is actually a value or just noise
    uint16_t HighCount = 0;
    uint8_t HighValue = 140;
    for(uint16_t i = 0; i < ADC_data_length-1; ++i){
        //Check for instances where it crosses the AverageReading
        if(((ADC_data[i] < AverageReading) && (ADC_data[i+1] > AverageReading)) ||
            ((ADC_data[i] > AverageReading) && (ADC_data[i+1] < AverageReading)) ){
            ++ZeroCrossings;
        }
        //Check if the value is high and increment high count if it is
        if(ADC_data[i] > HighValue){
            ++HighCount;
        }
    }
    
    //If the HighValue comparison did not trigger enough times
    //This is noise, not a valid signal
    if(HighCount < 10){
        ZeroCrossings = 0;
    }
    //Every period should consist of 2 of these counters

    //The time over which this threshold is average can be derived
    //The ADC adds a new value to the buffer at a rate of 16.67 khz
    //Assumes an ADC data length of 200
    //This is the value in ms
    //Sample time = Data length * 1000 / 20000
    uint8_t SampleTime = 12;

    //With this, the new frequency can be updated
    uint16_t CurrentFreq = (((uint32_t)ZeroCrossings * 1000)/2) / SampleTime;

    return CurrentFreq;
}