Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
Felipe Neves
Published © MIT

LittleBee: Acoustic Sensor For Measuring Bee Hissing Level

Have you noted the main cause of Bee mortality is associated with death caused by its attack? LittleBee senses and how Bees are aggressive!

IntermediateFull instructions provided8 hours1,596

Things used in this project

Hardware components

STEVAL-STLKT01V1
STMicroelectronics STEVAL-STLKT01V1
×1
STM32 Nucleo-64 Board
STMicroelectronics STM32 Nucleo-64 Board
×1
Android device
Android device
×1

Software apps and online services

Android Studio
Android Studio
TrueSTUDIO
Atollic TrueSTUDIO

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Code

LittleBee firmware main application loop.

C/C++
This code is only to ilustrate the how the firmware is structured, it was built around a event loop, which uses these events as helpers to communication between subapplications
/*
 *  @file 	lilbee.c
 *  @brief 	littlebee firmware application entry point
 *
 */

#include "lilbee.h"


/** internal functions */



/**
 * 	@fn sysclk_config()
 *  @brief setup processor clock units
 *
 *  @param
 *  @return
 *
 *
 */
static void sysclk_config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  HAL_PWR_EnableBkUpAccess();

  /* Enable the LSE Oscilator */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){
    while(1);
  }

  /* Enable the CSS interrupt in case LSE signal is corrupted or not present */
  HAL_RCCEx_DisableLSECSS();

  /* Enable MSI Oscillator and activate PLL with MSI as source */
  RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState            = RCC_MSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange       = RCC_MSIRANGE_11;
  RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource       = RCC_PLLSOURCE_MSI;
  RCC_OscInitStruct.PLL.PLLM            = 6;
  RCC_OscInitStruct.PLL.PLLN            = 40;
  RCC_OscInitStruct.PLL.PLLP            = 7;
  RCC_OscInitStruct.PLL.PLLQ            = 4;
  RCC_OscInitStruct.PLL.PLLR            = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){
    while(1);
  }

  /* Enable MSI Auto-calibration through LSE */
  HAL_RCCEx_EnableMSIPLLMode();

  /* Select MSI output as USB clock source */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_MSI;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
  clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK){
    while(1);
  }
}

/** Public functions */


/**
 * 	@fn main()
 *  @brief application entrypoint and aloop
 *
 *  @param
 *  @return
 */
int main(void)
{
	sysclk_config();
	HAL_Init();

	BSP_LED_Init(LED1);

	/* inits the sub applications */
	bee_dsp_init(AUDIO_SAMPLE_FREQ);
	audio_acq_init();
	bee_ble_init();

	/* start the analysis*/
	audio_start_capture();

	for(;;){
		system_event_t ev = event_queue_get();

		audio_handler(ev);
		bee_dsp_handler(ev);
		bee_ble_handler(ev);

		if(event_queue_peek() == k_noevent) {
			/* No event pending, sleep the cpu */
			__WFI();
		}

		BSP_LED_Toggle(LED1);
	}
}

LittleBee firmware main application loop.

C/C++
This code is only to ilustrate the how the firmware is structured, it was built around a event loop, which uses these events as helpers to communication between subapplications
/*
 *  @file 	lilbee.c
 *  @brief 	littlebee firmware application entry point
 *
 */

#include "lilbee.h"


/** internal functions */



/**
 * 	@fn sysclk_config()
 *  @brief setup processor clock units
 *
 *  @param
 *  @return
 *
 *
 */
static void sysclk_config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  HAL_PWR_EnableBkUpAccess();

  /* Enable the LSE Oscilator */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){
    while(1);
  }

  /* Enable the CSS interrupt in case LSE signal is corrupted or not present */
  HAL_RCCEx_DisableLSECSS();

  /* Enable MSI Oscillator and activate PLL with MSI as source */
  RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState            = RCC_MSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange       = RCC_MSIRANGE_11;
  RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource       = RCC_PLLSOURCE_MSI;
  RCC_OscInitStruct.PLL.PLLM            = 6;
  RCC_OscInitStruct.PLL.PLLN            = 40;
  RCC_OscInitStruct.PLL.PLLP            = 7;
  RCC_OscInitStruct.PLL.PLLQ            = 4;
  RCC_OscInitStruct.PLL.PLLR            = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){
    while(1);
  }

  /* Enable MSI Auto-calibration through LSE */
  HAL_RCCEx_EnableMSIPLLMode();

  /* Select MSI output as USB clock source */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_MSI;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
  clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK){
    while(1);
  }
}

