Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Manufacturing Technology >> Proses manufaktur

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1

Komponen dan Perlengkapan

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Arduino UNO
× 1
SparkFun I2C DAC Breakout - MCP4725
× 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Regulator Linier dengan Output Disesuaikan
× 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Op-Amp Dual Tujuan Umum Texas Instruments
× 1
Resistor 6.8k ohm
× 2
Resistor 3.3k ohm
× 1
Resistor 5.1k ohm
× 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Resistor 1k ohm
× 1
Resistor 10 ohm
× 1
Relay (generic)
× 1
Resistor 20k ohm
× 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
SparkFun Breadboard Power Supply 5V/3.3V
× 1
Suplai Daya 17V
× 1
YwRobot I2C SERIAL LCD 1602 MODULE
× 1

Tentang Proyek Ini

Saya mencari pengisi pintar yang dapat mengisi baterai 9V NiMH dalam beberapa jam dan tidak menemukan satu pun. Selain itu semua pengisi yang saya temukan benar-benar "bodoh". Arus pengisian tidak diketahui dan tidak ada fungsi untuk mengakhiri pengisian setelah baterai penuh. Masalah dengan pengisi seperti itu adalah mereka dapat mengisi baterai terlalu banyak dan secara signifikan mengurangi umur pakai. Jadi saya memutuskan untuk membuat pengisi "pintar".

Versi pertama saya berniat untuk menjaga kesederhanaan, jadi memungkinkan hal dasar seperti pengisian dengan arus konstan, penghentian otomatis pengisian setelah baterai penuh, pengisian gocok, dan pengukuran muatan yang ditransfer ke baterai.

Di versi selanjutnya saya akan menambahkan beberapa fitur berguna tambahan seperti pengosongan, pengukuran kapasitas dan siklus.

Peringatan: Pengisian baterai dengan arus tinggi dapat menyebabkan ledakan atau kebakaran baterai. Jangan biarkan pengisi tanpa pengawasan. Juga jangan mencoba mengisi baterai yang tidak dimaksudkan untuk diisi seperti Alkaline. Pengisi ini hanya diuji dengan baterai NiMH (dan masih Anda menggunakan risiko Anda sendiri dan saya sama sekali tidak memiliki tanggung jawab jika ada kerusakan yang disebabkan karena bug dalam desain atau kode). Pengisian jenis baterai lain akan memerlukan modifikasi kode.

Teori

Beberapa fakta berguna untuk diingat yang akan membantu memahami parameter yang dibutuhkan dari pengisi.

C - arus sama dengan kapasitas nominal baterai

Ketika diisi dengan tarif C tegangan sel tunggal dapat mencapai 1.6V. Tegangan ini mungkin lebih tinggi untuk baterai lama.

Tegangan nominal sel tunggal adalah 1.2V, tetapi sel yang penuh memiliki tegangan open-circuit hingga 1.5 volt.

Tarif pengisian gocok kurang dari 0.025 C (C/40) disarankan setelah baterai penuh.

Ada secara umum dua opsi untuk mengisi baterai NiMH:

1. Pengisian cepat. Arus pengisian 0.5C-1C. Status pengisian harus dipantau dan diakhiri oleh dV/dt (laju perubahan tegangan) atau dT/dt (laju perubahan suhu)

2. Pengisian lambat. Arus pengisian 0.1C. Waktu pengisian 14-16 jam. Penghentian pengisian oleh timer. dT/dt penghentian pengisian tidak mungkin untuk arus rendah. dV/dt penghentian mungkin tidak andal untuk arus di bawah 0.5C menurut literatur.

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1

Parameter Dasar Rangkaian Pengisian

Baterai 9V biasanya memiliki 7 sel yang terhubung beruntun, tetapi dalam beberapa kasus mungkin memiliki 6 atau 8 sel. Regulator tegangan harus dapat menyediakan tegangan pengisian setidaknya hingga 8*1.6=12.8V. Tegangan dropout regulator LM317 hingga 2V, jadi tegangan suplai diperlukan ~15V (ini tidak termasuk penurunan tegangan pada resistor pengindera arus).

Untuk arus pengisian maksimum 200mA dan resistor pengindera arus 10 Ohm penurunan tambahan pada resistor pengindera arus adalah 2V, jadi tegangan suplai 17V diperlukan.

