Skip to content

Commit

Permalink
🔀 Merge branch 'yann/feature/battery/use-polynomial-to-get-battery-vo…
Browse files Browse the repository at this point in the history
…ltage' into develop

* yann/feature/battery/use-polynomial-to-get-battery-voltage:
  ♻️ (battery): Modernize battery spike
  ✅ (battery): Set voltage in each Capacity and near expected value
  ✨ (battery): Add isCharging function
  ⚡ (battery): Use accurate conversion to get battery value
  🤡 (DigitalIn): Mock mbed DigitalIn
  • Loading branch information
ladislas committed Nov 17, 2021
2 parents 38c776e + 664495e commit b1d8287
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 118 deletions.
44 changes: 25 additions & 19 deletions drivers/CoreBattery/include/CoreBattery.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,49 @@
// Copyright 2021 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#ifndef _LEKA_OS_DRIVER_LK_CORE_BATTERY_H_
#define _LEKA_OS_DRIVER_LK_CORE_BATTERY_H_
#ifndef _LEKA_OS_DRIVER_CORE_BATTERY_H_
#define _LEKA_OS_DRIVER_CORE_BATTERY_H_

#include "drivers/AnalogIn.h"
#include "drivers/interfaces/InterfaceDigitalIn.h"

namespace leka {

class CoreBattery
{
public:
explicit CoreBattery(PinName pin) : _pin {mbed::AnalogIn(pin, voltage::reference)} {};
explicit CoreBattery(PinName voltage_pin, mbed::interface::DigitalIn &charge_status_input)
: _voltage_pin {mbed::AnalogIn(voltage_pin, analog_voltage_reference)},
_charge_status_input(charge_status_input) {};

auto getVoltage() -> float;

struct capacity {
static constexpr auto max = float {12.60};
static constexpr auto min = float {7.50};
auto isCharging() -> bool;

struct Capacity {
static constexpr auto full = float {12.52};
static constexpr auto three_quarter = float {11.73};
static constexpr auto half = float {11.08};
static constexpr auto quarter = float {10.47};
static constexpr auto empty = float {09.00};
};

struct resistor {
// TODO (@Benjamin) - find the resistor values, call Mikael
static constexpr auto r1 = float {47};
static constexpr auto r2 = float {169};
};
private:
static constexpr auto analog_voltage_reference = float {3.33};

struct voltage {
// TODO (@Benjamin) - should be float {resistor::r1 / (resistor::r1 + resistor::r2)};
static constexpr auto divider = float {0.129};
static constexpr auto reference = float {3.33};
struct PolynomialCoefficient {
static constexpr auto degree_0 = float {47.5};
static constexpr auto degree_1 = float {-50.7};
static constexpr auto degree_2 = float {15.8};
};

private:
auto readRawVoltage() -> float;
auto getAverageVoltage() -> float;
[[nodiscard]] auto convertToRealVoltage(float value) const -> float;

mbed::AnalogIn _pin;
mbed::AnalogIn _voltage_pin;
mbed::interface::DigitalIn &_charge_status_input;
};

} // namespace leka

#endif //_LEKA_OS_DRIVER_LK_CORE_BATTERY_H_
#endif //_LEKA_OS_DRIVER_CORE_BATTERY_H_
29 changes: 26 additions & 3 deletions drivers/CoreBattery/source/CoreBattery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,37 @@ namespace leka {

auto CoreBattery::getVoltage() -> float
{
auto raw = readRawVoltage();
auto voltage = raw / voltage::divider;
auto raw_average = getAverageVoltage();
auto voltage = convertToRealVoltage(raw_average);

return voltage;
}

auto CoreBattery::readRawVoltage() -> float
{
return _pin.read_voltage();
return _voltage_pin.read_voltage();
}

auto CoreBattery::getAverageVoltage() -> float
{
constexpr auto iterations = int {100};
auto raw_sum = float {};

for (int i = 0; i < iterations; i++) {
raw_sum += readRawVoltage();
}
return raw_sum / static_cast<float>(iterations);
}

auto CoreBattery::convertToRealVoltage(float value) const -> float
{
return PolynomialCoefficient::degree_0 + PolynomialCoefficient::degree_1 * value +
PolynomialCoefficient::degree_2 * value * value;
}

auto CoreBattery::isCharging() -> bool
{
return _charge_status_input.read() == 1;
}

} // namespace leka
98 changes: 80 additions & 18 deletions drivers/CoreBattery/tests/CoreBattery_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,105 @@
#include "CoreBattery.h"

#include "gtest/gtest.h"
#include "mocks/mbed/DigitalIn.h"
#include "stubs/mbed/AnalogIn.h"

using namespace leka;

CoreBattery corebattery(PinName::BATTERY_VOLTAGE);
using ::testing::Return;

TEST(CoreBatteryTest, initialization)
class CoreBatteryTest : public ::testing::Test
{
ASSERT_NE(&corebattery, nullptr);
protected:
CoreBatteryTest() : battery(PinName::BATTERY_VOLTAGE, charge_input) {}

// void SetUp() override {}
// void TearDown() override {}

mbed::mock::DigitalIn charge_input = {};

CoreBattery battery;
};

TEST_F(CoreBatteryTest, initialization)
{
ASSERT_NE(&battery, nullptr);
}

TEST_F(CoreBatteryTest, getVoltageAboveFull)
{
auto AnalogIn_voltage_value = float {2.208};
auto expected_battery_voltage = float {12.6};
spy_AnalogIn_setVoltageValue(AnalogIn_voltage_value);

ASSERT_NEAR(battery.getVoltage(), expected_battery_voltage, 0.1);
ASSERT_GT(battery.getVoltage(), CoreBattery::Capacity::full);
}

TEST_F(CoreBatteryTest, getVoltageBetweenThreeQuarterAndFull)
{
auto AnalogIn_voltage_value = float {2.183};
auto expected_battery_voltage = float {12.1};
spy_AnalogIn_setVoltageValue(AnalogIn_voltage_value);

ASSERT_NEAR(battery.getVoltage(), expected_battery_voltage, 0.1);
ASSERT_LT(battery.getVoltage(), CoreBattery::Capacity::full);
ASSERT_GT(battery.getVoltage(), CoreBattery::Capacity::three_quarter);
}

TEST(CoreBatteryTest, getVoltage)
TEST_F(CoreBatteryTest, getVoltageBetweenHalfAndThreeQuarter)
{
auto AnalogIn_read_voltage_value = float {1.2};
spy_AnalogIn_setVoltageValue(AnalogIn_read_voltage_value);
auto AnalogIn_voltage_value = float {2.144};
auto expected_battery_voltage = float {11.4};
spy_AnalogIn_setVoltageValue(AnalogIn_voltage_value);

ASSERT_NEAR(battery.getVoltage(), expected_battery_voltage, 0.1);
ASSERT_LT(battery.getVoltage(), CoreBattery::Capacity::three_quarter);
ASSERT_GT(battery.getVoltage(), CoreBattery::Capacity::half);
}

auto expected = AnalogIn_read_voltage_value / CoreBattery::voltage::divider;
TEST_F(CoreBatteryTest, getVoltageBetweenQuarterAndHalf)
{
auto AnalogIn_voltage_value = float {2.108};
auto expected_battery_voltage = float {10.8};
spy_AnalogIn_setVoltageValue(AnalogIn_voltage_value);

ASSERT_FLOAT_EQ(expected, corebattery.getVoltage());
ASSERT_NEAR(battery.getVoltage(), expected_battery_voltage, 0.1);
ASSERT_LT(battery.getVoltage(), CoreBattery::Capacity::half);
ASSERT_GT(battery.getVoltage(), CoreBattery::Capacity::quarter);
}

TEST(CoreBatteryTest, getVoltageLowest)
TEST_F(CoreBatteryTest, getVoltageBetweenEmptyAndQuarter)
{
auto AnalogIn_read_voltage_value = float {0};
spy_AnalogIn_setVoltageValue(AnalogIn_read_voltage_value);
auto AnalogIn_voltage_value = float {2.033};
auto expected_battery_voltage = float {9.7};
spy_AnalogIn_setVoltageValue(AnalogIn_voltage_value);

auto expected = AnalogIn_read_voltage_value / CoreBattery::voltage::divider;
ASSERT_NEAR(battery.getVoltage(), expected_battery_voltage, 0.1);
ASSERT_LT(battery.getVoltage(), CoreBattery::Capacity::quarter);
ASSERT_GT(battery.getVoltage(), CoreBattery::Capacity::empty);
}

ASSERT_FLOAT_EQ(expected, corebattery.getVoltage());
TEST_F(CoreBatteryTest, getVoltageBelowEmpty)
{
auto AnalogIn_voltage_value = float {1.82};
auto expected_battery_voltage = float {7.5};
spy_AnalogIn_setVoltageValue(AnalogIn_voltage_value);

ASSERT_NEAR(battery.getVoltage(), expected_battery_voltage, 0.1);
ASSERT_LT(battery.getVoltage(), CoreBattery::Capacity::empty);
}

TEST(CoreBatteryTest, getVoltageHighest)
TEST_F(CoreBatteryTest, isCharging)
{
auto AnalogIn_read_voltage_value = float {1.65};
spy_AnalogIn_setVoltageValue(AnalogIn_read_voltage_value);
EXPECT_CALL(charge_input, read).WillOnce(Return(1));

auto expected = AnalogIn_read_voltage_value / CoreBattery::voltage::divider;
ASSERT_TRUE(battery.isCharging());
}

TEST_F(CoreBatteryTest, isNotCharging)
{
EXPECT_CALL(charge_input, read).WillOnce(Return(0));

ASSERT_FLOAT_EQ(expected, corebattery.getVoltage());
ASSERT_FALSE(battery.isCharging());
}
88 changes: 10 additions & 78 deletions spikes/lk_sensors_battery/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,36 @@
// Copyright 2021 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include <array>
#include <cstdint>

#include "PinNames.h"

#include "drivers/BufferedSerial.h"
#include "drivers/DigitalIn.h"
#include "rtos/ThisThread.h"
#include "rtos/Thread.h"

#include "CoreBattery.h"
#include "CoreMotor.h"
#include "CorePwm.h"
#include "FATFileSystem.h"
#include "HelloWorld.h"
#include "LogKit.h"
#include "SDBlockDevice.h"
#include "Utils.h"

using namespace leka;
using namespace std::chrono;

constexpr auto PRINT_BATTERY_VOLTAGE_LOG = false;
constexpr auto fileName =
std::array<char, 36> {"2021_03_30-Battery-Voltage-Log.csv"}; // YYYY_MM_DD-Battery-Voltage-Log.csv

const FileManager sd;

auto main() -> int
{
static auto serial = mbed::BufferedSerial(USBTX, USBRX, 115200);
leka::logger::set_print_function([](const char *str, size_t size) { serial.write(str, size); });

auto charge_input = mbed::DigitalIn {PinName::BATTERY_CHARGE_STATUS};
auto corebattery = leka::CoreBattery {PinName::BATTERY_VOLTAGE, charge_input};

log_info("Hello, World!\n\n");

HelloWorld hello;
hello.start();

auto battery = CoreBattery {PinName::BATTERY_VOLTAGE};

auto battery_thread = rtos::Thread {};
auto battery_lambda = [&battery] {
auto now = [] { return static_cast<int>(rtos::Kernel::Clock::now().time_since_epoch().count()); };
auto voltage = [&] { return battery.getVoltage(); };

auto buffer = std::array<char, 64> {};

while (true) {
auto length = snprintf(buffer.data(), std::size(buffer), "%i, %f\n", now(), voltage());
writeSDFile(fileName.data(), buffer.data(), length);
rtos::ThisThread::sleep_for(30s);
}
};

auto motor_left_dir_1 = mbed::DigitalOut {MOTOR_LEFT_DIRECTION_1};
auto morot_left_dir_2 = mbed::DigitalOut {MOTOR_LEFT_DIRECTION_2};
auto morot_left_speed = CorePwm {MOTOR_LEFT_PWM};

auto motor_right_dir_1 = mbed::DigitalOut {MOTOR_RIGHT_DIRECTION_1};
auto morot_right_dir_2 = mbed::DigitalOut {MOTOR_RIGHT_DIRECTION_2};
auto morot_right_speed = CorePwm {MOTOR_RIGHT_PWM};

auto motor_left = CoreMotor {motor_left_dir_1, morot_left_dir_2, morot_left_speed};
auto motor_right = CoreMotor {motor_right_dir_1, morot_right_dir_2, morot_right_speed};

auto motors_thread = rtos::Thread {};
auto motors_lambda = [&motor_left, &motor_right] {
while (true) {
motor_left.spin(Rotation::counterClockwise, 1.0f);
motor_right.spin(Rotation::counterClockwise, 1.0f);

rtos::ThisThread::sleep_for(50s);

motor_left.stop();
motor_right.stop();

rtos::ThisThread::sleep_for(10s);
}
};

rtos::ThisThread::sleep_for(2s);

log_info("Starting...");

rtos::ThisThread::sleep_for(1s);

if (PRINT_BATTERY_VOLTAGE_LOG) {
auto buffer = std::array<char, 2048> {};
auto length = readSdFile(fileName.data(), buffer.data(), std::size(buffer));

log_info("Battery data:\n");
serial.write(buffer.data(), length);
} else {
battery_thread.start(battery_lambda);
motors_thread.start(motors_lambda);
}

while (true) {
log_info("Main thread is running...");
rtos::ThisThread::sleep_for(5s);
if (corebattery.isCharging()) {
log_info("Battery at %.2fV and in charge.", corebattery.getVoltage());
} else {
log_info("Battery at %.2fV.", corebattery.getVoltage());
}
rtos::ThisThread::sleep_for(1s);
}
}
26 changes: 26 additions & 0 deletions tests/unit/mocks/mocks/mbed/DigitalIn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Leka - LekaOS
// Copyright 2021 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#ifndef _LEKA_OS_MBED_MOCK_DIGITAL_IN_H_
#define _LEKA_OS_MBED_MOCK_DIGITAL_IN_H_

#include "PinNames.h"

#include "drivers/interfaces/InterfaceDigitalIn.h"

#include "gmock/gmock.h"

namespace mbed::mock {

class DigitalIn : public mbed::interface::DigitalIn
{
public:
MOCK_METHOD(int, read, (), (override));
MOCK_METHOD(void, mode, (PinMode pull), (override));
MOCK_METHOD(int, is_connected, (), (override));
};

} // namespace mbed::mock

#endif // _LEKA_OS_MBED_MOCK_DIGITAL_IN_H_

0 comments on commit b1d8287

Please sign in to comment.