1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2025-01-13 11:17:33 +01:00

VESC: added battery indicator

This commit is contained in:
Daniel Dakhno 2022-05-14 04:47:22 +02:00
parent 10d11d4818
commit c31213c34d
6 changed files with 274 additions and 45 deletions

View File

@ -184,6 +184,9 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_UM25_SHOW_THRESHOLD_NOTIFICATION = "um25_current_threshold_notify";
public static final String PREF_UM25_SHOW_THRESHOLD = "um25_current_threshold";
public static final String PREF_VESC_MINIMUM_VOLTAGE = "vesc_minimum_battery_voltage";
public static final String PREF_VESC_MAXIMUM_VOLTAGE = "vesc_maximum_battery_voltage";
public static final String PREF_SOUNDS = "sounds";
public static final String PREF_AUTH_KEY = "authkey";
public static final String PREF_USER_FITNESS_GOAL = "fitness_goal";

View File

@ -73,7 +73,7 @@ public class VescControlActivity extends AbstractGBActivity {
private void restoreValues(){
rpmEditText.setText(String.valueOf(preferences.getInt(PREFS_KEY_LAST_RPM, 0)));
breakCurrentEditText.setText(String.valueOf(preferences.getInt(PREFS_KEY_LAST_BREAK_CURRENT, 0)));
breakCurrentEditText.setText(String.valueOf(preferences.getInt(PREFS_KEY_LAST_BREAK_CURRENT, 0) / 1000));
}
@Override
@ -184,7 +184,11 @@ public class VescControlActivity extends AbstractGBActivity {
currentBreakCurrentMa = 0;
return;
}
VescControlActivity.this.currentBreakCurrentMa = Integer.parseInt(text) * 1000;
try {
VescControlActivity.this.currentBreakCurrentMa = Integer.parseInt(text) * 1000;
}catch (NumberFormatException e){
VescControlActivity.this.currentBreakCurrentMa = 0;
}
}
});

View File