Sel yang habis mungkin memiliki tegangan sangat rendah, bahkan negatif. Tegangan minimum regulator idealnya harus 0, tetapi menggunakan LM317 mungkin rendah hingga 1.2V.

Rangkaian

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1

Penjelasan Rangkaian

Ide dasarnya adalah untuk mengukur arus pengisian dan menyesuaikan tegangan regulator hingga arus yang diinginkan tercapai. Arus diukur dengan mengukur penurunan tegangan pada resistor pengindera arus R5. I=V/R.

SparkFun I2C DAC Breakout - MCP4725 - konverter digital ke analog 12 bit digunakan untuk mengontrol tegangan. Tegangan output dapat dikonfigurasi melalui I2C antara 0 dan 5V. Karena kita perlu dapat menyesuaikan tegangan dalam rentang yang lebih luas, dari 0 hingga 15V operational amplifier LM358 digunakan untuk memperkuat output tegangan DAC. Perkuatan operational amplifier diatur oleh resistor R4 dan R3. Gain=1+R4/R3=1+6800/3300=3.06 jadi output tegangan operational amplifier sekitar 0 hingga 15V.

Arus output maksimum LM358 adalah 50mA, jadi regulator tegangan disesuaikan LM317 digunakan untuk mengontrol arus yang lebih tinggi. Output operational amplifier terhubung ke terminal ADJ LM317. LM317 akan mempertahankan 1.2V antara terminal ADJ dan OUT, jadi tegangan aktual pada baterai dapat dikonfigurasi antara 1.2 dan 16.2V. LM317 membutuhkan minimal 3.5mA arus untuk mempertahankan regulasi. Jadi resistor 1kOhm R6 digunakan untuk memastikan regulasi jika baterai tidak terhubung. Kapasitor C1 digunakan untuk menyaring output tegangan dan meningkatkan stabilitas LM317.

Tegangan diukur pada dua titik berbeda.

1. Resistor R5 terhubung ke pin A2 Arduino. Tegangan pada resistor diukur dan kemudian arus pengisian dihitung sebagai Icharging=V/R

2. Tegangan pada baterai dapat hingga 16.2V, jadi pembagi resistansi R1,R2 digunakan untuk menurunkan tegangan di bawah 5V, yang diizinkan oleh Arduino. Output pembagi terhubung ke pin A0 Arduino. Untuk R1=5.1k Ohm dan R2=20kOhm Vout=Vin/(20000+5100)*5100=0.2 Jadi tegangan baterai dibagi 5.

Relay digunakan untuk memutuskan baterai dari rangkaian pengisian. Anda dapat melihat foto relay yang saya gunakan, tetapi secara umum setiap relay dengan kontrol 5V dapat digunakan. Lebih aman untuk menghubungkan baterai ke kontak normally open relay.

Saya menggunakan YwRobot I2C SERIAL LCD 1602 MODULE untuk menampilkan status pengisi, tetapi modul LCD I2C lainnya dapat digunakan. Tampaknya modul LCD YwRobot tidak didukung oleh perpustakaan LiquidCrystal_I2C standar, jadi saya menggunakan perpustakaan New LiquidCrystal Jika Anda menggunakan modul LCD yang berbeda Anda akan perlu mengubah baris ini:

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // set the LCD address to 0x27 for a 16 chars and 2 line display 

Untuk memberi daya ke konverter digital ke analog dan LCD saya menggunakan SparkFun Breadboard Power Supply 5V/3.3V. Mungkin akan baik untuk menggunakan 5V dari papan Arduino.

Anda juga perlu menyuplai 17V ke rangkaian pengisian. Jika Anda tidak memiliki suplai daya, Anda dapat menggunakan konverter DC/DC disesuaikan seperti ini

https://www.ebay.com/itm/DC-DC-Adjustable-Step-up-boost-Power-Converter-Module-XL6009-Replace-LM2577-/310717070508

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1

Fungsi

Saya tidak ingin banyak kabel, jadi tidak ada tombol untuk mengonfigurasi pengisian. Arus pengisian dikonfigurasi hanya dalam kode. Anda perlu mengatur arus pengisian yang diinginkan dalam charger.ino