/** Public functions */


/**
 * 	@fn main()
 *  @brief application entrypoint and aloop
 *
 *  @param
 *  @return
 */
int main(void)
{
	sysclk_config();
	HAL_Init();

	BSP_LED_Init(LED1);

	/* inits the sub applications */
	bee_dsp_init(AUDIO_SAMPLE_FREQ);
	audio_acq_init();
	bee_ble_init();

	/* start the analysis*/
	audio_start_capture();

	for(;;){
		system_event_t ev = event_queue_get();

		audio_handler(ev);
		bee_dsp_handler(ev);
		bee_ble_handler(ev);

		if(event_queue_peek() == k_noevent) {
			/* No event pending, sleep the cpu */
			__WFI();
		}

		BSP_LED_Toggle(LED1);
	}
}

Bee DSP module.

C/C++
Here is where the magic happens, this subapp receives audio events and get a frame to perform the DSP functions and broadcast a event to BLE module to send the data to peer!
/*
 *  @file bee_dsp.c
 *  @brief module responsible to perform some DSP on the acquired audio
 */

#include "lilbee.h"


/** internal variables */
static uint32_t sample = 0;
static bee_spectra_t spectra = {0};
static float aggro_level = 0;

static bool dsp_lock = false;
static float dsp_float_buffer[1056];
extern const arm_rfft_fast_instance_f32 arm_rfft_fast_sR_f32_len512;

/** internal functions */

/**
 * 	@fn on_dsp_audio()
 *  @brief
 *
 *  @param
 *  @return
 */
static void on_dsp_audio(void)
{
	dsp_lock = true;

	/* gets the audio frame we will about to process */
	audio_frame_t *audio_block = audio_get_current_frame();

	/* no audio available or corrupted */
	if(audio_block == NULL)
		goto on_dsp_audio_exit;


	/* convert audio samples to float value */
	for(uint32_t i = 0; i < audio_block->size; i+=16) {
		/* unroll loop for performance */
		dsp_float_buffer[i] = (float)audio_block->audio_buffer[i]/32768.0f - 1.0f;
		dsp_float_buffer[i+1] = (float)audio_block->audio_buffer[i+1]/32768.0f - 1.0f;
		dsp_float_buffer[i+2] = (float)audio_block->audio_buffer[i+2]/32768.0f - 1.0f;
		dsp_float_buffer[i+3] = (float)audio_block->audio_buffer[i+3]/32768.0f - 1.0f;
		dsp_float_buffer[i+4] = (float)audio_block->audio_buffer[i+4]/32768.0f - 1.0f;
		dsp_float_buffer[i+5] = (float)audio_block->audio_buffer[i+5]/32768.0f - 1.0f;
		dsp_float_buffer[i+6] = (float)audio_block->audio_buffer[i+6]/32768.0f - 1.0f;
		dsp_float_buffer[i+7] = (float)audio_block->audio_buffer[i+7]/32768.0f- 1.0f;
		dsp_float_buffer[i+8] = (float)audio_block->audio_buffer[i+8]/32768.0f - 1.0f;
		dsp_float_buffer[i+9] = (float)audio_block->audio_buffer[i+9]/32768.0f - 1.0f;
		dsp_float_buffer[i+10] = (float)audio_block->audio_buffer[i+10]/32768.0f - 1.0f ;
		dsp_float_buffer[i+11] = (float)audio_block->audio_buffer[i+11]/32768.0f - 1.0f;
		dsp_float_buffer[i+12] = (float)audio_block->audio_buffer[i+12]/32768.0f - 1.0f;
		dsp_float_buffer[i+13] = (float)audio_block->audio_buffer[i+13]/32768.0f - 1.0f;
		dsp_float_buffer[i+14] = (float)audio_block->audio_buffer[i+14]/32768.0f - 1.0f;
		dsp_float_buffer[i+15] = (float)audio_block->audio_buffer[i+15]/32768.0f - 1.0f;
	}



	/* prepare to compute the FFT */
	arm_rfft_fast_f32(&arm_rfft_fast_sR_f32_len512, dsp_float_buffer, &spectra.raw[0], 0);
	arm_cmplx_mag_f32(&spectra.raw[0], &spectra.raw[0], DSP_FFT_POINTS);


	spectra.spectral_points = DSP_FFT_POINTS;
	spectra.spectral_sample_rate = AUDIO_SAMPLE_FREQ;
	/* estimente the aggro level searching the hissing frequency interval */
	aggro_level = spectra.raw[32];

on_dsp_audio_exit:
	/* broadcast the dsp end of processing */
	event_queue_put(k_dsp_endprocess);
}


