Using the MBED BLE library and Nordic Puck library this is a simple scoring application using Bluetooth LE. It monitors three analog inputs and triggers on reception of a pulse on any one recording data for a short period on all three. This is then published via BLE characteristics. It's a demonstrator for a new UI dev toolkit that is under development.
Fork of Example_Puck_BLE by
Revision 3:a155da1cbde3, committed 2014-08-21
- Comitter:
- Bobty
- Date:
- Thu Aug 21 11:26:26 2014 +0000
- Parent:
- 2:e400fd4f501b
- Child:
- 4:cc164ecf6a36
- Commit message:
- Working but misses some quick pulses (thows)
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SampleChannel.cpp Thu Aug 21 11:26:26 2014 +0000 @@ -0,0 +1,142 @@ +// Simple averaging filter +#include "mbed.h" +#include "UUID.h" +#include "SampleChannel.h" + +SampleChannel::SampleChannel(PinName pin, const UUID uuid, Serial* pLogger) : chanUuid(uuid), ain(pin) +{ + pLocalLogger = pLogger; + for (int i = 0; i < NUM_FILTER_VALS; i++) + filterBuf[i] = 0; + curFilterIdx = 0; + for (int i = 0; i < NUM_RETURN_SAMPLES; i++) + sampleBuf[i] = 0; + curSampleIdx = 0; + isCapturing = false; + sampleThreshold = 100; + sampleDivisor = 10; + testSampleCount = 0; + sampleBaseVal = 0; +} + +void SampleChannel::SetThreshold(uint16_t threshold) +{ + sampleThreshold = threshold; +} + +void SampleChannel::SetDivisor(uint16_t divisor) +{ + sampleDivisor = divisor; +} + +void SampleChannel::AddSample(uint16_t val) +{ + testSampleCount++; + if (testSampleCount == 100) + { + pLocalLogger->printf("%d AvgT %d, AvgW %d, low %d\n", val, GetAvgOfTriggerWindow(), GetAvgOutsideTriggerWindow(), GetLowest()); + testSampleCount = 0; + } + + // If capturing add to sample buf + if (isCapturing) + { + if (curSampleIdx < NUM_RETURN_SAMPLES) + { + int mungedVal = val - sampleBaseVal; + if (mungedVal < 0) + mungedVal = 0; + mungedVal = mungedVal / sampleDivisor; + if (mungedVal > 255) + mungedVal = 255; + sampleBuf[curSampleIdx++] = (uint8_t)mungedVal; + } + } + // Add to filter + filterBuf[curFilterIdx] = val; + curFilterIdx++; + curFilterIdx = curFilterIdx % NUM_FILTER_VALS; +} + +int SampleChannel::GetAvgOutsideTriggerWindow() +{ + int sum = 0; + for (int i = 0; i < NUM_FILTER_VALS-TRIGGER_WINDOW; i++) + sum += filterBuf[(i + curFilterIdx) % NUM_FILTER_VALS]; + return sum / (NUM_FILTER_VALS - TRIGGER_WINDOW); +} + +int SampleChannel::GetAvgOfTriggerWindow() +{ + int sum = 0; + for (int i = 0; i < TRIGGER_WINDOW; i++) + sum += filterBuf[(curFilterIdx + NUM_FILTER_VALS - 1 - i) % NUM_FILTER_VALS]; + return sum / TRIGGER_WINDOW; +} + +uint16_t SampleChannel::GetLowest() +{ + uint16_t lowVal = 0xffff; + for (int i = 0; i < NUM_FILTER_VALS; i++) + if (lowVal > filterBuf[i]) + lowVal = filterBuf[i]; + return lowVal; +} + +void SampleChannel::Service() +{ + // take a sample and add to trigger buffer + unsigned short val = ain.read_u16(); + AddSample(val); +} + +bool SampleChannel::IsSampling() +{ + return isCapturing; +} + +bool SampleChannel::AreSamplesReady() +{ + // Check if sample ready + return (isCapturing && (curSampleIdx == NUM_RETURN_SAMPLES)); +} + +void SampleChannel::StopSampling() +{ + isCapturing = false; +} + +void SampleChannel::StartSampling() +{ + curSampleIdx = 0; + isCapturing = true; + sampleBaseVal = GetLowest(); + + // Copy across values from trigger window + for (int i = TRIGGER_WINDOW; i > 0; i--) + AddSample(filterBuf[(curFilterIdx + NUM_FILTER_VALS - i) % NUM_FILTER_VALS]); +} + +bool SampleChannel::CheckTrigger() +{ + // Check if the samples in the trigger window are significantly different from the average + for (int i = 0; i < TRIGGER_WINDOW; i++) + { + int spike = GetAvgOfTriggerWindow() - GetAvgOutsideTriggerWindow(); + if (spike < 0) + spike = -spike; + return (spike > sampleThreshold); + } + return false; +} + +uint8_t *SampleChannel::GetSamples() +{ + return sampleBuf; +} + +int SampleChannel::GetSamplesLen() +{ + return NUM_RETURN_SAMPLES; +} + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SampleChannel.h Thu Aug 21 11:26:26 2014 +0000 @@ -0,0 +1,46 @@ + +#ifndef __SampleChannel__H__ +#define __SampleChannel__H__ + +const int NUM_FILTER_VALS = 10; +const int TRIGGER_WINDOW = 3; +const int NUM_RETURN_SAMPLES = 20; + +class SampleChannel +{ + private: + UUID chanUuid; + uint16_t sampleThreshold; + uint16_t sampleDivisor; + uint16_t filterBuf[NUM_FILTER_VALS]; + int curFilterIdx; + uint8_t sampleBuf[NUM_RETURN_SAMPLES]; + int curSampleIdx; + bool isCapturing; + void AddSample(uint16_t val); + AnalogIn ain; + int GetAvgOutsideTriggerWindow(); + int GetAvgOfTriggerWindow(); + uint16_t GetLowest(); + Serial* pLocalLogger; + int testSampleCount; + uint16_t sampleBaseVal; + + public: + SampleChannel(PinName pin, const UUID uuid, Serial* pLogger); + void SetThreshold(uint16_t threshold); + void SetDivisor(uint16_t divisor); + void Service(); + bool IsSampling(); + bool AreSamplesReady(); + void StopSampling(); + void StartSampling(); + bool CheckTrigger(); + uint8_t *GetSamples(); + int GetSamplesLen(); + UUID GetUUID() { + return chanUuid; + } +}; + +#endif \ No newline at end of file
--- a/main.cpp Thu Aug 21 07:48:18 2014 +0000 +++ b/main.cpp Thu Aug 21 11:26:26 2014 +0000 @@ -7,37 +7,32 @@ #define LOG_LEVEL_INFO #include "Puck.h" +#include "SampleChannel.h" Puck* puck = &Puck::getPuck(); // Gatt characteristic and service UUIDs const UUID SCORING_GATT_SERVICE = stringToUUID("nod.score1.serv "); -const UUID SAMPLES_GATT_CHARACTERISTIC = stringToUUID("nod.score1.samp1"); const UUID THRESHOLD_GATT_CHARACTERISTIC = stringToUUID("nod.score1.thres"); -const UUID OFFSET_GATT_CHARACTERISTIC = stringToUUID("nod.score1.offs "); const UUID DIVISOR_GATT_CHARACTERISTIC = stringToUUID("nod.score1.div "); const UUID INTERVAL_US_GATT_CHARACTERISTIC = stringToUUID("nod.score1.intus"); -const int SAMPLES_LEN = 20; -uint8_t SAMPLES_BUF[SAMPLES_LEN]; - -// Sample threshold -uint16_t sampleThreshold = 800; - -// Sample offset -uint16_t samplesOffset = 760; - -// Sample divisor -uint16_t samplesDivisor = 1; +const int NUM_SAMPLE_CHANNELS = 1; // Sample interval (uS) -uint32_t sampleIntervalUs = 10000; +uint32_t sampleIntervalUs = 100000; -// Setup ADC -AnalogIn ain(P0_1); +// Sample Channels +SampleChannel sampleChannels[] = +{ + SampleChannel(P0_1, stringToUUID("nod.score1.samp1"), &logger), + SampleChannel(P0_2, stringToUUID("nod.score1.samp2"), &logger), + SampleChannel(P0_3, stringToUUID("nod.score1.samp3"), &logger) +}; // Timer to avoid repeat sampling -Timer antiRepeatTimer; +Timer intervalTimer; +int lastTriggerTime = 0; int lastSampleTime = 0; const int MIN_MS_BETWEEN_SAMPLES = 2000; @@ -45,28 +40,24 @@ { uint16_t threshold = value[0] * 256 + value[1]; LOG_INFO("Threshold=%d\n", threshold); - sampleThreshold = threshold; -} - -void onOffsetSet(uint8_t* value) -{ - uint16_t offset = value[0] * 256 + value[1]; - LOG_INFO("Offset=%d\n", offset); - samplesOffset = offset; + for (int chanIdx = 0; chanIdx < NUM_SAMPLE_CHANNELS; chanIdx++) + sampleChannels[chanIdx].SetThreshold(threshold); } void onDivisorSet(uint8_t* value) { uint16_t divisor = value[0] * 256 + value[1]; LOG_INFO("Divisor=%d\n", divisor); - samplesDivisor = divisor; + for (int chanIdx = 0; chanIdx < NUM_SAMPLE_CHANNELS; chanIdx++) + sampleChannels[chanIdx].SetDivisor(divisor); } void onIntervalSet(uint8_t* value) { uint32_t intervalUs = (value[0] << 24) + (value[1] << 16) + (value[2] << 8) + value[3]; LOG_INFO("SampleInterval(uS)=%d\n", intervalUs); - sampleIntervalUs = intervalUs; + if (intervalUs <= 1000000) + sampleIntervalUs = intervalUs; } int main(void) @@ -76,33 +67,28 @@ logger.baud(115200); // Add the Gatt characteristic for samples - puck->addCharacteristic( + for (int chanIdx = 0; chanIdx < NUM_SAMPLE_CHANNELS; chanIdx++) + { + puck->addCharacteristic( SCORING_GATT_SERVICE, - SAMPLES_GATT_CHARACTERISTIC, - SAMPLES_LEN, + sampleChannels[chanIdx].GetUUID(), + sampleChannels[chanIdx].GetSamplesLen(), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + } // Add the Gatt characteristic for threshold puck->addCharacteristic( SCORING_GATT_SERVICE, THRESHOLD_GATT_CHARACTERISTIC, - sizeof(sampleThreshold), + sizeof(uint16_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); puck->onCharacteristicWrite(&THRESHOLD_GATT_CHARACTERISTIC, onThresholdSet); - // Add the Gatt characteristic for offset - puck->addCharacteristic( - SCORING_GATT_SERVICE, - OFFSET_GATT_CHARACTERISTIC, - sizeof(samplesOffset), - GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); - puck->onCharacteristicWrite(&OFFSET_GATT_CHARACTERISTIC, onOffsetSet); - // Add the Gatt characteristic for sample divisor puck->addCharacteristic( SCORING_GATT_SERVICE, DIVISOR_GATT_CHARACTERISTIC, - sizeof(samplesDivisor), + sizeof(uint16_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); puck->onCharacteristicWrite(&DIVISOR_GATT_CHARACTERISTIC, onDivisorSet); @@ -118,37 +104,67 @@ puck->init(0xCD01); // Start timer - antiRepeatTimer.start(); + intervalTimer.start(); // Wait for something to be found - while(puck->drive()) + while(true) { - int curTimerVal = antiRepeatTimer.read_ms(); - if ((lastSampleTime < curTimerVal) || (curTimerVal - lastSampleTime > MIN_MS_BETWEEN_SAMPLES)) + // service all channel's state machines + bool isAnyChannelSampling = false; + for (int chanIdx = 0; chanIdx < NUM_SAMPLE_CHANNELS; chanIdx++) { - // Check threshold - unsigned short val = ain.read_u16(); - if(val > sampleThreshold) + sampleChannels[chanIdx].Service(); + if (sampleChannels[chanIdx].AreSamplesReady()) { - for (int i = 0; i < SAMPLES_LEN; i++) + // Set the value of the characteristic + puck->updateCharacteristicValue(sampleChannels[chanIdx].GetUUID(), sampleChannels[chanIdx].GetSamples(), sampleChannels[chanIdx].GetSamplesLen()); + sampleChannels[chanIdx].StopSampling(); + LOG_INFO("StopSampling\n"); + } + if (sampleChannels[chanIdx].IsSampling()) + isAnyChannelSampling = true; + } + + if (!isAnyChannelSampling) + { + // Service the puck + puck->drive(); + + int curTimerVal = intervalTimer.read_ms(); + if ((lastTriggerTime < curTimerVal) || (curTimerVal - lastTriggerTime > MIN_MS_BETWEEN_SAMPLES)) + { + + // check each channel to see if it's been triggered + bool anythingTriggered = false; + for (int chanIdx = 0; chanIdx < NUM_SAMPLE_CHANNELS; chanIdx++) { - SAMPLES_BUF[i] = (ain.read_u16() - samplesOffset) / samplesDivisor; - wait_us(sampleIntervalUs); + if (sampleChannels[chanIdx].CheckTrigger()) + { + anythingTriggered = true; + LOG_INFO("Triggered\n"); + break; + } } - - // Set the value of the characteristic - puck->updateCharacteristicValue(SAMPLES_GATT_CHARACTERISTIC, SAMPLES_BUF, SAMPLES_LEN); - - // Display readings - for (int j = 0; j < SAMPLES_LEN; j++) + if(anythingTriggered) { - LOG_INFO("%02x ", SAMPLES_BUF[j]); + for (int chanIdx = 0; chanIdx < NUM_SAMPLE_CHANNELS; chanIdx++) + { + sampleChannels[chanIdx].StartSampling(); + } + // Set timer to disallow repeated readings + lastTriggerTime = curTimerVal; } - LOG_INFO("\n"); + } + } + else + { + wait_us(sampleIntervalUs); + } - // Set timer to disallow repeated readings - lastSampleTime = curTimerVal; - } - } + // Inter-sample interval + //while ((intervalTimer.read_us() - lastSampleTime < sampleIntervalUs) && (intervalTimer.read_us() - lastSampleTime > 0)) + // wait_us(100); +// lastSampleTime = intervalTimer.read_us(); +// LOG_INFO("Timer %d\n", intervalTimer.read_ms()); } }