Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client specific additional features/examples #87

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ getTemperature
#### Example 5: record motion data on chip
To record, and playback a specific motion feature:
```
eraseStorage //erase the on-chip recorder (optional)
eraseStorage 0 //erase the on-chip recorder (optional), 0: quick erase, 1: mass erase
sessionRecordQuaternion <number of samples> //by default, the streaming frequency is 50 Hz.
sessionPlayback <session ID> <dump to file option> //use the same session ID as returned by the record command
```
Expand Down
Binary file modified docs/img/record_data.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions examples/streamRecord.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python
###################################################################################
#
# Copyright (c) 2010-2016 Motsai
#
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###################################################################################

import getopt
import signal
import sys
import time

from neblina import *
from neblinaAPI import NeblinaAPI

###################################################################################


class GracefulKiller:
isKilled = False

def __init__(self):
signal.signal(signal.SIGINT, self.exit)
signal.signal(signal.SIGTERM, self.exit)

def exit(self, signum, frame):
print("Signal received: {0}.".format(signum))
self.isKilled = True

###################################################################################


def main(address):
signalKiller = GracefulKiller()

print("Initialize NeblinaAPI")
api = NeblinaAPI(Interface.UART)
print("Setting up the connection...") # initial delay needed for the device to synchronize its processors
time.sleep(1)
print('.')
time.sleep(1)
print('.')
time.sleep(1)
print('.')
api.open(address)
api.streamDisableAll() # disable all streaming options after storing the initial state
api.setDataPortState(Interface.BLE, False) # Close BLE streaming to prevent slowed streaming
api.setDataPortState(Interface.UART, True) # Open UART streaming

print("Starting the Quaternion Streaming...")
api.streamQuaternion(True)
print("Starting the IMU Data Streaming...")
api.streamIMU(True)
print("Starting the Recording...")
api.sessionRecord(True)
print("Exiting...")

###################################################################################


def printArguments():
print("Neblina quaternion stream v1.0.0")
print("Copyright Motsai 2010-2016")
print("")
print("Neblina commands:")
print(" -h --help : Display available commands.")
print(" -a --address: Device address to use (COM port)")

###################################################################################

if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "ha:")
except getopt.GetoptError:
printArguments()
sys.exit()

for opt, arg in opts:
if opt in ("-h", "--help"):
printArguments()
sys.exit()
elif opt in ("-a", "--address"):
main(arg)
sys.exit()

print("No device address specified. Exiting.")


20 changes: 20 additions & 0 deletions examples/streammenu.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,17 @@ def do_streamEulerAngle(self, args):
print(self.api.getEulerAngle())
self.api.streamEulerAngle(False)

def do_streamMotionState(self, args):
"""
Stream MotionState until stopped with Ctrl+C

Usage: >>streamMotionState
"""
self.api.streamMotionState(True)
while not self.signalKiller.isKilled:
print(self.api.getMotionState())
self.api.streamMotionState(False)

def do_streamIMU(self, args):
"""
Stream 6-axis IMU (Inertial Measurement Unit) until stopped with Ctrl+C
Expand Down Expand Up @@ -550,6 +561,15 @@ def do_sessionPlayback(self, args):
self.api.sessionPlayback(mySessionID, dump)
print("sessionPlayback completed")

def do_streamRSSI(self, args):
"""
Stream BLE's RSSI until stopped with Ctrl+C

Usage: >>streamRSSI
"""
while not self.signalKiller.isKilled:
print(self.api.getRSSI())

def do_getFirmwareVersions(self, args):
"""
Retrieve firmware versions
Expand Down
5 changes: 4 additions & 1 deletion neblina.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class Debug:
UnitTestMotionData = 0x04
FWVersions = 0x05
InterfaceState = 0x09
RSSI = 0x07

class Power:
"""
Expand Down Expand Up @@ -211,6 +212,7 @@ class BLE:
(SubSystem.Debug, Commands.Debug.StartUnitTestMotion): 'Enable/Disable Unit Test Motion',
(SubSystem.Debug, Commands.Debug.UnitTestMotionData): 'Unit Test Data',
(SubSystem.Debug, Commands.Debug.FWVersions): 'Firmware Versions',
(SubSystem.Debug, Commands.Debug.RSSI): 'Firmware Versions',
(SubSystem.Motion, Commands.Motion.Downsample): 'Downsample',
(SubSystem.Motion, Commands.Motion.MotionState): 'MotionState',
(SubSystem.Motion, Commands.Motion.IMU): 'IMU Data',
Expand Down Expand Up @@ -273,12 +275,13 @@ class Data:
Temperature = "<I h 10s" # Temperature x100 in Celsius
FlashNumSessions = "<I H 10s" # Reserved, number of sessions
FWVersions = "<B 3B 3B 8s" # API Release, MCU Major/Minor/Build, BLE Major/Minor/Build, Device ID
RSSI = "<I b 11s>" #Timestamp, RSSI
UnitTestMotion = "<B 3h 3h 3h 4h 3h 3h 3h H B I I h B I I"
MotionState = "<I B 11s" # Timestamp, start/stop
ExternalForce = "<I 3h 6s" # Timestamp, External force xyz
TrajectoryDistance = "<I 3h H B 3s" # Timestamp, Euler angle errors, repeat count, completion percentage
Pedometer = "<I H B h 7s" # Timestamp, stepCount, stepsPerMinute, walking direction
FingerGesture = "<I B 11s" # Timestamp, rotationCount
FingerGesture = "<I B 11s" # Timestamp, swipe pattern
RotationInfo = "<I I H 6s" # Timestamp, rotationCount, rpm speed
Quaternion = "<I 4h 4s" # Timestamp, quaternion
IMU = "<I 3h 3h" # Timestamp, accelerometer(xyz), gyroscope(xyz)
Expand Down
13 changes: 13 additions & 0 deletions neblinaAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,18 @@ def getFingerGesture(self):
logging.debug("Received FingerGesture.")
return packet.data

def getRSSI(self):
"""
Retrieve RSSI value.
This is a blocking function until an RSSI value is retrieve.
Requires that RSSI streaming is activated, otherwise will hang forever.

