The Project board is for sharing projects based on Silicon Labs' component with other community members. View Projects Guidelines ›

Projects

    Publish
     
      • IoT demo using Thunderboard

        HallMark | 12/352/2016 | 02:56 PM

        Hello,

         

        First of all I would like to Thank you Silicon Labs for the Thunderboard Sense Kit ! Tiny but Swiss Knife of Sensors. 

         

        Trust me it is !

        SENSE.jpg

         

         

        I have decided to go with IoT demo using this kit which sends Temperature, Humidity and Battery Level to Cloud.

         

        I choose RaspberryPi 3 Model B to collect the data from Sense Kit and then upload it to https://thingspeak.com/ portal.

         

        Thingspeak is very easy to create and use platform. I have created my channel there with three fields. It gives you unique API Key using which you can push data to cloud and on Thingspeak you can view the graph of your logged data. There is limit on it so you can push data once at every 15 seconds only.

         

        Thingspeak_Dashboard

        Thingspeak Dashboard !

         

        Thingspeak_Graph

        Thingspeak Graph !

         

        Project posted by @Namo_Aton helped me to to start things and find characteristics I want to read out and push data to cloud.

         

        As I want to read Battery Level, Temperature and Humidity I have find out those three characteristics first and then read it. It will read parameters at every 1 second but push to cloud at 15 second interval.

         

        Here is the python script to connect with Sense Kit, Read data and push to Cloud.

        from __future__ import division
        import sys
        from bluepy.btle import *
        import struct
        import thread
        from time import sleep
        import urllib2
        
        #
        PRIVATE_KEY = '2HL151HLWNJNENQD'
        
        #Base URL of Thingspeak
        baseURL = 'https://api.thingspeak.com/update?api_key='
        
        def vReadSENSE():
        	scanner = Scanner(0)
        	devices = scanner.scan(3)
        	for dev in devices:
        		print "Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi)
        
        		for (adtype, desc, value) in dev.getScanData():
        			print "  %s = %s" % (desc, value)
        	num_ble = len(devices)
        	print num_ble
        	if num_ble==0:
        		return None
        	ble_service = []
        	char_sensor = 0
        	non_sensor = 0
        	bat_char = Characteristic
        	temperature_char = Characteristic
        	humidity_char = Characteristic
        	count = 15
            
        	for i in range(num_ble):
        		try:
        			devices[i].getScanData()
        			ble_service.append(Peripheral())
        			ble_service[char_sensor].connect(devices[i].addr,devices[i].addrType)
        			char_sensor = char_sensor + 1
        			print "Connected %s device with addr %s " % (char_sensor, devices[i].addr)
        		except:
        			non_sensor = non_sensor + 1
        	try:
        		for i in range(char_sensor):
        			
        			services = ble_service[i].getServices()
        			characteristics = ble_service[i].getCharacteristics()
        			for k in characteristics:
        				print k
        				if k.uuid=="2a19":
        					print "Battery Level"
        					bat_char = k
        				if k.uuid == "2a6e":
        					print "Temperature"
        					temperature_char = k
        				if k.uuid == "2a6f":
        					print "Humidity"
        					humidity_char = k
        			
        	except:
        		return None
        	while True:
        		bat_data = bat_char.read()
        		bat_data_value = ord(bat_data[0])
        		
        		temperature_data = temperature_char.read()
        		temperature_data_value =(ord(temperature_data[1])<<8)+ord(temperature_data[0])
        		float_temperature_data_value = (temperature_data_value / 100)
        		
        		humidity_data = humidity_char.read()
        		humidity_data_value =(ord(humidity_data[1])<<8)+ord(humidity_data[0])
        
        		print "Battery: ", bat_data_value
        		print "Temperature: ", float_temperature_data_value
        		print "Humidity: ", humidity_data_value
        		if count > 14:
        			f = urllib2.urlopen(baseURL + PRIVATE_KEY +"&field1=%s&field2=%s&field3=%s" % (bat_data_value, float_temperature_data_value, humidity_data_value))
        			print f.read()
        			f.close()
        			count = 0
        		count = count + 1 
        		sleep(1)
        
        while True:
        	vReadSENSE()
        	

         

        Here is the video for the same.

         

         

        Cheers !

         

      • Threading the office: Thunderboard Sense on Thread

        Alf | 12/350/2016 | 11:05 AM

        Summary

        What's more Christmas-y than the entire office decorated in multi-colored LEDs? After my last attempt at creating a wireless network I realized that a simple point-to-point network does not scale to the level of our entire office. So for the next iteration i chose to jump on the protocol of choice for indoor connected nodes: Thread. 

         

        I started out with zero knowledge about this and ended up with 18 Thread-connected nodes all polling the main server for a color. The nodes are implemented on Thunderboard Sense and glows in different colors dictated by the server. The server can either provide a fixed color or do a mode where it cycles through the colors along the edges of the RGB-gamut. Here's a couple of pictures from the installation. Detailed instructions below the pics.

        Thread - Server and Node

        The commissioning Thread server and one node.

         

        Thread - Multiple nodes

        Multiple Thread nodes all glowing in the same color.

         

        Thread - Around the Office

        Thread nodes all glowing around the office.

         

        Thread - In the Corner

        Thread nodes in every corner of the office.

         

         

        Detailed description

         

        Step 1: Get started

        The first thing I did was get the client/server example up running on two WSTKs. This example is using CoAP for communication between a client and a server, very much like communication between a webclient and a webserver using HTTP. You can use POST commands to push data to the server and GET commands to retrieve data back. For the initial phase I just followed QSG113: Getting Started with Silicon Labs Thread.

         

        It's best to remove bootloader, you can do that in the .isc file by configuring "HAL->Bootloader->None" and unchecking the box "Plugins->Common->TFTP Bootload Target", and then commenting out the non-compiling bits in client-implementation.c and server-implementation.c. If you remove the bootloader you can just press the debug-button in Simplicity Studio to run code on the device.

         

        Step 2: Get it running on a Thunderboard Sense

        The next thing I had to do was to get our Thread stack running on a Thunderboard Sense. The first thing I did was to create a new client-project under a different name, I used client_tbs. I then removed the bootloader as outlined above and changed the target part to EFR32MG1P132F256M48 and hit 'Generate'. After removing the boorloader parts from client-implementation.c this should run on the Thunderboard Sense, reporting back the temperature.

         

        To get it properly working with the peripherals on the actual board I did two things: 1) Update the buttons/LEDs and 2) include the BSP from the Bluetooth example.

         

        Getting it to work with the correct buttons and LEDs I copied the folder EFR32MG1_BRD4151A into my project and renamed it to EFR32MG1_BRD4160A. I then updated the bspconfig.c with the correct locations of the LEDs and buttons:

        #define BSP_GPIO_LED0_PORT      gpioPortD
        #define BSP_GPIO_LED0_PIN       11
        #define BSP_GPIO_LED1_PORT      gpioPortD
        #define BSP_GPIO_LED1_PIN       12
        
        #define BSP_GPIO_PB0_PORT       gpioPortD
        #define BSP_GPIO_PB0_PIN        14
        #define BSP_GPIO_PB1_PORT       gpioPortD
        #define BSP_GPIO_PB1_PIN        15

        If you then change the include path to this folder instead of the EFR32MG1_BRD4151A, the project should compile and you will get a green blinking LED.

         

         

        To get the BSP running you first have to copy the neccessary contents from SimplicityStudio\v4\offline\examples\ble_2000\examples_thunderboard_sense\bsp into your project. For this project I'm just using the PWM LEDs and the Temp/Humidity sensor, so I copied: 

         

        void emberAfMainCallback(MAIN_FUNCTION_PARAMETERS)
        {
          NVIC_EnableIRQ(SysTick_IRQn);
          UTIL_init();
          BOARD_init();
          BOARD_rgbledPowerEnable( 1 );
          BOARD_rgbledEnable( true, 0xf );
          SI7021_init();
        }

         

        board.c
        board.h
        board_pic_regs.h
        board_rgbled_profiles.h
        si7021.h
        si7021.c
        util.c
        util_sleep.c
        util.h

        As the BOARD.c BSP relies on the systick-timer you have to add the following to the top of client-implementation.c  to route the systick interrupt to the correct location:

         

         

        void halInternalSysTickIsr(void)
        {
        	SysTick_Handler();
        }

         

         

        To initialize the BSP you add the following in the top of the same file:

         

        void emberAfMainCallback(MAIN_FUNCTION_PARAMETERS)
        {
          NVIC_EnableIRQ(SysTick_IRQn);
          UTIL_init();
          BOARD_init();
          BOARD_rgbledPowerEnable( 1 );
          BOARD_rgbledEnable( true, 0xf );
          SI7021_init();
        }

        The function emberAfMainCallback is run immediately on boot. A stub for this function is located in thread-callback-stubs.c by default, so remove it from here. The second thing you have to do is in main.c move the INTERRUPTS_ON(); above emAfMain(MAIN_FUNCTION_ARGUMENTS); as interrupts are needed for initializing the BSP:

         

        int MAIN(MAIN_FUNCTION_PARAMETERS)
        {
         
          // Enable interrupts before main-init
          INTERRUPTS_ON();
          
          // Let the application and plugins do early initialization.  This function is
          // generated.
          emAfMain(MAIN_FUNCTION_ARGUMENTS);
        
        (...)

         

         

        You should now be able to use all the BSP-functions of the Thunderboard Sense. For example, getting the actual temperature of the RH/temp sensor to report back to the server by replacing data = getTemp_mC(); with:

         

        uint32_t rhData;
        int32_t  tempData;
        if( SI7021_measure( &rhData, &tempData ) != SI7021_OK )
        {
          rhData   = 25000;
          tempData = 50000;
        }
        data = tempData;

         

         

        Step 3: Use CoAP GET to get the colors from the server

        The original example was only using POST, so the third task was to get the application to use GET instead.

         

        Server:

        On the server side you should add:

         

          {EMBER_AF_COAP_DISPATCH_METHOD_GET, "client/get", 10, clientGetHandler},

        To the emberAfCoapDispatchTable[] in thread-coap-dispatch.c. This means that the server will run clientGetHandler every time it gets a GET request from a client. In server-implementation.c a corresponding handler will have to be added for this:

        void clientGetHandler(const EmberCoapMessage *request)
        {
          // Requests from clients are sent as CoAP GET requests to the "client/get"
          // URI.
        
          EmberCoapCode responseCode;
        
          if (state != ADVERTISE) {
            responseCode = EMBER_COAP_CODE_503_SERVICE_UNAVAILABLE;
          } else {
        	if (!colorFixed)
        	  incrementColorIndex();
        
        	coapmessage[0] = colorTableSine[colorIndex][0];
        	coapmessage[1] = colorTableSine[colorIndex][1];
        	coapmessage[2] = colorTableSine[colorIndex][2];
        
        	emberAfCorePrint("Sending %ld %ld %ld to client at ", coapmessage[0], coapmessage[1], coapmessage[2]);
            emberAfCoreDebugExec(emberAfPrintIpv6Address(&request->remoteAddress));
            emberAfCorePrintln("");
            responseCode = EMBER_COAP_CODE_205_CONTENT;
          }
        
          if (emberCoapIsSuccessResponse(responseCode)
              || request->localAddress.bytes[0] != 0xFF) { // not multicast
            emberCoapRespond(responseCode, coapmessage, 3); // Payload
          }
        }

         

        The rest of the additions to server-implementation.c looks like this:

        /* ********************* USER *************/
        static uint32_t buttonpress = 0;
        static uint8_t coapmessage[4] = {0};
        static uint8_t colorIndex   = 0;
        static uint8_t colorFixed   = 0;
        static uint8_t colorStep    = 0;
        static uint8_t colorStepped = 0;
        
        static const uint8_t colorTableSine[48][3] =
        {
          {0xFF, 0x0 , 0x0 },
          {0xE6, 0x19, 0x0 },
          {0xCE, 0x31, 0x0 },
          {0xB8, 0x47, 0x0 },
          {0xA5, 0x5A, 0x0 },
          {0x95, 0x6A, 0x0 },
          {0x8A, 0x75, 0x0 },
          {0x82, 0x7D, 0x0 },
          {0x80, 0x7F, 0x0 },
          {0x7D, 0x82, 0x0 },
          {0x75, 0x8A, 0x0 },
          {0x6A, 0x95, 0x0 },
          {0x5A, 0xA5, 0x0 },
          {0x47, 0xB8, 0x0 },
          {0x31, 0xCE, 0x0 },
          {0x19, 0xE6, 0x0 },
          {0x0,  0xFF, 0x0 },
          {0x0,  0xE6, 0x19},
          {0x0,  0xCE, 0x31},
          {0x0,  0xB8, 0x47},
          {0x0,  0xA5, 0x5A},
          {0x0,  0x95, 0x6A},
          {0x0,  0x8A, 0x75},
          {0x0,  0x82, 0x7D},
          {0x0,  0x80, 0x7F},
          {0x0,  0x7D, 0x82},
          {0x0,  0x75, 0x8A},
          {0x0,  0x6A, 0x95},
          {0x0,  0x5A, 0xA5},
          {0x0,  0x47, 0xB8},
          {0x0,  0x31, 0xCE},
          {0x0,  0x19, 0xE6},
          {0x0 , 0x0 , 0xFF},
          {0x19, 0x0,  0xE6},
          {0x31, 0x0,  0xCE},
          {0x47, 0x0,  0xB8},
          {0x5A, 0x0,  0xA5},
          {0x6A, 0x0,  0x95},
          {0x75, 0x0,  0x8A},
          {0x7D, 0x0,  0x82},
          {0x7F, 0x0,  0x80},
          {0x82, 0x0,  0x7D},
          {0x8A, 0x0,  0x75},
          {0x95, 0x0,  0x6A},
          {0xA5, 0x0,  0x5A},
          {0xB8, 0x0,  0x47},
          {0xCE, 0x0,  0x31},
          {0xE6, 0x0,  0x19}
        };
        
        static void incrementColorIndex(void)
        {
          if (colorStep <= 1)
        	colorIndex++;
          else
          {
        	if (colorStepped >= colorStep)
        	{
        		colorStepped = 1;
        		colorIndex++;
        	}
        	else
        		colorStepped++;
          }
        
          if (colorIndex >= 48)
            colorIndex = 0;
        }
        
        void halButtonIsr(uint8_t button, uint8_t state)
        {
          // button: BUTTON0 BUTTON1
          // state:  BUTTTON_RELEASED BUTTON_PRESSED
          if((button == BUTTON0) && (state == BUTTON_PRESSED))
          {
        	  colorStep = 0;
        	  colorFixed = 0x1;
          	  incrementColorIndex();
          }
          else if((button == BUTTON1) && (state == BUTTON_PRESSED))
          {
        	colorStep++;
        	colorFixed = 0x0;
          }
        }

        As you can see the two buttons can change between a mode where the color is fixed across the network and a mode where we cycle through the RGB-gamut. I had to create a non-linear color table (colorTableSine) because our eyes will view all shades in the corners of the gamut as that color, and if you cycle the colors linearly it will look like it's racing past the intermediate colors (purple, teal and yellow).

         

        Client:

        In client-implementation.c you can then replace the contents of reportDataToServer with a function that will run a GET-command instead of the POST-command. My version of reportDataToServer looks like this:

        static void reportDataToServer(void)
        {
          // We peridocally get data from the server. 
          // The actual data is read and used in processServerDataGet()
        
          EmberStatus status;
        
          status = emberCoapGet(&server,
        		  	  	  	  	clientGetUri,
        						processServerDataGet);
        
          if (status == EMBER_SUCCESS) {
            setNextState(WAIT_FOR_DATA_CONFIRMATION);
          } else {
            emberAfCorePrintln("ERR: Reporting failed: 0x%x", status);
            repeatStateWithDelay(REPORT_PERIOD_MS);
          }
        }

        As you might guess you also have to add a processServerDataGet. This looks like this:

        static void processServerDataGet(EmberCoapStatus status,
                                         EmberCoapMessage *coap,
                                         void *appData,
                                         uint16_t appDatalength)
        {
          // We track the success or failure of reports so that we can determine when
          // we have lost the server.  A series of consecutive failures is the trigger
          // to detach from the current server and find a new one.  Any successfully-
          // transmitted report clears past failures.
        
          if (state == WAIT_FOR_DATA_CONFIRMATION) {
        	if (status == EMBER_COAP_MESSAGE_ACKED
        		|| status == EMBER_COAP_MESSAGE_RESPONSE) {
        	  failedReports = 0;
        	} else {
        	  failedReports++;
        	  emberAfCorePrintln("ERR: Report timed out - failure %u of %u",
        						 failedReports,
        						 REPORT_FAILURE_LIMIT);
        	}
        	if (failedReports < REPORT_FAILURE_LIMIT) {
        	  setNextStateWithDelay(REPORT_DATA_TO_SERVER, REPORT_PERIOD_MS);
        	} else {
        	  detachFromServer();
        	}
          }
        
          uint8_t coapmessage[4] = {0};
          
          // Getting the contents of the CoAP GET request
          MEMCOPY(coapmessage, (void*)coap->payload, coap->payloadLength);
        
          // Set the color of the LEDs accordingly
          BOARD_rgbledSetColor(coapmessage[0], coapmessage[1], coapmessage[2]);
        
          // Printing the response for debugging purposes
          emberAfCorePrint("Got %ld %ld %ld from ", coapmessage[0], coapmessage[1], coapmessage[2]);
          emberAfCoreDebugExec(emberAfPrintIpv6Address(&server));
          emberAfCorePrintln("");
        }

        That's it! Here's another picture of multiple Thread nodes all lighting in the same color:

        Thread - Multiple colored nodes

         

         

        Merry Christmas!