Micro-ampèremètre 5

Micro-Ampèremètre Arduino Mega 2560, écran OLED et INA-226

Micro-ampèremètre Arduino, INA 226 OLED

Le montage est réalisé à partir de l’excellent article du blog de RitonDuino. Je vous invite à le consulter pour connaître les détails de l’objet.

Fonctionalités

  • Alimenté en USB, par le PC par exemple;
  • Mesure de courants de 500 nA à 800 mA;
  • Affichage de la tension et du courant sur un écran;
  • Un interrupteur pour commuter les fils de données de l’USB;
  • Un interrupteur pour passer en mode Série;
  • Un bouton pour la calibration avant mesure;
  • Connexion sur prises USB ou par douilles à visser;
  • Possibilité d’envoyer les données en série au PC et d’afficher un graphe;

Composants

  • 1 Arduino Mega 2560;
  • 1 Ecran OLED 0.91″ I2C;
  • 2 INA 226 avec shunt de 0,1 et 10 ohms;
  • 1 interrupteur bipolaire;
  • 1 interrupteur unipolaire;
  • 1 bouton poussoir;
  • 1 transistor IRZL44N;
  • 1 résistance 110 ohms;
  • 1 résistance 9100 ohms;
  • 1 condensateur 100 nF;
  • un fusible 500 mA;
  • 1 Diode … 33V;
  • 1 connecteur USB type A;
  • 1 connecteur USB type B;
  • 4 douilles à visser;
  • 1 boîtier imprimé en 3D.

Schéma

Schéma Micro-Amperemètre USB

Boîtier

Les fichiers 3D pourront bientôt être téléchargés en cliquant sur le bouton ci-dessous

Pour plus d'informations, cliquez le bouton

Si vous souhaitez les obtenir avant, laissez-moi un message.
Vous pouvez adapter les dimensions et modifier les formes à partir du fichier FreeCAD.

Code

Pour la programmation, j’utilise Platformio mais l’IDE Arduino convient également.

Platformio.ini

[env:megaatmega2560]
platform = atmelavr
board = megaatmega2560
framework = arduino
monitor_speed = 230400
lib_deps = 
	thomasfredericks/Bounce2@^2.72
	adafruit/Adafruit GFX Library@^1.11.11
	adafruit/Adafruit SSD1306@^2.5.13

main.cpp

#include <Arduino.h>
#include <Wire.h>
#include <EEPROM.h>
#include <Bounce2.h>
#include <INA226.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
String mat;
String mvt;
float ma;
float mv;

class MyINA226 : public INA226
{
  public:
    bool begin(uint8_t address = INA226_ADDRESS);
    int16_t readRegister16(uint8_t reg);
    int8_t addr;
};

bool MyINA226::begin(uint8_t address)
{
  INA226::begin(address);
  Wire.begin();
  addr = address;
  return true;
}

int16_t MyINA226::readRegister16(uint8_t reg)
{
  int16_t value;

  Wire.beginTransmission(addr);
#if ARDUINO >= 100
  Wire.write(reg);
#else
  Wire.send(reg);
#endif
  Wire.endTransmission();

  delay(1);

  Wire.beginTransmission(addr);
  Wire.requestFrom(addr, 2);
  while (!Wire.available()) {};
#if ARDUINO >= 100
  uint8_t vha = Wire.read();
  uint8_t vla = Wire.read();
#else
  uint8_t vha = Wire.receive();
  uint8_t vla = Wire.receive();
#endif
  Wire.endTransmission();

  value = vha << 8 | vla;

  return value;
}

Bounce debouncer = Bounce();
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
MyINA226 ina226_L;
MyINA226 ina226_H;
char *title = "Micro Amperemetre";

#define INA226_H_OHM                .1 // mohm
#define INA226_H_SHUNT              100 // mohm
#define INA226_L_OHM                10  // ohm
#define INA226_L_SHUNT              10  // ohm
#define MOSFET_GATE                 5
#define BUTTON                      6
#define LED                         13
#define GUI_SWITCH                  8
#define VOLTAGE_MEASUREMENT_PERIOD  2000
#define LOWPOWER_SEND_DATA_DELAY    1000
#define LOWPOWER_MEASUREMENT_PERIOD 2000
#define BUFFER_SIZE                 400

