Skip to content

Commit

Permalink
New firmware implementation (v3.0)
Browse files Browse the repository at this point in the history
The new implementation of the badge's firmware. Master's Thesis Michael Hopfengärtner (@MichaelHopfengaertner). It uses the Nordic Semiconductor's SDK v12.3.0 for the nRF51822 chip of the badge.

Various new techniques and methods have been developed and integrated:
- Filesystem: The internal flash-memory of the nRF51822 and the external EEPROM were abstracted and put together to provide one "large" virtual memory with byte level access. On the basis of this virtual memory a filesystem was implemented. It separates the available memory into partitions. A partition can be configured as static or dynamic. Static means that all elements stored in the partition must have the same size. In contrast, dynamic partitions allow a variable size of the elements. In dynamic partitions, a XOR linked list is used as data structure.
- Tinybuf: A serialization library orientated at Google's Protocol Buffers. It enables an efficient serialization of structured data and is used for storage and transmission. It supports C and Python as programming languages. By using Tinybuf, the transmission protocol between the badges and hubs can be extended and adapted easily, enabling a fast development process.
- Time synchronization: An exponential weighted moving average filter was implemented to robustly compensate oscillator frequency drifts during the time synchronization.
- Testing framework: A framework that allows to easily create and execute tests. Optionally, test coverage can be enabled. This allows the developer to see how good the tests are and where to add some more (unit) tests.
- ...

The firmware has a modular and maintainable architecture. This makes it relatively easy to migrate to other platforms.

The new firmware also transmit data twice as fast as the old one, enabling each hub to support more badges.
  • Loading branch information
OrenLederman authored Dec 10, 2018
2 parents 133e9fd + 2ca9234 commit 2f190d1
Show file tree
Hide file tree
Showing 243 changed files with 72,909 additions and 6,038 deletions.
394 changes: 394 additions & 0 deletions BadgeFramework/Demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,394 @@
from __future__ import division, absolute_import, print_function
import sys
import threading
import datetime
import random
import time

from badge import *
from ble_badge_connection import *
from bluepy import *
from bluepy import btle
from bluepy.btle import UUID, Peripheral, DefaultDelegate, AssignedNumbers ,Scanner
from bluepy.btle import BTLEException

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2TkAgg)
from matplotlib.figure import Figure
from Tkinter import Tk, Label, Button, Entry, LEFT, BOTH
import numpy as np

# It is important to enable the access to the host-display with "xhost +" before starting the docker-container
# You have to configure the ID's and group number of the badges via the terminal correctly to "find" them in a scan --> same group number
# Please enter the MAC-Address of the Badge during the call of this python script


MAC_ADDRESS = ""
DISPLAY_COUNTER_MIC = 200
DISPLAY_TIME_MIC_SEC = 10
DISPLAY_COUNTER_ACC = 200
DISPLAY_TIME_ACC_SEC = 10





t_mic = []
y_mic = []
t_acc = []
y_acc = []
y_scan = []
scan_alpha = 0.2

start_time = time.time()

scan_color_map=['r', 'g', 'b', 'cyan', 'magenta']


def get_distance(rssi):
n = 2
p_ref = -65
d = 10**((p_ref - rssi)/(10*n))

return d



class StreamThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.running = False



def run(self):
global t_mic
global y_mic
global t_acc
global y_acc
global y_scan

self.running = True
self.connect()
while(self.running):
try:
tmp = self.badge.get_stream()
while(not (tmp == [])):
#print(tmp)
if(not (tmp.microphone_stream == [])):
mic = tmp.microphone_stream[0].microphone_data.value
t_mic.append(time.time() - start_time)
y_mic.append(mic)

if(not (tmp.accelerometer_stream == [])):
acc = tmp.accelerometer_stream[0].accelerometer_raw_data.raw_acceleration
t_acc.append(time.time() - start_time)
y_acc.append(acc)



if(not (tmp.scan_stream == [])):

scan = tmp.scan_stream[0].scan_device
ID = scan.ID
RSSI = scan.rssi
IDs = [row[0] for row in y_scan]
idx = []
try:
idx = IDs.index(ID)
except Exception as e:
print(e)


