VESC, FlexiBMS, Arduino

Hello Guys!
Since few days Im trying to make something like a small OLED screen to show cell voltages and total voltage of battery pack. For this case Im using FlexiBMS Lite, Arduino and standard 1306 OLED. I Have big problem with coding.
For this time Im able to control every value and send every command via UART to VESC, and also send it to other VESC via CAN. But problem is with communication with FlexiBMS Lite.

Heres schematic:

Im sending command: COMM_GET_VALUES on CAN ID 10 (As set on FlexiBMS config) and trying to receive for example this struct:

struct BMSPackage {
		uint16_t data.highestCell;
		uint16_tdata.lowestCell;
		int32 data.cellVoltageHigh;
		int32 data.cellVoltageAverage;
		int32 data.cellVoltageLow;
		
etc...
};

and I dont know what Im doing wrong…

I’ve checked commands.h file in @SimosMCmuffin 's github repository and can’t make it working.

case COMM_GET_VALUES:
		send_buffer[ind++] = COMM_GET_VALUES;

		buffer_append_int32(send_buffer, (int32_t) ADC_convertedResults[batteryVoltage], &ind);  // packVoltage
		buffer_append_int32(send_buffer, (int32_t) ADC_convertedResults[chargeCurrent], &ind);  // packCurrent

		send_buffer[ind++] = 50;  // FIXME: SoC

		uint16_t cell_high = highestCell(nonVolPars.chgParas.packCellCount);
		uint16_t cell_low = lowestCell(nonVolPars.chgParas.packCellCount);
		buffer_append_int32(send_buffer, cell_high, &ind);  // cellVoltageHigh
		buffer_append_int32(send_buffer, (cell_high + cell_low) / 2, &ind);  // cellVoltageAverage (very rough approximation)
		buffer_append_int32(send_buffer, cell_low, &ind);  // cellVoltageLow
		buffer_append_int32(send_buffer, cell_high - cell_low, &ind);  // cellVoltageMisMatch

		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // loCurrentLoadVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // loCurrentLoadCurrent
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // hiCurrentLoadVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // hiCurrentLoadCurrent
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // auxVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // auxCurrent

Does anybody can help me? @janpom , maybe You have any solution?

1 Like

You dont need the arduino in between unless what you’re trying to do is super buss heavy.

I’m fairly certain this is done easiest with the newly integrated LISP support.
I’d take this conversation into the vesc discord.

Arduino block on my schematic is just for explain my problem. I have my own PCB with microcontroller to control for example front lights, rear lights and underglow.
I just want to understand how to communicate with FlexiBMS via CAN using commands.

Is it possible to take me to VESC Discord? :slight_smile:

maybe @janpom @SimosMCmuffin could help? :smiley:

1 Like

Is there any reason why you can’t just read the voltage data/ get the voltage data straight from the BMS? You might need a separate analog to digital converter (I have no idea if Arduinos have their own analog in pins or not) but it would be easier than trying to get them from the VESC. You might have to determine the total battery voltage on the Arduino though since I’m pretty sure even 42v would fry it.

Soldering an extra wire to each balance lead would be needed but it would be worth it to simplify the code plus you’d still have the UART ports available on the Unity if you ever need them for something else.

I made this a few years back. Arduino communicates via CAN to the FlexiBMS lite, displays cell info on small oled.

This does differ to your setup that uses the VESC to handle the CAN stuff. Still, there may be some bits of code in there that are helpful.

These days I’d be very tempted just to do it on the VESC as linsus mentioned. Would cut out the need for the arduino (and CAN transceiver in my case)

1 Like

@Buxzzix

What request do you send to get the Flexi data? You need to use the COMM_FORWARD_CAN . You know that, right?

Show us your code. Then we should be able to help you.

@lock I saw your project, It is really great! But I dont want any other additional modules to this application :smiley:

@janpom For this while for total telemetry Im using this and its work fine:

bool VescUart::getVescValues(uint8_t canId) {

	if (debugPort!=NULL){
		debugPort->println("Command: COMM_GET_VALUES "+String(canId));
	}

	int32_t index = 0;
	int payloadSize = (canId == 0 ? 1 : 3);
	uint8_t payload[payloadSize];
	if (canId != 0) {
		payload[index++] = { COMM_FORWARD_CAN };
		payload[index++] = canId;
	}
	payload[index++] = { COMM_GET_VALUES };

	packSendPayload(payload, payloadSize);

	uint8_t message[256];
	int messageLength = receiveUartMessage(message);

	if (messageLength > 55) {
		return processReadPacket(message); 
	}
	return false;
}

But for FlexiBMS Im trying something like this:

bool VescUart::getBMSValues(uint8_t canId) {

	if (debugPort!=NULL){
		debugPort->println("Command: COMM_GET_BMS_VALUES "+String(canId));
	}

	int32_t index = 0;
	int payloadSize = (canId == 0 ? 1 : 3);
	uint8_t payload[payloadSize];
	if (canId != 0) {
		payload[index++] = { COMM_FORWARD_CAN };
		payload[index++] = canId;
	}
	payload[index++] = { COMM_GET_BMS_VALUES };

	packSendPayload(payload, payloadSize);

	uint8_t message[256];
	int messageLength = receiveUartMessage(message);

	if (messageLength > 55) {
		return processReadPacket(message); 
	}
	return false;
}

