Skip to content
Snippets Groups Projects
Commit 2743ae2f authored by ttschol's avatar ttschol
Browse files

Added initial bleclient files, READMEs, gitlab-ci files and protocol for TimeFlip

parent aa7d50bd
No related branches found
No related tags found
No related merge requests found
Pipeline #41577 passed
*.idea
*.vscode
image: docker.uibk.ac.at:443/csat2410/skeleton-bleclient
stages:
- Static Analysis
- Test
before_script:
- mvn -version
- java -version
- echo $JAVA_HOME
- echo $MAVEN_HOME
- python3 --version
- pip3 --version
- echo $CI_PROJECT_DIR
# See: https://pmd.github.io/
pmd:
stage: Static Analysis
allow_failure: true
script:
- cd bleclient
- /opt/pmd-bin-6.31.0/bin/run.sh pmd -d src/main/java/ -f text -R rulesets/java/quickstart.xml -cache pmd_cache.txt
bleclient-test:
stage: Test
script:
- mkdir bleclient-test
- cd bleclient
- mvn clean install
- JACOCO_SCORE=$(awk -F, '{ instructions += $4 + $5; covered += $5 } END { printf "%.2f\n", 100*covered/instructions }' target/site/jacoco/jacoco.csv)
- anybadge --label=jacoco-coverage --file=target/site/jacoco/jacoco.svg --value=$JACOCO_SCORE coverage
- mvn dependency:tree -DoutputFile="$CI_PROJECT_DIR/bleclient-test/mvn_dependencies.txt"
- cp -r target/site/jacoco $CI_PROJECT_DIR/bleclient-test/jacoco
- cp -r target/apidocs $CI_PROJECT_DIR/bleclient-test/apidocs
artifacts:
paths:
- bleclient-test
# skeleton-bleclient
# Java und Bluetooth Low Energy auf dem Raspberry Pi
Installation von Bluetooth auf dem Raspberry Pi und Verwendung von Java, um mit einem
bluetooth-fähigen Gerät zu kommunizieren.
## Voraussetzungen
### Grundsätzliche Einstellungen
- `Raspberry Pi OS Lite` auf SD-Karte von Raspberry Pi geflashed
- Anmeldung mit Benutzername `pi` und Passwort `raspberry` und Änderung des Passworts mit `passwd`
- Bluetooth and WLAN sind aktiviert (weder `hard` noch `soft`-blocked)
sudo rfkill list all
- Aktivierung von z.B. WiFi mit (sollte WiFi der erste Eintrag sein):
sudo rfkill unblock 0
### Verwendung von WiFI
- Setzen des WiFi-Landes mit:
sudo raspi-config nonint do_wifi_country AT
sudo raspi-config nonint get_wifi_country
- Siehe: [Setting up a wireless LAN via the command line](https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md)
- Überprüfung, ob man mit dem richtigen Netzwerk verbunden ist:
iw wlan0 link
- Überprüfung, ob Internetverbindung besteht mit:
ping google.com
### Verbindung mit Raspberry Pi
- SSH wurde aktiviert (Ausgabe soll 0 sein):
sudo raspi-config nonint get_ssh
- Aktivierung von SSH mit:
sudo raspi-config nonint do_ssh 0
- Hostname des Raspberry Pi ist bekannt:
hostname -I
### Optional: Private/Public Key Authentifizierung
Siehe: [Passwordless SSH access](https://www.raspberrypi.org/documentation/remote-access/ssh/passwordless.md)
So vermeidet man beim Verbinden zum Raspberry Pi jedes Mal das Passwort einzugeben.
## 1) Installation
Mit Raspberry Pi verbinden:
ssh pi@<RASPBERRY_IP_ADDRESS>
### a) Initiale Packages
Auf den neuesten Stand bringen:
sudo apt update
sudo apt upgrade
Essentielle Tools installieren:
sudo apt install git
sudo apt install cmake
### b) Maven und JDK
Installation der JDK 1.8:
sudo apt install openjdk-8-jdk
Sicherstellen, dass tatsächlich JDK 1.8 verwendet wird:
sudo update-alternatives --config java
Version überprüfen:
java -version
Sicherstellen, dass sich in `usr/lib/jvm` nun `java-8-openjdk-armhf` befindet:
sudo find / -name "java"
Editieren von `bashrc`:
sudo nano ~/.bashrc
Es soll die Zeile `export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-armhf/` am Ende hinzugefügt werden und gespeichert werden.
Nun müssen wir das Terminal neu laden, um zu überprüfen, ob die Änderungen wirksam waren – notwendig für nächste Schritte:
bash
echo $JAVA_HOME
Maven installieren:
sudo apt install maven
### c) Installation von BlueZ 5.47
Installation der Build-Tools:
sudo apt install libglib2.0-dev libdbus-1-dev libudev-dev libical-dev libreadline-dev
Download des BlueZ Source-Codes (in geeignetem Directory):
wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.47.tar.xz
Das tar Archiv extrahieren und in den Ordner gehen:
tar -xf bluez-5.47.tar.xz && cd bluez-5.47
Konfigurieren des BlueZ Projekts:
./configure --prefix=/usr --mandir=/usr/share/man --sysconfdir=/etc --localstatedir=/var
BlueZ builden:
make
sudo make install
#### BlueZ Installation überprüfen
Der BlueZ Start-up Service soll nun auf das neu gebuildete BlueZ zeigen:
cat /lib/systemd/system/bluetooth.service
Dort sollte man die Zeile `ExecStart=/usr/libexec/bluetooth/bluetoothd` vorfinden.
Ausgabe der BlueZ Version – sollte nun `5.47` sein:
/usr/libexec/bluetooth/bluetoothd --version
#### Erlaubnis für BlueZ hinzufügen
Damit BlueZ Zugriff auf die Bluetooth-Gruppe hat, müssen wir eine eigene Erlaubnis hinzufügen. Dafür
editiert man die BlueZ DBus Konfiguration:
sudo nano /etc/dbus-1/system.d/bluetooth.conf
Anschließend fügt man nur die Policy für die Gruppe `bluetooth` hinzu:
```shell
<busconfig>
<policy user="root">
...
</policy>
<policy group="bluetooth">
<allow send_destination="org.bluez"/>
</policy>
...
</busconfig>
```
#### Zusätzliche Konfiguration und neu starten
OpenHab User zur Bluetooth-Gruppe hinzufügen (wir benötigten zwar OpenHab nicht, aber zur Vollständigkeit):
sudo adduser --system --no-create-home --group --disabled-login openhab
sudo usermod -a -G bluetooth openhab
Service Definitionen neu laden:
sudo systemctl daemon-reload
BlueZ neu starten:
sudo systemctl restart bluetooth
Überprüfen, ob Bluetooth-Service aktiv ist, die richtige Version läuft (`5.47`) und es keine Fehler gibt:
sudo systemctl status bluetooth
### d) Installation von tinyb
Installation der Abhängigkeiten von `tinyb`:
sudo apt install graphviz
sudo apt install doxygen
Klonen von tinyb (an geeigneter Stelle) und in den Ordner gehen:
git clone https://github.com/intel-iot-devkit/tinyb.git && cd tinyb
Ordner `build` erstellen und hineingehen:
mkdir build
cd build
Builden von `tinyb` mit `cmake` (`-E` steht für experimental und stellt sicher, dass `JAVA_HOME` verwendet wird, `cmake ..` generiert das Makefile im aktuellen Verzeichnis basierend auf `CMakeLists.txt` im parent-Verzeichnis, der Prefix `/usr/` stellt sicher, dass sich die native Libraries `libjavatinyb.so` und `libtinyb.so` im Java Library Path befinden):
sudo -E cmake -DBUILDJAVA=ON -DCMAKE_INSTALL_PREFIX=/usr ..
Ausführen von `make` und `make install`:
sudo make
sudo make install
## 2) Ausführen
Die Ausführung ist eher umständlich, da wir `tinyb.jar` nicht nur zur Compile-Zeit, sondern auch dynamisch zur Laufzeit laden müssen. Für diese Bluetooth-Library ist dies leider notwendig. Siehe auch: [Java* for Bluetooth® Low Energy Applications](https://web.archive.org/web/20190414051809/https:/software.intel.com/en-us/java-for-bluetooth-le-apps)
Sollten alle Befehle erfolgreich sein, dann können wir das Beispiel-Maven-Programm `bleclient` ausführen.
Dafür gehen wir in das Verzeichnis `bleclient` und führen das Programm basierend auf dem dort liegendem [README.md](bleclient/README.md) aus.
## 3) Optional: Installation z.B. auf Ubuntu 18.04/20.04
Führen Sie die Schritte `1a` und `1b` aus:
- Für `1b` ändert sich, dass wir statt `java-8-openjdk-armhf` die `java-8-openjdk-armhf` verwenden
Führen Sie Schritt `1c` aus:
- Sollte es bei `make` zum Fehler `error: ‘SIOCGSTAMP’ undeclared (first use in this function)` kommen, dann includen Sie `#include <linux/sockios.h>` in den nötigen Dateien z.B. `bluez-5.47/tools/rctest.c` und `bluez-5.47/tools/l2test.c`
- Dieses Problem scheint ab Ubuntu 20.04 aufzutreten. In Ubuntu 18.04 kann es sein, dass dieses Problem nicht auftritt.
Führen Sie die restlichen Schritte aus. Fertig.
Hinweis: Passen Sie beim Ausführen von Updaten/Upgrades auf. Es kann sein, dass dann Bluetooth upgegradet wird. Dies wollen wir vermeiden. Wir wollen maximal Version `5.47` verwenden.
## Fragen und Antworten
### Was mache ich, wenn irgendetwas in der Installation schief läuft?
- Sicherstellen, dass alle Befehle richtig ausgeführt wurden.
- Generell kann es helfen, noch einmal alle Befehle von vorne auszuführen.
### Was wenn der der Java BluetoothManager eine Exception wirf (z.B. NullPointerException)?
- Sicherstellen, dass das gebuildete `tinyb.jar`zur Verfügung steht
- `tinyb.jar` muss zur Laufzeit als Parameter übergeben werden z.B. `–cp target/<JAR_FILE>:./lib/tinyb.jar:./target/dependencies/*`
### Was wenn ich beim Ausführen ein Problem mit der nativen API Version bekomme?
- Wahrscheinlich wurde die `tinyb.jar` nicht korrekt zur Laufzeit geladen
- Sicherstellen, dass sie richtig geladen wird z.B. `–cp target/<JAR_FILE>:./lib/tinyb.jar:./target/dependencies/*`
### Wieso kann ich das Programm nicht mit -jar ausführen?
- Entweder man verwendet `-cp` für Classpath oder `-jar`, aber nicht beides
- Für `-jar` benötigt man weiters auch noch eine Manifest-Datei (diese müsste man in Bezug auf das `tinyb.jar` auch konfigurieren)
### Wieso können die native Libraries nicht gefunden werden?
- Sollte grundsätzlich kein Problem sein, wenn man der Anleitung gefolgt hat
- Durch `-DCMAKE_INSTALL_PREFIX=/usr` sollten diese korrekt gesetzt sein
- Siehe auch bei Installation `tinyb/build/install_manifest.txt`
- Mit `java -cp` korrekt ausführen und nicht mit `-jar`
- Wenn es immer noch nicht möglich ist, dann muss man wirklich sicherstellen, dass sich `libjavatinyb.so` und `libtinyb.so` tatsächlich im Java Library Path befinden.
- Prinzipiell könnte man diese Dateien `tinyb/build/java/jni/libjavatinyb.so` und `tinyb/build/src/libtinby.so` auch direkt nach `/usr/lib` kopieren
### Wieso bekomme ich eine BluetoothException mit Timeout was reached?
- Sicherstellen, dass sich der TimeFlip tatsächlich in Reichweite befindet
- Sicherstellen, dass die Batterie richtig im TimeFlip ist
### Wieso bekomme ich Exceptions beim Auslesen von Charakteristiken?
- Sicherstellen, dass das Passwort richtig vorher an den TimeFlip geschrieben wurde, dann sollte man alle Charakteristiken auslesen können
- Es kann sein, dass man versehentlich das Passwort über die TimeFlip App gesetzt hat. Wenn man die Batterie kurz herausnimmt und noch einmal einsetzt, dann sollte das Passwort zurückgesetzt sein.
### Was mache ich, wenn das bleclient Programm keinen TimeFlip ausgibt?
- Sicherstellen, dass der TimeFlip eingeschalten ist (z.B. Überprüfung mit Handy-App wie `nRF Connect`)
- Am besten notiert man sich die UUID des TimeFlips
- Sicherstellen, dass der TimeFlip mit keinem anderen Gerät gekoppelt ist
- Entkoppeln von Bluetooth, TimeFlip App, etc.
- Theoretisch ist es möglich, dass aus irgendeinem Grund das Passwort auf dem TimeFlip z.B. mit der TimeFlip App gesetzt wurde. In diesem Fall sollte man die Batterie kurz entfernen und wieder reinstecken
## Links
* [Raspberry Pi Bluetooth Manager TinyB - Building bluez 5.47 from sources](https://github.com/sputnikdev/bluetooth-manager-tinyb)
* [TinyB Bluetooth LE Library](https://github.com/intel-iot-devkit/tinyb)
* [Raspberry Pi Installation of TinyB (Note: do not install bluez)](http://www.martinnaughton.com/2017/07/install-intel-tinyb-java-bluetooth.html)
* [Java for Bluetooth LE applications](https://www.codeproject.com/Articles/1086361/Java-for-Bluetooth-LE-applications)
* [TinyB Java examples (HelloTinyB.java, etc.)](https://github.com/intel-iot-devkit/tinyb/tree/master/examples/java)
* [Non-interactive raspi-config interface](https://github.com/raspberrypi-ui/rc_gui/blob/master/src/rc_gui.c#L23-L70)
/target/
.idea/
.settings/
.classpath
.project
*.iml
# Bluetooth Low Energy auf dem Raspberry Pi
### Voraussetzungen
* Die Installation von `tinyb` war erfolgreich
* Ein bluetooth-fähiges Gerät befindet sich in der Nähe
* Im Ordner `lib` befindet sich die gebuildete Datei `tinyb.jar` von der Installation (`tinyb/build/java/tinyb.jar`)
## Builden
Zuerst müssen wir das JAR builden, damit wir `bleclient.jar` im `target`-Verzeichnis bekommen.
Zusätzlich werden alle Dependencies im `pom.xml` in `target/dependencies` abgelegt. Wir erreichen dies durch die Plugins
`maven-install-plugin` und `maven-dependency-plugin`.
### Builden
mvn clean package -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
### Builden mit Tests und Javadoc
mvn clean install
## Ausführen
Ausführung des Programms `bleclient.jar` mit `tinyb.jar` und den Dependencies in `target/dependencies/*`.
Auch Angabe des `fully-qualified name` der auszuführenden Java-Klasse:
sudo java -cp target/bleclient.jar:./lib/tinyb.jar:./target/dependencies/* at.qe.skeleton.bleclient.Main
File added
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>at.qe.skeleton</groupId>
<artifactId>bleclient</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- TinyB -->
<dependency>
<groupId>org.sputnikdev</groupId>
<artifactId>bluetooth-manager-tinyb</artifactId>
<version>1.3.3</version>
</dependency>
<!-- Tests -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- Remove version from JAR file to keep consistent to keep execution consistent even when version changes -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Install TinyB library into local maven repository -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>install-tinyb</id>
<phase>package</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<file>lib/tinyb.jar</file>
<groupId>intel-iot-devkit</groupId>
<artifactId>tinyb</artifactId>
<version>0.6.0</version>
<packaging>jar</packaging>
</configuration>
</execution>
</executions>
</plugin>
<!-- Copy all dependencies into separate directory -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dependencies</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
<!-- Enable jacoco analysis -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<forkMode>once</forkMode>
<argLine>
@{coverageAgent}
</argLine>
</configuration>
</plugin>
<!-- Test and generate coverage with Jacoco -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<configuration>
<propertyName>coverageAgent</propertyName>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Generate javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package at.qe.skeleton.bleclient;
import tinyb.BluetoothDevice;
import tinyb.BluetoothException;
import tinyb.BluetoothManager;
import java.util.List;
// TODO: use logging instead of System.out/System.err
/**
* Entry point for program to search for Bluetooth devices and communicate with them
*/
public final class Main {
private Main() {
}
/**
* This program should connect to TimeFlip devices and read the facet characteristic exposed by the devices
* over Bluetooth Low Energy.
*
* @param args the program arguments
* @see <a href="https://github.com/DI-GROUP/TimeFlip.Docs/blob/master/Hardware/BLE_device_commutication_protocol_v3.0_en.md" target="_top">BLE device communication protocol v3.0</a>
*/
public static void main(String[] args) {
BluetoothManager manager = BluetoothManager.getBluetoothManager();
final String findDeviceName = "TimeFlip";
final boolean discoveryStarted = manager.startDiscovery();
System.out.println("The discovery started: " + (discoveryStarted ? "true" : "false"));
try {
manager.stopDiscovery();
} catch (BluetoothException e) {
System.err.println("Discovery could not be stopped.");
}
System.out.println("All found devices:");
manager.getDevices().forEach(d -> System.out.println(d.getAddress() + " - " + d.getName() + " (" + d.getRSSI() + ")"));
List<BluetoothDevice> filteredDevices = TinybUtil.getFilteredDevices(manager, findDeviceName);
if (filteredDevices.isEmpty()) {
System.err.println("No " + findDeviceName + " devices found during discovery.");
System.exit(-1);
}
System.out.println("Found " + filteredDevices.size() + " " + findDeviceName + " device(s).");
for (BluetoothDevice device : filteredDevices) {
System.out.println("Found " + findDeviceName + " device with address " + device.getAddress() + " and RSSI " +
device.getRSSI());
if (device.connect()) {
System.out.println("Connection established");
// TODO: read from device
device.disconnect();
} else {
System.out.println("Connection not established - trying next one");
}
}
}
}
package at.qe.skeleton.bleclient;
import com.google.common.base.Preconditions;
import tinyb.BluetoothDevice;
import tinyb.BluetoothManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
// TODO: use logging instead of System.out
/**
* An utility class for bluetooth low energy devices
*/
public final class TinybUtil {
public static final int MIN_RSSI_ALLOWED = -80;
private TinybUtil() {
}
/**
* Filter Bluetooth devices based on searchDevice
* <p>
* If the signal strength is too low then we should not connect to the device. The communication may
* be unstable.
*
* @param manager the Bluetooth manager from which we get the Bluetooth devices
* @param searchDevice the devices we want to search for
* @return filtered bluetooth devices
*/
public static List<BluetoothDevice> getFilteredDevices(final BluetoothManager manager, final String searchDevice) {
Preconditions.checkNotNull(manager, "Precondition violation - argument 'manager' must not be NULL!");
Preconditions.checkNotNull(searchDevice, "Precondition violation - argument 'searchDevice' must not be NULL!");
List<BluetoothDevice> devices = new ArrayList<>();
for (BluetoothDevice device : manager.getDevices()) {
if (device.getName().toLowerCase(Locale.ROOT).contains(searchDevice)) {
final int rssi = device.getRSSI();
if (rssi == 0) {
System.out.println(searchDevice + " with address " + device.getAddress() + " has no signal.");
} else if (rssi < MIN_RSSI_ALLOWED) {
System.out.println(searchDevice + " with address" + device.getAddress() + " has a very low signal ("
+ rssi + ")");
} else {
devices.add(device);
}
}
}
return devices;
}
}
package at.qe.skeleton.bleclient;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import tinyb.BluetoothDevice;
import tinyb.BluetoothManager;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.when;
public class TinybUtilTest {
@Test
public void testGetFilteredDevices() {
BluetoothManager bluetoothManager = Mockito.mock(BluetoothManager.class);
List<BluetoothDevice> mockDevices = utilMockDevices();
when(bluetoothManager.getDevices()).thenReturn(mockDevices);
List<BluetoothDevice> devices = TinybUtil.getFilteredDevices(bluetoothManager, "timeflip");
Assert.assertNotNull(devices);
Assert.assertEquals(2, devices.size());
for (BluetoothDevice device : devices) {
if (device.getName().equals("timeflip")) {
Assert.assertEquals("A8:A8:9F:B9:28:AD", device.getAddress());
Assert.assertEquals(-20, device.getRSSI());
} else if (device.getName().equals("TimeFlip2")) {
Assert.assertEquals("B8:A8:9F:B9:28:AD", device.getAddress());
Assert.assertEquals(-44, device.getRSSI());
} else {
Assert.fail("Unexpected device " + device.getName());
}
}
}
private List<BluetoothDevice> utilMockDevices() {
BluetoothDevice mockTimeFlip1 = Mockito.mock(BluetoothDevice.class);
when(mockTimeFlip1.getName()).thenReturn("timeflip");
when(mockTimeFlip1.getAddress()).thenReturn("A8:A8:9F:B9:28:AD");
when(mockTimeFlip1.getRSSI()).thenReturn((short) -20);
BluetoothDevice mockTimeFlip2 = Mockito.mock(BluetoothDevice.class);
when(mockTimeFlip2.getName()).thenReturn("TimeFlip2");
when(mockTimeFlip2.getAddress()).thenReturn("B8:A8:9F:B9:28:AD");
when(mockTimeFlip2.getRSSI()).thenReturn((short) -44);
BluetoothDevice mockTimeFlip3 = Mockito.mock(BluetoothDevice.class);
when(mockTimeFlip3.getName()).thenReturn("timeflip3");
when(mockTimeFlip3.getAddress()).thenReturn("C8:A8:9F:B9:28:AD");
when(mockTimeFlip3.getRSSI()).thenReturn((short) -91);
BluetoothDevice mockHeadphones = Mockito.mock(BluetoothDevice.class);
when(mockHeadphones.getName()).thenReturn("headphones");
when(mockHeadphones.getAddress()).thenReturn("D8:A8:9F:B9:28:AD");
when(mockHeadphones.getRSSI()).thenReturn((short) -35);
List<BluetoothDevice> mockDevices = new ArrayList<>();
mockDevices.add(mockTimeFlip1);
mockDevices.add(mockTimeFlip2);
mockDevices.add(mockTimeFlip3);
mockDevices.add(mockHeadphones);
return mockDevices;
}
}
FROM debian:buster-slim
ENV TZ=UTC
ENV DEBIAN_FRONTEND=noninteractive
ARG MAVEN_VERSION=3.6.1
ARG JDK_VERSION=8
ARG PMD_VERSION=6.31.0
WORKDIR /opt
# Install basic dependencies and utility packages
RUN \
apt-get update && apt-get -y --no-install-recommends install \
apt-utils \
ca-certificates \
apt-transport-https \
git \
zip \
unzip \
curl \
make \
wget
# Install Python 3 (not essential, but quite useful)
RUN \
apt-get update && apt-get -y --no-install-recommends install \
python3-software-properties \
python3-pip
# Install Python package(s)
RUN pip3 install anybadge
# Install OpenJDK (See: https://adoptopenjdk.net/)
RUN \
wget -O jdk-${JDK_VERSION}.tar.gz https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u282-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u282b08.tar.gz \
&& mkdir jdk-${JDK_VERSION} && tar zxvf jdk-${JDK_VERSION}.tar.gz -C jdk-${JDK_VERSION} --strip-components 1 \
&& mv jdk-${JDK_VERSION}/ /usr/local/ \
&& rm jdk-${JDK_VERSION}.tar.gz
# Install maven
RUN \
wget --no-verbose -O /tmp/apache-maven-${MAVEN_VERSION}.tar.gz http://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
&& tar xzf /tmp/apache-maven-${MAVEN_VERSION}.tar.gz -C /opt/ \
&& ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven \
&& ln -s /opt/maven/bin/mvn /usr/local/bin \
&& rm -f /tmp/apache-maven-${MAVEN_VERSION}.tar.gz
# Set environment variables
ENV MAVEN_HOME /opt/maven
ENV JAVA_HOME=/usr/local/jdk-${JDK_VERSION}
ENV PATH=$PATH:$JAVA_HOME/bin
# Install Code Quality tool(s)
RUN \
wget https://github.com/pmd/pmd/releases/download/pmd_releases%2F${PMD_VERSION}/pmd-bin-${PMD_VERSION}.zip && \
unzip pmd-bin-${PMD_VERSION}.zip && rm pmd-bin-${PMD_VERSION}.zip
RUN apt-get clean
TARGET_REPOSITORY=csat2410/skeleton-bleclient
# Login and build docker image
docker-build:
docker login docker.uibk.ac.at:443
docker build -t docker.uibk.ac.at:443/${TARGET_REPOSITORY} .
# Upload docker images to Gitlab registry
docker-push:
docker push docker.uibk.ac.at:443/${TARGET_REPOSITORY}
# Run locally
docker-run:
docker run -it docker.uibk.ac.at:443/${TARGET_REPOSITORY} bash
# DATA TRANSFER PROTOCOL
From: https://github.com/DI-GROUP/TimeFlip.Docs/blob/master/Hardware/BLE_device_commutication_protocol_v3.0_en.md
All values are stored in TimeFlip on-board RAM memory and are reset to default when the battery is taken out or replaced.
TimeFlip device uses Bluetooth Low Energy (BLE) protocol. Services and specifications are listed in the table:
| Service name / UUID | Description |
|:----------------------------|:-------------------------------------|
|Device Information / 0x180A | Contains device specific info |
|Battery Service / 0x180F | Battery charge |
|TimeFlip / F1196F50-71A4-11E6-BDF4-0800200C9A66 | Time and facets |
#### Device Information Service / 0x180A
| Characteristic's name | Size, bytes | Properties, R/W/N |
| :----------------- |:------------:|:---------------:|
| Firmware Revision String / 0x2A26| 6 | R |
R – reading W – writing N – notification
#### Battery Service / 0x180F
| Characteristic's name | Size, bytes | Properties, R/W/N |
| :----------------- |:------------:|:---------------:|
| Battery Level / 0x2A19| 1 | R,N |
R – reading W – writing N – notification
#### TimeFlip Service / F1196F50-71A4-11E6-BDF4-0800200C9A66
| Characteristic's name | Size, bytes | Properties, R/W/N |
| :----------------- |:------------:|:---------------:|
| Accelerometer data / F1196F51-71A4-11E6-BDF4-0800200C9A66 | 6 | R |
| Facets / F1196F52-71A4-11E6-BDF4-0800200C9A66 | 1 | R, N |
| Command result output / F1196F53-71A4-11E6-BDF4-0800200C9A66 | 21 | R |
| Command / F1196F54-71A4-11E6-BDF4-0800200C9A66 | 21 | R, W |
| Double tap definition / F1196F55-71A4-11E6-BDF4-0800200C9A66 | 1 | N |
| Calibration version / F1196F56-71A4-11E6-BDF4-0800200C9A66 | 4 | R, W |
| Password / F1196F57-71A4-11E6-BDF4-0800200C9A66 | 6 | W |
R – reading W – writing N – notification
### Firmware Revision String / 0x2A26
Contains stock firmware version.
0x544676332E31 = “TFv3.1”
### Battery Level / 0x2A19
Battery charge
### Accelerometer values characteristic / F1196F51-71A4-11E6-BDF4-0800200C9A66
_big-endian_ 0xXXYYZZ - (x,y,z) acceleration vector.
### Facets characteristic / F1196F52-71A4-11E6-BDF4-0800200C9A66
ID value of notified facet (0..47)
### Command result output characteristic / F1196F53-71A4-11E6-BDF4-0800200C9A66
Output of command result, for example history read request "0x01" returns result in "Command result output characteristic"
History is read out in packages of 21 bytes.
History block contains 3 bytes. Example:
| Byte number | 0 | | | | | | | | 1 | | | | | | | | 2 | | | | | | | |
|:-----------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| Bit number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| Time | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | X | | | | | | |
| Facet | | | | | | | | | | | | | | | | | | | X | X | X | X | X | X |
Maximum amount of time stored in history – 262144 seconds or ~ 3,03 days. New interval created on exceed.
Maximum facets number – 48
One package contains 7 history blocks.
#### History read-out protocol
After history read-out request is sent, the first package of 7 history blocks will be written in the characteristics. History blocks will be updated along with history reading-out until the very last one. Example:
| Byte number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
|--------------|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| History block | 0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 3 | 3 | 3 | 4 | 4 | 4 | 5 | 5 | 5 | 6 | 6 | 6 |
If the last history package is not full, missing values will be filled with zeros.
Penultimate package will contain the information on the number of sent history packages. Example:
| Byte number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
|:--------------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| History block count | X | X | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
The last package contains zeros, it thus communicates the end of history data transmission.
| Byte number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
|:--------------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| History block | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
### Command characteristic / F1196F54-71A4-11E6-BDF4-0800200C9A66
Executed command is shown in characteristics as 0xXX 0x02 where 0хХХ – is command message.
Unexecuted command is shown in characteristics as 0xXX 0x01 where 0хХХ – is command message.
|Type | Access | Size, bytes | Description |
|---------|--------|----|----------|
| Command | Write | 21 | 0xXXYY..YY, where XX - command, YY - data. response is in “**command result output**” |
| | Read | 2 | 0xXXYY, where XX - command YY - error code (2 - OK, 1 - ERROR)|
#### Commands:
0x01 – history read out request
0x02 – delete history
0x03 – calibration reset.
Resets values for TimeFlip facets and resets characteristics Calibration version (UUID: F1196F56-71A4-11E6-BDF4-0800200C9A66) to zero.
0x04 0x01 – lock function* on
0x04 0x02 – lock function off
0x05 0xXX 0xXX – auto-pause** (off by default).
0xXX 0xXX – timer value in minutes after which the auto-pause is activated (0 = auto-pause off)
Auto-pause timer is automatically reset when TimeFlip is flipped to another facet or on successful password write.
0x06 0x01 - pause*** function on
0x06 0x02 – pause function off
0x10 – status request (response in “command result outup” shown as 0xXXYYZZZZ)
0xXX – lock function (0x01 – on, 0x02 – off)
0xYY - pause (0x01 – on, 0x02 – off)
0xZZ 0xZZ – auto-pause timer value (in minutes)
0x15 0xXX 0xZZ … 0xZZ - write name
0xXX – number of symbols in name
0xZZ … 0xZZ - name (19 symbols MAX. ASCII coding)
0х30 0xZZ … 0xZZ – set new password
0xZZ … 0xZZ – password set, length is 6 symbols
0x50 0xAA – Delete current firmware and reboot to firmware loader.
\* lock function – locks TimeFlip to count time on current active facet and blocks the device from switching facets when TimeFlip is turned or flipped.
\*\* Autopause function – automatically sets time count on pause after pre-set period of time (timer value).
\*\*\* Pause – time count is set on pause, but the facets continue to be notified (user can turn/flip TimeFlip and assign new tasks to facets). This appear in history as facet with ID 63 (0b111111).
### Double tap characteristic / F1196F55-71A4-11E6-BDF4-0800200C9A66
Reserved for future use
### Calibration version characteristic / F1196F56-71A4-11E6-BDF4-0800200C9A66
| Type | Access | Size, byte | Description |
|-----|----------------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| Command | Write, Read | 4 | Saves the value recorded between connections. The value is reset to ”0” when the battery is pulled out or “reset calibration” command is executed (0x03)”. |
This characteristics is used to check whether TimeFlip facets calibration corresponds to facets calibration in the mobile app. The check is performed by comparing value read out from this characteristics and the one stored in the mobile app. When the facets are first time assigned in TimeFlip, an arbitrary number is written to present characteristics and in the same time in the mobile app.
### Password characteristic / F1196F57-71A4-11E6-BDF4-0800200C9A66
| Type | Access | Size, byte | Description |
|--------|--------|--------------|---|
| Password | Write | 6 | Requires password to be written in it to allow TimeFlip operation. |
If the password is not provided, or provided incorrect, TimeFlip service's characteristics will return nothing on reading. Note that:
- TimeFlip requires password input after re-connect to authorize connected device
- password is reset to default every time the battery is taken out or replaced
- default password is ASCII "000000" or {0x30, 0x30, 0x30, 0x30, 0x30, 0x30}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment