Created
April 4, 2017 20:21
-
-
Save serby/ce4b4a82b4a6f42312115d7922bea5dd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Energia.h" | |
#ifndef __CC3200R1M1RGC__ | |
// Do not include SPI for CC3200 LaunchPad | |
#include <SPI.h> | |
#endif | |
#include <WiFi.h> | |
#include <SLFS.h> | |
#include <inc/hw_types.h> | |
#include <inc/hw_ints.h> | |
#include <driverlib/prcm.h> | |
#include <driverlib/rom.h> | |
#include <driverlib/rom_map.h> | |
#include <driverlib/pin.h> | |
#include <driverlib/timer.h> | |
#include <inc/hw_memmap.h> | |
#include <vector> | |
#include <string> | |
/* HW settings */ | |
#define SOUND_PIN 24 // select the input pin for the microphone | |
#define SAMPLING_FREQ 8000 // sampling frequency, faster may require changes to ADC and possibly DMA | |
#define NUM_CHANNELS 1 | |
#define BITS_PER_SAMPLE 8 | |
#define SOUND_DATA_SIZE 30000 //50000 // buffer size for the mic data | |
#define DC_OFFSET 2000 // the reading you get with no audio present | |
// Audio variables | |
int AUDIO_PIN = 0; // P60 | |
static volatile uint32_t g_ulBase = TIMERA0_BASE; | |
void timer0_interrupt(void); | |
int8_t *audio_samples; | |
int32_t audio_buffer_size; | |
int32_t audio_buff_head = 0; | |
int32_t audio_buff_tail = 0; | |
int16_t audio_data = 0; | |
int16_t *sound_data_ptr; | |
int32_t sound_data_idx = 0; | |
int8_t sound_data[SOUND_DATA_SIZE]; | |
uint16_t sound_data_counter = 0; | |
struct soundHeader { | |
char riff[4]; /* "RIFF" */ | |
int32_t fLength; /* file length in bytes - data + 36 */ | |
char wave[4]; /* "WAVE" */ | |
char fmt[4]; /* "fmt " */ | |
int32_t chunkSize; /* size of FMT chunk in bytes (usually 16) */ | |
int16_t formatTag; /* 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM */ | |
int16_t numChannels; /* 1=mono, 2=stereo */ | |
int32_t samplesPerSec; /* Sampling frequency */ | |
int32_t bytesPerSecond; /* Speed of data stream = (numChannels*samplesPerSec*bitsPerSample)/8 */ | |
int16_t blockAlignment; /* (numChannels*bitsPerSample)/8 */ | |
int16_t bitsPerSample; /* Number of bits per sample */ | |
char data[4]; /* "data" */ | |
int32_t dataLength; /* data length in bytes (filelength - 44) */ | |
char terminator; | |
} wavHeader; | |
#define SSID_LOCATION "/storage/ssid" | |
#define PASSWORD_LOCATION "/storage/password" | |
#define MAX_FILE_SIZE 1024 | |
#define DETECTION_TIMEOUT 50000 | |
// WiFi variables | |
// This is a unique code | |
char device_id[] = "d8e8fca2dc0f896fd7cb4cb0031ba249"; | |
char ssid[] = "Gearbox Automatic"; | |
char password[] = "automatic"; | |
// These should be read from the flash in the setup | |
String user_ssid = ""; | |
String user_password = ""; | |
std::vector<String> networks; | |
WiFiServer server(80); | |
WiFiClient client; | |
String detectionServer = "gearboxautomatic.herokuapp.com"; | |
//IPAddress detectionServer(10, 0, 3, 177); | |
int detectionServerPort = 80;//9834;//80; | |
String detectionEndPoint = "/detector/"; | |
String deviceID = "123456"; // TODO: actual ID | |
#define DETECTION_THRESHOLD 30 | |
boolean paused = false; | |
int8_t attemptCounter = 0; | |
int status = WL_IDLE_STATUS; | |
boolean connnected = false; | |
boolean setupMode = false; | |
volatile boolean sendAudio = false; | |
volatile boolean clearBoardFlag = false; | |
void timer0_interrupt(void); | |
void audio_stop_sampling(void); | |
void audio_start_sampling(void); | |
void audio_reset_buffer(void); | |
/* | |
* Initialise the timer interrupt at the sampling frequency | |
* | |
* sample_frequency : Frequency in Herts | |
* sample_buffer : Buffer | |
* sample_buffer_size : Size of buffer | |
* audio_pin : Pin for audio source | |
*/ | |
void audio_init (uint32_t sample_frequency, int8_t sample_buffer[], int32_t sample_buffer_size, int audio_pin) { | |
uint32_t ticks = (F_CPU/sample_frequency); | |
analogReadResolution(BITS_PER_SAMPLE); | |
audio_samples = sample_buffer; | |
audio_buffer_size = sample_buffer_size; | |
AUDIO_PIN = audio_pin; | |
MAP_PRCMPeripheralClkEnable(PRCM_TIMERA0, PRCM_RUN_MODE_CLK); | |
MAP_PRCMPeripheralReset(PRCM_TIMERA0); | |
MAP_TimerConfigure(TIMERA0_BASE, TIMER_CFG_PERIODIC); | |
MAP_TimerPrescaleSet(TIMERA0_BASE, TIMER_A, 0); | |
MAP_TimerIntRegister(g_ulBase, TIMER_A, timer0_interrupt); | |
MAP_TimerIntEnable(g_ulBase, TIMER_TIMA_TIMEOUT); | |
MAP_TimerLoadSet(g_ulBase,TIMER_A, ticks); | |
MAP_TimerEnable(g_ulBase, TIMER_A); | |
} | |
/* | |
* Return the current count of audio samples | |
*/ | |
int32_t audio_get_size () { | |
if ((audio_buff_head - audio_buff_tail) < 0){ | |
return (audio_buffer_size + (audio_buff_head - audio_buff_tail)); | |
} | |
return (audio_buff_head - audio_buff_tail); | |
} | |
/* | |
* ISR for timer0 | |
*/ | |
void timer0_interrupt () { | |
audio_data = (int8_t) analogRead(SOUND_PIN); | |
if (sound_data_counter != SOUND_DATA_SIZE-1) { | |
audio_samples[sound_data_counter++] = audio_data; | |
} else { | |
Serial.println(F("Stopping recording...")); | |
audio_stop_sampling(); | |
// audio_reset_buffer(); | |
sound_data_counter = 0; | |
sendAudio = true; | |
} | |
// Clear the timer interrupt | |
MAP_TimerIntClear(g_ulBase, MAP_TimerIntStatus(g_ulBase, true)); | |
// audio_buff_head = (audio_buff_head+1) % audio_buffer_size; | |
} | |
/* | |
* Return a pointer to the audio samples | |
* | |
* ptr_data : Pointer to the pointer for the destination buffer | |
* samples_to_get : Number of samples to get | |
*/ | |
void audio_get_samples (int16_t **ptr_data, int32_t samples_to_get) { | |
*ptr_data = (int16_t *) &audio_samples[audio_buff_tail]; | |
audio_buff_tail = (audio_buff_tail + samples_to_get) % audio_buffer_size; | |
} | |
/* | |
* Disable audio sampling | |
*/ | |
void audio_stop_sampling () { | |
MAP_TimerDisable(g_ulBase, TIMER_A); | |
} | |
/* | |
* Enable audio sampling | |
*/ | |
void audio_start_sampling () { | |
MAP_TimerEnable(g_ulBase, TIMER_A); | |
} | |
/* | |
* Resets the sound data counter | |
*/ | |
void audio_reset_buffer () { | |
sound_data_counter = 0; | |
} | |
/* | |
* Store the string in flash memory under a filename | |
* | |
* | |
*/ | |
void storeInFlash(char *location, String data) { | |
Serial.println("STORING IN FLASH"); | |
Serial.print("location: "); | |
Serial.println(location); | |
Serial.print("data: "); | |
Serial.println(data); | |
int retVal = SerFlash.open(location, FS_MODE_OPEN_CREATE(MAX_FILE_SIZE, _FS_FILE_OPEN_FLAG_COMMIT)); | |
if (retVal != SL_FS_OK) { | |
Serial.print("Error creating file "); | |
Serial.print(location); | |
Serial.println(", error code: "); | |
Serial.println(SerFlash.lastErrorString()); | |
Serial.flush(); // flush pending serial output before entering suspend() | |
while(1) ; // halting (suspend() isn't support by Energia MT at the moment FYI) | |
} | |
SerFlash.write(&data[0], data.length()); | |
SerFlash.close(); | |
} | |
/* | |
* Initialise the LEDs | |
*/ | |
void initLeds() { | |
pinMode(RED_LED, OUTPUT); | |
pinMode(GREEN_LED, OUTPUT); | |
pinMode(YELLOW_LED, OUTPUT); | |
pinMode(50, INPUT); | |
digitalWrite(RED_LED, HIGH); | |
digitalWrite(YELLOW_LED, LOW); | |
digitalWrite(GREEN_LED, LOW); | |
} | |
/* | |
* Scan for networks | |
*/ | |
void scanNetworks() { | |
WiFi.init(); | |
int numSsid = WiFi.scanNetworks(); | |
Serial.print(F("Networks found:")); | |
Serial.println(numSsid); | |
if (numSsid == -1) { | |
return; | |
} | |
for (int thisNet = 0; thisNet < numSsid; thisNet++) { | |
Serial.println(WiFi.SSID(thisNet)); | |
} | |
} | |
/* | |
* Scan for networks and save them | |
*/ | |
void scanSaveNetworks() { | |
WiFi.init(); | |
int numSsid = WiFi.scanNetworks(); | |
Serial.print(F("Networks found:")); | |
Serial.println(numSsid); | |
if (numSsid == -1) { | |
return; | |
} | |
for (int thisNet = 0; thisNet < numSsid; thisNet++) { | |
String network = WiFi.SSID(thisNet); | |
networks.push_back(network); | |
Serial.println(network); | |
} | |
} | |
/* | |
* Send array of SSIDs to the client | |
*/ | |
void sendNetworks(WiFiClient client) { | |
client.print('['); | |
boolean firstRun = true; | |
for (String network : networks) { | |
if (!firstRun) { | |
client.print(','); | |
} else { | |
firstRun = false; | |
} | |
client.print('"'); | |
client.print(network); | |
client.print('"'); | |
Serial.println(network); | |
} | |
client.println(']'); | |
} | |
/* | |
* Runs the setup server | |
*/ | |
void runSetupServer() { | |
WiFiClient connectedClient = server.available(); | |
int clients = WiFi.getTotalDevices(); | |
if (connectedClient) { | |
Serial.println(F("New client")); | |
int i = 0; | |
char buffer[150] = {0}; | |
unsigned int thisNet = 1; | |
char c; | |
while (connectedClient.connected()) { | |
if (connectedClient.available()) { | |
String requestLine = connectedClient.readStringUntil('\n'); | |
Serial.print(F("Request received:")); | |
Serial.println(requestLine); | |
if (requestLine.indexOf("GET /networks") != -1) { // GET /networks | |
// Return a list of the available networks | |
connectedClient.println(F("HTTP/1.1 200 OK")); | |
connectedClient.println(F("Content-type: text/plain")); | |
connectedClient.println(); | |
sendNetworks(connectedClient); | |
} else if (requestLine.indexOf("POST /network") != -1) { | |
// We can safely assume the networks vector is no longer needed so delete it | |
// networks.clear(); | |
// delete &networks; | |
// Save the received details | |
Serial.println(F("Recieved")); | |
String headerLine = connectedClient.readStringUntil('\n'); | |
while (headerLine.length()) { | |
if (headerLine.length() == 1) { | |
// Now we're on the line before the data | |
String postData = connectedClient.readStringUntil('\n'); | |
Serial.println(postData); | |
String param1 = postData.substring(0, postData.indexOf('&')); | |
String param2 = postData.substring(postData.indexOf('&') + 1, postData.length()); | |
String data1 = ¶m1.substring(param1.indexOf('=') + 1, param1.length())[0]; | |
String data2 = ¶m2.substring(param2.indexOf('=') + 1, param2.length())[0]; | |
data1.replace("+", " "); | |
data2.replace("+", " "); | |
param1 = param1.substring(0, param1.indexOf('=')); | |
param2 = param2.substring(0, param2.indexOf('=')); | |
Serial.println(param1); | |
Serial.println(data1); | |
Serial.println(param2); | |
Serial.println(data2); | |
if (param1 == "ssid") { // TODO: check length of SSID | |
storeInFlash(SSID_LOCATION, data1); | |
} else if (param2 == "ssid") { | |
storeInFlash(SSID_LOCATION, data2); | |
} | |
if (param1 == "password") { // TODO: check length of password | |
storeInFlash(PASSWORD_LOCATION, data1); | |
} else if (param2 == "password") { | |
storeInFlash(PASSWORD_LOCATION, data2); | |
} | |
connectedClient.println(F("HTTP/1.1 200 OK")); | |
connectedClient.println(F("Content-type: text/plain")); | |
connectedClient.stop(); | |
return; | |
} | |
headerLine = connectedClient.readStringUntil('\n'); | |
} | |
} else { | |
connectedClient.println(F("HTTP/1.1 404 Not Found")); | |
connectedClient.println(F("Content-type: text/plain")); | |
} | |
connectedClient.stop(); | |
return; | |
} | |
// Receive password and SSID from the client. | |
} | |
} | |
} | |
/* | |
* Start the access point for setup | |
*/ | |
void startAp() { | |
Serial.print(F("Starting WIFI Access Point: ")); | |
WiFi.beginNetwork((char *)ssid, (char *)password); | |
WiFi.init(); | |
Serial.println(ssid); | |
while (WiFi.localIP() == INADDR_NONE) { | |
Serial.print("."); | |
digitalWrite(YELLOW_LED, LOW); | |
delay(500); | |
digitalWrite(YELLOW_LED, HIGH); | |
} | |
Serial.print(F("IP Address: ")); | |
Serial.println(WiFi.localIP()); | |
setupMode = true; | |
return; | |
} | |
/* | |
* Run the setup process | |
*/ | |
void setupDevice() { | |
Serial.println(F("Entering Setup Mode")); | |
digitalWrite(RED_LED, HIGH); | |
digitalWrite(YELLOW_LED, HIGH); | |
digitalWrite(GREEN_LED, LOW); | |
startAp(); | |
} | |
/* | |
* Connect to the stored network | |
*/ | |
void connectToInternet() { | |
Serial.println(F("Connecting to Internet...")); | |
do { | |
Serial.print(F("Attempting to connect to WPA SSID: ")); | |
Serial.println(&user_ssid[0]); | |
// Connect to WPA/WPA2 network | |
status = WiFi.begin(&user_ssid[0], &user_password[0]); // Cast strings to (char *) | |
// Wait for connection | |
delay(500); | |
} while (status != WL_CONNECTED); | |
connnected = true; | |
Serial.println(F("Connected")); | |
IPAddress ip = WiFi.localIP(); | |
Serial.print(F("IP Address: ")); | |
Serial.println(ip); | |
} | |
/* | |
* Retrieve the credentials stored in the flash memory | |
*/ | |
void getCredentials() { | |
Serial.println(F("Checking for stored credentials")); | |
SerFlash.open(SSID_LOCATION, FS_MODE_OPEN_READ); | |
String ssid = SerFlash.readBytes(); | |
Serial.print(F("Read ")); | |
Serial.println(F(SSID_LOCATION)); | |
Serial.println(ssid); | |
SerFlash.close(); | |
if (ssid.length()) { | |
SerFlash.open(PASSWORD_LOCATION, FS_MODE_OPEN_READ); | |
String key = SerFlash.readBytes(); | |
Serial.print(F("Read ")); | |
Serial.println(F(PASSWORD_LOCATION)); | |
Serial.println(key); | |
if (key.length()) { // Only if there is a key, replace the credentials | |
user_ssid = ssid; | |
user_password = key; | |
Serial.println(F("Saved credentials:")); | |
Serial.print(F("SSID: ")); | |
Serial.println(user_ssid); | |
} else { | |
Serial.println(F("No password saved!")); | |
} | |
} else { | |
Serial.println(F("No SSID saved!")); | |
} | |
SerFlash.freeString(); | |
SerFlash.close(); | |
} | |
/* | |
* Sends the audio to the server | |
*/ | |
boolean sendAudioToServer() { | |
// Close any previous connection before sending a new request | |
client.stop(); | |
Serial.println(F("Connecting to detection server...")); | |
// If there's a successful connection | |
if (client.connect(&detectionServer[0], detectionServerPort)) { // Cast string to (char *) | |
// Send the HTTP GET request: | |
client.println("PUT " + detectionEndPoint + deviceID + " HTTP/1.1"); | |
client.print(F("Host: ")); | |
client.println(detectionServer); | |
client.println(F("Content-Type: text/csv")); | |
client.print(F("Content-Length: ")); | |
client.println(SOUND_DATA_SIZE + 44); | |
client.println(F("Accept: */*")); | |
client.println(F("User-Agent: GearboxAutomaticHardware")); | |
client.println(F("Connection: close")); | |
client.println(); | |
client.write((uint8_t *) &wavHeader, sizeof(wavHeader)); | |
client.write((uint8_t *) &sound_data, SOUND_DATA_SIZE); | |
client.print(F("\r\n")); | |
Serial.println(F("Sent audio successfully")); | |
// Wait for a response | |
int start = millis(); | |
while (client.available() == 0) { | |
if (millis() - start > DETECTION_TIMEOUT) { | |
Serial.println(F("Detection Service timeout!")); | |
client.stop(); | |
return false; | |
} | |
} | |
while (client.available()) { | |
Serial.println(F("Got response:")); | |
String requestLine = client.readStringUntil('\n'); | |
Serial.println(requestLine); | |
String line = client.readStringUntil('\n'); | |
while (line.length()) { | |
Serial.println(line); | |
line = client.readStringUntil('\n'); | |
} | |
if (requestLine.indexOf("HTTP/1.1 200 OK") == 0) { | |
return true; | |
} | |
return false; | |
} | |
} else { | |
// if you couldn't make a connection: | |
Serial.println(F("Connection failed!")); | |
return false; | |
} | |
} | |
/* | |
* Detect audio | |
* | |
* Crudely detects audio by looking for a large difference between | |
* values (think waveforms, higher peaks/valleys = better chance it's sound) | |
* | |
*/ | |
boolean detectAudio () { | |
// detect silence | |
int8_t buffer[8000] = {0}; | |
int8_t minValue = 127; | |
int8_t maxValue = -127; | |
for (int i = 0; i < 2000; i++) { | |
buffer[i] = (int8_t) analogRead(SOUND_PIN); | |
if (buffer[i] < minValue) minValue = buffer[i]; | |
if (buffer[i] > maxValue) maxValue = buffer[i]; | |
} | |
boolean isAudioPresent = maxValue - minValue > DETECTION_THRESHOLD; | |
return isAudioPresent; | |
} | |
/* | |
* Clears the saved SSID and password | |
*/ | |
void clearBoard() { | |
String blank = ""; | |
storeInFlash(SSID_LOCATION, blank); | |
storeInFlash(PASSWORD_LOCATION, blank); | |
} | |
/* | |
* Reset the board | |
*/ | |
void softReset() { | |
Serial.println("Resetting board!"); | |
PRCMMCUReset(1); | |
PRCMSOCReset(); | |
} | |
/* | |
* | |
*/ | |
void setClearBoardFlag () { | |
clearBoardFlag = true; | |
} | |
/* | |
* Setup | |
* | |
* Gets the saved credentials | |
* Scans for networks | |
* Initialise the LEDs | |
* If there is no `user_ssid` from the flash memory | |
* Run the setup | |
* Otherwise, connect to the internet | |
* | |
* Build the WAV header | |
* Initialise the audio sampling interrupt, and pause it | |
*/ | |
void setup () { | |
Serial.begin(115200); | |
SerFlash.begin(); // This calls WiFi.init() in case the user hasn't already run WiFi.begin() | |
/* | |
* Enable internal pullup on SW2 and register the interrupt | |
*/ | |
pinMode(PUSH2, INPUT_PULLUP); | |
attachInterrupt(PUSH2, setClearBoardFlag, FALLING); | |
getCredentials(); | |
initLeds(); | |
if (user_ssid.length() == 0) { | |
scanSaveNetworks(); | |
setupDevice(); | |
} else { | |
connectToInternet(); | |
} | |
Serial.println(F("Starting Web Server...")); | |
server.begin(); | |
Serial.println(F("Web Server Started")); | |
// Build wav header | |
strncpy(wavHeader.riff, "RIFF", 4); | |
wavHeader.fLength = SOUND_DATA_SIZE + 36; | |
strncpy(wavHeader.wave, "WAVE" ,4); | |
strncpy(wavHeader.fmt, "fmt ", 4); | |
wavHeader.chunkSize = 16; | |
wavHeader.formatTag = 1; | |
wavHeader.numChannels = NUM_CHANNELS; | |
wavHeader.samplesPerSec = SAMPLING_FREQ; | |
wavHeader.bytesPerSecond = (NUM_CHANNELS * SAMPLING_FREQ * BITS_PER_SAMPLE)/8; | |
wavHeader.blockAlignment = (NUM_CHANNELS * BITS_PER_SAMPLE)/8; | |
wavHeader.bitsPerSample = BITS_PER_SAMPLE; | |
strncpy(wavHeader.data, "data", 4); | |
wavHeader.dataLength = SOUND_DATA_SIZE - 44; | |
wavHeader.terminator = '\0'; | |
audio_init(SAMPLING_FREQ, sound_data, SOUND_DATA_SIZE, SOUND_PIN); | |
audio_stop_sampling(); | |
} | |
/* | |
* Loop | |
* | |
* Follows main control logic diagram | |
*/ | |
void loop () { | |
if (clearBoardFlag) { | |
clearBoard(); | |
softReset(); | |
} | |
if (setupMode) { | |
runSetupServer(); | |
} else if (connnected) { | |
if (detectAudio()) { | |
// audio present | |
Serial.println(F("Audio present")); | |
if (!paused) { | |
while (attemptCounter++ < 3) { | |
Serial.print(F("Trying to recognise...")); | |
Serial.println(attemptCounter); | |
audio_start_sampling(); | |
while (!sendAudio); // Wait for the buffer | |
if (sendAudioToServer()) { | |
paused = true; | |
break; | |
} | |
sendAudio = false; | |
} | |
} else { | |
Serial.println(F("Paused")); | |
delay(100); | |
} | |
} else { | |
// no audio | |
Serial.println(F("No audio present")); | |
paused = false; | |
attemptCounter = 0; | |
delay(100); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment