Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1

Composants et fournitures

Arduino UNO × 1
SparkFun I2C DAC Breakout - MCP4725 × 1
Régulateur linéaire à sortie ajustable (LM317) × 1
Ampli-op double usage général Texas Instruments (LM358) × 1
Résistance 6,8 kΩ × 2
Résistance 3,3 kΩ × 1
Résistance 5,1 kΩ × 1
Résistance 1 kΩ × 1
Résistance 10 Ω × 1
Relais (générique) × 1
Résistance 20 kΩ × 1
Alimentation breadboard SparkFun 5V/3,3V × 1
Alimentation 17 V × 1
Module LCD I2C série YwRobot 1602 × 1

À propos du projet

Je cherchais un chargeur intelligent capable de charger une batterie 9V NiMH en quelques heures, sans en trouver. Tous les chargeurs trouvés étaient vraiment « bêtes » : courant de charge inconnu, pas de détection de fin de charge. Ils surchargent la batterie et réduisent sa durée de vie. J'ai donc décidé de créer un chargeur « intelligent ».

Cette première version reste simple : charge à courant constant, arrêt automatique quand la batterie est pleine, charge de maintien (trickle), mesure de la charge transférée.

La prochaine version ajoutera : décharge, mesure de capacité, cyclage.

Avertissement : Charger une batterie à courant élevé peut provoquer explosion ou incendie. Ne laissez jamais le chargeur sans surveillance. N'essayez pas de charger des piles non rechargeables (alcalines). Ce chargeur a été testé uniquement avec des NiMH (utilisation à vos risques, je décline toute responsabilité en cas de dommage dû à des bugs de conception ou de code). Charger d'autres types de batteries nécessitera une modification du code.

Théorie

Quelques faits utiles pour comprendre les paramètres du chargeur :

  1. Charge rapide : 0,5C–1C. Surveillance de l'état de charge, arrêt par dV/dt (taux de variation de tension) ou dT/dt (taux de variation de température).
  2. Charge lente : 0,1C. Durée 14–16 h. Arrêt par minuterie. dT/dt impossible à faible courant. dV/dt peu fiable sous 0,5C selon la littérature.
Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1

Paramètres de base du circuit de charge

Une batterie 9 V a généralement 7 cellules en série (parfois 6 ou 8). Le régulateur doit fournir au moins 8 × 1,6 = 12,8 V. La tension de dropout du LM317 est jusqu'à 2 V, donc une alimentation ~15 V est nécessaire (sans compter la chute sur la résistance de détection de courant).

Pour un courant max de 200 mA et une résistance de détection de 10 Ω, la chute supplémentaire est de 2 V → alimentation 17 V requise.

Une cellule profondément déchargée peut avoir une tension très basse, voire négative. La tension minimale du régulateur idéalement 0 V, mais avec LM317 ~1,2 V.

Circuit

Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1
Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1

Explications du circuit

L'idée de base : mesurer le courant de charge et ajuster la tension du régulateur jusqu'à obtenir le courant désiré. Le courant est mesuré par la chute de tension sur la résistance de détection R5 (I = V/R).

Deux mesures de tension :

  1. Résistance R5 → broche A2 Arduino. Tension sur R5 → courant de charge (I = V/R).
  2. Tension batterie (jusqu'à 16,2 V) → diviseur R1/R2 (5,1 kΩ / 20 kΩ) → broche A0 Arduino. Division par 5 (Vout = Vin × 5100/(20000+5100) ≈ 0,2 Vin).

Relais pour déconnecter la batterie du circuit de charge (contacts normalement ouverts pour sécurité).

Affichage : module LCD I2C YwRobot 1602. La bibliothèque standard LiquidCrystal_I2C ne le supporte pas ; j'ai utilisé la bibliothèque New LiquidCrystal. Si vous utilisez un autre module LCD, modifiez la ligne :

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Alimentation du DAC et du LCD : module SparkFun Breadboard Power Supply 5V/3,3V (ou le 5 V de l'Arduino).

Alimentation 17 V pour le circuit de charge : si pas d'alim, utilisez un convertisseur DC/DC ajustable type XL6009.

Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1
Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1
Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1

Fonctionnalités

Pas de boutons pour configurer la charge : le courant se règle uniquement dans le code (charger.ino) :

//*************************** Paramètres de charge ***************************************** 
float target_current_mA = 30;           // Courant de charge mA 
float battery_nominal_capacity_mA = 170; // Capacité nominale batterie mA 
float max_time_for_trickle_charge = 6;   // Temps max charge maintien (minutes) 
//*****************************************************************************************

Courant de charge généralement jusqu'à la capacité nominale (ex. 170 mAh → max 170 mA). Minimum habituel C/10 (17 mA pour 170 mAh).

Au démarrage, le chargeur vérifie la présence de la batterie. Si présente, charge à courant constant jusqu'à détection de fin de charge par dV/dt négatif sur 5 minutes. Puis passe en charge de maintien (C/40). Se déconnecte après le temps maximal de maintien.

Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1
Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1
Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1

Écrans LCD : 1) dV/dt, 2) temps de charge (min) / 1) temps de charge, 2) charge transférée (mAh).