struct buffer
{
  long timeStamp;
  long measurement;
  char type;
};

struct buffer dataBuffer[BUFFER_SIZE];

int dataBufferIndex;

int shuntCurrentOffset;

void enableLowPowerShunt(void)
{
  digitalWrite(MOSFET_GATE, LOW);
}

void disableLowPowerShunt(void)
{
  digitalWrite(MOSFET_GATE, HIGH);
}

void displayTitle(void)
{
  int16_t  x1, y1;
  uint16_t w, h;
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.getTextBounds(title, 0, 0, &x1, &y1, &w, &h);
  display.setCursor((display.width() - w) / 2, 0);
  display.println(title);
  display.println("");
}

void displayMessages(char *msg1, char *msg2)
{
  displayTitle();
  display.println(msg1);
  display.println(msg2);
  display.display();
  delay(2000);
}

void displayCalibration(char *prompt, int current, long t)
{
  displayTitle();
  display.println(prompt);
  display.print("COURANT: ");
  if (current < 0) {
    display.print('-');
    current = abs(current);
  }
  display.print(current / 10);
  display.print('.');
  display.print(current % 10);
  display.println(" uA");
  display.display();
  delay(t);
}

void wait4Key(char *prompt)
{
  displayTitle();
  display.println(prompt);
  display.display();
  while (debouncer.read() == HIGH) {
      debouncer.update();
  }
  while (debouncer.read() == LOW) {
      debouncer.update();
  }
}

void displayMeasurements(float vbus, float current)
{
  displayTitle();
  display.print("TENSION: ");
  display.print(vbus);
  display.println(" V");
  display.print("COURANT: ");
  if (current > 1000) {
    display.print(current / 1000, 1);
    display.print(" ");
    display.println("mA");
  }
  else {
    display.print(current, 1);
    display.print(" ");
    display.println("uA");
  }
  display.display();
}

void setup() 
{
  Serial.begin(230400);
  Serial.println("INA226 POWER BENCH");

  pinMode(MOSFET_GATE, OUTPUT);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  disableLowPowerShunt();

  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(GUI_SWITCH, INPUT_PULLUP);
  debouncer.attach(BUTTON);
  debouncer.interval(50);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  ina226_H.begin(0x40);
  ina226_H.configure(INA226_AVERAGES_1, INA226_BUS_CONV_TIME_140US, INA226_SHUNT_CONV_TIME_140US, INA226_MODE_SHUNT_BUS_CONT);
  ina226_H.calibrate(INA226_H_OHM, 0.82);
  ina226_L.begin(0x41);
  ina226_L.configure(INA226_AVERAGES_16, INA226_BUS_CONV_TIME_8244US, INA226_SHUNT_CONV_TIME_8244US, INA226_MODE_SHUNT_BUS_CONT);
  ina226_L.calibrate(INA226_L_SHUNT, 0.0082);
  displayMessages("DISCONNECT LOAD", "PRESS TO CALIBRATE");

  EEPROM.get(0, shuntCurrentOffset);
  Serial.print("read current offset from EEPROM: ");
  Serial.println(shuntCurrentOffset);
}

long readBusVoltage(void)
{
  long busVoltage = ina226_H.readRegister16(INA226_REG_BUSVOLTAGE) * 125L / 100L; 
  return busVoltage;
}

long readShuntCurrent(void)
{
  // return shunt current in tenth of µA
  // shunt voltage unit is 100nV
  long shuntVoltage = ina226_H.readRegister16(INA226_REG_SHUNTVOLTAGE) * 25L;
  // shunt current unit is 100nA
  long shuntCurrent = (shuntVoltage / INA226_H_SHUNT) * 1000;
  return shuntCurrent;
}

long readLowPowerShuntCurrent(void)
{
  // return shunt current in tenth of µA
  // shunt voltage unit is 100nV
  long shuntVoltage = ina226_L.readRegister16(INA226_REG_SHUNTVOLTAGE) * 25L;
  // shunt current unit is 100nA
  long shuntCurrent = (shuntVoltage / INA226_L_SHUNT);
  return shuntCurrent;
}

