Hooks into the CE pin of TP4056 to add some extra features - overvolt cutoff - overtime cutoff - overtemperature cutoff (by use of MCP9808) - info on little OLED screen - battery presence detection Future features - current detection and cutoff (waiting for INA219 breakout for this) - Runtime configurable parameters by serial - Send stats over serial to desktop application Known flaws - see readme Circuit schematic coming soon (tm), see readme Designed and tested for nucleo F303RE but should be easily adaptable to any board. License: GPL v3

Dependencies:   OLED_SSD1306 MCP9808

Committer:
kuutei
Date:
Tue Sep 15 03:54:15 2020 -0400
Revision:
10:4ac5d8748268
Parent:
9:272f6963c3b8
add serialCLI lib

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kuutei 0:56122e281547 1 #include "mbed.h"
kuutei 0:56122e281547 2 #include "platform/mbed_thread.h"
kuutei 0:56122e281547 3
kuutei 0:56122e281547 4 #include <string>
kuutei 0:56122e281547 5 #include <iomanip>
kuutei 0:56122e281547 6 #include <sstream>
kuutei 0:56122e281547 7
kuutei 0:56122e281547 8 #include "SSD1306I2C.h"
kuutei 4:f63aab9fec77 9 #include "MCP9808.h"
kuutei 0:56122e281547 10
kuutei 9:272f6963c3b8 11 #include "serialCLI.h"
kuutei 9:272f6963c3b8 12
kuutei 0:56122e281547 13 #define OLED_ADR 0x3C
kuutei 0:56122e281547 14 #define OLED_SDA I2C_SDA
kuutei 0:56122e281547 15 #define OLED_SCL I2C_SCL
kuutei 0:56122e281547 16
kuutei 9:272f6963c3b8 17 #define MCP9808_SDA I2C_SDA
kuutei 9:272f6963c3b8 18 #define MCP9808_SCL I2C_SCL
kuutei 9:272f6963c3b8 19 //#define MCP9808_SDA PB_5
kuutei 9:272f6963c3b8 20 //#define MCP9808_SCL PA_8
kuutei 4:f63aab9fec77 21
kuutei 5:c07438005b16 22 #define BAT_SAMPLERATE 1000
kuutei 1:7749656733dd 23
kuutei 0:56122e281547 24
kuutei 0:56122e281547 25 // Blinking rate in milliseconds
kuutei 0:56122e281547 26 #define BLINKING_RATE_MS 5000
kuutei 0:56122e281547 27
kuutei 0:56122e281547 28 #define VOLTAGE_DIVIDER_R1 99800
kuutei 0:56122e281547 29 #define VOLTAGE_DIVIDER_R2 99100
kuutei 0:56122e281547 30
kuutei 9:272f6963c3b8 31 //Buffered UARTSerial used, but any buffered serial object with read() and write() should work
kuutei 9:272f6963c3b8 32 UARTSerial pc(USBTX,USBRX,115200);
kuutei 9:272f6963c3b8 33 serialCLI sCLI(&pc);
kuutei 0:56122e281547 34
kuutei 1:7749656733dd 35 SSD1306I2C oled_i2c(OLED_ADR, OLED_SDA, OLED_SCL);
kuutei 1:7749656733dd 36
kuutei 4:f63aab9fec77 37 //DS1820 ds1820_sensor(DS18B20_DATA);
kuutei 4:f63aab9fec77 38 //sda,scl,address,freq
kuutei 9:272f6963c3b8 39 //TODO: write MCP9808 constructor that doesn't change frequency
kuutei 4:f63aab9fec77 40 MCP9808 myMCP9808 ( MCP9808_SDA, MCP9808_SCL, MCP9808::MCP9808_ADDRESS_0, 400000 ); // I2C_SDA | I2C_SCL
kuutei 1:7749656733dd 41
kuutei 0:56122e281547 42 //DigitalIn mybutton(USER_BUTTON);
kuutei 0:56122e281547 43 AnalogIn divider_analogin(A0);
kuutei 0:56122e281547 44 AnalogIn vref(ADC_VREF);
kuutei 0:56122e281547 45
kuutei 0:56122e281547 46 //AnalogIn adc_refint(VREF_INT);
kuutei 0:56122e281547 47
kuutei 9:272f6963c3b8 48 //Open drain with no pull up/down since the CE pin is pulled up to 5V
kuutei 9:272f6963c3b8 49 //This also means a 5V tolereant pin (FT or FTf) must be used
kuutei 9:272f6963c3b8 50 DigitalInOut tp4056ChipEnable(PA_14, PIN_OUTPUT, OpenDrainNoPull, 0);
kuutei 1:7749656733dd 51 bool TP4056ChargingState = false; //reflects internal state of TP4056 control thread
kuutei 5:c07438005b16 52 uint64_t chargingTimePassed = 0; //reflects charge time recorded by control thread
kuutei 0:56122e281547 53
kuutei 9:272f6963c3b8 54 DigitalIn tp4056ChargeDone(PA_1);
kuutei 9:272f6963c3b8 55
kuutei 0:56122e281547 56 InterruptIn chargeButton(USER_BUTTON);
kuutei 1:7749656733dd 57 volatile bool buttonPressed = false; //set to true by button ISR, set to false when acknowledged
kuutei 0:56122e281547 58
kuutei 1:7749656733dd 59 bool batteryPresenceState = false;
kuutei 0:56122e281547 60
kuutei 0:56122e281547 61 float bat_voltage = -1.11;
kuutei 0:56122e281547 62 float bat_voltage_avg = -1.11;
kuutei 0:56122e281547 63 float vddref_voltage = -1.11;
kuutei 0:56122e281547 64
kuutei 9:272f6963c3b8 65 float temperatureSenseC = -301.0;
kuutei 0:56122e281547 66
kuutei 0:56122e281547 67 //Configurable protection conditions
kuutei 9:272f6963c3b8 68 float MAX_VOLTAGE = 4.20; //default: no more than ___
kuutei 0:56122e281547 69 float MIN_VOLTAGE = 2.5; //default: no less than 2.5v
kuutei 9:272f6963c3b8 70 float MIN_DETECT_VOLTAGE = 0.5; //default: no less than 0.5v
kuutei 5:c07438005b16 71 float MAX_TEMPERATURE = 35.0; //default: no more than 35C at the temperature sensor
kuutei 0:56122e281547 72 float MIN_TEMPERATURE = 10.0; //default: no less than 10C
kuutei 0:56122e281547 73 uint64_t MAX_TIME_ms = 3600000; //default: no more than 1h
kuutei 0:56122e281547 74
kuutei 0:56122e281547 75
kuutei 0:56122e281547 76 void blinkled()
kuutei 0:56122e281547 77 {
kuutei 0:56122e281547 78 // Initialise the digital pin LED1 as an output
kuutei 0:56122e281547 79 DigitalOut led(LED1);
kuutei 0:56122e281547 80
kuutei 0:56122e281547 81 while (true) {
kuutei 0:56122e281547 82 led = !led;
kuutei 0:56122e281547 83 ThisThread::sleep_for(BLINKING_RATE_MS);
kuutei 0:56122e281547 84 }
kuutei 0:56122e281547 85 }
kuutei 0:56122e281547 86
kuutei 0:56122e281547 87
kuutei 0:56122e281547 88 // Button to initiate / stop charging
kuutei 0:56122e281547 89 // Called on button rise
kuutei 0:56122e281547 90 // Set to true to signal to EN control thread that button was pressed
kuutei 1:7749656733dd 91 void buttonISR()
kuutei 0:56122e281547 92 {
kuutei 1:7749656733dd 93 static uint64_t lastButtonPushTime = 0;
kuutei 1:7749656733dd 94
kuutei 0:56122e281547 95 uint64_t now = Kernel::get_ms_count();
kuutei 1:7749656733dd 96
kuutei 0:56122e281547 97 const uint64_t buttonMinimumWaitTime = 500; //minimum 500 ms wait between button pushes
kuutei 0:56122e281547 98
kuutei 9:272f6963c3b8 99 if(now - lastButtonPushTime > buttonMinimumWaitTime)
kuutei 0:56122e281547 100 {
kuutei 0:56122e281547 101 buttonPressed = true;
kuutei 1:7749656733dd 102 lastButtonPushTime = now;
kuutei 0:56122e281547 103 }
kuutei 0:56122e281547 104
kuutei 0:56122e281547 105 }
kuutei 0:56122e281547 106
kuutei 0:56122e281547 107 float readRefVoltage()
kuutei 0:56122e281547 108 {
kuutei 0:56122e281547 109 double vdd;
kuutei 0:56122e281547 110 double vdd_calibed;
kuutei 0:56122e281547 111 double vref_calibed;
kuutei 0:56122e281547 112 double vref_f;
kuutei 0:56122e281547 113 uint16_t vref_u16;
kuutei 0:56122e281547 114 uint16_t vref_cal;
kuutei 0:56122e281547 115
kuutei 0:56122e281547 116 vref_cal= *((uint16_t*)VREFINT_CAL_ADDR); //F303RE
kuutei 0:56122e281547 117
kuutei 0:56122e281547 118 vref_u16 = vref.read_u16();
kuutei 0:56122e281547 119 //1.22 comes from 3.3 * 1524 / 4095 - voltage at calibration time times calibration measurement divided by maximum counts
kuutei 0:56122e281547 120 //vdd = 1.228132 / vref.read();
kuutei 0:56122e281547 121 vdd = 3.3 * (double)vref_cal / 4095.0 / vref.read();
kuutei 0:56122e281547 122
kuutei 9:272f6963c3b8 123 return vdd;
kuutei 0:56122e281547 124 }
kuutei 0:56122e281547 125
kuutei 0:56122e281547 126 //Reads voltage divider, and uses internal calibrated reference voltage to calculate real voltage
kuutei 0:56122e281547 127 //Not 100% accurate, but better than assuming 3.3v
kuutei 0:56122e281547 128 float readVoltageDivider_Calibrated()
kuutei 0:56122e281547 129 {
kuutei 0:56122e281547 130 uint16_t vref_cal= *((uint16_t*)VREFINT_CAL_ADDR); //factory calibration value for 3.3v
kuutei 0:56122e281547 131 uint16_t vref_u16 = vref.read_u16(); //read the internal voltage calibration
kuutei 0:56122e281547 132 float vdd = 3.3 * (double)vref_cal / 4095.0 / vref.read();
kuutei 0:56122e281547 133
kuutei 0:56122e281547 134 //ain.read() returns float value between 0 and 1
kuutei 0:56122e281547 135 float reading = divider_analogin.read();
kuutei 0:56122e281547 136
kuutei 9:272f6963c3b8 137 //sCLI.printf("raw reading: %f\r\n", reading);
kuutei 0:56122e281547 138
kuutei 0:56122e281547 139 return reading * vdd * (VOLTAGE_DIVIDER_R1 + VOLTAGE_DIVIDER_R2) / VOLTAGE_DIVIDER_R2;
kuutei 0:56122e281547 140
kuutei 0:56122e281547 141 }
kuutei 0:56122e281547 142
kuutei 0:56122e281547 143 //Measurement assuming 3.3v - for comparison to calibrated reading only
kuutei 0:56122e281547 144 //Can be very inaccurate as nucleo voltage regulator drops as far as 3.2v
kuutei 0:56122e281547 145 float readVoltageDivider_3v3()
kuutei 0:56122e281547 146 {
kuutei 8:c781f4ae7eb3 147 float vdd = 3.3;
kuutei 0:56122e281547 148
kuutei 0:56122e281547 149 //ain.read() returns float value between 0 and 1
kuutei 0:56122e281547 150 float reading = divider_analogin.read();
kuutei 0:56122e281547 151
kuutei 0:56122e281547 152 return reading * vdd * (VOLTAGE_DIVIDER_R1 + VOLTAGE_DIVIDER_R2) / VOLTAGE_DIVIDER_R2;
kuutei 0:56122e281547 153
kuutei 0:56122e281547 154 }
kuutei 0:56122e281547 155
kuutei 0:56122e281547 156 // Reads DS18B20 sense temperature
kuutei 0:56122e281547 157 void TemperatureInputThread()
kuutei 5:c07438005b16 158 {
kuutei 4:f63aab9fec77 159 MCP9808::MCP9808_status_t aux;
kuutei 4:f63aab9fec77 160 MCP9808::MCP9808_config_reg_t myMCP9808_Config;
kuutei 4:f63aab9fec77 161 MCP9808::MCP9808_data_t myMCP9808_Data;
kuutei 4:f63aab9fec77 162
kuutei 4:f63aab9fec77 163 // Shutdown the device, low-power mode enabled
kuutei 4:f63aab9fec77 164 aux = myMCP9808.MCP9808_GetCONFIG ( &myMCP9808_Config );
kuutei 4:f63aab9fec77 165
kuutei 4:f63aab9fec77 166 myMCP9808_Config.shdn = MCP9808::CONFIG_SHDN_SHUTDOWN;
kuutei 4:f63aab9fec77 167 aux = myMCP9808.MCP9808_SetCONFIG ( myMCP9808_Config );
kuutei 4:f63aab9fec77 168
kuutei 4:f63aab9fec77 169 // Get manufacturer ID
kuutei 4:f63aab9fec77 170 aux = myMCP9808.MCP9808_GetManufacturerID ( &myMCP9808_Data );
kuutei 4:f63aab9fec77 171
kuutei 4:f63aab9fec77 172 // Get device ID and device revision
kuutei 4:f63aab9fec77 173 aux = myMCP9808.MCP9808_GetDeviceID ( &myMCP9808_Data );
kuutei 4:f63aab9fec77 174
kuutei 4:f63aab9fec77 175 // Configure the device
kuutei 4:f63aab9fec77 176 // - T_UPPER and T_LOWER limit hysteresis at 0C
kuutei 4:f63aab9fec77 177 // - Continuous conversion mode
kuutei 4:f63aab9fec77 178 // - T_CRIT unlocked
kuutei 4:f63aab9fec77 179 // - Window lock unlocked
kuutei 4:f63aab9fec77 180 // - Alert output control disabled
kuutei 4:f63aab9fec77 181 // - Alert output select: Alert for T_UPPER, T_LOWER and T_CRIT
kuutei 4:f63aab9fec77 182 // - Alert output polaruty: Active-low
kuutei 4:f63aab9fec77 183 // - Alert output mode: Comparator output
kuutei 4:f63aab9fec77 184 //
kuutei 4:f63aab9fec77 185 myMCP9808_Config.t_hyst = MCP9808::CONFIG_T_HYST_0_C;
kuutei 4:f63aab9fec77 186 myMCP9808_Config.shdn = MCP9808::CONFIG_SHDN_CONTINUOUS_CONVERSION;
kuutei 4:f63aab9fec77 187 myMCP9808_Config.t_crit = MCP9808::CONFIG_CRIT_LOCK_UNLOCKED;
kuutei 4:f63aab9fec77 188 myMCP9808_Config.t_win_lock = MCP9808::CONFIG_WIN_LOCK_UNLOCKED;
kuutei 4:f63aab9fec77 189 myMCP9808_Config.alert_cnt = MCP9808::CONFIG_ALERT_CNT_DISABLED;
kuutei 4:f63aab9fec77 190 myMCP9808_Config.alert_sel = MCP9808::CONFIG_ALERT_SEL_TUPPER_TLOWER_TCRIT;
kuutei 4:f63aab9fec77 191 myMCP9808_Config.alert_pol = MCP9808::CONFIG_ALERT_POL_ACTIVE_LOW;
kuutei 4:f63aab9fec77 192 myMCP9808_Config.alert_mod = MCP9808::CONFIG_ALERT_MOD_COMPARATOR_OUTPUT;
kuutei 4:f63aab9fec77 193 aux = myMCP9808.MCP9808_SetCONFIG ( myMCP9808_Config );
kuutei 4:f63aab9fec77 194
kuutei 4:f63aab9fec77 195 // Set resolution: +0.0625C ( t_CON ~ 250ms )
kuutei 4:f63aab9fec77 196 myMCP9808_Data.resolution = MCP9808::RESOLUTION_0_0625_C;
kuutei 4:f63aab9fec77 197 aux = myMCP9808.MCP9808_SetResolution ( myMCP9808_Data );
kuutei 4:f63aab9fec77 198
kuutei 4:f63aab9fec77 199
kuutei 4:f63aab9fec77 200 while(true)
kuutei 4:f63aab9fec77 201 {
kuutei 4:f63aab9fec77 202 // Get ambient temperature
kuutei 4:f63aab9fec77 203 aux = myMCP9808.MCP9808_GetTA ( &myMCP9808_Data );
kuutei 4:f63aab9fec77 204
kuutei 9:272f6963c3b8 205 //sCLI.printf ( "T: %0.4f C\r\n", myMCP9808_Data.t_a );
kuutei 9:272f6963c3b8 206 temperatureSenseC = myMCP9808_Data.t_a;
kuutei 0:56122e281547 207
kuutei 0:56122e281547 208 ThisThread::sleep_for(250);
kuutei 4:f63aab9fec77 209 }
kuutei 4:f63aab9fec77 210
kuutei 0:56122e281547 211
kuutei 0:56122e281547 212 }
kuutei 0:56122e281547 213
kuutei 0:56122e281547 214 // Detect battery presence by voltage activity
kuutei 9:272f6963c3b8 215 // TODO: voltage must be above threshold for a minimum time?
kuutei 2:18d8f9d3286c 216 void BatteryPresenceThread()
kuutei 0:56122e281547 217 {
kuutei 2:18d8f9d3286c 218 while(true)
kuutei 0:56122e281547 219 {
kuutei 2:18d8f9d3286c 220 if(bat_voltage_avg > MIN_DETECT_VOLTAGE)
kuutei 2:18d8f9d3286c 221 {
kuutei 2:18d8f9d3286c 222 batteryPresenceState = true;
kuutei 2:18d8f9d3286c 223 }
kuutei 2:18d8f9d3286c 224 else
kuutei 2:18d8f9d3286c 225 {
kuutei 2:18d8f9d3286c 226 batteryPresenceState = false;
kuutei 2:18d8f9d3286c 227 }
kuutei 2:18d8f9d3286c 228
kuutei 2:18d8f9d3286c 229 ThisThread::sleep_for(250);
kuutei 1:7749656733dd 230 }
kuutei 0:56122e281547 231 }
kuutei 0:56122e281547 232
kuutei 0:56122e281547 233 void TP4056ControlThread()
kuutei 0:56122e281547 234 {
kuutei 0:56122e281547 235 //uint64_t now = Kernel::get_ms_count();
kuutei 0:56122e281547 236 uint64_t chargeStartTime = 0;
kuutei 0:56122e281547 237
kuutei 0:56122e281547 238 bool enableCharging = false; //internal variable to track whether TP4056 CE should be enabled or not
kuutei 0:56122e281547 239
kuutei 0:56122e281547 240
kuutei 0:56122e281547 241 while(true)
kuutei 0:56122e281547 242 {
kuutei 0:56122e281547 243 //First check conditions to see if charging should be enabled or disabled
kuutei 9:272f6963c3b8 244 bool temperature_en = ( ( temperatureSenseC < MAX_TEMPERATURE ) && ( temperatureSenseC > MIN_TEMPERATURE ) );
kuutei 9:272f6963c3b8 245 //bool temperature_en = true; //override as sensor code is bugged
kuutei 1:7749656733dd 246 bool voltage_en = ( ( bat_voltage_avg < MAX_VOLTAGE ) && ( bat_voltage_avg > MIN_VOLTAGE ) );
kuutei 2:18d8f9d3286c 247 bool presence_en = batteryPresenceState;
kuutei 5:c07438005b16 248 bool charge_time_exceeded = ( chargingTimePassed > MAX_TIME_ms );
kuutei 1:7749656733dd 249
kuutei 0:56122e281547 250 //Charging can be enabled if battery is present, no protections triggered, and user starts the charge
kuutei 0:56122e281547 251 if(enableCharging == false)
kuutei 9:272f6963c3b8 252 {
kuutei 9:272f6963c3b8 253 //button must be pressed to start charge, but can also be pressed when eg battery not inserted
kuutei 9:272f6963c3b8 254 if(buttonPressed)
kuutei 9:272f6963c3b8 255 {
kuutei 9:272f6963c3b8 256 if( voltage_en && temperature_en && presence_en )
kuutei 9:272f6963c3b8 257 {
kuutei 9:272f6963c3b8 258 enableCharging = true;
kuutei 9:272f6963c3b8 259 chargeStartTime = Kernel::get_ms_count();
kuutei 9:272f6963c3b8 260 }
kuutei 9:272f6963c3b8 261
kuutei 9:272f6963c3b8 262 //regardless of if charging was started, acknowledge the button press
kuutei 1:7749656733dd 263 buttonPressed = false;
kuutei 1:7749656733dd 264 }
kuutei 9:272f6963c3b8 265
kuutei 1:7749656733dd 266 }
kuutei 1:7749656733dd 267 //Charging must be stopped if overvoltage, overtemperature, overtime, battery removed, or user pushes button
kuutei 5:c07438005b16 268 //Charging time passed is maintained, but will be reset if user starts charge again
kuutei 5:c07438005b16 269 //TODO: Only reset charge time once battery removed?
kuutei 1:7749656733dd 270 else
kuutei 0:56122e281547 271 {
kuutei 1:7749656733dd 272 //Disable charging if any protection condition is triggered
kuutei 2:18d8f9d3286c 273 if( !voltage_en || !temperature_en || !presence_en || charge_time_exceeded )
kuutei 1:7749656733dd 274 {
kuutei 1:7749656733dd 275 enableCharging = false;
kuutei 1:7749656733dd 276 }
kuutei 1:7749656733dd 277 //or if user pushed button
kuutei 1:7749656733dd 278 else if(buttonPressed)
kuutei 3:0bad8eec80ee 279 {
kuutei 3:0bad8eec80ee 280 enableCharging = false;
kuutei 1:7749656733dd 281 buttonPressed = false;
kuutei 1:7749656733dd 282 }
kuutei 5:c07438005b16 283
kuutei 0:56122e281547 284 }
kuutei 0:56122e281547 285
kuutei 0:56122e281547 286
kuutei 0:56122e281547 287 //With charge state calculated, realize it on the CE pin
kuutei 5:c07438005b16 288 //Allow pullup to '5V' to enable charging at CE pin
kuutei 0:56122e281547 289 if(enableCharging)
kuutei 0:56122e281547 290 {
kuutei 5:c07438005b16 291 //Using HiZ still results in internal clamping diode activating, resulting in 3.6v
kuutei 9:272f6963c3b8 292 tp4056ChipEnable.write(1); //open drain mode -> HiZ
kuutei 5:c07438005b16 293
kuutei 5:c07438005b16 294 //Continually update surpassed charging time if it is enabled
kuutei 5:c07438005b16 295 chargingTimePassed = Kernel::get_ms_count() - chargeStartTime ;
kuutei 0:56122e281547 296 }
kuutei 5:c07438005b16 297 //To disable charging, open drain the CE pin -> 0V
kuutei 0:56122e281547 298 else
kuutei 0:56122e281547 299 {
kuutei 9:272f6963c3b8 300 tp4056ChipEnable.write(0); //open drain, pull to GND -> overpull 470k pullup that brings 5V to CE
kuutei 0:56122e281547 301 }
kuutei 0:56122e281547 302
kuutei 0:56122e281547 303 //Update flag that indicates state of TP4056 CE pin to other threads
kuutei 1:7749656733dd 304 TP4056ChargingState = enableCharging;
kuutei 0:56122e281547 305
kuutei 2:18d8f9d3286c 306 ThisThread::sleep_for(100);
kuutei 0:56122e281547 307 }
kuutei 0:56122e281547 308 }
kuutei 0:56122e281547 309
kuutei 0:56122e281547 310 void oledOutputThread()
kuutei 0:56122e281547 311 {
kuutei 5:c07438005b16 312 std::stringstream volts_stream, max_volts_stream;
kuutei 5:c07438005b16 313 std::stringstream temperature_stream;
kuutei 5:c07438005b16 314 std::stringstream chargetimepassed_stream;
kuutei 5:c07438005b16 315 std::stringstream chargetimemax_stream;
kuutei 5:c07438005b16 316
kuutei 0:56122e281547 317 while(true)
kuutei 5:c07438005b16 318 {
kuutei 5:c07438005b16 319 volts_stream.str("");
kuutei 5:c07438005b16 320 volts_stream << std::fixed << std::setprecision(2) << bat_voltage_avg;
kuutei 5:c07438005b16 321
kuutei 5:c07438005b16 322 max_volts_stream.str("");
kuutei 5:c07438005b16 323 max_volts_stream << std::fixed << std::setprecision(2) << MAX_VOLTAGE;
kuutei 5:c07438005b16 324
kuutei 5:c07438005b16 325 temperature_stream.str("");
kuutei 9:272f6963c3b8 326 temperature_stream << std::fixed << std::setprecision(2) << temperatureSenseC;
kuutei 5:c07438005b16 327
kuutei 5:c07438005b16 328 chargetimepassed_stream.str("");
kuutei 5:c07438005b16 329 chargetimepassed_stream << std::fixed << std::setprecision(1) << (float(chargingTimePassed)/1000/60);
kuutei 5:c07438005b16 330
kuutei 5:c07438005b16 331 chargetimemax_stream.str("");
kuutei 5:c07438005b16 332 chargetimemax_stream << std::fixed << std::setprecision(1) << (float(MAX_TIME_ms)/1000/60);
kuutei 5:c07438005b16 333
kuutei 5:c07438005b16 334 std::string str_temp = "";
kuutei 5:c07438005b16 335
kuutei 5:c07438005b16 336 //clear the screen first
kuutei 0:56122e281547 337 oled_i2c.clear();
kuutei 0:56122e281547 338
kuutei 5:c07438005b16 339 //If no battery present, show screen indicating one should be inserted
kuutei 5:c07438005b16 340 if( !batteryPresenceState )
kuutei 5:c07438005b16 341 {
kuutei 5:c07438005b16 342 oled_i2c.setFont(ArialMT_Plain_16);
kuutei 5:c07438005b16 343 oled_i2c.drawString(0, 0, "Insert battery");
kuutei 5:c07438005b16 344
kuutei 5:c07438005b16 345 str_temp = volts_stream.str() + "v " + temperature_stream.str() + "ºC";
kuutei 5:c07438005b16 346 oled_i2c.drawString(0,16, str_temp.c_str() );
kuutei 5:c07438005b16 347
kuutei 0:56122e281547 348 }
kuutei 5:c07438005b16 349 //battery present, show screen with voltage, CE status, temperature
kuutei 5:c07438005b16 350 //Further splits into charge enabled or disabled - if charge enabled, show time so far and time limit
kuutei 5:c07438005b16 351 else
kuutei 5:c07438005b16 352 {
kuutei 5:c07438005b16 353 oled_i2c.setFont(ArialMT_Plain_16);
kuutei 5:c07438005b16 354 if(TP4056ChargingState){
kuutei 5:c07438005b16 355 str_temp = "Charge to " + max_volts_stream.str();
kuutei 5:c07438005b16 356 oled_i2c.drawString(0, 0, str_temp.c_str());
kuutei 5:c07438005b16 357 //TODO: shows 'charge complete' if above threshold
kuutei 5:c07438005b16 358 } else {
kuutei 5:c07438005b16 359 oled_i2c.drawString(0, 0, "Push to charge");
kuutei 5:c07438005b16 360 }
kuutei 5:c07438005b16 361
kuutei 5:c07438005b16 362 str_temp = volts_stream.str() + "v " + temperature_stream.str() + "ºC";
kuutei 5:c07438005b16 363 oled_i2c.drawString(0, 16, str_temp.c_str() );
kuutei 5:c07438005b16 364
kuutei 5:c07438005b16 365 str_temp = "T: " + chargetimepassed_stream.str() + "m/" + chargetimemax_stream.str() + "m";
kuutei 5:c07438005b16 366 oled_i2c.drawString(0,16*2, str_temp.c_str() );
kuutei 5:c07438005b16 367
kuutei 5:c07438005b16 368
kuutei 5:c07438005b16 369 //std::string voltage_output = "Voltage: "+std::to_string(bat_voltage);
kuutei 5:c07438005b16 370 //str_temp = "Voltage: " + volts_stream.str();
kuutei 5:c07438005b16 371 ///oled_i2c.drawString(0, 16, str_temp.c_str() );
kuutei 5:c07438005b16 372 //str_temp = "Temp: " + temperature_stream.str();
kuutei 5:c07438005b16 373 //oled_i2c.drawString(0,16*2, str_temp.c_str() );
kuutei 5:c07438005b16 374
kuutei 5:c07438005b16 375 }
kuutei 5:c07438005b16 376 oled_i2c.display();
kuutei 0:56122e281547 377
kuutei 0:56122e281547 378 ThisThread::sleep_for(250);
kuutei 0:56122e281547 379 }
kuutei 0:56122e281547 380
kuutei 0:56122e281547 381 }
kuutei 0:56122e281547 382
kuutei 0:56122e281547 383 int main()
kuutei 0:56122e281547 384 {
kuutei 9:272f6963c3b8 385 //sCLI.baud(115200);
kuutei 9:272f6963c3b8 386 sCLI.printf("Started\r\n");
kuutei 0:56122e281547 387
kuutei 0:56122e281547 388 //BUILT IN LED THREAD
kuutei 0:56122e281547 389 Thread led1;
kuutei 0:56122e281547 390 //osStatus err = led1.start(&blinkled);
kuutei 0:56122e281547 391 led1.start(blinkled);
kuutei 0:56122e281547 392
kuutei 0:56122e281547 393 // CONFIGURE TP4056 CE CONTROL FOR OPEN DRAIN WITH EXTERNAL 5V PULLUP
kuutei 0:56122e281547 394 //https://forums.mbed.com/t/how-to-configure-open-drain-output-pin-on-stm32/7007
kuutei 9:272f6963c3b8 395 tp4056ChipEnable.mode(OpenDrainNoPull);
kuutei 0:56122e281547 396
kuutei 2:18d8f9d3286c 397 // TEMPERATURE INPUT THREAD
kuutei 9:272f6963c3b8 398 Thread temperatureSenseC_thread;
kuutei 9:272f6963c3b8 399 temperatureSenseC_thread.start(TemperatureInputThread);
kuutei 2:18d8f9d3286c 400
kuutei 0:56122e281547 401 // CHARGE ENABLE CONTROL THREAD
kuutei 0:56122e281547 402 Thread tp4056_control_thread;
kuutei 0:56122e281547 403 tp4056_control_thread.start(TP4056ControlThread);
kuutei 0:56122e281547 404
kuutei 2:18d8f9d3286c 405 // PRESENCE DETECTION THREAD
kuutei 2:18d8f9d3286c 406 Thread bat_presence_thread;
kuutei 2:18d8f9d3286c 407 bat_presence_thread.start(BatteryPresenceThread);
kuutei 2:18d8f9d3286c 408
kuutei 0:56122e281547 409 //OLED 128x64 INIT AND THREAD
kuutei 0:56122e281547 410 //initialize ssd1306 on i2c0
kuutei 0:56122e281547 411 oled_i2c.init();
kuutei 9:272f6963c3b8 412 //oled_i2c.flipScreenVertically();
kuutei 0:56122e281547 413 oled_i2c.setFont(ArialMT_Plain_16);
kuutei 0:56122e281547 414 oled_i2c.drawString(0,0,"init!");
kuutei 9:272f6963c3b8 415 oled_i2c.setBrightness(64);
kuutei 0:56122e281547 416 oled_i2c.display();
kuutei 0:56122e281547 417
kuutei 0:56122e281547 418 Thread oled_thread;
kuutei 0:56122e281547 419 oled_thread.start(oledOutputThread);
kuutei 0:56122e281547 420
kuutei 0:56122e281547 421 // START/STOP CHARGE BUTTON
kuutei 0:56122e281547 422 chargeButton.rise(&buttonISR);
kuutei 0:56122e281547 423
kuutei 0:56122e281547 424
kuutei 0:56122e281547 425 float bat_voltage_min;
kuutei 0:56122e281547 426 float bat_voltage_max;
kuutei 0:56122e281547 427 float bat_voltage_avg_sum;
kuutei 0:56122e281547 428
kuutei 0:56122e281547 429 uint64_t now = Kernel::get_ms_count();
kuutei 9:272f6963c3b8 430 uint64_t lastADCAverageStartTime = now;
kuutei 9:272f6963c3b8 431 uint64_t lastADCReadStartTime = now;
kuutei 9:272f6963c3b8 432 uint64_t time_tmp = 0;
kuutei 0:56122e281547 433
kuutei 0:56122e281547 434 while(true)
kuutei 0:56122e281547 435 {
kuutei 0:56122e281547 436 // reset min and max values
kuutei 0:56122e281547 437 bat_voltage_min = 10.0;
kuutei 0:56122e281547 438 bat_voltage_max = -10.0;
kuutei 0:56122e281547 439 bat_voltage_avg_sum = 0.0;
kuutei 0:56122e281547 440
kuutei 9:272f6963c3b8 441 lastADCAverageStartTime = Kernel::get_ms_count();
kuutei 5:c07438005b16 442 for(int i = 0; i < BAT_SAMPLERATE; i++)
kuutei 0:56122e281547 443 {
kuutei 9:272f6963c3b8 444 lastADCReadStartTime = Kernel::get_ms_count();
kuutei 9:272f6963c3b8 445
kuutei 0:56122e281547 446 bat_voltage = readVoltageDivider_Calibrated();
kuutei 0:56122e281547 447 vddref_voltage = readRefVoltage();
kuutei 0:56122e281547 448 bat_voltage_avg_sum += bat_voltage;
kuutei 0:56122e281547 449 if( bat_voltage < bat_voltage_min )
kuutei 0:56122e281547 450 {
kuutei 0:56122e281547 451 bat_voltage_min = bat_voltage;
kuutei 0:56122e281547 452 }
kuutei 0:56122e281547 453 if( bat_voltage > bat_voltage_max )
kuutei 0:56122e281547 454 {
kuutei 0:56122e281547 455 bat_voltage_max = bat_voltage;
kuutei 0:56122e281547 456 }
kuutei 0:56122e281547 457
kuutei 5:c07438005b16 458 //sleep appropriate interval to meet specified sample rate, evenly spaced over 1s
kuutei 5:c07438005b16 459 //This sleep is also where the other threads run
kuutei 9:272f6963c3b8 460 //sleep amount calculated as: (nominal time between bat voltage reads) - (time it has taken to do the current measurement)
kuutei 9:272f6963c3b8 461 time_tmp = 1000/BAT_SAMPLERATE - (lastADCReadStartTime - Kernel::get_ms_count()) ;
kuutei 9:272f6963c3b8 462 //if we have already exceeded our time window, sleep nominal time as sample rate is not possible to meet
kuutei 9:272f6963c3b8 463 if( time_tmp < 0 )
kuutei 9:272f6963c3b8 464 {
kuutei 9:272f6963c3b8 465 ThisThread::sleep_for(1000/BAT_SAMPLERATE);
kuutei 9:272f6963c3b8 466 }
kuutei 9:272f6963c3b8 467 else
kuutei 9:272f6963c3b8 468 {
kuutei 9:272f6963c3b8 469 //otherwise, sleep the remaining time in our window to try to exactly meet our sample rate
kuutei 9:272f6963c3b8 470 ThisThread::sleep_for(time_tmp);
kuutei 9:272f6963c3b8 471 }
kuutei 0:56122e281547 472 }
kuutei 9:272f6963c3b8 473
kuutei 0:56122e281547 474 now = Kernel::get_ms_count();
kuutei 5:c07438005b16 475 bat_voltage_avg = bat_voltage_avg_sum / BAT_SAMPLERATE;
kuutei 0:56122e281547 476
kuutei 5:c07438005b16 477 //TODO: Dedicated serial thread, output messages when conditions change (eg OTP, OVP, charge started/stopped)
kuutei 9:272f6963c3b8 478 sCLI.printf("\r\nADC0 Last Reading: %6.4f\r\n", bat_voltage);
kuutei 9:272f6963c3b8 479 sCLI.printf("ADC0 1s min: %6.4f\r\n", bat_voltage_min);
kuutei 9:272f6963c3b8 480 sCLI.printf("ADC0 1s max: %6.4f\r\n", bat_voltage_max);
kuutei 9:272f6963c3b8 481 sCLI.printf("ADC0 1s avg: %6.4f\r\n", bat_voltage_avg);
kuutei 9:272f6963c3b8 482 sCLI.printf("ADC0 time (nominal 1000ms): %llu\r\n", (now-lastADCAverageStartTime));
kuutei 9:272f6963c3b8 483 sCLI.printf("VDDREF Reading: %6.4f\r\n", vddref_voltage);
kuutei 9:272f6963c3b8 484 sCLI.printf("Temp: %6.4f\r\n", temperatureSenseC);
kuutei 9:272f6963c3b8 485 sCLI.printf("Bat present: %d\r\n", batteryPresenceState);
kuutei 5:c07438005b16 486
kuutei 0:56122e281547 487 }
kuutei 0:56122e281547 488
kuutei 0:56122e281547 489 }