:return: RSSI instance.
"""
packet = self.core.waitForPacket(PacketType.RegularResponse, SubSystem.Debug, Commands.Debug.RSSI)
logging.debug("Received RSSI.")
return packet.data

def getIMU(self):
"""
Retrieve IMU (Inertial Motion Unit).
Expand Down Expand Up @@ -410,6 +422,7 @@ def streamPedometer(self, state):
self.core.waitForAck(SubSystem.Motion, Commands.Motion.Pedometer)
logging.debug("Acknowledgment received.")


def streamQuaternion(self, state):
"""
Start/Stop Quaternion streaming.
Expand Down
30 changes: 28 additions & 2 deletions neblinaData.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,10 @@ def __init__(self, dataString):
self.timestamp,\
startStopByte,\
garbage = struct.unpack(Formatting.Data.MotionState, dataString)
self.startStop = (startStopByte == 0)
self.startStop = (startStopByte == 1)

def __str__(self):
return "{0}us: startStop:{1})"\
return "{0}us: startStop:{1}"\
.format(self.timestamp,self.startStop)

def encode(self):
Expand All @@ -482,6 +482,32 @@ def encode(self):
###################################################################################


class RSSIData(object):
""" Neblina RSSI data

Formatting:
- Timestamp
- RSSI in dB
"""
def __init__(self, dataString):
self.timestamp,\
self.RSSI,\
garbage = struct.unpack(Formatting.Data.MotionState, dataString)

def __str__(self):
return "{0}us: RSSI:{1}"\
.format(self.timestamp,self.RSSI-256)

def encode(self):
garbage = ('\000'*11).encode('utf-8')
packetString = struct.pack(Formatting.Data.RSSI, self.timestamp,\
self.RSSI, garbage)
return packetString


###################################################################################


class ExternalForceData(object):
""" Neblina external force data

Expand Down
2 changes: 1 addition & 1 deletion neblinaResponsePacket.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
Commands.Debug.StartUnitTestMotion: BlankData,
Commands.Debug.UnitTestMotionData: UnitTestMotionData,
Commands.Debug.FWVersions: FirmwareVersionsData.decode,
Commands.Debug.RSSI: RSSIData,
6: BlankData,
7: BlankData,
8: BlankData,
Commands.Debug.InterfaceState: DataPortStatusData.decode,
}
Expand Down
19 changes: 19 additions & 0 deletions neblinaUtilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import os
import time
import math

from itertools import zip_longest

Expand Down Expand Up @@ -95,6 +96,8 @@ def saveFlashPlayback(sessionID, packetList):
filepath = [0]*size
filehandle = [0]*size
filesize = [0]*size
filesizeQuatToEuler = 0
filehandleQuatToEuler = [0]

filepath[0] = os.path.join(indexPath, "dump.txt")
filepath[Commands.Motion.MAG] = os.path.join(indexPath, "mag.csv")
Expand All @@ -120,6 +123,20 @@ def saveFlashPlayback(sessionID, packetList):
filesize[packet.header.command] += 1
filehandle[packet.header.command].write("{0}\n".format(packet.data.csvString()))

if packet.header.command==Commands.Motion.Quaternion:
a = packet.data.quaternions[0]/32768;
b = packet.data.quaternions[1]/32768;
c = packet.data.quaternions[2]/32768;
d = packet.data.quaternions[3]/32768;
timestamp = packet.data.timestamp
roll = math.atan2(2*(a*b+c*d), 1-2*(b*b+c*c))
pitch = math.asin(2*(a*c-b*d))
yaw = math.atan2(2*(a*d+b*c), 1-2*(d*d+c*c))
filesizeQuatToEuler += 1
if filesizeQuatToEuler==1:
filehandleQuatToEuler = open(os.path.join(indexPath, "QuatToEuler.csv"), "a")
filehandleQuatToEuler.write("{0},{1},{2},{3}\n".format(timestamp,yaw,pitch,roll))

for i in range(size):
if filepath[i]:
filehandle[i].close()
Expand All @@ -128,3 +145,5 @@ def saveFlashPlayback(sessionID, packetList):
if filesize[i]==0 and filepath[i]:
os.remove(filepath[i])