d = get_distance(RSSI)


if(idx == []):
y_scan.append([ID, d])
# sort ID
y_scan = sorted(y_scan, key=lambda x: x[0])

else:
former_d = y_scan[idx][1]
new_d = scan_alpha*d + (1-scan_alpha)* former_d
y_scan[idx]= [ID, new_d]

print(y_scan)

#t_scan.append(time.time() - start_time)
# sort to greatest
#y_scan.append(scan)



# TODO: Add here the other data-source (accel...)
tmp = self.badge.get_stream()
except Exception as e:
print(e)
self.connection.disconnect()
self.connect()

self.connection.disconnect()

def stop(self):
self.running = False

def connect(self):
print("Connecting to badge", MAC_ADDRESS)
self.connection = BLEBadgeConnection.get_connection_to_badge(MAC_ADDRESS)
self.connection.connect()
self.badge = OpenBadge(self.connection)
print("Connected!")
print(self.badge.get_status())
print("Start streaming..")
self.badge.start_microphone_stream(sampling_period_ms=50)

self.badge.start_accelerometer_stream(timeout_minutes=0, operating_mode=2, full_scale=4, datarate=25, fifo_sampling_period_ms=50)
self.badge.start_scan_stream(timeout_minutes=0, window_ms=100, interval_ms=300,duration_seconds=5, period_seconds=6, aggregation_type=0)

############ START other Datasources #############

self.clear()

def clear(self):
global t_mic
global y_mic
global t_acc
global y_acc
global y_scan
print("Clear stream queue")
self.badge.stream_clear()
print("Cleared stream queue")
t_mic = []
y_mic = []
t_acc = []
y_acc = []
y_scan = []


class App:
def __init__(self):
self.stream_thread_running = 0
self.plot_thread_running = 0

self.f = Figure(figsize=(5,4), dpi=100)
self.axis_mic = self.f.add_subplot(131)
self.axis_mic.set_ylim(0,100)
self.axis_mic.grid(b=True)

self.axis_acc = self.f.add_subplot(132)
self.axis_acc.set_ylim(-500,500)
self.axis_acc.grid(b=True)


self.axis_scan = self.f.add_subplot(133)
self.axis_scan.set_ylim(0,4)
self.axis_scan.grid(b=True)



########### Add other axis ##########

self.window = Tk()
self.canvas = FigureCanvasTkAgg(self.f, master = self.window)
self.canvas.draw()
self.canvas.get_tk_widget().pack(fill=BOTH, expand=True)



self.start_button = Button(master=self.window, text="Start", command = self.start_button_pressed)
self.start_button.pack(side=LEFT)
self.stop_button = Button(master=self.window, text="Stop", command = self.stop_button_pressed)
self.stop_button.pack(side=LEFT)
self.window.mainloop()


def reset(self):
global t_mic
global y_mic
global t_acc
global y_acc
global y_scan

t_mic = []
y_mic = []
t_acc = []
y_acc = []
t_acc_int = []
y_acc_int = []
t_bat = []
y_bat = []
t_scan = []
y_scan = []


def start_button_pressed(self):
self.reset()
global start_time
start_time = time.time()

if(not self.plot_thread_running):
self.plot_thread_running = 1
self.plot_thread = PlotThread(self.canvas, self.axis_mic, self.axis_acc, self.axis_scan)
self.plot_thread.start()


if(not self.stream_thread_running):
self.stream_thread_running = 1
self.stream_thread = StreamThread()
self.stream_thread.start()

def stop_button_pressed(self):
self.reset()

if(self.plot_thread_running):
self.plot_thread_running = 0
self.plot_thread.stop()

if(self.stream_thread_running):
self.stream_thread_running = 0
self.stream_thread.stop()


class PlotThread(threading.Thread):
def __init__(self, canvas, axis_mic, axis_acc, axis_scan):
threading.Thread.__init__(self)
self.canvas = canvas
self.axis_mic = axis_mic
self.axis_acc = axis_acc
self.axis_scan = axis_scan
self.running = False

def run(self):