@ -24,6 +24,7 @@ import android.os.ParcelUuid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
@ -38,9 +39,11 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class VescCoordinator extends AbstractDeviceCoordinator {
public final static String UUID_SERVICE_SERIAL_HM10 = "0000ffe0-0000-1000-8000-00805f9b34fb";
public final static String UUID_CHARACTERISTIC_SERIAL_TX_HM10 = "0000ffe1-0000-1000-8000-00805f9b34fb";
public final static String UUID_CHARACTERISTIC_SERIAL_RX_HM10 = "0000ffe1-0000-1000-8000-00805f9b34fb";
public final static String UUID_SERVICE_SERIAL_NRF = "0000ffe0-0000-1000-8000-00805f9b34fb";
public final static String UUID_CHARACTERISTIC_SERIAL_TX_NRF = "0000ffe0-0000-1000-8000-00805f9b34fb";
public final static String UUID_CHARACTERISTIC_SERIAL_RX_NRF = "0000ffe1-0000-1000-8000-00805f9b34fb";
@Override
@ -48,6 +51,13 @@ public class VescCoordinator extends AbstractDeviceCoordinator {
}
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return new int[]{
R.xml.devicesettings_vesc
};
}
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
ParcelUuid[] uuids = candidate.getServiceUuids();
@ -77,7 +87,7 @@ public class VescCoordinator extends AbstractDeviceCoordinator {
@Override
public boolean supportsActivityDataFetching() {
return false;
return true;
}
@Override

View File

@ -17,17 +17,47 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.vesc;
public enum CommandType {
SET_CURRENT((byte) 0x06),
SET_CURRENT_BRAKE((byte) 0x07),
SET_RPM((byte) 0x08),
COMM_FW_VERSION,
COMM_JUMP_TO_BOOTLOADER,
COMM_ERASE_NEW_APP,
COMM_WRITE_NEW_APP_DATA,
COMM_GET_VALUES,
COMM_SET_DUTY,
COMM_SET_CURRENT,
COMM_SET_CURRENT_BRAKE,
COMM_SET_RPM,
COMM_SET_POS,
COMM_SET_HANDBRAKE,
COMM_SET_DETECT,
COMM_SET_SERVO_POS,
COMM_SET_MCCONF,
COMM_GET_MCCONF,
COMM_GET_MCCONF_DEFAULT,
COMM_SET_APPCONF,
COMM_GET_APPCONF,
COMM_GET_APPCONF_DEFAULT,
COMM_SAMPLE_PRINT,
COMM_TERMINAL_CMD,
COMM_PRINT,
COMM_ROTOR_POSITION,
COMM_EXPERIMENT_SAMPLE,
COMM_DETECT_MOTOR_PARAM,
COMM_DETECT_MOTOR_R_L,
COMM_DETECT_MOTOR_FLUX_LINKAGE,
COMM_DETECT_ENCODER,
COMM_DETECT_HALL_FOC,
COMM_REBOOT,
COMM_ALIVE,
COMM_GET_DECODED_PPM,
COMM_GET_DECODED_ADC,
COMM_GET_DECODED_CHUK,
COMM_FORWARD_CAN,
COMM_SET_CHUCK_DATA,
COMM_CUSTOM_APP_DATA,
COMM_NRF_START_PAIRING
;
byte commandByte;
CommandType(byte commandByte){
this.commandByte = commandByte;
}
public byte getCommandByte(){
return this.commandByte;
return (byte) this.ordinal();
}
}

View File

@ -16,11 +16,14 @@
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;
@ -28,8 +31,11 @@ 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;
@ -37,32 +43,44 @@ 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;
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;
public VescDeviceSupport(DeviceType type){
private ByteBuffer responseBuffer = ByteBuffer.allocate(100);
public VescDeviceSupport(DeviceType type) {
super();
logger.debug("VescDeviceSupport() {}", type);
responseBuffer.order(ByteOrder.BIG_ENDIAN);
deviceType = type;
if(type == DeviceType.VESC_NRF){
if (type == DeviceType.VESC_NRF) {
addSupportedService(UUID.fromString(VescCoordinator.UUID_SERVICE_SERIAL_NRF));
}else if(type == DeviceType.VESC_HM10){
} 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");
@ -71,15 +89,148 @@ public class VescDeviceSupport extends VescBaseDeviceSupport{
initBroadcast();
if(deviceType == DeviceType.VESC_NRF){
if (deviceType == DeviceType.VESC_NRF) {
this.serialWriteCharacteristic = getCharacteristic(UUID.fromString(VescCoordinator.UUID_CHARACTERISTIC_SERIAL_TX_NRF));
}else if(deviceType == DeviceType.VESC_HM10){
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());
@ -87,86 +238,99 @@ public class VescDeviceSupport extends VescBaseDeviceSupport{
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)){
if (intent.getAction().equals(COMMAND_SET_RPM)) {
VescDeviceSupport.this.setRPM(
intent.getIntExtra(EXTRA_RPM, 0)
);
}else if(intent.getAction().equals(COMMAND_SET_BREAK_CURRENT)){
} 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)){
} 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.SET_CURRENT, currentMillisAmperes);
public void setCurrent(int currentMillisAmperes) {
buildAndQueryPacket(CommandType.COMM_SET_CURRENT, currentMillisAmperes);
}
public void setBreakCurrent(int breakCurrentMillisAmperes){
buildAndQueryPacket(CommandType.SET_CURRENT_BRAKE, breakCurrentMillisAmperes);
public void setBreakCurrent(int breakCurrentMillisAmperes) {
buildAndQueryPacket(CommandType.COMM_SET_CURRENT_BRAKE, breakCurrentMillisAmperes);
}
public void setRPM(int rpm){
buildAndQueryPacket(CommandType.SET_RPM, rpm);
public void getValues() {
buildAndQueryPacket(CommandType.COMM_GET_VALUES);
}
public void buildAndQueryPacket(CommandType commandType, Object ... args){
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){
public void queryPacket(byte[] data) {
new TransactionBuilder("write serial packet")
.write(this.serialWriteCharacteristic, data)
.queue(getQueue());
}
public byte[] buildPacket(CommandType commandType, Object ... args){
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;
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);
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){
public byte[] buildPacket(CommandType commandType, byte[] data) {
return buildPacket(commandType.getCommandByte(), data);
}
private byte[] buildPacket(byte commandByte, byte[] 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){
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{
if (dataLength < 256) {
buffer.put((byte) 0x02);
buffer.put((byte) dataLength);
} else {
buffer.put((byte) 0x03);
buffer.putShort((short) dataLength);
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference
android:title="Minimum battery voltage"
android:key="vesc_minimum_battery_voltage"
android:inputType="number"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
android:title="Maximum battery voltage"
android:key="vesc_maximum_battery_voltage"
android:inputType="number"
app:useSimpleSummaryProvider="true" />
</PreferenceScreen>