1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-01 17:26:18 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/VescDeviceSupport.java
2022-05-14 04:47:22 +02:00

344 lines
13 KiB
Java

/* Copyright (C) 2021 Daniel Dakhno
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.vesc;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.devices.vesc.VescCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
public class VescDeviceSupport extends VescBaseDeviceSupport {
BluetoothGattCharacteristic serialWriteCharacteristic, serialReadCharacteristic;
public static final String COMMAND_SET_RPM = "nodomain.freeyourgadget.gadgetbridge.vesc.command.SET_RPM";
public static final String COMMAND_SET_CURRENT = "nodomain.freeyourgadget.gadgetbridge.vesc.command.SET_CURRENT";
public static final String COMMAND_SET_BREAK_CURRENT = "nodomain.freeyourgadget.gadgetbridge.vesc.command.SET_BREAK_CURRENT";
public static final String COMMAND_GET_VALUES = "nodomain.freeyourgadget.gadgetbridge.vesc.command.GET_VALUES";
public static final String EXTRA_RPM = "EXTRA_RPM";
public static final String EXTRA_CURRENT = "EXTRA_CURRENT";
public static final String EXTRA_VOLTAGE = "EXTRA_VOLTAGE";
public static final String ACTION_GOT_VALUES = "nodomain.freeyourgadget.gadgetbridge.vesc.action.GOT_VALUES";
private Logger logger = LoggerFactory.getLogger(getClass());
private DeviceType deviceType;
private ByteBuffer responseBuffer = ByteBuffer.allocate(100);
public VescDeviceSupport(DeviceType type) {
super();
responseBuffer.order(ByteOrder.BIG_ENDIAN);
deviceType = type;
if (type == DeviceType.VESC_NRF) {
addSupportedService(UUID.fromString(VescCoordinator.UUID_SERVICE_SERIAL_NRF));
} else if (type == DeviceType.VESC_HM10) {
addSupportedService(UUID.fromString(VescCoordinator.UUID_SERVICE_SERIAL_HM10));
}
}
@Override
public void onFetchRecordedData(int dataTypes) {
super.onFetchRecordedData(dataTypes);
getValues();
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
logger.debug("initializing device");
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
initBroadcast();
if (deviceType == DeviceType.VESC_NRF) {
this.serialWriteCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_TX_NRF));
this.serialReadCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_RX_NRF));
} else if (deviceType == DeviceType.VESC_HM10) {
this.serialWriteCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_TX_HM10));
this.serialReadCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_RX_HM10));
}
builder.notify(this.serialReadCharacteristic, true);
return builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
}
@Override
public boolean onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
getValues();
return true;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
handleRxCharacteristic(characteristic);
return true;
}
private void handleRxCharacteristic(BluetoothGattCharacteristic characteristic) {
if (characteristic != serialReadCharacteristic) return;
responseBuffer.put(characteristic.getValue());
short length = 0;
int oldPosition = responseBuffer.position();
responseBuffer.position(0);
byte lengthType = responseBuffer.get();
if (lengthType == 2) {
length = responseBuffer.get();
} else if (lengthType == 3) {
length = responseBuffer.getShort();
} else {
return;
}
if (length == oldPosition - 5) {
// whole message transmitted
responseBuffer.position(oldPosition);
handleResponseBuffer(responseBuffer);
oldPosition = 0;
}
responseBuffer.position(oldPosition);
}
private void handleResponseBuffer(ByteBuffer responseBuffer) {
int bufferLength = responseBuffer.position();
int payloadStartPosition = responseBuffer.get(0);
byte[] payload = new byte[bufferLength - 3 - payloadStartPosition];
System.arraycopy(responseBuffer.array(), payloadStartPosition, payload, 0, payload.length);
int actualCrc = CheckSums.getCRC16(payload, 0);
int expectedCrc = responseBuffer.getShort(bufferLength - 3);
byte responseType = payload[0];
if (responseType == 0x04) {
handleResponseValues(responseBuffer);
}
}
private void handleResponseValues(ByteBuffer valueBuffer) {
valueBuffer.position(3);
float temp_mos = buffer_get_float16(valueBuffer, 1e1);
float temp_motor = buffer_get_float16(valueBuffer, 1e1);
float current_motor = buffer_get_float32(valueBuffer, 1e2);
float current_in = buffer_get_float32(valueBuffer, 1e2);
float id = buffer_get_float32(valueBuffer, 1e2);
float iq = buffer_get_float32(valueBuffer, 1e2);
float duty_now = buffer_get_float16(valueBuffer, 1e3);
float rpm = buffer_get_float32(valueBuffer, 1e0);
float v_in = buffer_get_float16(valueBuffer, 1e1);
float amp_hours = buffer_get_float32(valueBuffer, 1e4);
float amp_hours_charged = buffer_get_float32(valueBuffer, 1e4);
float watt_hours = buffer_get_float32(valueBuffer, 1e4);
float watt_hours_charged = buffer_get_float32(valueBuffer, 1e4);
float tachometer = buffer_get_int32(valueBuffer);
float tachometer_abs = buffer_get_int32(valueBuffer);
handleBatteryVoltage(v_in);
Intent intent = new Intent(ACTION_GOT_VALUES);
intent.putExtra(EXTRA_VOLTAGE, v_in);
}
void handleBatteryVoltage(float voltage){
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
float minimalVoltage = Float.parseFloat(prefs.getString(DeviceSettingsPreferenceConst.PREF_VESC_MINIMUM_VOLTAGE, "-1"));
float maximalVoltage = Float.parseFloat(prefs.getString(DeviceSettingsPreferenceConst.PREF_VESC_MAXIMUM_VOLTAGE, "-1"));
if(minimalVoltage == -1){
return;
}
if(maximalVoltage == -1){
return;
}
float voltageAboveMinimum = voltage - minimalVoltage;
float voltageRange = maximalVoltage - minimalVoltage;
float fullness = voltageAboveMinimum / voltageRange;
int fullnessPercent = (int)(fullness * 100);
fullnessPercent = Math.max(fullnessPercent, 0);
fullnessPercent = Math.min(fullnessPercent, 100);
getDevice().setBatteryLevel(fullnessPercent);
getDevice().setBatteryVoltage(voltage);
getDevice().sendDeviceUpdateIntent(getContext());
}
float buffer_get_float16(ByteBuffer buffer, double scale){
return (float) (buffer.getShort() / scale);
}
float buffer_get_float32(ByteBuffer buffer, double scale){
return (float) (buffer.getInt() / scale);
}
int buffer_get_int32(ByteBuffer buffer){
return buffer.getInt();
}
@Override
public void onTestNewFunction() {
getValues();
// getDecodedADC();
}
private void getDecodedADC() {
buildAndQueryPacket(CommandType.COMM_GET_DECODED_ADC);
}
private void initBroadcast() {
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
IntentFilter filter = new IntentFilter();
filter.addAction(COMMAND_SET_RPM);
filter.addAction(COMMAND_SET_CURRENT);
filter.addAction(COMMAND_SET_BREAK_CURRENT);
filter.addAction(COMMAND_GET_VALUES);
broadcastManager.registerReceiver(commandReceiver, filter);
}
@Override
public void dispose() {
super.dispose();
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
}
BroadcastReceiver commandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(COMMAND_SET_RPM)) {
VescDeviceSupport.this.setRPM(
intent.getIntExtra(EXTRA_RPM, 0)
);
} else if (intent.getAction().equals(COMMAND_SET_BREAK_CURRENT)) {
VescDeviceSupport.this.setBreakCurrent(
intent.getIntExtra(EXTRA_CURRENT, 0)
);
} else if (intent.getAction().equals(COMMAND_SET_CURRENT)) {
VescDeviceSupport.this.setCurrent(
intent.getIntExtra(EXTRA_CURRENT, 0)
);
} else if (intent.getAction().equals(COMMAND_GET_VALUES)) {
VescDeviceSupport.this.getValues();
}
}
};
public void setCurrent(int currentMillisAmperes) {
buildAndQueryPacket(CommandType.COMM_SET_CURRENT, currentMillisAmperes);
}
public void setBreakCurrent(int breakCurrentMillisAmperes) {
buildAndQueryPacket(CommandType.COMM_SET_CURRENT_BRAKE, breakCurrentMillisAmperes);
}
public void getValues() {
buildAndQueryPacket(CommandType.COMM_GET_VALUES);
}
public void setRPM(int rpm) {
buildAndQueryPacket(CommandType.COMM_SET_RPM, rpm);
}
public void buildAndQueryPacket(CommandType commandType, Object... args) {
byte[] data = buildPacket(commandType, args);
queryPacket(data);
}
public void queryPacket(byte[] data) {
new TransactionBuilder("write serial packet")
.write(this.serialWriteCharacteristic, data)
.queue(getQueue());
}
public byte[] buildPacket(CommandType commandType, Object... args) {
int dataLength = 0;
for (Object arg : args) {
if (arg instanceof Integer) dataLength += 4;
else if (arg instanceof Short) dataLength += 2;
}
ByteBuffer buffer = ByteBuffer.allocate(dataLength);
for (Object arg : args) {
if (arg instanceof Integer) buffer.putInt((Integer) arg);
if (arg instanceof Short) buffer.putShort((Short) arg);
}
return buildPacket(commandType, buffer.array());
}
public byte[] buildPacket(CommandType commandType, byte[] data) {
return buildPacket(commandType.getCommandByte(), data);
}
private byte[] buildPacket(byte commandByte, byte[] data) {
byte[] contents = new byte[data.length + 1];
contents[0] = commandByte;
System.arraycopy(data, 0, contents, 1, data.length);
return buildPacket(contents);
}
private byte[] buildPacket(byte[] contents) {
int dataLength = contents.length;
ByteBuffer buffer = ByteBuffer.allocate(dataLength + (dataLength < 256 ? 5 : 6));
if (dataLength < 256) {
buffer.put((byte) 0x02);
buffer.put((byte) dataLength);
} else {
buffer.put((byte) 0x03);
buffer.putShort((short) dataLength);
}
buffer.put(contents);
buffer.putShort((short) CheckSums.getCRC16(contents, 0));
buffer.put((byte) 0x03);
return buffer.array();
}
}