How is this one defined? Should be 4.

Are you getting any response back and processing the response fails or is there no response at all?

@janpom unfortunately I have no response :C

For telemetry I have something like this:

bool VescUartUnity::processReadPacket(uint8_t * message) {

	COMM_PACKET_ID packetId;
	int32_t ind = 0;

	packetId = (COMM_PACKET_ID)message[0];
	message++; // Removes the packetId from the actual message (payload)

	switch (packetId){
		case COMM_GET_VALUES: // Structure defined here: https://github.com/vedderb/bldc/blob/43c3bbaf91f5052a35b75c2ff17b5fe99fad94d1/commands.c#L164

			ind = 0;
			data.filteredFetTemp0  		= buffer_get_float16(message, 10.0, &ind);
			data.filteredFetTemp1  		= buffer_get_float16(message, 10.0, &ind);
			data.filteredMotorTemp0  	= buffer_get_float16(message, 10.0, &ind);
			data.filteredMotorTemp1  	= buffer_get_float16(message, 10.0, &ind);
			data.avgMotorCurrent0 		= buffer_get_float32(message, 100.0, &ind);
			data.avgMotorCurrent1 		= buffer_get_float32(message, 100.0, &ind);
			data.avgInputCurrent 		= buffer_get_float32(message, 100.0, &ind);

			ind += 16;
			// buffer_append_float32(send_buffer, mc_interface_read_reset_avg_id(), 1e2, &ind);
			// buffer_append_float32(send_buffer, mc_interface_read_reset_avg_id2(), 1e2, &ind);
			// buffer_append_float32(send_buffer, mc_interface_read_reset_avg_iq(), 1e2, &ind);
			// buffer_append_float32(send_buffer, mc_interface_read_reset_avg_iq2(), 1e2, &ind);
			data.dutyCycleNow0 		= buffer_get_float16(message, 1000.0, &ind);
			data.dutyCycleNow1 		= buffer_get_float16(message, 1000.0, &ind);
			data.rpm 			= buffer_get_int32(message, &ind);

			ind += 4;
			// buffer_append_float32(send_buffer, mc_interface_get_rpm2(), 1e0, &ind);
			data.inpVoltage 		= buffer_get_float16(message, 10.0, &ind);
			data.ampHours 			= buffer_get_float32(message, 10000.0, &ind);
			data.ampHoursCharged 		= buffer_get_float32(message, 10000.0, &ind);

			ind += 8;
			// buffer_append_float32(send_buffer, mc_interface_get_watt_hours(false), 1e4, &ind);
			// buffer_append_float32(send_buffer, mc_interface_get_watt_hours_charged(false), 1e4, &ind);
			data.tachometer 		= buffer_get_int32(message, &ind);

			ind += 8;
			// buffer_append_int32(send_buffer, mc_interface_get_tachometer_value2(false), &ind);
			data.tachometerAbs 		= buffer_get_int32(message, &ind);

			ind += 8;
			// buffer_append_int32(send_buffer, mc_interface_get_tachometer_abs_value2(false), &ind);
			return true;

		break;

		default:
			return false;
		break;
	}
}

And I’m not sure how should look like code for BMS values. I tried something like that:

