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