//*************************** Charging parameters ***************************************** //***************************************************************************************** float target_current_mA=30; //Charging current mA float battery_nominal_capacity_mA=170; //Nominal capacity of battery mA float max_time_for_trickle_charge=6; //Maximum trickle charge time in minutes //***************************************************************************************** //***************************************************************************************** 

target_current_mA - arus pengisian konstan

max_time_for_trickle_charge - waktu maksimum dalam menit untuk pengisian gocok, dapat diatur hingga 600 (10j)

battery_nominal_capacity_mA - kapasitas baterai yang digunakan untuk menghitung arus gocok

Secara umum arus pengisian dapat hingga kapasitas nominal. Untuk baterai dengan kapasitas nominal 170mAh arus pengisian maksimum adalah 170mA. Arus pengisian minimum biasanya C/10 - 17mA untuk baterai 170mAh.

Setelah daya menyala pengisi akan memeriksa apakah baterai terhubung. Jika baterai terhubung baterai akan diisi dengan arus konstan yang dikonfigurasi hingga penuh. Pengisian diakhiri dengan mendeteksi negatif dV/dt selama 5 menit. Setelah pengisian selesai pengisi akan beralih ke pengisian gocok dengan arus C/40. Pengisi akan memutuskan diri dari baterai setelah waktu pengisian gocok maksimum terlewati.

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1

1 - dV/dt

2 - waktu pengisian dalam menit

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1
Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1

1 - Waktu pengisian

2 - Muatan yang ditransfer ke baterai

Informasi tambahan tentang baterai NiMH:

1. https://data.energizer.com/PDFs/nickelmetalhydride_appman.pdf

2. https://batteryuniversity.com/learn/article/charging_nickel_metal_hydride

Kode

charger.inoArduino
Main file
//THis for charging 9V NiMH battery //Battery can have have 6,7 or 8 1.25V cells //This makes nominal voltage between 7.5 and 10V #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <TimeLib.h> #include <TimerOne.h> #include <elapsedMillis.h> LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // set the LCD address to 0x27 for a 16 chars and 2 line display //Hardware defenitions #define MCP4725_ADDR 0x60 //DAC address #define DAC_REF_VOLTAGE 5.0 #define CHARGE_RELAY_PIN 2 #define DISCONNECT_CHARGE_RELAY HIGH #define CONNECT_CHARGE_RELAY LOW #define CURRENT_SENSING_SESISTOR_PIN A2 #define CURRENT_SENSING_SESISTOR 10.2 //OHm #define BATTERY_VOLTAGE_PIN A0 #define R1 5090 //Low side resistor of voltage sensing divider #define R2 19910//High side resistor of voltage sensing divider #define ADC_REF_VOLTAGE 4.89 //Arduino real supply voltage or AREF voltage for proper ADC to voltage translation #define R3 3300.0 #define R4 6800.0 #define AMP_GAIN (1+R4/R3) //Various definitions //#define MINIMUM_VOLTAGE 8.1 //Minimum voltage after regulator #define BATTERY_GOOD_VOLTAGE_THRESHOLD 7.0 #define MAXIMIM_ALLOWED_CURRENT 200 //Maximum allowed battery current mA (hard cut-off) #define MAXIMUM_BATTERY_VOLTAGE 15.0 //Maximum allowed battery voltage V (hard cut-off) #define VOLTAGE_STEP 0.001 //Step for voltage regulation #define POINTS_FOR_AVERAGING 100 //How many points taken for averaging #define MEASURE2MEASURE_US 5000 //Time between measurments in microseconds (need be more then 200 becouse of two analog reads) #define VOLTAGE_LOG_TIME_MIN 30.0 //Time in minutes to save voltage for dV/dT cutoff #define MINUTES_TO_LOG 5 #define TRICKLE_CURRENT_RATIO 40 //Charger states #define INITIALIZATION 0 #define NO_BATTERY 5 #define BATTERY_CONNECTED 10 #define BATTERY_VOLTAGE_LOW 15 #define CURRENT_RAMP_UP 20 #define CHARGING 30 #define CURRENT_RAMP_DOWN 32 #define TRICKLE 35 #define END_OF_TRICKLE 37 #define UNEXPECTED_CURRENT_FALL 40 #define UNABLE_TO_REACH_CURRENT 60 #define FULLY_CHARGED 70 #define CURRENT_TOO_HIGH 80 #define REGULATION_FAILED 90 #define OVERCURRENT 100 #define OVERVOLTAGE 101 #define FINAL_STATE 200 /// unsigned int voltage_readings[POINTS_FOR_AVERAGING],current_sensing_resistor_readings[POINTS_FOR_AVERAGING],averaging_index;//For averaging long unsigned int voltage_sum,current_sum;//Sum of averaging float regulator_voltage_code,resistor_voltage,csr_voltage_code,regulator_voltage,current_mA,battery_voltage;//Measurments float tmp_resistor_voltage,tmp_current_mA,tmp_regulator_voltage,tmp_battery_voltage; int i,j,charger_state; short unsigned int last_second,lcd_last_second,log_last_second; String lcd_last_string1,lcd_last_string2; String empty_string=""; String msg,eoc_line1,eoc_line2; unsigned char sec_index,min_index; //long long int charging_started; float sec_log[60],min_log[MINUTES_TO_LOG],last_blf; float trickle_current_mA; int total_minutes_average=0; elapsedMillis ChargingTimeMillis,TrickleChargingTimeMillis; float wanted_dac_voltage=0; float last_dac_voltage=0; //Messages const char msg_battery_detected[] PROGMEM ="Battery detected"; const char msg_no_battery[] PROGMEM ="No battery"; const char msg_battery_ok[] PROGMEM ="Battery ok"; const char msg_voltage_too_low[] PROGMEM ="Battery voltage too low"; const char msg_voltage_too_low_short[] PROGMEM ="V Battery low"; const char msg_ramp_up[] PROGMEM ="Ramp up"; const char msg_charging[] PROGMEM ="Charging"; const char msg_space[] PROGMEM =" "; const char msg_ramp_down[] PROGMEM ="Ramp down"; const char msg_trickle_charge[] PROGMEM ="Trickle charge"; const char msg_no_current[] PROGMEM ="No current"; const char msg_current_unreachable[] PROGMEM ="I unreachable"; const char msg_current_unreachable_long[] PROGMEM ="Unable to reach desired current"; const char msg_completed[] PROGMEM ="Completed"; const char msg_charge[] PROGMEM ="Charge"; const char msg_high_current[] PROGMEM ="High current"; const char msg_regulation_fault[] PROGMEM ="Regulation fault"; const char msg_overcurrent[] PROGMEM ="Current too high"; const char msg_overvoltage[] PROGMEM ="Voltage too high"; const char msg_trickle_completed[] PROGMEM ="Trickle finished"; //*************************** Charging parameters ***************************************** //***************************************************************************************** float target_current_mA=30; //Charging current mA float battery_nominal_capacity_mA=170; //Nominal capacity of battery mA float max_time_for_trickle_charge=6; //Maximum trickle charge time in minutes //***************************************************************************************** //***************************************************************************************** struct mytime { unsigned char hours; unsigned char minutes; unsigned int total_minutes; } elapsed_time; void setup() { pinMode(CHARGE_RELAY_PIN, OUTPUT); disconnect_charging_circuit(); //Disconnect charger ftom battery Wire.begin();//I2C dac_write_voltage(0);//Make sure lower possible current set Serial.begin(115200); last_second=second(); lcd_last_second=second(); log_last_second=second(); Timer1.initialize(MEASURE2MEASURE_US); //Will use to measure voltage and current of battery (microseconds) Timer1.attachInterrupt(read_hw); // attaches read_hw() as a timer overflow interrupt averaging_index=0; sec_index=0; min_index=0; charger_state=0;//Initial state of state machine wanted_dac_voltage=0;//Make shure minimum voltage outout last_blf=1.0; trickle_current_mA=battery_nominal_capacity_mA/TRICKLE_CURRENT_RATIO; //ChargingTimeMillis=0; //LCD lcd.begin(16,2); lcd.backlight(); lcd.clear(); update_lcd(F("Power up..."),empty_string); delay(1000); } float log_battery_voltage(){ //Log only once per second if (log_last_second==second()) return last_blf; else log_last_second=second(); sec_log[sec_index]=battery_voltage; if (sec_index<59) { sec_index++; } else {//If one minute logged //Calculate avetage per minute if (min_index>=MINUTES_TO_LOG) min_index=0; sec_index=0; float sum_v=0; for (i=0;i<60;i++){ sum_v+=sec_log[i]; } float min_average=sum_v/60.0; for(i=1;i<MINUTES_TO_LOG;i++) min_log[i-1]=min_log[i]; min_log[MINUTES_TO_LOG-1]=min_average;//Save minute average last_blf=best_linear_fit(min_log); min_index++; total_minutes_average++; } return last_blf; } 
main.inoArduino
void loop() { String msg1; switch(charger_state){ case INITIALIZATION://Initial state disconnect_charging_circuit();//Make shure relay disconnected dac_write_voltage(0);//Make sure lower possible current set wanted_dac_voltage=0;//Make shure minimum voltage outout delay(100); read_status(); if (battery_voltage>0.1) { charger_state=BATTERY_CONNECTED;//Battery detected update_lcd(M2S(msg_battery_detected),empty_string); Serial.println(M2S(msg_battery_detected)); delay(2000); } else {//No battery Serial.println(M2S(msg_no_battery)); update_lcd(M2S(msg_no_battery),construct_status_string()); charger_state=NO_BATTERY;//Battery detected delay(1000); } break; case NO_BATTERY://No battery read_status(); if (battery_voltage>0.1) { charger_state=BATTERY_CONNECTED;//Battery detected Serial.println(M2S(msg_battery_detected)); update_lcd(M2S(msg_battery_detected),construct_status_string()); delay(1500); } else{ //If no battery stay in this state update_lcd(M2S(msg_no_battery),construct_status_string()); delay(1100); } break; case BATTERY_CONNECTED://Battery connected dac_write_voltage(0);//Make sure lower possible current set wanted_dac_voltage=0; delay(100); read_status(); if (battery_voltage>BATTERY_GOOD_VOLTAGE_THRESHOLD){ charger_state=CURRENT_RAMP_UP;//Start charging current rampup update_lcd(M2S(msg_battery_ok),construct_status_string()); Serial.println(M2S(msg_battery_ok)); delay(2000); wanted_dac_voltage=get_approximated_dac_voltage(battery_voltage);//Set needed regulator voltage connect_charging_circuit(); delay(200); } else { charger_state=BATTERY_VOLTAGE_LOW;//Battery voltage too low Serial.println(M2S(msg_voltage_too_low)); update_lcd(M2S(msg_voltage_too_low_short),construct_status_string()); delay(1000); } break; case BATTERY_VOLTAGE_LOW://Battery voltage too low update_lcd(M2S(msg_voltage_too_low_short),construct_status_string()); Serial.println(M2S(msg_voltage_too_low)); charger_state=FINAL_STATE;//Halt break; case CURRENT_RAMP_UP:///Current rampup read_status(); update_lcd(M2S(msg_ramp_up),construct_status_string()); delay(50); if (current_mA<target_current_mA) { if (wanted_dac_voltage<4.99) { wanted_dac_voltage=wanted_dac_voltage+VOLTAGE_STEP; dac_write_voltage(wanted_dac_voltage); } else charger_state=UNABLE_TO_REACH_CURRENT;//Unable to reach desired current } else { charger_state=CHARGING;//Charging ChargingTimeMillis=0;//Reset counter } break; case CHARGING://Charging delay(200); read_status(); if (current_mA<1.0) charger_state=UNEXPECTED_CURRENT_FALL;//Current unexpectedly fell if (battery_voltage>MAXIMUM_BATTERY_VOLTAGE ) charger_state=OVERVOLTAGE;//Overvoltage if (abs(current_mA-target_current_mA)>0.2){//If current deviated from target if (current_mA<target_current_mA) {//If current less theb target if (wanted_dac_voltage<4.99) wanted_dac_voltage=wanted_dac_voltage+VOLTAGE_STEP; else charger_state=UNABLE_TO_REACH_CURRENT;//Unable to reach desired current } else { if (wanted_dac_voltage>0.01) wanted_dac_voltage=wanted_dac_voltage-VOLTAGE_STEP; else charger_state=CURRENT_TOO_HIGH;//Current too high, unable to lower } } if (abs(current_mA-target_current_mA)>5){//Regulation failed, difference too high charger_state=REGULATION_FAILED;//Regulation error, difference too high } dac_write_voltage(wanted_dac_voltage); if (total_minutes_average<MINUTES_TO_LOG) msg =String(M2S(msg_charging)+M2S(msg_space)+String("---")); else msg =String(M2S(msg_charging)+M2S(msg_space)+String(last_blf,3)); update_lcd(msg,construct_status_string()); log_battery_voltage(); if (last_blf<0) charger_state=FULLY_CHARGED;//If negative dV/dt detected, battery fully charged break; case CURRENT_RAMP_DOWN:///Current rampup read_status(); update_lcd(M2S(msg_ramp_down),construct_status_string()); Serial.println(M2S(msg_ramp_down)); delay(50); if (current_mA>trickle_current_mA) { if (wanted_dac_voltage>VOLTAGE_STEP) { wanted_dac_voltage=wanted_dac_voltage-VOLTAGE_STEP; dac_write_voltage(wanted_dac_voltage); } else charger_state=CURRENT_TOO_HIGH;//Unable to reduce current to trickle rate } else { charger_state=TRICKLE;//Charging TrickleChargingTimeMillis=0; Serial.println(M2S(msg_trickle_charge)); } break; case TRICKLE://Charging delay(200); read_status(); if (current_mA<0.2) charger_state=UNEXPECTED_CURRENT_FALL;//Current unexpectedly fell if (battery_voltage>MAXIMUM_BATTERY_VOLTAGE ) charger_state=OVERVOLTAGE;//Overvoltage if (abs(current_mA-trickle_current_mA)>0.2){//If current deviated from target if (current_mA<trickle_current_mA) {//If current less theb target if (wanted_dac_voltage<4.99) wanted_dac_voltage=wanted_dac_voltage+VOLTAGE_STEP; else charger_state=UNABLE_TO_REACH_CURRENT;//Unable to reach desired current } else { if (wanted_dac_voltage>0.01) wanted_dac_voltage=wanted_dac_voltage-VOLTAGE_STEP; else charger_state=CURRENT_TOO_HIGH;//Current too high, unable to lower } } if (abs(current_mA-trickle_current_mA)>5){//Regulation failed, difference too high charger_state=REGULATION_FAILED;//Regulation error, difference too high } dac_write_voltage(wanted_dac_voltage); if ((second()%8)<4) update_lcd(M2S(msg_trickle_charge),construct_status_string()); else update_lcd(eoc_line1,eoc_line2); elapsed_time=mills2time(TrickleChargingTimeMillis); if (elapsed_time.total_minutes>max_time_for_trickle_charge) {//Maximum allowed trickle charge update_lcd(eoc_line1,eoc_line2); charger_state=END_OF_TRICKLE;//Halt disconnect_charging_circuit(); //Disconnect charger from battery } break; case END_OF_TRICKLE: if ((second()%8)<4) update_lcd(M2S(msg_trickle_completed),construct_status_string()); else update_lcd(eoc_line1,eoc_line2); break; case UNEXPECTED_CURRENT_FALL://Current unexpectedly fell Serial.println(F("Current unexpectedly fell")); disconnect_charging_circuit(); wanted_dac_voltage=0; update_lcd(M2S(msg_no_current),construct_status_string()); charger_state=FINAL_STATE;//Halt delay(1000); break; case UNABLE_TO_REACH_CURRENT://Unable to reach desired current Serial.println(M2S(msg_current_unreachable_long)); disconnect_charging_circuit(); wanted_dac_voltage=0; dac_write_voltage(wanted_dac_voltage); delay(1000); update_lcd(M2S(msg_current_unreachable),construct_status_string()); charger_state=FINAL_STATE;//Halt break; case FULLY_CHARGED://Fully charged elapsed_time=mills2time(ChargingTimeMillis); int charge_mAh; charge_mAh=calculate_charge(ChargingTimeMillis); msg =String(M2S(msg_completed)+M2S(msg_space)+construct_time_string(elapsed_time)); msg1=String(M2S(msg_charge)+M2S(msg_space)+String(charge_mAh)+String("mAh")); eoc_line1=msg; eoc_line2=msg1; update_lcd(msg,msg1); Serial.println(msg); delay(3000); charger_state=CURRENT_RAMP_DOWN;//Halt break; case CURRENT_TOO_HIGH://Current too high Serial.println(F("Unable to lower current to target")); disconnect_charging_circuit(); wanted_dac_voltage=0; dac_write_voltage(0); update_lcd(M2S(msg_high_current),construct_status_string()); delay(1000); charger_state=FINAL_STATE;//Halt break; case REGULATION_FAILED://Regulation failed Serial.println(M2S(msg_regulation_fault)); disconnect_charging_circuit(); wanted_dac_voltage=0; dac_write_voltage(0); update_lcd(M2S(msg_regulation_fault),construct_status_string()); delay(1000); charger_state=FINAL_STATE;//Halt break; case OVERCURRENT://Overcurrent disconnect_charging_circuit(); Serial.println(M2S(msg_overcurrent)); wanted_dac_voltage=0; dac_write_voltage(wanted_dac_voltage); update_lcd(M2S(msg_overcurrent),construct_status_string()); delay(1000); charger_state=FINAL_STATE;//Halt break; case OVERVOLTAGE://Overvoltage disconnect_charging_circuit(); Serial.println(M2S(msg_overvoltage)); wanted_dac_voltage=0; dac_write_voltage(wanted_dac_voltage); update_lcd(M2S(msg_overvoltage),construct_status_string()); delay(1000); charger_state=FINAL_STATE;//Halt break; case FINAL_STATE://Halt delay(10000); break; default: wanted_dac_voltage=0; charger_state=0; } read_status(); if (last_second!=second()){ Serial.print(current_mA); Serial.print(","); Serial.println(battery_voltage); last_second=second(); } } 
hw.inoArduino
float get_approximated_dac_voltage(float vbat){ float offset_voltage=1.2; float adc_voltage=(vbat-offset_voltage)/AMP_GAIN-0.5; if (adc_voltage<0) adc_voltage=0; return adc_voltage; } int voltage_to_code(float voltage){ int code=4095.0/DAC_REF_VOLTAGE*voltage; return code; } void dac_write(int code){ Wire.beginTransmission(MCP4725_ADDR); Wire.write(64); // cmd to update the DAC Wire.write(code >> 4); // the 8 most significant bits... Wire.write((code & 15) << 4); // the 4 least significant bits... Wire.endTransmission(); } void read_status(){ voltage_sum=0; current_sum=0; for (i=0;i<POINTS_FOR_AVERAGING;i++){ voltage_sum+=voltage_readings[i]; current_sum+=current_sensing_resistor_readings[i]; } regulator_voltage_code =((float)voltage_sum)/POINTS_FOR_AVERAGING; csr_voltage_code=((float)current_sum)/POINTS_FOR_AVERAGING; regulator_voltage = regulator_voltage_code * (ADC_REF_VOLTAGE / 1023.0)*(R1+R2)/R1; resistor_voltage=csr_voltage_code * (ADC_REF_VOLTAGE / 1023.0); current_mA = resistor_voltage/CURRENT_SENSING_SESISTOR*1000; battery_voltage=regulator_voltage-resistor_voltage; } void dac_write_voltage(float voltage){ last_dac_voltage=voltage; dac_write(voltage_to_code(voltage)); } void read_hw(){ voltage_readings[averaging_index]=analogRead(BATTERY_VOLTAGE_PIN); current_sensing_resistor_readings[averaging_index]=analogRead(CURRENT_SENSING_SESISTOR_PIN); tmp_resistor_voltage=current_sensing_resistor_readings[averaging_index] * (ADC_REF_VOLTAGE / 1023.0); tmp_current_mA=tmp_resistor_voltage/CURRENT_SENSING_SESISTOR*1000; tmp_regulator_voltage=voltage_readings[averaging_index] * (ADC_REF_VOLTAGE / 1023.0)*(R1+R2)/R1; tmp_battery_voltage=tmp_regulator_voltage-tmp_resistor_voltage; averaging_index++; if (averaging_index>=POINTS_FOR_AVERAGING) averaging_index=0; if(tmp_battery_voltage>MAXIMUM_BATTERY_VOLTAGE) { disconnect_charging_circuit(); charger_state=OVERVOLTAGE; } if (tmp_current_mA>MAXIMIM_ALLOWED_CURRENT ){ disconnect_charging_circuit(); charger_state=OVERCURRENT; } } void disconnect_charging_circuit(){ digitalWrite(CHARGE_RELAY_PIN,DISCONNECT_CHARGE_RELAY); } void connect_charging_circuit(){ digitalWrite(CHARGE_RELAY_PIN,CONNECT_CHARGE_RELAY); } 
lcd.inoArduino
void update_lcd(String first_line,String second_line){ if (lcd_last_string1!=first_line){ lcd.clear(); lcd.setCursor(0,0); lcd.print(first_line); lcd_last_string1=first_line; lcd.setCursor(0,1); lcd.print(second_line); lcd_last_string2=second_line; } if(lcd_last_second!=second()){ if (lcd_last_string2!=second_line){ lcd.setCursor(0,1); lcd.print(second_line); lcd_last_string2=second_line; } } lcd_last_second=second(); } String construct_status_string(void){ String v,i; if (battery_voltage<10) v=String(battery_voltage, 2); else v=String(battery_voltage, 1); if (current_mA<10) i=String(current_mA, 2); else i=String(current_mA, 1); mytime elapsed; String msg,msg_time; switch(charger_state){ case CHARGING: elapsed=mills2time(ChargingTimeMillis); break; case TRICKLE: elapsed=mills2time(TrickleChargingTimeMillis); break; } if (charger_state==CHARGING || charger_state==TRICKLE){ if (elapsed.total_minutes<10) msg_time=String(elapsed.total_minutes)+" "; else if (elapsed.total_minutes<100) msg_time=String(elapsed.total_minutes)+" "; else msg_time=String(elapsed.total_minutes); } switch(charger_state){ case CHARGING: msg=v+String(F("V "))+i+String(F("mA"))+" "+msg_time; break; case TRICKLE: msg=v+String(F("V "))+i+String(F("mA"))+" "+msg_time; break; default: msg=v+String(F("V "))+i+String(F("mA")); } msg.replace("-",""); return msg; } String construct_time_string(mytime timeinfo){ String mystring=String(timeinfo.hours,DEC)+String(F(":"))+String(timeinfo.minutes,DEC); return mystring; } 
calculations.inoArduino
float best_linear_fit(float y[MINUTES_TO_LOG]){ float sx = 0.0, sy = 0.0, sxx = 0.0, sxy = 0.0; for (i = 0; i < MINUTES_TO_LOG; ++i){ sx += (float)i; sy += y[i]; sxx += (float)i*(float)i; sxy += (float)i*y[i]; } float delta = MINUTES_TO_LOG*sxx - sx*sx; float slope = (MINUTES_TO_LOG*sxy - sx*sy)/delta; return slope; } mytime mills2time(unsigned long int time_mills){ mytime result; result.total_minutes=time_mills/1000/60; result.hours=result.total_minutes/60; result.minutes=result.total_minutes%60; return result; } int calculate_charge(unsigned long time_mills){ unsigned int elapsed_sec=time_mills/1000; float elapsed_hours=elapsed_sec/60.0/60.0*target_current_mA; return (int)elapsed_hours; } String M2S(const char signMessage[]){ int k,len; len= strlen_P(signMessage); String mess; char char_message[len+1],myChar; for (k = 0; k < len; k++){ mess+= (char)pgm_read_byte_near(signMessage + k); } return mess; } 
New LiquidCrystal library used for this projectArduino
No preview (download only).

Skematik

Pengisi Baterai 9V NiMH Canggih – Cerdas, Akurat, dan Terpercaya – Versi 1charger_AgK96zxw2T.zip

Proses manufaktur

  1. Alarm Tanaman Cerdas Thirst Alert – Sensor DIY untuk Hidrasi Tanaman Optimal
  2. Bangun Jam Kata Italia dengan Arduino Nano, NeoPixel, LCD, dan RTC
  3. Hemat Pin Arduino: Hubungkan Keypad 4x3 dengan Hanya Tiga Koneksi
  4. Membuat Meter kWh Sigfox Terpercaya dengan Arduino MKR Fox 1200
  5. Pemantau Suhu dengan Bluetooth menggunakan Arduino Nano
  6. Kunci Gestur Sentuh Bebas: Akses Aman dengan APDS-9960 dan Arduino
  7. Kit Pendamping Arduino Nano – Komponen & Alat Esensial untuk Elektronika DIY
  8. Kit Adapter USB MIDI untuk Arduino Micro / Leonardo – Daftar Lengkap Komponen
  9. Bangun Rangkaian Input Analog Terisolasi untuk Arduino
  10. Tes Waktu Reaksi Presisi Menggunakan Arduino UNO