global t_mic
global y_mic
global t_acc
global y_acc
global y_scan
self.running = True
while(self.running):
################# MIC ###################
x_tmp = []
y_tmp = []
start_index, end_index = self.get_list_indizes(t_mic, DISPLAY_COUNTER_MIC)

x_tmp = t_mic[start_index:end_index]
y_tmp = y_mic[start_index:end_index]
t_mic = t_mic[start_index:]
y_mic = y_mic[start_index:]


self.axis_mic.clear()
self.axis_mic.plot(x_tmp, y_tmp, "-", label='Mic')
#self.axis_mic.legend(loc="upper right")
y_lim_min, y_lim_max = self.get_y_lim(y_tmp, 0, 90)
self.axis_mic.set_ylim(y_lim_min, y_lim_max + 10)
x_lim_min, x_lim_max = self.get_x_lim_time(DISPLAY_TIME_MIC_SEC, start_time)
self.axis_mic.set_xlim(x_lim_min, x_lim_max)
self.axis_mic.grid(b=True)
self.axis_mic.set_title("Microphone")
self.axis_mic.set_xlabel("Time (s)")


##### ACC #####
x_tmp = []
y_tmp = []
start_index, end_index = self.get_list_indizes(t_acc, DISPLAY_COUNTER_ACC)

x_tmp = t_acc[start_index:end_index]
y_tmp = y_acc[start_index:end_index]
t_acc = t_acc[start_index:]
y_acc = y_acc[start_index:]


self.axis_acc.clear()
self.axis_acc.plot(x_tmp, [row[0] for row in y_tmp] , "-", label='x')
self.axis_acc.plot(x_tmp, [row[1] for row in y_tmp], "-", label='y')
self.axis_acc.plot(x_tmp, [row[2] for row in y_tmp], "-", label='z')
self.axis_acc.legend(loc="upper right")
y_lim_min, y_lim_max = self.get_y_lim(y_tmp, -750, 750)
self.axis_acc.set_ylim(y_lim_min - 10, y_lim_max + 10)
x_lim_min, x_lim_max = self.get_x_lim_time(DISPLAY_TIME_ACC_SEC, start_time)
self.axis_acc.set_xlim(x_lim_min, x_lim_max)
self.axis_acc.grid(b=True)
self.axis_acc.set_title("Acceleration (mg)")
self.axis_acc.set_xlabel("Time (s)")


##### Scan #####
x_label = []
x_pos = []
y = []
colors = []
for i in range(0, len(y_scan)):
colors.append(scan_color_map[i%len(scan_color_map)])
x_label.append("ID: " + str(y_scan[i][0]))
y.append(y_scan[i][1])
x_pos.append(i)


barwidth = 0.5
self.axis_scan.clear()
self.axis_scan.set_xticks(x_pos)
self.axis_scan.set_xticklabels(x_label)
self.axis_scan.bar(x_pos, y, barwidth, align='center', alpha=0.5, color = colors)
self.axis_scan.set_title("Approximate distance (m)")

self.canvas.draw()
print("Draw")

def stop(self):
self.running = False

def get_list_indizes(self, x, max_values):
l = len(x)
start_index = 0
end_index = 0
if(l < max_values):
start_index = 0
end_index = l
else:
start_index = l-max_values
end_index = l

return start_index, end_index

def get_y_lim(self, y, default_min, default_max):
l = len(y)
y_lim_max = default_max
y_lim_min = default_min
if(l == 0):
return y_lim_min, y_lim_max

maximum = np.max(y)
minimum = np.min(y)

if(maximum > default_max):
y_lim_max = maximum
if(minimum < default_min):
y_lim_min = minimum
return y_lim_min, y_lim_max

def get_x_lim_time(self, display_time, start_time):
cur_time = time.time()
x_lim_min = 0
x_lim_max = cur_time - start_time

if(x_lim_max < display_time):
x_lim_min = 0
else:
x_lim_min = x_lim_max - display_time

return x_lim_min, x_lim_max


if __name__ == "__main__":
if len(sys.argv) != 2:
print("Please enter badge MAC address")
exit(1)
device_addr = sys.argv[1]
MAC_ADDRESS = device_addr
app = App()

Loading

0 comments on commit 2f190d1

Please sign in to comment.