void sendDatas(void)
{
  for (int i = 0 ; i < dataBufferIndex ; i++) {
    Serial.print(dataBuffer[i].timeStamp);
    Serial.print("-");
    if (dataBuffer[i].type == 'V') {
      mvt=(dataBuffer[i].measurement);
      mv=mvt.toFloat();
      Serial.print("V:");
      Serial.println(mv, 3);
    }
    if (dataBuffer[i].type == 'I') {
      Serial.print("I:");
      mat=(dataBuffer[i].measurement);
      ma=mat.toFloat()/1000;
      Serial.println(ma, 3);
    }
  }
  memset(dataBuffer, 0, sizeof(dataBuffer));
  dataBufferIndex = 0;
}

void loop()
{
  long currentTime = millis();
  static long periodicMeasurementTime;
  static long highPowerMeasurementTime;
  static long lowPowerShuntCurrent;
  int offset = 0;
  int samples = 0;
  static long busVoltage;
  static long lastBusVoltage;
  static long lastShuntCurrent;

  long shuntCurrent = readShuntCurrent();
  if (periodicMeasurementTime == 0 || periodicMeasurementTime + VOLTAGE_MEASUREMENT_PERIOD < currentTime) {
    busVoltage = readBusVoltage();
    if (busVoltage != lastBusVoltage) {
      if (dataBufferIndex < BUFFER_SIZE) {
        dataBuffer[dataBufferIndex].timeStamp = currentTime;
        dataBuffer[dataBufferIndex].type = 'V';
        dataBuffer[dataBufferIndex++].measurement = busVoltage;
        lastBusVoltage = busVoltage;
      }
      else {
        digitalWrite(LED, HIGH);
        sendDatas();
        digitalWrite(LED, LOW);
      }
    }
  }
  if (shuntCurrent < 50000) {
    if (periodicMeasurementTime == 0 || periodicMeasurementTime + LOWPOWER_MEASUREMENT_PERIOD < currentTime) {
      periodicMeasurementTime = currentTime;
      enableLowPowerShunt();
      delayMicroseconds(20);
      lowPowerShuntCurrent = readLowPowerShuntCurrent();
      disableLowPowerShunt();
      if (highPowerMeasurementTime + LOWPOWER_SEND_DATA_DELAY < currentTime) {
        sendDatas();
      }
    }
    if (lowPowerShuntCurrent != lastShuntCurrent) {
      if (dataBufferIndex < BUFFER_SIZE) {
        dataBuffer[dataBufferIndex].timeStamp = currentTime;
        dataBuffer[dataBufferIndex].type = 'I';
        dataBuffer[dataBufferIndex++].measurement = lowPowerShuntCurrent;
        lastShuntCurrent = lowPowerShuntCurrent;
      }
      else {
        digitalWrite(LED, HIGH);
        sendDatas();
        digitalWrite(LED, LOW);
      }
    }
    if (digitalRead(GUI_SWITCH) == LOW) {
      displayMeasurements((float)busVoltage / 1000, (float)lowPowerShuntCurrent / 10);
    }
  }
  else {
    if (dataBufferIndex < BUFFER_SIZE) {
      if (shuntCurrent != lastShuntCurrent) {
        dataBuffer[dataBufferIndex].timeStamp = currentTime;
        dataBuffer[dataBufferIndex].type = 'I';
        dataBuffer[dataBufferIndex++].measurement = shuntCurrent;
        lastShuntCurrent = shuntCurrent;
      }
    }
    else {
      digitalWrite(LED, HIGH);
      sendDatas();
      digitalWrite(LED, LOW);
    }
    highPowerMeasurementTime = millis();
    if (digitalRead(GUI_SWITCH) == LOW) {
      displayMeasurements((float)busVoltage / 1000, (float)shuntCurrent / 10);
    }
  }
  if (digitalRead(GUI_SWITCH) == HIGH) {
    static bool started = false;
    if (!started) {
      displayMessages("SENDING DATA", "TO SERIAL LINE");
      started = true;
    }
  }

  if (digitalRead(GUI_SWITCH) == LOW) {
    debouncer.update();
    if (debouncer.read() == LOW) {
      enableLowPowerShunt();
      delayMicroseconds(2000);
      while (debouncer.read() == LOW) {
        lowPowerShuntCurrent = readLowPowerShuntCurrent();
        offset += lowPowerShuntCurrent;
        samples++;
        displayCalibration("CALIBRATION", lowPowerShuntCurrent, 1000);
        Serial.print("Correcting offset: ");
        Serial.print(lowPowerShuntCurrent);
        Serial.println("");
        debouncer.update();
      }
      shuntCurrentOffset = offset / samples;
      EEPROM.put(0, shuntCurrentOffset);
      displayCalibration("CALIBRATION FINIE", shuntCurrentOffset, 2000);
      Serial.print("Stored offset: ");
      Serial.print(shuntCurrentOffset);
      Serial.println("");
      wait4Key("PRESS TO START");
      periodicMeasurementTime = 0;
    }
  }
}