/**
 * 	@fn on_dsp_endproc()
 *  @brief
 *
 *  @param
 *  @return
 */
static void on_dsp_endproc(void)
{
	dsp_lock = false;

	/* broadcast a new processed aggro level */
	audio_start_capture();
	event_queue_put(k_aggresivity_available);
}



/** public functions */

void bee_dsp_init(uint32_t dsp_sample)
{
	sample = dsp_sample;
}


uint32_t bee_dsp_get_sample_rate(void)
{
	return(sample);
}

float bee_dsp_get_aggro_level(void)
{

	return(aggro_level);
}

bee_retcode_t bee_dsp_get_spectra(bee_spectra_t *raw)
{
	bee_ble_retcode_t ret = k_bee_err;

	if(!dsp_lock && raw) {

		dsp_lock = true;
		__disable_irq();
		memcpy(raw, &spectra, sizeof(spectra));
		__enable_irq();
		dsp_lock = false;

		ret = k_bee_ret_ok;
	}

	return(ret);
}

void bee_dsp_handler(system_event_t ev)
{
	switch(ev) {
	case k_dsp_incoming_audio_available:
		on_dsp_audio();
		break;

	case k_dsp_endprocess:
		on_dsp_endproc();
		break;
	}
}

Android app BLE activity

Java
This file ilustrate how the ble communication is implemented on android peer device
package com.project.hackathon.motorola.bluetoothexample;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.*;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.util.Log;
import android.content.BroadcastReceiver;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;
import java.util.List;
import java.lang.Object;

public class bleActivity extends AppCompatActivity {

    BluetoothAdapter mBluetoothAdapter;
    BluetoothGatt    mGatt;
    BluetoothGattCharacteristic mBeeChar;
    public static float aggroLevel;


    private static final int REQUEST_ENABLE_BT = 1;
    private static final long SCAN_PERIOD = 10000;
    private static final String BLE_TAG = "BEE_BLE_LOG";
    Context mCtx;

    private boolean mScanning;
    private Handler mHandler;
    private int bleStatus;


    private UUID myUUID;
    private final String BEE_BLESERVICE_UUID =
            "00000000-000F-11E1-9AB4-0002A5D5C51B";

