Skip to content

Commit 7cb0721

Browse files
authored
Add Freebeat Fit bike support via internal UART (#4825)
1 parent e695994 commit 7cb0721

14 files changed

Lines changed: 1242 additions & 40 deletions

src/android/src/Usbserial.java

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import android.hardware.usb.UsbDevice;
99
import android.hardware.usb.UsbDeviceConnection;
1010
import android.hardware.usb.UsbManager;
11+
import android.system.ErrnoException;
12+
import android.system.Os;
13+
import android.system.OsConstants;
14+
import android.system.StructPollfd;
1115
import org.cagnulen.qdomyoszwift.QLog;
1216
import android.app.Service;
1317
import android.media.RingtoneManager;
@@ -39,15 +43,35 @@
3943
public class Usbserial {
4044

4145
static UsbSerialPort port = null;
46+
static java.io.FileDescriptor localSerialFd = null;
47+
static boolean localSerialMode = false;
4248
static byte[] receiveData = new byte[4096];
4349
static int lastReadLen = 0;
50+
static final String[] localSerialCandidates = {
51+
"/dev/ttyS4",
52+
"/dev/ttyS0",
53+
"/dev/ttyS1",
54+
"/dev/ttyS2",
55+
"/dev/ttyS3"
56+
};
4457

4558
public static void open(Context context) {
4659
open(context, 2400); // Default baud rate for Computrainer
4760
}
4861

4962
public static void open(Context context, int baudRate) {
63+
open(context, baudRate, "");
64+
}
65+
66+
public static void open(Context context, int baudRate, String devicePath) {
67+
if (devicePath != null && (devicePath.startsWith("/dev/") || devicePath.equalsIgnoreCase("auto"))) {
68+
openLocalSerial(devicePath, baudRate);
69+
return;
70+
}
71+
5072
QLog.d("QZ","UsbSerial open with baud rate: " + baudRate);
73+
localSerialMode = false;
74+
localSerialFd = null;
5175
// Find all available drivers from attached devices.
5276
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
5377
List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
@@ -110,7 +134,76 @@ public void onReceive(Context context, Intent intent) {
110134
}
111135
}
112136

137+
private static void openLocalSerial(String devicePath, int baudRate) {
138+
QLog.d("QZ","UsbSerial local serial open " + devicePath + " with baud rate: " + baudRate);
139+
port = null;
140+
localSerialFd = null;
141+
localSerialMode = false;
142+
lastReadLen = 0;
143+
144+
if (devicePath.equalsIgnoreCase("auto")) {
145+
for (String candidate : localSerialCandidates) {
146+
if (openLocalSerialPath(candidate, baudRate)) {
147+
return;
148+
}
149+
}
150+
QLog.d("QZ","UsbSerial local serial auto open failed");
151+
return;
152+
}
153+
154+
openLocalSerialPath(devicePath, baudRate);
155+
}
156+
157+
private static boolean openLocalSerialPath(String devicePath, int baudRate) {
158+
try {
159+
configureLocalSerial(devicePath, baudRate);
160+
localSerialFd = Os.open(devicePath, OsConstants.O_RDWR | OsConstants.O_NOCTTY | OsConstants.O_NONBLOCK, 0);
161+
localSerialMode = true;
162+
QLog.d("QZ","UsbSerial local serial opened successfully: " + devicePath);
163+
return true;
164+
}
165+
catch (ErrnoException e) {
166+
QLog.d("QZ","UsbSerial local serial open failed: " + e.getMessage());
167+
return false;
168+
}
169+
}
170+
171+
private static void configureLocalSerial(String devicePath, int baudRate) {
172+
try {
173+
String[] command = {
174+
"stty",
175+
"-F",
176+
devicePath,
177+
String.valueOf(baudRate),
178+
"cs8",
179+
"-cstopb",
180+
"-parenb",
181+
"raw",
182+
"-echo"
183+
};
184+
Process process = Runtime.getRuntime().exec(command);
185+
int rc = process.waitFor();
186+
QLog.d("QZ","UsbSerial local serial stty exit code: " + rc);
187+
}
188+
catch (Exception e) {
189+
QLog.d("QZ","UsbSerial local serial stty failed: " + e.getMessage());
190+
}
191+
}
192+
113193
public static void write (byte[] bytes) {
194+
if(localSerialMode) {
195+
if(localSerialFd == null)
196+
return;
197+
198+
try {
199+
Os.write(localSerialFd, bytes, 0, bytes.length);
200+
}
201+
catch (ErrnoException | IOException e) {
202+
QLog.d("QZ","UsbSerial local serial write failed: " + e.getMessage());
203+
}
204+
return;
205+
}
206+
114207
if(port == null)
115208
return;
116209

@@ -128,6 +221,32 @@ public static int readLen() {
128221
}
129222

130223
public static byte[] read() {
224+
if(localSerialMode) {
225+
if(localSerialFd == null) {
226+
lastReadLen = 0;
227+
return receiveData;
228+
}
229+
230+
try {
231+
StructPollfd pollFd = new StructPollfd();
232+
pollFd.fd = localSerialFd;
233+
pollFd.events = (short)OsConstants.POLLIN;
234+
int ready = Os.poll(new StructPollfd[]{pollFd}, 0);
235+
if(ready <= 0) {
236+
lastReadLen = 0;
237+
return receiveData;
238+
}
239+
240+
lastReadLen = Os.read(localSerialFd, receiveData, 0, receiveData.length);
241+
QLog.d("QZ","UsbSerial local serial reading " + lastReadLen);
242+
}
243+
catch (ErrnoException | IOException e) {
244+
lastReadLen = 0;
245+
QLog.d("QZ","UsbSerial local serial read failed: " + e.getMessage());
246+
}
247+
return receiveData;
248+
}
249+
131250
if(port == null) {
132251
lastReadLen = 0;
133252
return receiveData;

src/devices/bluetooth.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,8 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
627627
settings.value(QZSettings::computrainer_serialport, QZSettings::default_computrainer_serialport).toString();
628628
QString kettlerUsbSerialPort =
629629
settings.value(QZSettings::kettler_usb_serialport, QZSettings::default_kettler_usb_serialport).toString();
630+
QString freebeatSerialPort =
631+
settings.value(QZSettings::freebeat_serialport, QZSettings::default_freebeat_serialport).toString();
630632
QString csaferowerSerialPort = settings.value(QZSettings::csafe_rower, QZSettings::default_csafe_rower).toString();
631633
bool waterrowerUSBEnabled = settings.value(QZSettings::waterrower_usb, QZSettings::default_waterrower_usb).toBool();
632634
QString csafeellipticalSerialPort =
@@ -954,6 +956,19 @@ void bluetooth::deviceDiscovered(const QBluetoothDeviceInfo &device) {
954956
emit searchingStop();
955957
}
956958
this->signalBluetoothDeviceConnected(kettlerUsbBike);
959+
} else if (!freebeatSerialPort.isEmpty() && !freebeatBike) {
960+
this->stopDiscovery();
961+
freebeatBike =
962+
new freebeatbike(noWriteResistance, noHeartService, bikeResistanceOffset, bikeResistanceGain);
963+
emit deviceConnected(b);
964+
connect(freebeatBike, &bluetoothdevice::connectedAndDiscovered, this,
965+
&bluetooth::connectedAndDiscovered);
966+
connect(freebeatBike, &freebeatbike::debug, this, &bluetooth::debug);
967+
freebeatBike->deviceDiscovered(b);
968+
if (this->discoveryAgent && !this->discoveryAgent->isActive()) {
969+
emit searchingStop();
970+
}
971+
this->signalBluetoothDeviceConnected(freebeatBike);
957972
} else if (!csaferowerSerialPort.isEmpty() && !csafeRower) {
958973
this->stopDiscovery();
959974
csafeRower = new csaferower(noWriteResistance, noHeartService, false);
@@ -4220,6 +4235,11 @@ void bluetooth::restart() {
42204235
delete kettlerUsbBike;
42214236
kettlerUsbBike = nullptr;
42224237
}
4238+
if (freebeatBike) {
4239+
4240+
delete freebeatBike;
4241+
freebeatBike = nullptr;
4242+
}
42234243
if (csafeRower) {
42244244

42254245
delete csafeRower;
@@ -4589,6 +4609,8 @@ bluetoothdevice *bluetooth::device() {
45894609
return computrainerBike;
45904610
} else if (kettlerUsbBike) {
45914611
return kettlerUsbBike;
4612+
} else if (freebeatBike) {
4613+
return freebeatBike;
45924614
} else if (csafeRower) {
45934615
return csafeRower;
45944616
} else if (csafeElliptical) {

src/devices/bluetooth.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#ifndef Q_OS_IOS
3737
#include "devices/computrainerbike/computrainerbike.h"
3838
#include "devices/kettlerusbbike/kettlerusbbike.h"
39+
#include "devices/freebeatbike/freebeatbike.h"
3940
#include "devices/csaferower/csaferower.h"
4041
#include "devices/csafeelliptical/csafeelliptical.h"
4142
#endif
@@ -203,6 +204,7 @@ class bluetooth : public QObject, public SignalHandler {
203204
#ifndef Q_OS_IOS
204205
computrainerbike *computrainerBike = nullptr;
205206
kettlerusbbike *kettlerUsbBike = nullptr;
207+
freebeatbike *freebeatBike = nullptr;
206208
csaferower *csafeRower = nullptr;
207209
csafeelliptical *csafeElliptical = nullptr;
208210
#endif

0 commit comments

Comments
 (0)