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.

Dependencies:   Puck mbed

Fork of Example_Puck_BLE by Nordic Semiconductor

Files at this revision

API Documentation at this revision

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

SampleChannel.cpp Show annotated file Show diff for this revision Revisions of this file
SampleChannel.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /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());
     }
 }