Ressources sur batteries NiMH :

  1. Energizer NiMH Application Manual
  2. Battery University - Charging NiMH

Code

charger.ino (Arduino) – Fichier principal

//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  
#include  
#include  
#include  
#include  
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 
// Hardware definitions 
#define MCP4725_ADDR 0x60 
#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 
#define R2 19910 
#define ADC_REF_VOLTAGE 4.89 
#define R3 3300.0 
#define R4 6800.0 
#define AMP_GAIN (1+R4/R3) 
// Various definitions 
#define BATTERY_GOOD_VOLTAGE_THRESHOLD 7.0 
#define MAXIMIM_ALLOWED_CURRENT 200 
#define MAXIMUM_BATTERY_VOLTAGE 15.0 
#define VOLTAGE_STEP 0.001 
#define POINTS_FOR_AVERAGING 100 
#define MEASURE2MEASURE_US 5000 
#define VOLTAGE_LOG_TIME_MIN 30.0 
#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; 
long unsigned int voltage_sum, current_sum; 
float regulator_voltage_code, resistor_voltage, csr_voltage_code, regulator_voltage, current_mA, battery_voltage; 
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; 
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 (stockés en PROGMEM) 
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"; 
// Paramètres de charge (voir plus haut) 
struct mytime { 
  unsigned char hours; 
  unsigned char minutes; 
  unsigned int total_minutes; 
} elapsed_time; 

void setup() { 
  pinMode(CHARGE_RELAY_PIN, OUTPUT); 
  disconnect_charging_circuit(); 
  Wire.begin(); 
  dac_write_voltage(0); 
  Serial.begin(115200); 
  last_second = second(); 
  lcd_last_second = second(); 
  log_last_second = second(); 
  Timer1.initialize(MEASURE2MEASURE_US); 
  Timer1.attachInterrupt(read_hw); 
  averaging_index = 0; 
  sec_index = 0; 
  min_index = 0; 
  charger_state = 0; 
  wanted_dac_voltage = 0; 
  last_blf = 1.0; 
  trickle_current_mA = battery_nominal_capacity_mA / TRICKLE_CURRENT_RATIO; 
  lcd.begin(16, 2); 
  lcd.backlight(); 
  lcd.clear(); 
  update_lcd(F("Power up..."), empty_string); 
  delay(1000); 
} 

float log_battery_voltage() { 
  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 (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; 
    last_blf = best_linear_fit(min_log); 
    min_index++; 
    total_minutes_average++; 
  } 
  return last_blf; 
} 

main.ino (Arduino)

void loop() { 
  String msg1; 
  switch(charger_state){ 
    case INITIALIZATION: 
      disconnect_charging_circuit(); 
      dac_write_voltage(0); 
      wanted_dac_voltage = 0; 
      delay(100); 
      read_status(); 
      if (battery_voltage > 0.1) { 
        charger_state = BATTERY_CONNECTED; 
        update_lcd(M2S(msg_battery_detected), empty_string); 
        Serial.println(M2S(msg_battery_detected)); 
        delay(2000); 
      } else { 
        Serial.println(M2S(msg_no_battery)); 
        update_lcd(M2S(msg_no_battery), construct_status_string()); 
        charger_state = NO_BATTERY; 
        delay(1000); 
      } 
      break; 
    case NO_BATTERY: 
      read_status(); 
      if (battery_voltage > 0.1) { 
        charger_state = BATTERY_CONNECTED; 
        Serial.println(M2S(msg_battery_detected)); 
        update_lcd(M2S(msg_battery_detected), construct_status_string()); 
        delay(1500); 
      } else { 
        update_lcd(M2S(msg_no_battery), construct_status_string()); 
        delay(1100); 
      } 
      break; 
    case BATTERY_CONNECTED: 
      dac_write_voltage(0); 
      wanted_dac_voltage = 0; 
      delay(100); 
      read_status(); 
      if (battery_voltage > BATTERY_GOOD_VOLTAGE_THRESHOLD) { 
        charger_state = CURRENT_RAMP_UP; 
        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); 
        connect_charging_circuit(); 
        delay(200); 
      } else { 
        charger_state = BATTERY_VOLTAGE_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: 
      update_lcd(M2S(msg_voltage_too_low_short), construct_status_string()); 
      Serial.println(M2S(msg_voltage_too_low)); 
      charger_state = FINAL_STATE; 
      break; 
    case CURRENT_RAMP_UP: 
      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 += VOLTAGE_STEP; 
          dac_write_voltage(wanted_dac_voltage); 
        } else charger_state = UNABLE_TO_REACH_CURRENT; 
      } else { 
        charger_state = CHARGING; 
        ChargingTimeMillis = 0; 
      } 
      break; 
    case CHARGING: 
      delay(200); 
      read_status(); 
      if (current_mA < 1.0) charger_state = UNEXPECTED_CURRENT_FALL; 
      if (battery_voltage > MAXIMUM_BATTERY_VOLTAGE) charger_state = OVERVOLTAGE; 
      if (abs(current_mA - target_current_mA) > 0.2) { 
        if (current_mA < target_current_mA) { 
          if (wanted_dac_voltage < 4.99) wanted_dac_voltage += VOLTAGE_STEP; 
          else charger_state = UNABLE_TO_REACH_CURRENT; 
        } else { 
          if (wanted_dac_voltage > 0.01) wanted_dac_voltage -= VOLTAGE_STEP; 
          else charger_state = CURRENT_TOO_HIGH; 
        } 
      } 
      if (abs(current_mA - target_current_mA) > 5) charger_state = REGULATION_FAILED; 
      dac_write_voltage(wanted_dac_voltage); 
      if (total_minutes_average < MINUTES_TO_LOG) 
        msg = String(M2S(msg_charging) + M2S(msg_space) + "---"); 
      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; 
      break; 
    case CURRENT_RAMP_DOWN: 
      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 -= VOLTAGE_STEP; 
          dac_write_voltage(wanted_dac_voltage); 
        } else charger_state = CURRENT_TOO_HIGH; 
      } else { 
        charger_state = TRICKLE; 
        TrickleChargingTimeMillis = 0; 
        Serial.println(M2S(msg_trickle_charge)); 
      } 
      break; 
    case TRICKLE: 
      delay(200); 
      read_status(); 
      if (current_mA < 0.2) charger_state = UNEXPECTED_CURRENT_FALL; 
      if (battery_voltage > MAXIMUM_BATTERY_VOLTAGE) charger_state = OVERVOLTAGE; 
      if (abs(current_mA - trickle_current_mA) > 0.2) { 
        if (current_mA < trickle_current_mA) { 
          if (wanted_dac_voltage < 4.99) wanted_dac_voltage += VOLTAGE_STEP; 
          else charger_state = UNABLE_TO_REACH_CURRENT; 
        } else { 
          if (wanted_dac_voltage > 0.01) wanted_dac_voltage -= VOLTAGE_STEP; 
          else charger_state = CURRENT_TOO_HIGH; 
        } 
      } 
      if (abs(current_mA - trickle_current_mA) > 5) charger_state = REGULATION_FAILED; 
      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) { 
        update_lcd(eoc_line1, eoc_line2); 
        charger_state = END_OF_TRICKLE; 
        disconnect_charging_circuit(); 
      } 
      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: 
      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; 
      delay(1000); 
      break; 
    case UNABLE_TO_REACH_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; 
      break; 
    case FULLY_CHARGED: 
      elapsed_time = mills2time(ChargingTimeMillis); 
      int 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) + "mAh"); 
      eoc_line1 = msg; 
      eoc_line2 = msg1; 
      update_lcd(msg, msg1); 
      Serial.println(msg); 
      delay(3000); 
      charger_state = CURRENT_RAMP_DOWN; 
      break; 
    case 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; 
      break; 
    case 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; 
      break; 
    case 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; 
      break; 
    case 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; 
      break; 
    case FINAL_STATE: 
      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.ino (Arduino)

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); 
  Wire.write(code >> 4); 
  Wire.write((code & 15) << 4); 
  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) { 
  if (last_dac_voltage == voltage) return; 
  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.ino (Arduino)

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 + "V " + i + "mA " + msg_time; break; 
    case TRICKLE: msg = v + "V " + i + "mA " + msg_time; break; 
    default: msg = v + "V " + i + "mA"; 
  } 
  msg.replace("-", ""); 
  return msg; 
} 
String construct_time_string(mytime timeinfo) { 
  return String(timeinfo.hours, DEC) + ":" + String(timeinfo.minutes, DEC); 
} 

calculations.ino (Arduino)

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; 
  for (k = 0; k < len; k++) { 
    mess += (char)pgm_read_byte_near(signMessage + k); 
  } 
  return mess; 
} 

Bibliothèque New LiquidCrystal utilisée

Téléchargement uniquement (pas d'aperçu).

Schémas

Chargeur avancé 9V NiMH – Intelligent, précis et fiable – Version 1

charger_AgK96zxw2T.zip

Processus de fabrication

  1. Alerte Soif Plante Intelligente – Capteur DIY pour une Hydratation Optimale
  2. Horloge à Mots Italienne avec Arduino Nano, NeoPixel, LCD et RTC
  3. Économisez les broches Arduino : clavier 4×3 sur 3 entrées analogiques
  4. Compteur kWh Sigfox fiable avec Arduino MKR Fox 1200
  5. Moniteur de température Bluetooth avec Arduino Nano
  6. Serrure à gestes sans contact : Accès sécurisé avec APDS-9960 et Arduino
  7. Kit Compagnon Arduino Nano – Composants et Outils Essentiels pour l'Électronique DIY
  8. Circuit d'entrée analogique isolée pour Arduino : Protection contre les parasites industriels
  9. Test Précis de Temps de Réaction avec Arduino UNO