diff --git a/docs/Battery.md b/docs/Battery.md index 93812b3b5..4fed9e14b 100644 --- a/docs/Battery.md +++ b/docs/Battery.md @@ -40,7 +40,7 @@ Note: When battery monitoring is enabled on the CC3D RC5 can no-longer be used f Enable the `VBAT` feature. -Configure min/max cell voltages using the following CLI commands: +Configure min/max cell voltages using the following CLI setting: `vbat_scale` - adjust this to match battery voltage to reported value. @@ -55,3 +55,28 @@ set vbat_scale = 110 set vbat_max_cell_voltage = 43 set vbat_min_cell_voltage = 33 ``` + +# Current Monitoring + +Current monitoring (Amperage) is supported by connecting a current meter to the appropriate current meter ADC input (See Board documentation). + +When enabled Amps, mAh used and capacity remaining calculated and used by the telemetry and OLED display subsystems. + +## Configuration + +Enable current monitoring using the cli command + +``` +feature CURRENT_METER +``` + +Configure capacity using the `battery_capacity` setting, it takes a value in mAh. + +The current meter may need to be configured so that the value read at the ADC input matches actual current draw. Just like you need a voltmeter to correctly calibrate your voltage reading you also need an ammeter to calibrate your current sensor. + +Use the following settings to adjust calibrtion. + +`current_meter_scale` +`current_meter_offset` + +If you're using an OSD that expects the multiwii current meter output value then set `multiwii_current_meter_output` to `1`. diff --git a/src/main/config/config.c b/src/main/config/config.c index 2a09be420..8b89b793d 100755 --- a/src/main/config/config.c +++ b/src/main/config/config.c @@ -104,7 +104,7 @@ profile_t *currentProfile; static uint8_t currentControlRateProfileIndex = 0; controlRateConfig_t *currentControlRateProfile; -static const uint8_t EEPROM_CONF_VERSION = 84; +static const uint8_t EEPROM_CONF_VERSION = 85; static void resetAccelerometerTrims(flightDynamicsTrims_t *accelerometerTrims) { @@ -209,7 +209,17 @@ void resetTelemetryConfig(telemetryConfig_t *telemetryConfig) telemetryConfig->gpsNoFixLongitude = 0; telemetryConfig->frsky_coordinate_format = FRSKY_FORMAT_DMS; telemetryConfig->frsky_unit = FRSKY_UNIT_METRICS; - telemetryConfig->batterySize = 0; +} + +void resetBatteryConfig(batteryConfig_t *batteryConfig) +{ + batteryConfig->vbatscale = VBAT_SCALE_DEFAULT; + batteryConfig->vbatmaxcellvoltage = 43; + batteryConfig->vbatmincellvoltage = 33; + batteryConfig->currentMeterOffset = 0; + batteryConfig->currentMeterScale = 400; // for Allegro ACS758LCB-100U (40mV/A) + batteryConfig->batteryCapacity = 0; + } void resetSerialConfig(serialConfig_t *serialConfig) @@ -315,11 +325,7 @@ static void resetConf(void) masterConfig.yaw_control_direction = 1; masterConfig.gyroConfig.gyroMovementCalibrationThreshold = 32; - masterConfig.batteryConfig.vbatscale = VBAT_SCALE_DEFAULT; - masterConfig.batteryConfig.vbatmaxcellvoltage = 43; - masterConfig.batteryConfig.vbatmincellvoltage = 33; - masterConfig.batteryConfig.currentMeterOffset = 0; - masterConfig.batteryConfig.currentMeterScale = 400; // for Allegro ACS758LCB-100U (40mV/A) + resetBatteryConfig(&masterConfig.batteryConfig); resetTelemetryConfig(&masterConfig.telemetryConfig); diff --git a/src/main/io/display.c b/src/main/io/display.c index be0cf6fb7..bd67ce0ba 100644 --- a/src/main/io/display.c +++ b/src/main/io/display.c @@ -49,6 +49,8 @@ #include "display.h" +//#define ENABLE_DEBUG_OLED_PAGE + #define MILLISECONDS_IN_A_SECOND (1000 * 1000) #define DISPLAY_UPDATE_FREQUENCY (MILLISECONDS_IN_A_SECOND / 10) @@ -58,6 +60,8 @@ static uint32_t nextDisplayUpdateAt = 0; static rxConfig_t *rxConfig; +#define PAGE_TITLE_LINE_COUNT 1 + static char lineBuffer[SCREEN_CHARACTER_COLUMN_COUNT + 1]; typedef enum { @@ -66,8 +70,11 @@ typedef enum { PAGE_BATTERY, PAGE_SENSORS, PAGE_RX, - PAGE_PROFILE, + PAGE_PROFILE +#ifdef ENABLE_DEBUG_OLED_PAGE + , PAGE_DEBUG +#endif } pageId_e; const char* pageTitles[] = { @@ -76,18 +83,24 @@ const char* pageTitles[] = { "BATTERY", "SENSORS", "RX", - "PROFILE", + "PROFILE" +#ifdef ENABLE_DEBUG_OLED_PAGE + , "DEBUG" +#endif }; #define PAGE_COUNT (PAGE_RX + 1) const uint8_t cyclePageIds[] = { PAGE_PROFILE, - PAGE_DEBUG, + PAGE_RX, PAGE_BATTERY, - PAGE_SENSORS, - PAGE_RX + PAGE_SENSORS +#ifdef ENABLE_DEBUG_OLED_PAGE + , + PAGE_DEBUG, +#endif }; #define CYCLE_PAGE_ID_COUNT (sizeof(cyclePageIds) / sizeof(cyclePageIds[0])) @@ -186,32 +199,37 @@ void drawRxChannel(uint8_t channelIndex, uint8_t width) drawHorizonalPercentageBar(width - 1, percentage); } +#define RX_CHANNELS_PER_PAGE_COUNT 14 void showRxPage(void) { - for (uint8_t channelIndex = 0; channelIndex < 8; channelIndex += 2) { - i2c_OLED_set_line((channelIndex / 2) + 1); + for (uint8_t channelIndex = 0; channelIndex < rxRuntimeConfig.channelCount && channelIndex < RX_CHANNELS_PER_PAGE_COUNT; channelIndex += 2) { + i2c_OLED_set_line((channelIndex / 2) + PAGE_TITLE_LINE_COUNT); uint8_t width = SCREEN_CHARACTER_COLUMN_COUNT / 2; drawRxChannel(channelIndex, width); + if (channelIndex >= rxRuntimeConfig.channelCount) { + continue; + } + if (width * 2 != SCREEN_CHARACTER_COLUMN_COUNT) { LCDprint(' '); } - drawRxChannel(channelIndex + 1, width); + drawRxChannel(channelIndex + PAGE_TITLE_LINE_COUNT, width); } } void showWelcomePage(void) { tfp_sprintf(lineBuffer, "Rev: %s", shortGitRevision); - i2c_OLED_set_line(1); + i2c_OLED_set_line(PAGE_TITLE_LINE_COUNT + 0); i2c_OLED_send_string(lineBuffer); tfp_sprintf(lineBuffer, "Target: %s", targetName); - i2c_OLED_set_line(2); + i2c_OLED_set_line(PAGE_TITLE_LINE_COUNT + 1); i2c_OLED_send_string(lineBuffer); } @@ -222,29 +240,44 @@ void showArmedPage(void) void showProfilePage(void) { tfp_sprintf(lineBuffer, "Profile: %d", getCurrentProfile()); - i2c_OLED_set_line(1); + i2c_OLED_set_line(PAGE_TITLE_LINE_COUNT + 0); i2c_OLED_send_string(lineBuffer); tfp_sprintf(lineBuffer, "Rate profile: %d", getCurrentControlRateProfile()); - i2c_OLED_set_line(2); + i2c_OLED_set_line(PAGE_TITLE_LINE_COUNT + 1); i2c_OLED_send_string(lineBuffer); } void showBatteryPage(void) { - tfp_sprintf(lineBuffer, "Volts: %d.%d, Cells: %d", vbat / 10, vbat % 10, batteryCellCount); - i2c_OLED_set_line(1); - i2c_OLED_send_string(lineBuffer); - padLineBuffer(); + uint8_t rowIndex = PAGE_TITLE_LINE_COUNT; - uint32_t batteryPercentage = calculateBatteryPercentage(); - i2c_OLED_set_line(2); - drawHorizonalPercentageBar(SCREEN_CHARACTER_COLUMN_COUNT, batteryPercentage); + if (feature(FEATURE_VBAT)) { + tfp_sprintf(lineBuffer, "Volts: %d.%d, Cells: %d", vbat / 10, vbat % 10, batteryCellCount); + i2c_OLED_set_line(rowIndex++); + i2c_OLED_send_string(lineBuffer); + padLineBuffer(); + + uint8_t batteryPercentage = calculateBatteryPercentage(); + i2c_OLED_set_line(rowIndex++); + drawHorizonalPercentageBar(SCREEN_CHARACTER_COLUMN_COUNT, batteryPercentage); + } + + if (feature(FEATURE_CURRENT_METER)) { + tfp_sprintf(lineBuffer, "Amps: %d.%d, mAh: %d", amperage / 100, amperage % 100, mAhDrawn); + i2c_OLED_set_line(rowIndex++); + i2c_OLED_send_string(lineBuffer); + padLineBuffer(); + + uint8_t capacityPercentage = calculateBatteryCapacityRemainingPercentage(); + i2c_OLED_set_line(rowIndex++); + drawHorizonalPercentageBar(SCREEN_CHARACTER_COLUMN_COUNT, capacityPercentage); + } } void showSensorsPage(void) { - uint8_t rowIndex = 1; + uint8_t rowIndex = PAGE_TITLE_LINE_COUNT; i2c_OLED_set_line(rowIndex++); i2c_OLED_send_string(" X Y Z"); @@ -270,7 +303,7 @@ void showSensorsPage(void) #endif } -#define PAGE_TITLE_LINE_COUNT 1 +#ifdef ENABLE_DEBUG_OLED_PAGE void showDebugPage(void) { @@ -283,6 +316,7 @@ void showDebugPage(void) i2c_OLED_send_string(lineBuffer); } } +#endif void updateDisplay(void) { @@ -346,9 +380,11 @@ void updateDisplay(void) case PAGE_PROFILE: showProfilePage(); break; +#ifdef ENABLE_DEBUG_OLED_PAGE case PAGE_DEBUG: showDebugPage(); break; +#endif } if (!armedState) { updateTicker(); diff --git a/src/main/io/serial_cli.c b/src/main/io/serial_cli.c index 0d4d6823f..c1687b095 100644 --- a/src/main/io/serial_cli.c +++ b/src/main/io/serial_cli.c @@ -278,8 +278,8 @@ const clivalue_t valueTable[] = { { "frsky_default_longitude", VAR_FLOAT | MASTER_VALUE, &masterConfig.telemetryConfig.gpsNoFixLongitude, -180.0, 180.0 }, { "frsky_coordinates_format", VAR_UINT8 | MASTER_VALUE, &masterConfig.telemetryConfig.frsky_coordinate_format, 0, FRSKY_FORMAT_NMEA }, { "frsky_unit", VAR_UINT8 | MASTER_VALUE, &masterConfig.telemetryConfig.frsky_unit, 0, FRSKY_UNIT_IMPERIALS }, - { "frsky_battery_size", VAR_UINT16 | MASTER_VALUE, &masterConfig.telemetryConfig.batterySize, 0, 20000 }, + { "battery_capacity", VAR_UINT16 | MASTER_VALUE, &masterConfig.batteryConfig.batteryCapacity, 0, 20000 }, { "vbat_scale", VAR_UINT8 | MASTER_VALUE, &masterConfig.batteryConfig.vbatscale, VBAT_SCALE_MIN, VBAT_SCALE_MAX }, { "vbat_max_cell_voltage", VAR_UINT8 | MASTER_VALUE, &masterConfig.batteryConfig.vbatmaxcellvoltage, 10, 50 }, { "vbat_min_cell_voltage", VAR_UINT8 | MASTER_VALUE, &masterConfig.batteryConfig.vbatmincellvoltage, 10, 50 }, diff --git a/src/main/mw.c b/src/main/mw.c index c752b09fb..3a9b26083 100755 --- a/src/main/mw.c +++ b/src/main/mw.c @@ -229,15 +229,15 @@ void annexCode(void) vbatCycleTime += cycleTime; if (!(++vbatTimer % VBATFREQ)) { - if (feature(FEATURE_VBAT)) { - updateBatteryVoltage(); + if (feature(FEATURE_VBAT)) { + updateBatteryVoltage(); batteryWarningEnabled = shouldSoundBatteryAlarm(); - } + } - if (feature(FEATURE_CURRENT_METER)) { - updateCurrentMeter(vbatCycleTime); - } - vbatCycleTime = 0; + if (feature(FEATURE_CURRENT_METER)) { + updateCurrentMeter(vbatCycleTime); + } + vbatCycleTime = 0; } } diff --git a/src/main/rx/rx.c b/src/main/rx/rx.c index 629ae3d36..4836bd211 100644 --- a/src/main/rx/rx.c +++ b/src/main/rx/rx.c @@ -56,7 +56,7 @@ bool sumhInit(rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig, rcReadRa bool rxMspInit(rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig, rcReadRawDataPtr *callback); -const char rcChannelLetters[] = "AERT1234"; +const char rcChannelLetters[] = "AERT12345678abcdefgh"; uint16_t rssi; // range: [0;1023] diff --git a/src/main/sensors/battery.c b/src/main/sensors/battery.c index eb9c28f78..b2c42e46e 100644 --- a/src/main/sensors/battery.c +++ b/src/main/sensors/battery.c @@ -18,6 +18,8 @@ #include "stdbool.h" #include "stdint.h" +#include "common/maths.h" + #include "drivers/adc.h" #include "drivers/system.h" @@ -32,7 +34,7 @@ uint8_t vbat = 0; // battery voltage in 0.1V steps int32_t amperage = 0; // amperage read by current sensor in centiampere (1/100th A) int32_t mAhDrawn = 0; // milliampere hours drawn from the battery since start -static batteryConfig_t *batteryConfig; +batteryConfig_t *batteryConfig; uint16_t batteryAdcToVoltage(uint16_t src) { @@ -110,7 +112,14 @@ void updateCurrentMeter(int32_t lastUpdateAt) mAhDrawn = mAhdrawnRaw / (3600 * 100); } -uint32_t calculateBatteryPercentage(void) +uint8_t calculateBatteryPercentage(void) { return (((uint32_t)vbat - (batteryConfig->vbatmincellvoltage * batteryCellCount)) * 100) / ((batteryConfig->vbatmaxcellvoltage - batteryConfig->vbatmincellvoltage) * batteryCellCount); } + +uint8_t calculateBatteryCapacityRemainingPercentage(void) +{ + uint16_t batteryCapacity = batteryConfig->batteryCapacity; + + return constrain((batteryCapacity - constrain(mAhDrawn, 0, 0xFFFF)) * 100.0 / batteryCapacity , 0, 100); +} diff --git a/src/main/sensors/battery.h b/src/main/sensors/battery.h index c78e963c8..7991be75e 100644 --- a/src/main/sensors/battery.h +++ b/src/main/sensors/battery.h @@ -31,6 +31,7 @@ typedef struct batteryConfig_s { // FIXME this doesn't belong in here since it's a concern of MSP, not of the battery code. uint8_t multiwiiCurrentMeterOutput; // if set to 1 output the amperage in milliamp steps instead of 0.01A steps via msp + uint16_t batteryCapacity; // mAh } batteryConfig_t; extern uint8_t vbat; @@ -47,4 +48,5 @@ void batteryInit(batteryConfig_t *initialBatteryConfig); void updateCurrentMeter(int32_t lastUpdateAt); int32_t currentMeterToCentiamps(uint16_t src); -uint32_t calculateBatteryPercentage(void); +uint8_t calculateBatteryPercentage(void); +uint8_t calculateBatteryCapacityRemainingPercentage(void); diff --git a/src/main/telemetry/frsky.c b/src/main/telemetry/frsky.c index 4b7a6c1e0..1e9eb6f1b 100644 --- a/src/main/telemetry/frsky.c +++ b/src/main/telemetry/frsky.c @@ -58,6 +58,8 @@ static serialPort_t *frskyPort; static telemetryConfig_t *telemetryConfig; +extern batteryConfig_t *batteryConfig; + extern int16_t telemTemperature1; // FIXME dependency on mw.c #define CYCLETIME 125 @@ -181,7 +183,7 @@ static void sendThrottleOrBatterySizeAsRpm(void) if (ARMING_FLAG(ARMED)) { serialize16(rcCommand[THROTTLE] / BLADE_NUMBER_DIVIDER); } else { - serialize16((telemetryConfig->batterySize / BLADE_NUMBER_DIVIDER)); + serialize16((batteryConfig->batteryCapacity / BLADE_NUMBER_DIVIDER)); } } @@ -371,12 +373,10 @@ static void sendAmperage(void) static void sendFuelLevel(void) { - uint16_t batterySize = telemetryConfig->batterySize; - sendDataHead(ID_FUEL_LEVEL); - if (batterySize > 0) { - serialize16((uint16_t)constrain((batterySize - constrain(mAhDrawn, 0, 0xFFFF)) * 100.0 / batterySize , 0, 100)); + if (batteryConfig->batteryCapacity > 0) { + serialize16((uint16_t)calculateBatteryCapacityRemainingPercentage()); } else { serialize16((uint16_t)constrain(mAhDrawn, 0, 0xFFFF)); } diff --git a/src/main/telemetry/telemetry.h b/src/main/telemetry/telemetry.h index 5a356ad0d..798f9e857 100644 --- a/src/main/telemetry/telemetry.h +++ b/src/main/telemetry/telemetry.h @@ -50,7 +50,6 @@ typedef struct telemetryConfig_s { float gpsNoFixLongitude; frskyGpsCoordFormat_e frsky_coordinate_format; frskyUnit_e frsky_unit; - uint16_t batterySize; } telemetryConfig_t; void checkTelemetryState(void);