    private final String BEE_BLESERVICE_CHAR =
            "00000002-000F-11E1-AC36-0002A5D5C51B";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ble);


        mCtx = this;
        mHandler = new Handler();

        // obtaints the ble adapter
        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(mCtx.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();


    }

    @Override
    protected void onStart() {
        super.onStart();

        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

        scanLeDevice(true);
    }


    //BLE scan callback
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, int rssi,
                                     byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Log.d(BLE_TAG, "Device found is:" + device.getAddress());
                            Log.d(BLE_TAG, "Device found is:" + device.getName());

                            if(bleStatus != BluetoothProfile.STATE_CONNECTED) {

                                TextView tv = (TextView) findViewById(R.id.bleScanStatus);


                                // if device is a littleBee gather this device
                                if(new String("LB").equals(device.getName())) {

                                    tv.setText("LitteBee found! Connecting to it! ");

                                    // gets the device instance and connect to it
                                    mGatt = device.connectGatt(mCtx, true, mGattCallback);
                                }

                            }
                        }
                    });
                }
            };



    private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
                @Override
                public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                    super.onConnectionStateChange(gatt, status, newState);

                    bleStatus = newState;

                    if(newState == BluetoothProfile.STATE_DISCONNECTED){
                        Log.d(BLE_TAG, "LittleBee was disconected! restarting the advertisement");
                        scanLeDevice(true);


                    } else if(newState == BluetoothProfile.STATE_CONNECTED) {
                        Log.d(BLE_TAG, "LittleBee is connected! ");
                        TextView tv = (TextView) findViewById(R.id.bleScanStatus);

                        //aftert connection, disable the scanning
                        scanLeDevice(false);

                        // discover services then start the graph activity
                        gatt.discoverServices();

                        Intent it = new Intent(bleActivity.this, MainActivity.class);
                        startActivity(it);

                    }

                }

                @Override
                public void onServicesDiscovered(BluetoothGatt gatt, int status){
                    super.onServicesDiscovered(gatt, status);

                    if (status == BluetoothGatt.GATT_SUCCESS) {

                        Log.d(BLE_TAG, "Discovered database! ");
                        List <BluetoothGattService> services = gatt.getServices();
                        myUUID = UUID.fromString(BEE_BLESERVICE_UUID);

                        //Iterate through characteristics to find the desired one
                        for(BluetoothGattService service: services) {

                            if(service.getUuid().equals(myUUID)) {
                                myUUID = UUID.fromString(BEE_BLESERVICE_CHAR);
                                mBeeChar = service.getCharacteristic(myUUID);

                                // Set notification properties:
                                gatt.setCharacteristicNotification(mBeeChar, true);

                                for(BluetoothGattDescriptor desc : mBeeChar.getDescriptors()) {

                                    desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                    gatt.writeDescriptor(desc);
                                }

                                break;
                            }
                        }


                    } else {

                        Log.d(BLE_TAG, "Failed to services discovered, status " +  status);

                    }
                }

                @Override
                public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                    super.onCharacteristicChanged(gatt, characteristic);

                    Log.d(BLE_TAG, "Characteristic update from device " + characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT,0));

                    int tmp = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32,0);
                    aggroLevel = Float.intBitsToFloat(tmp);
                }
            };


    private void scanLeDevice(final boolean enable) {

        TextView tv = (TextView) findViewById(R.id.bleScanStatus);

        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            tv.setText("Finding LittleBee Device...");

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }

}

Android app main activity

Java
This file ilustrates how main activity obtains data, instantiate a Graph View and then refresh it in real time
package com.project.hackathon.motorola.bluetoothexample;

import android.Manifest;
import android.graphics.Color;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.Viewport;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;


public class MainActivity extends AppCompatActivity {


    LineGraphSeries<DataPoint> mSeries ;
    GraphView mBeeGraph;
    private Handler  mGraphHandler;
    private Runnable mGraphTimer;
    private Viewport mVp;
    private int xpos;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBeeGraph = (GraphView)findViewById(R.id.graph);
        mGraphHandler = new Handler();

        mSeries =  new LineGraphSeries<>();
        mSeries.setTitle("Bee Hissing Level");
        mSeries.setBackgroundColor(Color.GRAY);

        mBeeGraph.addSeries(mSeries);

        mVp= mBeeGraph.getViewport();
        mVp.setXAxisBoundsManual(true);
        mVp.setMinX(0);
        mVp.setMaxX(1000);
        mVp.setMinY(-2);
        mVp.setMaxY(2);


        xpos = 0;
    }


    @Override
    protected void onStart() {
        super.onStart();

    }


    @Override
    protected void onResume() {
        super.onResume();

        mGraphTimer = new Runnable() {
            @Override
            public void run() {

                xpos++;
                mSeries.appendData(new DataPoint(xpos, bleActivity.aggroLevel), true, 1000);
                mGraphHandler.postDelayed(this, 100);
            }
        };

        mGraphHandler.postDelayed(mGraphTimer, 100);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

}

littlebee

This repo is the Android application source code for LittleBee system

LilBee

This repo is the firmware source code for LittleBee environmental beekeeper audio sensor

Credits

Felipe Neves
2 projects • 14 followers
Passionate embedded systems engineer, lead developer for smart wearable devices, spends his free time making amazing and shareable projects.

Comments