Programme Python pour l’affichage sur PC (adapté pour python v3.x)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# A PYTHON script to display data from the power bench
# Usage : python INA226_powerbench.py usb_device duration
#
 
import sys, time
import serial
import matplotlib.pyplot as plt; plt.rcdefaults()
import numpy as np
import seaborn as sns

sns.set()
duration = 60
plot = "plot"
voltageData = {}
currentData = {}
voltage = 0
current= 0
maxVoltage = 0.0
minVoltage = 1000000.0
maxCurrent = 0.0
minCurrent = 1000000.0
x_ticks = []

if len(sys.argv) > 2:
	duration = int(sys.argv[2])
if len(sys.argv) > 3:
	plot = sys.argv[3]

start_time = time.time()
line = serial.Serial(sys.argv[1],230400,timeout=1000, parity=serial.PARITY_NONE)
while time.time() < start_time + duration:
	s = str(line.readline())
	s=s[2:-5]
	if s.find("INA226",0) == -1 and s.find('read',0) == -1:
		if s.find('V',0) != -1:
			t, d = s.split('-')
			dummy, voltage = d.split(':')
			voltage = float(voltage)/1000
			t = int(t)
			voltageData[t] = voltage
			print (t, 'V:', voltageData[t])
			if maxVoltage < voltage:
				maxVoltage = voltage
			if minVoltage > voltage:
				minVoltage = voltage
		if s.find('I',0) != -1:
			t, d = s.split('-')
			dummy, current = d.split(':')
			current = float(current)/10
			t = int(t)
			x_ticks.append(t)
			currentData[t] = current
			print (t, 'I:', currentData[t])
			if maxCurrent < current:
				maxCurrent = current
			if minCurrent > current:
				minCurrent = current

voltageSerie = []
currentSerie = []
totalVoltage = 0.0
totalCurrent = 0.0
for t in range(duration * 1000):
	if t in voltageData:
		voltage = voltageData[t]
		voltageSerie.append(voltage)
	totalVoltage += voltage
	if t in currentData:
		print ("current change:", current, 'at', t)
		current = currentData[t]
		currentSerie.append(current)
	totalCurrent += current

text = "DURATION: %s s\n" % duration
text += "MIN VOLTAGE: %s V\n" % minVoltage
text += "MAX VOLTAGE: %s V\n" % maxVoltage
text += "MIN CURRENT: %s mA\n" % minCurrent
text += "MAX CURRENT: %s mA\n" % maxCurrent
text += "AVG VOLTAGE: %1.2f V\n" % (totalVoltage / (duration * 1000))
text += "AVG CURRENT: %1.2f mA\n" % (totalCurrent / (duration * 1000))
print (text)

if plot == "plot":
	plt.plot(x_ticks, currentSerie)
elif plot == "bar":
	plt.bar(x_ticks, currentSerie)
plt.ylabel('mA')
plt.yscale('log')
plt.grid(True,which="both")
plt.title('POWER TESTBENCH')
plt.text(200, int(maxCurrent) / 4, text, fontdict={'family': 'serif', 'color':  'darkred', 'weight': 'normal', 'size': 10})
plt.show()
N’hésitez pas à laisser un commentaire et poser vos questions via le formulaire ci-dessous.

Laissez un commentaire et posez vos questions ci-dessous

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *