Member | Action | Date |
---|---|---|
|
Replied
to
Thunderboard Sense source code - EFM8, CCS811
And here is a version that you can import into the updated version of Simplicity Studio. I've tested with the 8051 SDK 4.0.3, and it compiles. Note that I have not verified that the resulting binary actually works.
In the latest 8041 SDK, the i2c_0.c peripheral driver has been renamed to the more appropriate smb_0.c, so I had to update the function names in ioexp_reg.c
Dewald |
Oct 28 2017, 4:35 PM |
|
Replied
to
Thunderboard Sense source code - EFM8, CCS811
Hi, thanks for your interest in this. I've attached the source code for the binary that we program onto the EFM8SB10 during production.
I originally built this with Simplicity Studio v3, and when I tried to import the project today with v4 I couldn't get it to compile out of the box. Anyway, it uses the peripheral library driver for the SMB peripheral.
Hope the source code helps you on your way, and don't hesitate to reach out if you have questions!
Dewald |
Oct 28 2017, 4:35 PM |
|
Posted
Thunderboard Sense with Raspberry Pi and Python on Projects
The goal of this project was to make a very simple python script that runs on a Raspberry Pi and collects data from one or more Thunderboard Sense devices, using the same Google Firebase backend and web application that the official app uses.
To get started, go to https://github.com/siliconlabs/thundercloud, and clone the repository. Follow the instructions and set up a firebase account and project, replacing the database name in src/main.js as instructed. For this example, I have not yet gotten authentication working, so change the database.rules.json for now to allow anyone to write (basically replacing "auth.uid === 'YsGcsiI8hkwjImSrr25uZdqlNw3Qkgi8vUWx9MU6'": with "true")
{ "rules": { ".read": false, ".write": false, "thunderboard":{ ".read": true, ".write": true }, "topCharts":{ ".read": true, ".write": true }, "sessions":{ ".write": true, ".indexOn": ["startTime", "contactInfo/deviceName"], "$session":{//2592000000 is 30 days ".read": "data.child('startTime').val() > (now - 2992000000)", ".write": true } } } }
Now, deploy the app to Firebase using the firebase tools
sudo npm install -g firebase firebase login firebase deploy --project <your firebase project id>
In the Firebase console you should now see your deployed rules, as well as the application url. Following the URL should bring you to the default page: Now the next step is to log onto the Raspberry Pi, and install the necessary tools. The script uses bluepy to communicate with the sensor, and python-firebase to push data to the cloud. I did have some trouble installing python-firebase because of the specific version of the requests library, but eventually got it installed.
Remember to put in your own database URL in the thundercloud.py script: from firebase import firebase import uuid import time class Thundercloud: def __init__(self): self.addr = 'https://-- Your database URL --.firebaseio.com/' self.firebase = firebase.FirebaseApplication(self.addr, None)
The tbsense_scan.py script continuously looks for advertising thunderboards, and automatically connects to the board if a new one is discovered.
$ sudo python3 tbsense_scan.py No Thunderboard Sense devices found! No Thunderboard Sense devices found! Starting thread <Thread(Thread-1, initial)> for 37020 Thunder Sense #37020 Ambient Light: 0.25 Lux Sound Level: 37.83 tVOC: 0 Humidity: 28.25 %RH Temperature: 29.86 C Pressure: 951.586 UV Index: 0 eCO2: 0 When a Thunderboard Sense has been discovered and connected to, the script will print out the read data periodically. Take note of the number in "Thunder Sense #37020". We will need to give the number to the web application.
The script generates a session ID for each board connected, and then continuously generates json strings for the data read from the Thunderboard Sense. The json string is then inserted into the appropriate location in the database. Firebase has a useful live database view that shows us that our data is indeed being pushed into the cloud: Finally if you go to the app url and append your Thunderboard Sense id, you should see the data being displayed https://<project id>.firebaseapp.com/#/37020 tbsense.py simply discovers and sets up handles to the different characteristics. It also contains functions to read these characteristics:
from bluepy.btle import * import struct from time import sleep class Thunderboard: def __init__(self, dev): self.dev = dev self.char = dict() self.name = '' self.session = '' self.coinCell = False # Get device name and characteristics scanData = dev.getScanData() for (adtype, desc, value) in scanData: if (desc == 'Complete Local Name'): self.name = value ble_service = Peripheral() ble_service.connect(dev.addr, dev.addrType) characteristics = ble_service.getCharacteristics() for k in characteristics: if k.uuid == '2a6e': self.char['temperature'] = k elif k.uuid == '2a6f': self.char['humidity'] = k elif k.uuid == '2a76': self.char['uvIndex'] = k elif k.uuid == '2a6d': self.char['pressure'] = k elif k.uuid == 'c8546913-bfd9-45eb-8dde-9f8754f4a32e': self.char['ambientLight'] = k elif k.uuid == 'c8546913-bf02-45eb-8dde-9f8754f4a32e': self.char['sound'] = k elif k.uuid == 'efd658ae-c401-ef33-76e7-91b00019103b': self.char['co2'] = k elif k.uuid == 'efd658ae-c402-ef33-76e7-91b00019103b': self.char['voc'] = k elif k.uuid == 'ec61a454-ed01-a5e8-b8f9-de9ec026ec51': self.char['power_source_type'] = k def readTemperature(self): value = self.char['temperature'].read() value = struct.unpack('<H', value) value = value[0] / 100 return value def readHumidity(self): value = self.char['humidity'].read() value = struct.unpack('<H', value) value = value[0] / 100 return value def readAmbientLight(self): value = self.char['ambientLight'].read() value = struct.unpack('<L', value) value = value[0] / 100 return value def readUvIndex(self): value = self.char['uvIndex'].read() value = ord(value) return value def readCo2(self): value = self.char['co2'].read() value = struct.unpack('<h', value) value = value[0] return value def readVoc(self): value = self.char['voc'].read() value = struct.unpack('<h', value) value = value[0] return value def readSound(self): value = self.char['sound'].read() value = struct.unpack('<h', value) value = value[0] / 100 return value def readPressure(self): value = self.char['pressure'].read() value = struct.unpack('<L', value) value = value[0] / 1000 return value
thundercloud.py handles the connection to the Firebase database. getSession() generates a new session ID and is called once for every new Thunderboard Sense connection. putEnvironmentData() inserts the data and updates the timestamps:
from firebase import firebase import uuid import time class Thundercloud: def __init__(self): self.addr = 'https://'-- Firebase Database Name --'.firebaseio.com/' self.firebase = firebase.FirebaseApplication(self.addr, None) def getSession(self, deviceId): timestamp = int(time.time() * 1000) guid = str(uuid.uuid1()) url = 'thunderboard/{}/sessions'.format(deviceId) self.firebase.put(url, timestamp, guid) d = { "startTime" : timestamp, "endTime" : timestamp, "shortURL": '', "contactInfo" : { "fullName":"First and last name", "phoneNumber":"12345678", "emailAddress":"name@example.com", "title":"", "deviceName": 'Thunderboard #{}'.format(deviceId) }, "temperatureUnits" : 0, "measurementUnits" : 0, } url = 'sessions' self.firebase.put(url, guid, d) return guid def putEnvironmentData(self, guid, data): timestamp = int(time.time() * 1000) url = 'sessions/{}/environment/data'.format(guid) self.firebase.put(url, timestamp, data) url = 'sessions/{}'.format(guid) self.firebase.put(url, 'endTime', timestamp)
Finally, tbsense_scan.py continuously searches for new Thunderboard Sense devices, and spawns a new thread for each one successfully connected to:
from bluepy.btle import * import struct from time import sleep from tbsense import Thunderboard from thundercloud import Thundercloud import threading def getThunderboards(): scanner = Scanner(0) devices = scanner.scan(3) tbsense = dict() for dev in devices: scanData = dev.getScanData() for (adtype, desc, value) in scanData: if desc == 'Complete Local Name': if 'Thunder Sense #' in value: deviceId = int(value.split('#')[-1]) tbsense[deviceId] = Thunderboard(dev) return tbsense def sensorLoop(fb, tb, devId): session = fb.getSession(devId) tb.session = session value = tb.char['power_source_type'].read() if ord(value) == 0x04: tb.coinCell = True while True: text = '' text += '\n' + tb.name + '\n' data = dict() try: for key in tb.char.keys(): if key == 'temperature': data['temperature'] = tb.readTemperature() text += 'Temperature:\t{} C\n'.format(data['temperature']) elif key == 'humidity': data['humidity'] = tb.readHumidity() text += 'Humidity:\t{} %RH\n'.format(data['humidity']) elif key == 'ambientLight': data['ambientLight'] = tb.readAmbientLight() text += 'Ambient Light:\t{} Lux\n'.format(data['ambientLight']) elif key == 'uvIndex': data['uvIndex'] = tb.readUvIndex() text += 'UV Index:\t{}\n'.format(data['uvIndex']) elif key == 'co2' and tb.coinCell == False: data['co2'] = tb.readCo2() text += 'eCO2:\t\t{}\n'.format(data['co2']) elif key == 'voc' and tb.coinCell == False: data['voc'] = tb.readVoc() text += 'tVOC:\t\t{}\n'.format(data['voc']) elif key == 'sound': data['sound'] = tb.readSound() text += 'Sound Level:\t{}\n'.format(data['sound']) elif key == 'pressure': data['pressure'] = tb.readPressure() text += 'Pressure:\t{}\n'.format(data['pressure']) except: return print(text) fb.putEnvironmentData(session, data) sleep(1) def dataLoop(fb, thunderboards): threads = [] for devId, tb in thunderboards.items(): t = threading.Thread(target=sensorLoop, args=(fb, tb, devId)) threads.append(t) print('Starting thread {} for {}'.format(t, devId)) t.start() if __name__ == '__main__': fb = Thundercloud() while True: thunderboards = getThunderboards() if len(thunderboards) == 0: print("No Thunderboard Sense devices found!") else: dataLoop(fb, thunderboards)
|
Oct 28 2017, 4:28 PM |