From c31213c34d357f60823fafe57093809f2006ce1b Mon Sep 17 00:00:00 2001 From: Daniel Dakhno Date: Sat, 14 May 2022 04:47:22 +0200 Subject: [PATCH] VESC: added battery indicator --- .../DeviceSettingsPreferenceConst.java | 3 + .../devices/vesc/VescControlActivity.java | 8 +- .../devices/vesc/VescCoordinator.java | 12 +- .../service/devices/vesc/CommandType.java | 48 +++- .../devices/vesc/VescDeviceSupport.java | 230 +++++++++++++++--- app/src/main/res/xml/devicesettings_vesc.xml | 18 ++ 6 files changed, 274 insertions(+), 45 deletions(-) create mode 100644 app/src/main/res/xml/devicesettings_vesc.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index 39964bdcd..ad1aa2c91 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -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"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java index 8c745f975..18f904a9d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescControlActivity.java @@ -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; + } } }); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescCoordinator.java index f34dc2083..773b7e6a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vesc/VescCoordinator.java @@ -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 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/CommandType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/CommandType.java index b1ce868b4..bd0ca8eae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/CommandType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/CommandType.java @@ -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(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/VescDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/VescDeviceSupport.java index ca10ef14a..2007b6f1d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/VescDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vesc/VescDeviceSupport.java @@ -16,11 +16,14 @@ along with this program. If not, see . */ 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); } diff --git a/app/src/main/res/xml/devicesettings_vesc.xml b/app/src/main/res/xml/devicesettings_vesc.xml new file mode 100644 index 000000000..d4906ee06 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_vesc.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file