![](/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@0:56122e281547, 2020-08-24 (annotated)
- Committer:
- kuutei
- Date:
- Mon Aug 24 04:09:04 2020 +0000
- Revision:
- 0:56122e281547
- Child:
- 1:7749656733dd
adding OTP, OVP, time limits
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 | 0:56122e281547 | 9 | |
kuutei | 0:56122e281547 | 10 | #define OLED_ADR 0x3C |
kuutei | 0:56122e281547 | 11 | #define OLED_SDA I2C_SDA |
kuutei | 0:56122e281547 | 12 | #define OLED_SCL I2C_SCL |
kuutei | 0:56122e281547 | 13 | |
kuutei | 0:56122e281547 | 14 | |
kuutei | 0:56122e281547 | 15 | // Blinking rate in milliseconds |
kuutei | 0:56122e281547 | 16 | #define BLINKING_RATE_MS 5000 |
kuutei | 0:56122e281547 | 17 | |
kuutei | 0:56122e281547 | 18 | #define VOLTAGE_DIVIDER_R1 99800 |
kuutei | 0:56122e281547 | 19 | #define VOLTAGE_DIVIDER_R2 99100 |
kuutei | 0:56122e281547 | 20 | |
kuutei | 0:56122e281547 | 21 | Serial pc(USBTX,USBRX); |
kuutei | 0:56122e281547 | 22 | |
kuutei | 0:56122e281547 | 23 | //DigitalIn mybutton(USER_BUTTON); |
kuutei | 0:56122e281547 | 24 | AnalogIn divider_analogin(A0); |
kuutei | 0:56122e281547 | 25 | AnalogIn vref(ADC_VREF); |
kuutei | 0:56122e281547 | 26 | |
kuutei | 0:56122e281547 | 27 | //AnalogIn adc_refint(VREF_INT); |
kuutei | 0:56122e281547 | 28 | |
kuutei | 0:56122e281547 | 29 | DigitalInOut toggleOut(PA_14, PIN_OUTPUT, OpenDrainNoPull, 0); |
kuutei | 0:56122e281547 | 30 | bool enableChargingState = false; //reflects internal state of TP4056 control thread |
kuutei | 0:56122e281547 | 31 | |
kuutei | 0:56122e281547 | 32 | InterruptIn chargeButton(USER_BUTTON); |
kuutei | 0:56122e281547 | 33 | bool buttonPressed = false; //set to true by button ISR, set to false when acknowledged |
kuutei | 0:56122e281547 | 34 | |
kuutei | 0:56122e281547 | 35 | SSD1306I2C oled_i2c(OLED_ADR, OLED_SDA, OLED_SCL); |
kuutei | 0:56122e281547 | 36 | |
kuutei | 0:56122e281547 | 37 | float bat_voltage = -1.11; |
kuutei | 0:56122e281547 | 38 | float bat_voltage_avg = -1.11; |
kuutei | 0:56122e281547 | 39 | float vddref_voltage = -1.11; |
kuutei | 0:56122e281547 | 40 | |
kuutei | 0:56122e281547 | 41 | |
kuutei | 0:56122e281547 | 42 | //Configurable protection conditions |
kuutei | 0:56122e281547 | 43 | float MAX_VOLTAGE = 4.25; //default: no more than 4.25v |
kuutei | 0:56122e281547 | 44 | float MIN_VOLTAGE = 2.5; //default: no less than 2.5v |
kuutei | 0:56122e281547 | 45 | float MAX_TEMPERATURE = 40.0; //default: no more than 40C |
kuutei | 0:56122e281547 | 46 | float MIN_TEMPERATURE = 10.0; //default: no less than 10C |
kuutei | 0:56122e281547 | 47 | uint64_t MAX_TIME_ms = 3600000; //default: no more than 1h |
kuutei | 0:56122e281547 | 48 | |
kuutei | 0:56122e281547 | 49 | |
kuutei | 0:56122e281547 | 50 | void blinkled() |
kuutei | 0:56122e281547 | 51 | { |
kuutei | 0:56122e281547 | 52 | // Initialise the digital pin LED1 as an output |
kuutei | 0:56122e281547 | 53 | DigitalOut led(LED1); |
kuutei | 0:56122e281547 | 54 | |
kuutei | 0:56122e281547 | 55 | while (true) { |
kuutei | 0:56122e281547 | 56 | led = !led; |
kuutei | 0:56122e281547 | 57 | //thread_sleep_for(BLINKING_RATE_MS); |
kuutei | 0:56122e281547 | 58 | ThisThread::sleep_for(BLINKING_RATE_MS); |
kuutei | 0:56122e281547 | 59 | } |
kuutei | 0:56122e281547 | 60 | } |
kuutei | 0:56122e281547 | 61 | |
kuutei | 0:56122e281547 | 62 | //TODO: make this an ISR that causes an immediate read of the analog pin |
kuutei | 0:56122e281547 | 63 | void pa14_toggle() |
kuutei | 0:56122e281547 | 64 | { |
kuutei | 0:56122e281547 | 65 | |
kuutei | 0:56122e281547 | 66 | } |
kuutei | 0:56122e281547 | 67 | |
kuutei | 0:56122e281547 | 68 | // Button to initiate / stop charging |
kuutei | 0:56122e281547 | 69 | // Called on button rise |
kuutei | 0:56122e281547 | 70 | // Set to true to signal to EN control thread that button was pressed |
kuutei | 0:56122e281547 | 71 | void buttonISR |
kuutei | 0:56122e281547 | 72 | { |
kuutei | 0:56122e281547 | 73 | uint64_t now = Kernel::get_ms_count(); |
kuutei | 0:56122e281547 | 74 | uint64_t lastButtonPushTime = now; |
kuutei | 0:56122e281547 | 75 | const uint64_t buttonMinimumWaitTime = 500; //minimum 500 ms wait between button pushes |
kuutei | 0:56122e281547 | 76 | |
kuutei | 0:56122e281547 | 77 | if(Kernel::get_ms_count() - lastButtonPushTime > buttonMinimumWaitTime) |
kuutei | 0:56122e281547 | 78 | { |
kuutei | 0:56122e281547 | 79 | buttonPressed = true; |
kuutei | 0:56122e281547 | 80 | } |
kuutei | 0:56122e281547 | 81 | |
kuutei | 0:56122e281547 | 82 | } |
kuutei | 0:56122e281547 | 83 | |
kuutei | 0:56122e281547 | 84 | float readRefVoltage() |
kuutei | 0:56122e281547 | 85 | { |
kuutei | 0:56122e281547 | 86 | double vdd; |
kuutei | 0:56122e281547 | 87 | double vdd_calibed; |
kuutei | 0:56122e281547 | 88 | double vref_calibed; |
kuutei | 0:56122e281547 | 89 | double vref_f; |
kuutei | 0:56122e281547 | 90 | uint16_t vref_u16; |
kuutei | 0:56122e281547 | 91 | uint16_t vref_cal; |
kuutei | 0:56122e281547 | 92 | |
kuutei | 0:56122e281547 | 93 | vref_cal= *((uint16_t*)VREFINT_CAL_ADDR); //F303RE |
kuutei | 0:56122e281547 | 94 | |
kuutei | 0:56122e281547 | 95 | vref_u16 = vref.read_u16(); |
kuutei | 0:56122e281547 | 96 | //1.22 comes from 3.3 * 1524 / 4095 - voltage at calibration time times calibration measurement divided by maximum counts |
kuutei | 0:56122e281547 | 97 | //vdd = 1.228132 / vref.read(); |
kuutei | 0:56122e281547 | 98 | vdd = 3.3 * (double)vref_cal / 4095.0 / vref.read(); |
kuutei | 0:56122e281547 | 99 | |
kuutei | 0:56122e281547 | 100 | return vdd; |
kuutei | 0:56122e281547 | 101 | } |
kuutei | 0:56122e281547 | 102 | |
kuutei | 0:56122e281547 | 103 | //Reads voltage divider, and uses internal calibrated reference voltage to calculate real voltage |
kuutei | 0:56122e281547 | 104 | //Not 100% accurate, but better than assuming 3.3v |
kuutei | 0:56122e281547 | 105 | float readVoltageDivider_Calibrated() |
kuutei | 0:56122e281547 | 106 | { |
kuutei | 0:56122e281547 | 107 | uint16_t vref_cal= *((uint16_t*)VREFINT_CAL_ADDR); //factory calibration value for 3.3v |
kuutei | 0:56122e281547 | 108 | uint16_t vref_u16 = vref.read_u16(); //read the internal voltage calibration |
kuutei | 0:56122e281547 | 109 | float vdd = 3.3 * (double)vref_cal / 4095.0 / vref.read(); |
kuutei | 0:56122e281547 | 110 | |
kuutei | 0:56122e281547 | 111 | //ain.read() returns float value between 0 and 1 |
kuutei | 0:56122e281547 | 112 | float reading = divider_analogin.read(); |
kuutei | 0:56122e281547 | 113 | |
kuutei | 0:56122e281547 | 114 | //pc.printf("raw reading: %f\r\n", reading); |
kuutei | 0:56122e281547 | 115 | |
kuutei | 0:56122e281547 | 116 | return reading * vdd * (VOLTAGE_DIVIDER_R1 + VOLTAGE_DIVIDER_R2) / VOLTAGE_DIVIDER_R2; |
kuutei | 0:56122e281547 | 117 | |
kuutei | 0:56122e281547 | 118 | } |
kuutei | 0:56122e281547 | 119 | |
kuutei | 0:56122e281547 | 120 | //Measurement assuming 3.3v - for comparison to calibrated reading only |
kuutei | 0:56122e281547 | 121 | //Can be very inaccurate as nucleo voltage regulator drops as far as 3.2v |
kuutei | 0:56122e281547 | 122 | float readVoltageDivider_3v3() |
kuutei | 0:56122e281547 | 123 | { |
kuutei | 0:56122e281547 | 124 | uint16_t vref_cal= *((uint16_t*)VREFINT_CAL_ADDR); //factory calibration value for 3.3v |
kuutei | 0:56122e281547 | 125 | uint16_t vref_u16 = vref.read_u16(); //read the internal voltage calibration |
kuutei | 0:56122e281547 | 126 | float vdd = 3.3 * (double)vref_cal / 4095.0 / vref.read(); |
kuutei | 0:56122e281547 | 127 | |
kuutei | 0:56122e281547 | 128 | //ain.read() returns float value between 0 and 1 |
kuutei | 0:56122e281547 | 129 | float reading = divider_analogin.read(); |
kuutei | 0:56122e281547 | 130 | |
kuutei | 0:56122e281547 | 131 | return reading * vdd * (VOLTAGE_DIVIDER_R1 + VOLTAGE_DIVIDER_R2) / VOLTAGE_DIVIDER_R2; |
kuutei | 0:56122e281547 | 132 | |
kuutei | 0:56122e281547 | 133 | } |
kuutei | 0:56122e281547 | 134 | |
kuutei | 0:56122e281547 | 135 | // Reads DS18B20 sense temperature |
kuutei | 0:56122e281547 | 136 | void TemperatureInputThread() |
kuutei | 0:56122e281547 | 137 | { |
kuutei | 0:56122e281547 | 138 | while(true) |
kuutei | 0:56122e281547 | 139 | { |
kuutei | 0:56122e281547 | 140 | |
kuutei | 0:56122e281547 | 141 | |
kuutei | 0:56122e281547 | 142 | ThisThread::sleep_for(250); |
kuutei | 0:56122e281547 | 143 | |
kuutei | 0:56122e281547 | 144 | } |
kuutei | 0:56122e281547 | 145 | } |
kuutei | 0:56122e281547 | 146 | |
kuutei | 0:56122e281547 | 147 | // Detect battery presence by voltage activity |
kuutei | 0:56122e281547 | 148 | bool batteryPresent() |
kuutei | 0:56122e281547 | 149 | { |
kuutei | 0:56122e281547 | 150 | if(bat_voltage_avg > 0.0 && bat_voltage_avg) |
kuutei | 0:56122e281547 | 151 | { |
kuutei | 0:56122e281547 | 152 | return true; |
kuutei | 0:56122e281547 | 153 | } |
kuutei | 0:56122e281547 | 154 | } |
kuutei | 0:56122e281547 | 155 | |
kuutei | 0:56122e281547 | 156 | void TP4056ControlThread() |
kuutei | 0:56122e281547 | 157 | { |
kuutei | 0:56122e281547 | 158 | //uint64_t now = Kernel::get_ms_count(); |
kuutei | 0:56122e281547 | 159 | uint64_t chargeStartTime = 0; |
kuutei | 0:56122e281547 | 160 | |
kuutei | 0:56122e281547 | 161 | bool enableCharging = false; //internal variable to track whether TP4056 CE should be enabled or not |
kuutei | 0:56122e281547 | 162 | |
kuutei | 0:56122e281547 | 163 | |
kuutei | 0:56122e281547 | 164 | while(true) |
kuutei | 0:56122e281547 | 165 | { |
kuutei | 0:56122e281547 | 166 | //First check conditions to see if charging should be enabled or disabled |
kuutei | 0:56122e281547 | 167 | //Charging can be enabled if battery is present, no protections triggered, and user starts the charge |
kuutei | 0:56122e281547 | 168 | if(enableCharging == false) |
kuutei | 0:56122e281547 | 169 | { |
kuutei | 0:56122e281547 | 170 | bool temperature_en = (MAX_TEMPERATURE); |
kuutei | 0:56122e281547 | 171 | } |
kuutei | 0:56122e281547 | 172 | |
kuutei | 0:56122e281547 | 173 | //Charging must be stopped if overvoltage, overtemperature, overtime, battery removed, or user pushes button |
kuutei | 0:56122e281547 | 174 | |
kuutei | 0:56122e281547 | 175 | //With charge state calculated, realize it on the CE pin |
kuutei | 0:56122e281547 | 176 | //Allow pullup to 5V to enable charging at CE pin |
kuutei | 0:56122e281547 | 177 | if(enableCharging) |
kuutei | 0:56122e281547 | 178 | { |
kuutei | 0:56122e281547 | 179 | toggleOut.write(1); //open drain mode -> HiZ |
kuutei | 0:56122e281547 | 180 | chargeStartTime = Kernel::get_ms_count(); |
kuutei | 0:56122e281547 | 181 | } |
kuutei | 0:56122e281547 | 182 | //To disable charging, open drain the CE pin |
kuutei | 0:56122e281547 | 183 | else |
kuutei | 0:56122e281547 | 184 | { |
kuutei | 0:56122e281547 | 185 | toggleOut.write(0); //open drain, pull to GND -> overpull 470k pullup that brings 5V to CE |
kuutei | 0:56122e281547 | 186 | } |
kuutei | 0:56122e281547 | 187 | |
kuutei | 0:56122e281547 | 188 | //Update flag that indicates state of TP4056 CE pin to other threads |
kuutei | 0:56122e281547 | 189 | enableChargingState = enableCharging; |
kuutei | 0:56122e281547 | 190 | |
kuutei | 0:56122e281547 | 191 | ThisThread::sleep_for(100); |
kuutei | 0:56122e281547 | 192 | } |
kuutei | 0:56122e281547 | 193 | } |
kuutei | 0:56122e281547 | 194 | |
kuutei | 0:56122e281547 | 195 | void oledOutputThread() |
kuutei | 0:56122e281547 | 196 | { |
kuutei | 0:56122e281547 | 197 | while(true) |
kuutei | 0:56122e281547 | 198 | { |
kuutei | 0:56122e281547 | 199 | oled_i2c.clear(); |
kuutei | 0:56122e281547 | 200 | |
kuutei | 0:56122e281547 | 201 | std::stringstream stream; |
kuutei | 0:56122e281547 | 202 | stream << std::fixed << std::setprecision(4) << bat_voltage_avg; |
kuutei | 0:56122e281547 | 203 | //std::string voltage_output = "Voltage: "+std::to_string(bat_voltage); |
kuutei | 0:56122e281547 | 204 | std::string voltage_output = "Voltage: " + stream.str(); |
kuutei | 0:56122e281547 | 205 | oled_i2c.drawString(0, 0, voltage_output.c_str() ); |
kuutei | 0:56122e281547 | 206 | |
kuutei | 0:56122e281547 | 207 | if(enableChargingState){ |
kuutei | 0:56122e281547 | 208 | oled_i2c.drawString(0, 16, "CE: Enabled"); |
kuutei | 0:56122e281547 | 209 | } else { |
kuutei | 0:56122e281547 | 210 | oled_i2c.drawString(0, 16, "CE: Disabled"); |
kuutei | 0:56122e281547 | 211 | } |
kuutei | 0:56122e281547 | 212 | |
kuutei | 0:56122e281547 | 213 | |
kuutei | 0:56122e281547 | 214 | stream.str(""); |
kuutei | 0:56122e281547 | 215 | stream << std::fixed << std::setprecision(4) << vddref_voltage; |
kuutei | 0:56122e281547 | 216 | std::string vddref_output = "VDDREF: " + stream.str(); |
kuutei | 0:56122e281547 | 217 | oled_i2c.drawString(0,16*2, vddref_output.c_str() ); |
kuutei | 0:56122e281547 | 218 | |
kuutei | 0:56122e281547 | 219 | |
kuutei | 0:56122e281547 | 220 | oled_i2c.display(); |
kuutei | 0:56122e281547 | 221 | ThisThread::sleep_for(250); |
kuutei | 0:56122e281547 | 222 | } |
kuutei | 0:56122e281547 | 223 | |
kuutei | 0:56122e281547 | 224 | } |
kuutei | 0:56122e281547 | 225 | |
kuutei | 0:56122e281547 | 226 | int main() |
kuutei | 0:56122e281547 | 227 | { |
kuutei | 0:56122e281547 | 228 | pc.baud(9600); |
kuutei | 0:56122e281547 | 229 | pc.printf("Started\r\n"); |
kuutei | 0:56122e281547 | 230 | |
kuutei | 0:56122e281547 | 231 | //BUILT IN LED THREAD |
kuutei | 0:56122e281547 | 232 | Thread led1; |
kuutei | 0:56122e281547 | 233 | //osStatus err = led1.start(&blinkled); |
kuutei | 0:56122e281547 | 234 | led1.start(blinkled); |
kuutei | 0:56122e281547 | 235 | |
kuutei | 0:56122e281547 | 236 | // CONFIGURE TP4056 CE CONTROL FOR OPEN DRAIN WITH EXTERNAL 5V PULLUP |
kuutei | 0:56122e281547 | 237 | //https://forums.mbed.com/t/how-to-configure-open-drain-output-pin-on-stm32/7007 |
kuutei | 0:56122e281547 | 238 | toggleOut.mode(OpenDrainNoPull); |
kuutei | 0:56122e281547 | 239 | |
kuutei | 0:56122e281547 | 240 | // CHARGE ENABLE CONTROL THREAD |
kuutei | 0:56122e281547 | 241 | Thread tp4056_control_thread; |
kuutei | 0:56122e281547 | 242 | tp4056_control_thread.start(TP4056ControlThread); |
kuutei | 0:56122e281547 | 243 | |
kuutei | 0:56122e281547 | 244 | //OLED 128x64 INIT AND THREAD |
kuutei | 0:56122e281547 | 245 | //initialize ssd1306 on i2c0 |
kuutei | 0:56122e281547 | 246 | oled_i2c.init(); |
kuutei | 0:56122e281547 | 247 | oled_i2c.flipScreenVertically(); |
kuutei | 0:56122e281547 | 248 | oled_i2c.setFont(ArialMT_Plain_16); |
kuutei | 0:56122e281547 | 249 | oled_i2c.drawString(0,0,"init!"); |
kuutei | 0:56122e281547 | 250 | oled_i2c.display(); |
kuutei | 0:56122e281547 | 251 | |
kuutei | 0:56122e281547 | 252 | Thread oled_thread; |
kuutei | 0:56122e281547 | 253 | oled_thread.start(oledOutputThread); |
kuutei | 0:56122e281547 | 254 | |
kuutei | 0:56122e281547 | 255 | // START/STOP CHARGE BUTTON |
kuutei | 0:56122e281547 | 256 | chargeButton.rise(&buttonISR); |
kuutei | 0:56122e281547 | 257 | |
kuutei | 0:56122e281547 | 258 | |
kuutei | 0:56122e281547 | 259 | float bat_voltage_min; |
kuutei | 0:56122e281547 | 260 | float bat_voltage_max; |
kuutei | 0:56122e281547 | 261 | float bat_voltage_avg_sum; |
kuutei | 0:56122e281547 | 262 | |
kuutei | 0:56122e281547 | 263 | uint64_t now = Kernel::get_ms_count(); |
kuutei | 0:56122e281547 | 264 | uint64_t lastADCStartTime = now; |
kuutei | 0:56122e281547 | 265 | |
kuutei | 0:56122e281547 | 266 | while(true) |
kuutei | 0:56122e281547 | 267 | { |
kuutei | 0:56122e281547 | 268 | // reset min and max values |
kuutei | 0:56122e281547 | 269 | bat_voltage_min = 10.0; |
kuutei | 0:56122e281547 | 270 | bat_voltage_max = -10.0; |
kuutei | 0:56122e281547 | 271 | bat_voltage_avg_sum = 0.0; |
kuutei | 0:56122e281547 | 272 | |
kuutei | 0:56122e281547 | 273 | lastADCStartTime = Kernel::get_ms_count(); |
kuutei | 0:56122e281547 | 274 | for(int i = 0; i < 1000; i++) |
kuutei | 0:56122e281547 | 275 | { |
kuutei | 0:56122e281547 | 276 | bat_voltage = readVoltageDivider_Calibrated(); |
kuutei | 0:56122e281547 | 277 | vddref_voltage = readRefVoltage(); |
kuutei | 0:56122e281547 | 278 | bat_voltage_avg_sum += bat_voltage; |
kuutei | 0:56122e281547 | 279 | if( bat_voltage < bat_voltage_min ) |
kuutei | 0:56122e281547 | 280 | { |
kuutei | 0:56122e281547 | 281 | bat_voltage_min = bat_voltage; |
kuutei | 0:56122e281547 | 282 | } |
kuutei | 0:56122e281547 | 283 | if( bat_voltage > bat_voltage_max ) |
kuutei | 0:56122e281547 | 284 | { |
kuutei | 0:56122e281547 | 285 | bat_voltage_max = bat_voltage; |
kuutei | 0:56122e281547 | 286 | } |
kuutei | 0:56122e281547 | 287 | |
kuutei | 0:56122e281547 | 288 | ThisThread::sleep_for(1); |
kuutei | 0:56122e281547 | 289 | } |
kuutei | 0:56122e281547 | 290 | now = Kernel::get_ms_count(); |
kuutei | 0:56122e281547 | 291 | |
kuutei | 0:56122e281547 | 292 | bat_voltage_avg = bat_voltage_avg_sum/1000.0; |
kuutei | 0:56122e281547 | 293 | |
kuutei | 0:56122e281547 | 294 | pc.printf("\r\nADC0 Reading: %6.4f\r\n", bat_voltage); |
kuutei | 0:56122e281547 | 295 | pc.printf("ADC0 1s min: %6.4f\r\n", bat_voltage_min); |
kuutei | 0:56122e281547 | 296 | pc.printf("ADC0 1s max: %6.4f\r\n", bat_voltage_max); |
kuutei | 0:56122e281547 | 297 | pc.printf("ADC0 1s avg: %6.4f\r\n", bat_voltage_avg); |
kuutei | 0:56122e281547 | 298 | pc.printf("VDDREF Reading: %6.4f\r\n", vddref_voltage); |
kuutei | 0:56122e281547 | 299 | pc.printf("ADC0 time (nominal 1000ms): %d\r\n", (now-lastADCStartTime)); |
kuutei | 0:56122e281547 | 300 | |
kuutei | 0:56122e281547 | 301 | } |
kuutei | 0:56122e281547 | 302 | |
kuutei | 0:56122e281547 | 303 | } |