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

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
Composants
Schéma

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

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()






