Skip to content

Instantly share code, notes, and snippets.

@serby
Created April 4, 2017 20:21
Show Gist options
  • Save serby/ce4b4a82b4a6f42312115d7922bea5dd to your computer and use it in GitHub Desktop.
Save serby/ce4b4a82b4a6f42312115d7922bea5dd to your computer and use it in GitHub Desktop.
#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 = &param1.substring(param1.indexOf('=') + 1, param1.length())[0];
String data2 = &param2.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