bool VescUartUnity::processReadPacket(uint8_t * message) {

			send_buffer[ind++] = COMM_GET_VALUES;

		buffer_append_int32(send_buffer, (int32_t) ADC_convertedResults[batteryVoltage], &ind);  // packVoltage
		buffer_append_int32(send_buffer, (int32_t) ADC_convertedResults[chargeCurrent], &ind);  // packCurrent

		send_buffer[ind++] = 50;  // FIXME: SoC

		uint16_t cell_high = highestCell(nonVolPars.chgParas.packCellCount);
		uint16_t cell_low = lowestCell(nonVolPars.chgParas.packCellCount);
		buffer_append_int32(send_buffer, cell_high, &ind);  // cellVoltageHigh
		buffer_append_int32(send_buffer, (cell_high + cell_low) / 2, &ind);  // cellVoltageAverage (very rough approximation)
		buffer_append_int32(send_buffer, cell_low, &ind);  // cellVoltageLow
		buffer_append_int32(send_buffer, cell_high - cell_low, &ind);  // cellVoltageMisMatch

		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // loCurrentLoadVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // loCurrentLoadCurrent
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // hiCurrentLoadVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // hiCurrentLoadCurrent
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // auxVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // auxCurrent

		buffer_append_int16(send_buffer, (int16_t) (ADC_convertedResults[externalTemp] * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBatteryHigh
		buffer_append_int16(send_buffer, (int16_t) (ADC_convertedResults[externalTemp] * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBatteryAverage

		uint16_t mcu_temp = ADC_convertedResults[mcuInternalTemp];
		uint16_t ltc_temp = LTC6803_getTemperature();
		uint16_t bms_temp_high = (mcu_temp > ltc_temp) ? mcu_temp : ltc_temp;
		uint16_t bms_temp_avg = (mcu_temp + ltc_temp) / 2;
		buffer_append_int16(send_buffer, (int16_t) (bms_temp_high * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBMSHigh
		buffer_append_int16(send_buffer, (int16_t) (bms_temp_avg * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBMSAverage

		// TODO: use runtimePars.chargingState and runtimePars.charging
		send_buffer[ind++] = (uint8_t) OP_STATE_INIT;  // FIXME: operationalState
		send_buffer[ind++] = 0;  // FIXME: chargeBalanceActive (Indicator for charging)

		send_buffer[ind++] = 0;  // Future faultstate

		send_buffer[ind++] = nonVolPars.genParas.canID;
		commands_send_packet(from, send_buffer, ind);

		break;
}

And values from code above are written in array.

Ohh… bad copy paste. Here is code for receiving packet from BMS:

bool VescUartUnity::processReadPacket(uint8_t * message) {

			send_buffer[ind++] = COMM_GET_BMS_VALUES;

		buffer_append_int32(send_buffer, (int32_t) ADC_convertedResults[batteryVoltage], &ind);  // packVoltage
		buffer_append_int32(send_buffer, (int32_t) ADC_convertedResults[chargeCurrent], &ind);  // packCurrent

		send_buffer[ind++] = 50;  // FIXME: SoC

		uint16_t cell_high = highestCell(nonVolPars.chgParas.packCellCount);
		uint16_t cell_low = lowestCell(nonVolPars.chgParas.packCellCount);
		buffer_append_int32(send_buffer, cell_high, &ind);  // cellVoltageHigh
		buffer_append_int32(send_buffer, (cell_high + cell_low) / 2, &ind);  // cellVoltageAverage (very rough approximation)
		buffer_append_int32(send_buffer, cell_low, &ind);  // cellVoltageLow
		buffer_append_int32(send_buffer, cell_high - cell_low, &ind);  // cellVoltageMisMatch

		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // loCurrentLoadVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // loCurrentLoadCurrent
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // hiCurrentLoadVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // hiCurrentLoadCurrent
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // auxVoltage
		buffer_append_float16(send_buffer, 0.0, 1e2, &ind);  // auxCurrent

		buffer_append_int16(send_buffer, (int16_t) (ADC_convertedResults[externalTemp] * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBatteryHigh
		buffer_append_int16(send_buffer, (int16_t) (ADC_convertedResults[externalTemp] * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBatteryAverage

		uint16_t mcu_temp = ADC_convertedResults[mcuInternalTemp];
		uint16_t ltc_temp = LTC6803_getTemperature();
		uint16_t bms_temp_high = (mcu_temp > ltc_temp) ? mcu_temp : ltc_temp;
		uint16_t bms_temp_avg = (mcu_temp + ltc_temp) / 2;
		buffer_append_int16(send_buffer, (int16_t) (bms_temp_high * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBMSHigh
		buffer_append_int16(send_buffer, (int16_t) (bms_temp_avg * 1e1 - CELSIUS_TO_KELVIN_1E1), &ind);  // tempBMSAverage

		// TODO: use runtimePars.chargingState and runtimePars.charging
		send_buffer[ind++] = (uint8_t) OP_STATE_INIT;  // FIXME: operationalState
		send_buffer[ind++] = 0;  // FIXME: chargeBalanceActive (Indicator for charging)

		send_buffer[ind++] = 0;  // Future faultstate

		send_buffer[ind++] = nonVolPars.genParas.canID;
		commands_send_packet(from, send_buffer, ind);

		break;
}

How do you tell there’s no response? Have you done any debugging prints on the received packets? If there’s no response it’s premature to troubleshoot the response processing.

Best if you could put your full code on GitHub. The snippets don’t help much if they reference identifiers for which we can’t see the definitions.

Yep, I tried debugging. Only packets I get is packets from VESC’s. Today Im going to put all code on github and post link right here. I’ll try one more time… maybe I’ll get any idea while looking at the code.
BTW I’m going to think that I have troubles with CAN chip. But on three FlexiBMS modules? Gonna see. I will reply as soon as possible

Okay… after one day of trying to do something I finally got LED blinking on FlexiBMS (Can packets are received by FlexiBMS).
I know that FlexiBMS after receiving CAN Packet is sending some information because I used CAN Analyzer device to check readings - I got some frames.

Now I need to change code in libraries to make it work.

Here is github: GitHub - BartekBraszak/VESC-Monitor: VESC UART/CAN monitor for telemetry data and BMS data. Only for private usage

@janpom @lock any ideas? :smiley:

Okay, small update… Im sending Commands to Flexi (LED is blinking on BMS), but I dont receive any packets. This CAN Analyzer device is just broken -.-

This is wrong:

The GET BMS CELLS command ID for FlexiBMS is 51. The definition of COMM_GET_BMS_CELLS in your code is different. Just hardcode 51 there:

payload[index++] = { 51 };

and you should at least be getting some response back.

After that you may need to troubleshoot the response processing as well.

Now I have something like that:


Is that correct behavior of LED?

Okay… after few days of trying to decode CAN frame from FlexiBMS I really dont know how to do it :C