diff --git a/ino/charger_2_1.ino b/ino/charger_2_1.ino new file mode 100644 index 0000000..c0f5239 --- /dev/null +++ b/ino/charger_2_1.ino @@ -0,0 +1,841 @@ +#include +#include +#include +#include +#include + +//I2C device found at address 0x3C ! -> SSD1306 +//I2C device found at address 0x48 ! -> ADS1115 +//I2C device found at address 0x60 ! -> MCP4725 + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 32 // OLED display height, in pixels +#define MCP4725 96 // Adresse 96 oder 97, wie Brücke +#define OLED_RESET 10 // Reset pin # (or -1 if sharing Arduino reset pin) +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +Adafruit_ADS1115 ads1115(0x48); + +//IO pins +byte const io_enter = 2; +byte const io_right = 3; +byte const io_discharge = 4; +byte const io_akku_on = 5; +byte const io_left = 6; +byte const io_U_akku = A7; +byte const io_U_own = A0; +byte const io_U_temp = A1; + +//control parameters +int dev_dawert = 3500; +int dawert = 3500; +int const step_up = 1; +int const step_down = 1; +int const search_step_up = 5; +int const search_step_down = 5; +int16_t time; +float T; +float T_start; +byte const T_count = 20; + +//limit values +int U_akku_max = 0; +int U_akku_dis_min; +int U_akku_dis_max; +int U_akku_dis_tmp; +int U_akku_dis_tmp_step; +byte const dawert_min = 119; +byte const dawert_max = 131; + +//sensor valueas +byte const U_rep = 50; +float dU[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; +float dU_xy = 0; +float dU_xx = 0; +float dU_X; +float dU_Y; +float dU_mid = 0; +float dU_mid_max = 0; + +int U_akku; +int U_out; +int I_akku; +int32_t U_sum; +int16_t U; +int16_t coulomb_u; +float coulomb_v; +int16_t coulomb; +float U_temp; +float U_own; +float R_T; +float dT[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; +const byte dT_count = 20; +byte dT_n; +float dT_mid; +const byte dT_t = 30; // t = dT_t(s) * 1024 +int dT_sum = 0; +byte dT_position = 0; +int16_t dT_t_tmp = 0; +float dT_mid_max = 0; +float dT_xy = 0; +float dT_xx = 0; +float dT_X; +float dT_Y; + +//temp sensor parameters +float const B = 3435.0; +float const R_R = 10000.0; +float const R_ST = 1000.0; + +//menue +bool menu = true; +int sub_menu = 0; +byte position = 0; +byte menu_step; +byte old_position = 0; +byte new_position = 0; +bool press; +bool pressed; + +bool select_type = true; +byte type; +String types[] = {"NiMh", "NiCd", "Alkali"}; +byte const types_count = 3; + +bool select_I_charge = true; +int I_charge; +int charges[] = {10, 20, 30, 40, 50, 80, 100, 150, 200, 250, 300, 350, 400, 450, 500}; +byte const charges_count = 15; + +bool select_discharge = true; +bool discharge = false; +bool direction = true; +bool max_discharge = true; +String discharges[] = {"true", "false"}; +byte const discharge_count = 2; + +//global bools +bool full = false; +bool search_mode = true; + +void setup() { + analogReference(INTERNAL4V096); + pinMode(io_enter, INPUT); + pinMode(io_right, INPUT); + pinMode(io_discharge, ANALOG); + pinMode(io_left, INPUT); + pinMode(io_akku_on, OUTPUT); + pinMode(io_U_akku, INPUT); + + digitalWrite(io_akku_on, LOW); + analogWrite(io_discharge, 0); + + Wire.begin(); + writemcp4725(dev_dawert); + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + display.clearDisplay(); + display.display(); + ads1115.begin(); +} + +void loop() { + if (menu == true) { + sub_menu = 0; + position = (types_count * 5); + while (select_type) { + position = position + arrow(); + write_display_menu(sub_menu, position); + select_type = enter(); + } + type = position % types_count; + sub_menu += 1; + position = (charges_count * 5) + 6; + while (select_I_charge) { + write_display_menu(sub_menu, position); + position = position + arrow(); + select_I_charge = enter(); + } + I_charge = charges[position % charges_count]; + sub_menu += 1; + position = (discharge_count * 5); + while (select_discharge && type != 2) { + write_display_menu(sub_menu, position); + position = position + arrow(); + select_discharge = enter(); + } + discharge = (position + 1) % discharge_count; + menu = false; + + U_akku = map(U_measure(0, 2, 5), 0, 32768, 0, 2048); + + if (U_akku < 100) { + menu = true; + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + display.setTextSize(2); + display.setCursor(0, 5); + display.print("no battery"); + //print + display.display(); + delay(2000); + } + } + + display.clearDisplay(); + display.display(); + + if (discharge == true && type != 2) { + dawert = 150; + fnc_discharge(); + } + + for (int i = 0; i < dT_count; i++) { + T = temp(); + U_akku = map(U_measure(0, 2, 20), 0, 32768, 0, 2048); + dT[i] = T; + dU[i] = U_akku; + } + + switch (type) { + case 0: //NiMh + dawert = 4096; + while (true) { + charge_nimh(); + } + break; + case 1: //NiCd + dawert = 4096; + while (true) { + charge_nicd(); + } + break; + case 2: //Alkali + dawert = 4096; + while (true) { + charge_alkali(); + } + break; + } +} + +bool enter() { + press = false; + pressed = true; + if (digitalRead(io_enter) == HIGH) { + delay(10); + if (digitalRead(io_enter) == HIGH) { + press = true; + } + } + while (press) { + if (digitalRead(io_enter) == LOW) { + pressed = false; + press = false; + } + } + return pressed; +} + +byte arrow() { + press = false; + menu_step = 0; + if (digitalRead(io_right) == HIGH) { + delay(10); + if (digitalRead(io_right) == HIGH) { + press = true; + } + } + if (digitalRead(io_left) == HIGH) { + delay(10); + if (digitalRead(io_left) == HIGH) { + press = true; + } + } + while (press) { + if (digitalRead(io_left) == LOW) { + press = false; + menu_step = 1; + } + if (digitalRead(io_right) == LOW) { + press = false; + menu_step = -1; + } + } + delay(250); + return menu_step; +} + +void write_display_menu(int sub_menu, int position) { + switch (sub_menu) { + case 0: //select_type + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + display.setTextSize(1); + display.setCursor(0, 0); + display.print("select batterie type"); + display.setTextSize(2); + display.setCursor(0, 16); + display.print(">"); + display.setCursor(20, 16); + display.print(types[position % types_count]); + //print + display.display(); + break; + case 1: //select_I_charge + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + display.setTextSize(1); + display.setCursor(0, 0); + display.print("select I charge"); + display.setTextSize(2); + display.setCursor(0, 16); + display.print(">"); + display.setCursor(20, 16); + display.print(charges[position % charges_count]); + display.setCursor(10, 35); + display.print("mA"); + //print + display.display(); + break; + case 2: //select_discharge + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + display.setTextSize(1); + display.setCursor(0, 0); + display.print("discharge before load"); + display.setTextSize(2); + display.setCursor(0, 16); + display.print(">"); + display.setCursor(20, 16); + display.print(discharges[position % discharge_count]); + //print + display.display(); + break; + } +} + +int16_t U_measure(byte channel, int gain, int rep) { + switch (gain) { + case 0: + ads1115.setGain(GAIN_TWOTHIRDS); + break; + case 1: + ads1115.setGain(GAIN_ONE); + break; + case 2: + ads1115.setGain(GAIN_TWO); + break; + case 4: + ads1115.setGain(GAIN_FOUR); + break; + case 8: + ads1115.setGain(GAIN_EIGHT); + break; + case 16: + ads1115.setGain(GAIN_SIXTEEN); + break; + case 101: + // + break; + default: + ads1115.setGain(GAIN_TWOTHIRDS); + break; + } + U_sum = 0; + for (int i = 0; i < rep; i++) { + switch (channel) { + case 0: + U = ads1115.readADC_Differential_0_1();; + break; + case 1: + U = ads1115.readADC_Differential_2_3(); + break; + case 2: + U = analogRead(io_U_akku); + break; + } + U_sum += U; + } + U = U_sum / rep; + return U; +} + +void search_I(int I_charge, bool direction) { + analogWrite(io_discharge, 0); + digitalWrite(io_akku_on, LOW); + I_akku = map(U_measure(1, 16, 10), 0, 32768, 0, 2560) * -1; + writemcp4725(dawert); + direction = true; + while (direction) { + U_out = U_measure(2, 101, 50); + U_akku = map(U_measure(0, 2, 10), 0, 32768, 0, 2048); + + if (U_out > (U_akku + 10)) { + direction = false; + digitalWrite(io_akku_on, HIGH); + } else { + dawert -= search_step_down; + writemcp4725(dawert); + digitalWrite(io_akku_on, LOW); + } + + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + //line 1 M + display.setCursor(10, 0); + display.print(""); + //line 2 L + display.setCursor(0, 12); + display.print("U_a: "); + display.print(U_akku); + //line 2 L + display.setCursor(75, 12); + display.print("I_a: "); + display.print(I_akku); + //line 3 L + display.setCursor(0, 22); + display.print("U_o: "); + display.print(U_out); + //print + display.display(); + } + + while (I_akku != I_charge && !direction) { + U_akku = map(U_measure(0, 2, 10), 0, 32768, 0, 2048); + I_akku = map(U_measure(1, 16, 10), 0, 32768, 0, 2560) * -1; + if (I_akku < I_charge) { + dawert -= search_step_down; + writemcp4725(dawert); + } else { + dawert += search_step_up; + writemcp4725(dawert); + } + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + //line 1 M + display.setCursor(10, 0); + display.print(""); + //line 2 L + display.setCursor(0, 12); + display.print("U_a: "); + display.print(U_akku); + //line 2 R + display.setCursor(75, 12); + display.print("I_a: "); + display.print(I_akku); + //print + display.display(); + } +} + +void write_display_data() { + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + + temp_dT(); + if (coulomb < I_charge && discharge) { + dT_mid_max = 0; + dU_mid_max = 0; + } + + //line 1 L + display.setCursor(0, 0); + display.print("dUmU: "); + if (dU_mid >= 0) { + display.print(" "); + } + display.print(dU_mid, 0); + display.setCursor(70, 0); + if (dU_mid_max == 0) { + display.print("--"); + } else { + display.print(dU_mid_max, 0); + } + display.setCursor(100, 0); + display.print(U_akku); + + //line 2 L + display.setCursor(0, 12); + display.print("I-Uh: "); + display.print(I_akku); + display.setCursor(70, 12); + if (U_akku_max - U_akku == 0) { + display.print("--"); + } else { + display.print(U_akku_max - U_akku); + } + display.setCursor(100, 12); + display.print(fnc_coulomb()); + + //line 3 L + display.setCursor(0, 22); + display.print("dTmT: "); + if (dT_mid >= 0) { + display.print(" "); + } + display.print(dT_mid, 1); + display.setCursor(70, 22); + if (dT_mid_max == 0) { + display.print("--"); + } else { + display.print(dT_mid_max, 1); + } + display.setCursor(100, 22); + display.print(T, 1); + + display.display(); +} + +void writemcp4725(int wert) { + // Analog Wert 0 bis 4095 + if (wert < 0) { + wert = 0; + } + if (wert > 4095) { + wert = 4095; + } + Wire.beginTransmission(MCP4725); + Wire.write(64); // Kommando Update DAC + Wire.write(wert >> 4); // die 8 höherwertigen Bits + Wire.write((wert & 15) << 4); // die 4 niederwertigen Bits + Wire.endTransmission(); +} + +float temp() { + analogReference(INTERNAL1V024); + T = 0; + for (int i = 0; i < T_count; i++) { + U_own = 14000;// analogRead(io_U_own) * 5; //voltage divider 5:1 => 5V == 1V (alternativ ~14000) + U_temp = analogRead(io_U_temp); + R_T = ((U_own / U_temp) * R_ST) - R_ST; + T += pow(((log(R_T / R_R) / B) + (1 / 293.15)), -1); + } + analogReference(INTERNAL4V096); + T = T / T_count; + T = T - 273.15; + T = T * 1.05; + return T; +} + +void temp_dT() { + dT_sum += (millis() - dT_t_tmp); + dT_t_tmp = millis(); + if (dT_sum > (dT_t * 1024)) { + dT_sum = 0; + dT[dT_position] = temp(); + dT_position += 1; + if (dT_position == dT_count) { + dT_position = 0; + } + + //dT + + dT_xx = 0; + dT_xy = 0; + dT_n = 1; + + //avarage x + dT_X = dT_count / 2; + + //avarage y + dT_Y = 0; + for (int i = 0; i < dT_count; i++) { + dT_Y += dT[i]; + } + dT_Y = dT_Y / dT_count; + + for (int i = dT_position; i < dT_count; i++) { + dT_xy += (dT_n - dT_X) * (dT[i] - dT_Y); + dT_xx += pow((dT_n - dT_X), 2); + dT_n++; + } + + for (int i = 0; i < dT_position; i++) { + dT_xy += (dT_n - dT_X) * (dT[i] - dT_Y); + dT_xx += pow((dT_n - dT_X), 2); + dT_n++; + } + + dT_mid = dT_xy / dT_xx; + dT_mid = dT_mid * (3600 / dT_t); + + if (dT_mid_max < dT_mid) { + dT_mid_max = dT_mid; + } + + //dU + + dU[dT_position] = U_akku; + + dU_xx = 0; + dU_xy = 0; + dT_n = 1; + + //avarage x + dU_X = dT_count / 2; + + //avarage y + dU_Y = 0; + for (int i = 0; i < dT_count; i++) { + dU_Y += dU[i]; + } + dU_Y = dU_Y / dT_count; + + for (int i = dT_position; i < dT_count; i++) { + dU_xy += (dT_n - dU_X) * (dU[i] - dU_Y); + dU_xx += pow((dT_n - dU_X), 2); + dT_n++; + } + + for (int i = 0; i < dT_position; i++) { + dU_xy += (dT_n - dU_X) * (dU[i] - dU_Y); + dU_xx += pow((dT_n - dU_X), 2); + dT_n++; + } + + dU_mid = dU_xy / dU_xx; + dU_mid = dU_mid * (3600 / dT_t); + + if (dU_mid_max < dU_mid) { + dU_mid_max = dU_mid; + } + + } +} + +void fnc_discharge () { + switch (type) { + case 0: //NiMh + U_akku_dis_min = 1000; + U_akku_dis_max = 1100; + break; + case 1: //NiCd + U_akku_dis_min = 950; + U_akku_dis_max = 1050; + break; + case 2: //Alkali + break; + } + writemcp4725(200); + dawert = dawert_min; + U_akku_dis_tmp = U_akku_dis_max - ((U_akku_dis_max - U_akku_dis_min) / (dawert_max - dawert_min) * (dawert - dawert_min)); + U_akku_dis_tmp_step = ((U_akku_dis_max - U_akku_dis_min) / (dawert_max - dawert_min)); + analogWrite(io_discharge, dawert); + discharge = true; + max_discharge = true; + + while (max_discharge) { + U_akku = map(U_measure(0, 2, 20), 0, 32768, 0, 2048); + if (dawert == dawert_max || U_akku <= U_akku_dis_tmp) { + max_discharge = false; + } else { + dawert += step_up; + if (dawert > dawert_max) { + dawert = dawert_max; + } + analogWrite(io_discharge, dawert); + delay(1000); + } + U_akku_dis_tmp = U_akku_dis_max - ((U_akku_dis_max - U_akku_dis_min) / (dawert_max - dawert_min) * (dawert - dawert_min)); + + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + //line 1 M + display.setCursor(30, 0); + display.print(""); + //line 2 L + display.setCursor(0, 12); + display.print("U_a: "); + display.print(U_akku); + //line 2 R + display.setCursor(70, 12); + display.print("d_w: "); + display.print(dawert - dawert_min); + //line 3 L + display.setCursor(0, 22); + display.print("T_a: "); + display.print(temp(), 1); + //line 3 R + display.setCursor(70, 22); + display.print("U_m: "); + display.print(U_akku_dis_tmp); + //print + display.display(); + } + + while (discharge) { + U_akku = map(U_measure(0, 2, 20), 0, 32768, 0, 2048); + if (dawert <= dawert_min) { + discharge = false; + } + if (U_akku <= U_akku_dis_tmp) { + U_akku_dis_tmp += U_akku_dis_tmp_step; + dawert -= step_down; + if (dawert < 0) { + dawert = 0; + } + analogWrite(io_discharge, dawert); + delay(1000); + } + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + //line 1 M + display.setCursor(30, 0); + display.print(""); + //line 2 L + display.setCursor(0, 12); + display.print("U_a: "); + display.print(U_akku); + //line 2 R + display.setCursor(70, 12); + display.print("d_w: "); + display.print(dawert - dawert_min); + //line 3 L + display.setCursor(0, 22); + display.print("T_a: "); + display.print(temp(), 1); + //line 3 R + display.setCursor(70, 22); + display.print("U_m: "); + display.print(U_akku_dis_tmp); + //print + display.display(); + } + analogWrite(io_discharge, 0); + discharge = true; +} +int fnc_coulomb() { + coulomb_u = (millis() - time); + coulomb_v += (float)coulomb_u * I_akku / 1024.0; + coulomb = coulomb_v / 3600.0; + time = millis(); + return coulomb; +} + +void charge_nimh() { + if (search_mode) { + search_I(I_charge, true); + delay(100); + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + U_akku_max = U_akku; + time = millis(); + search_mode = false; + T_start = temp(); + } + + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + I_akku = -1 * map(U_measure(1, 16, U_rep), 0, 32768, 0, 2560); + + if (U_akku_max < U_akku || (coulomb < I_charge && discharge)) { + U_akku_max = U_akku; + } + + if (U_akku_max - U_akku > 5 || temp() > 35) {// || dT_mid < dT_mid_max - 2) { + I_charge = 10; + search_I(I_charge, false); + delay(100); + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + U_akku_max = U_akku; + time = millis(); + } + + if (I_akku < I_charge) { + dawert -= step_down; + writemcp4725(dawert); + } else { + if (I_akku > I_charge) { + dawert += step_up; + writemcp4725(dawert); + } + } + write_display_data(); +} + +void charge_nicd() { + if (search_mode) { + search_I(I_charge, true); + delay(100); + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + U_akku_max = U_akku; + time = millis(); + search_mode = false; + T_start = temp(); + } + + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + I_akku = -1 * map(U_measure(1, 16, U_rep), 0, 32768, 0, 2560); + + if (U_akku_max < U_akku || (coulomb < I_charge)) {// && discharge)) { + U_akku_max = U_akku; + } + + if (U_akku_max - U_akku > 15 || temp() > 35) { + I_charge = 10; + search_I(I_charge, false); + delay(100); + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + U_akku_max = U_akku; + time = millis(); + } + + if (I_akku < I_charge) { + dawert -= step_down; + writemcp4725(dawert); + } else { + if (I_akku > I_charge) { + dawert += step_up; + writemcp4725(dawert); + } + } + write_display_data(); +} + +void charge_alkali() { + if (search_mode) { + search_I(I_charge, true); + delay(100); + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + U_akku_max = U_akku; + time = millis(); + search_mode = false; + } + + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + I_akku = -1 * map(U_measure(1, 16, U_rep), 0, 32768, 0, 2560); + + if (U_akku_max < U_akku) { + U_akku_max = U_akku; + } + + if (I_akku < I_charge) { + dawert -= step_down; + writemcp4725(dawert); + } else { + if (I_akku > I_charge) { + dawert += step_up; + writemcp4725(dawert); + } + } + + if (U_akku > 1690) { + while (true) { + U_akku = map(U_measure(0, 2, U_rep), 0, 32768, 0, 2048); + I_akku = -1 * map(U_measure(1, 16, U_rep), 0, 32768, 0, 2560); + if (U_akku < 1690) { + dawert -= step_down; + writemcp4725(dawert); + } else { + if (U_akku > 1690) { + dawert += step_up; + writemcp4725(dawert); + } + } + write_display_data(); + } + } + write_display_data(); +}