Capteur inductif sans fil pour compteur d’eau avec « cyble » – Partie 2 : améliorations du prototype

Capteur inductif sans fil pour compteur d’eau avec « cyble » – Partie 2 : améliorations du prototype

Dans la première partie, on avait un circuit dont le principe de base était opérationnel mais où il restait des choses à améliorer ou à concevoir :

  • L’alimentation se fait via un accu et les tensions de bias et de référence vont baisser avec la décharge alors qu’elle doivent rester constantes.
  • Faire baisser au maximum la consommation du circuit (pouvoir couper le bias et la référence quand il n’y a pas de mesures en cours)

Améliorations du circuit

Pour poursuivre le circuit, on va donc ajouter une référence de tension de 2.5V qui sera activée par une sortie de l’atmega durant les mesures.

Le schéma de la partie 1 représentant une seule boucle de mesure, ce qui permet de compter le nombre de tour en supposant qu’elle tourne toujours dans le même sens. Il en faudra 2 ou 3 si on désire également détecter le sens de rotation de la cyble. Pour tester, on va donc doubler ce circuit pour obtenir le schéma suivant :

Circuit avec référence de tension et deux oscillateurs

 

Et là c’est le drame, ce qui était prévisible se produit, bien que les oscillateurs soient déclenchés l’un après l’autre, il y a un couplage inductif entre les deux inductances qui déforme complètement le signal. L’effet est diminué en éloignant les bobines, mais elle doivent néanmoins êtres placées assez près l’une de l’autre pour entrer dans le diamètre de la cyble.

Pour limiter le phénomène, on va mettre les entrées de l’atmega où sont connectés les PULSE_IN_X en sortie et à l’état bas durant la mesure de la boucle active, et ça a quand même une meilleure tête.

Détection de la position et comptage des tours

Selon la présence de métal ou non sous chaque bobine, on peut déduire la position de la partie métallique, puis à l’aide d’une machine à états, en déduire quand un tour a été effectué.

On peut utiliser une, deux ou les trois bobines selon la précision désirée et le fait de détecter ou non le sens de rotation :

  • une bobine : il y a 2 états possibles, avec ou sans métal (0 et 1), à chaque changement d’état le disque a fait un demi-tour mais on ne peut pas connaître le sens de rotation
  • deux bobines : il y a 4 états possibles (00, 10, 11 et 01) , on peut détecter le sens de rotation
  • trois bobines : il y a 6 états possibles (011, 001, 101, 100, 110 et 010, les états 000 et 111 étant théoriquement impossibles)
Les différents états possibles avec 2 bobines
Les différents états possibles avec 3 bobines

 

En prenant la configuration à deux bobines, il va falloir gérer les transitions suivantes :

Etat N Etat N+1 Evènement
00 01 NbTours–
00 10 ok
00 11 erreur
01 00 NbTours++
01 10 erreur
01 11 ok
10 00 ok
10 01 erreur
10 11 ok
11 00 erreur
11 01 ok
11 10 ok

Test sur breadboard

Pour éviter d’éventrer mon compteur d’eau pour récupérer la cyble et qu’il m’arrive des bricoles, j’en ai fait un prototype maison à partir de ce qui trainait dans le coin. Le mandrin de la visseuse fera l’affaire pour que ça tourne.

Super prototype de cyble...
Super prototype de cyble…

A chaque mesure des deux oscillateurs, on détecte s’il y a presénce de métal ou non en fonction du nombre d’oscillations. Par la suite il sera nécéssaire d’ajouter une fonction de calibration pour détecter le maximum et éventuellement se recalibrer périodiquement.
On regarde ensuite dans quel cas de transition on est, si elle est valide et s’il est nécessaire d’incrémenter le nombre de tour. On passe en veille profonde entre deux mesures pour limiter au maximum la consommation de la batterie.

La prochaine étape sera d’ajouter le module radio et de réaliser un circuit au propre…

#include <avr/io.h>
#include <avr/interrupt.h>
#include <LowPower.h>

#define PULSE_OUT_A 3 //PD3
#define PULSE_OUT_B 4 //PD4

#define PULSE_IN_A 14 //PC0
#define PULSE_IN_B 15 //PC1

#define AIN0 6 //PD6
#define EN_VREF 5 //PD5

volatile uint8_t pulseCntInt = 0;

bool calibrationNeeded = true;

uint8_t pulseCntA = 0;
uint8_t pulseCntAMax = 70;

uint8_t pulseCntB = 0;
uint8_t pulseCntBMax = 64;

// State is four bits : [A, B, new A, new B], 0 without metal, 1 with metal
uint8_t rotationState = 0b0011;

int32_t rotationIndex = 0;
uint32_t errorIndex = 0;

uint8_t calcState(){
//make state as old
rotationState = (rotationState << 2) & 0b1111;

//get disc position
if(pulseCntA <= (0.75*pulseCntAMax)) rotationState |= 0b0010;
if(pulseCntB <= (0.75*pulseCntBMax)) rotationState |= 0b0001;

//treat transitions with events
switch(rotationState){
case 0b0001:
rotationIndex--;
break;

case 0b0100:
rotationIndex++;
break;

case 0b0011:
case 0b0110:
case 0b1001:
case 0b1100:
errorIndex++;
break;
}
}

void setup() {
Serial.begin(115200);

pinMode(PULSE_OUT_A , OUTPUT);
digitalWrite(PULSE_OUT_A, LOW);

pinMode(PULSE_OUT_B , OUTPUT);
digitalWrite(PULSE_OUT_B, LOW);

pinMode(EN_VREF , OUTPUT);
digitalWrite(EN_VREF, LOW);

pinMode(PULSE_IN_A, INPUT);
pinMode(PULSE_IN_B, INPUT);

pinMode(AIN0, INPUT);

cli();
ADCSRA &= ~(1<<ADEN); // disable ADC
ADCSRB = (1 << ACME); // Enable multiplexer (PCx as AIN1)
DIDR1 = (1<<AIN1D) | (1<< AIN0D); // Disable comparator input buffer
sei();

delay(100);

}

void loop() {
pulseCntInt = 0;
pulseCntA = 0;
pulseCntB = 0;

// Enable 2.5V for bias and reference
digitalWrite(EN_VREF, HIGH);
delayMicroseconds(200);

// Pulse input as output with low state to avoid inductor coupling.
pinMode(PULSE_IN_A , OUTPUT);
digitalWrite(PULSE_IN_A, LOW);

pinMode(PULSE_IN_B , OUTPUT);
digitalWrite(PULSE_IN_B, LOW);

pulseCntA = countPulse(PULSE_OUT_A, PULSE_IN_A);
pulseCntB = countPulse(PULSE_OUT_B, PULSE_IN_B);

digitalWrite(EN_VREF, LOW);

pinMode(PULSE_IN_A, INPUT);
pinMode(PULSE_IN_B, INPUT);

calcState();

Serial.println(String("A:") + pulseCntA + ", B:" + pulseCntB + ", rotationState:" + String(rotationState, BIN) + ", index:" + rotationIndex + ", error:" + errorIndex);
delay(50);

// Save battery, keep ADC_ON param to avoid to enable it after power down. ADC is already disabled for the analog comparator.
LowPower.powerDown(SLEEP_250MS, ADC_ON, BOD_OFF);

}

uint8_t countPulse(uint8_t pulseOutPort, uint8_t pulseInPort){
pinMode(pulseInPort , INPUT);

// Set PC0 or PC1 as remplacement for AIN1
ADMUX = pulseInPort-14;

pulseCntInt = 0;

// Start oscillation
digitalWrite(pulseOutPort, HIGH);
delayMicroseconds(25);
digitalWrite(pulseOutPort,LOW);

// Enable analog comparator interrupts on rising and falling edge, enable the comparator
ACSR = (1<<ACIE);
delayMicroseconds(250);

// Disable comparator and its interrupts
ACSR = (1<<ACD);

// Pulse input as output with low state to avoid inductor coupling.
pinMode(pulseInPort , OUTPUT);
digitalWrite(pulseInPort, LOW);

return pulseCntInt;
}

// Comparator interrupt
ISR(ANALOG_COMP_vect){
pulseCntInt++;
}

19:41:13.560 -> A:32, B:42, rotationState:1011, index:0, error:1
19:41:13.891 -> A:36, B:40, rotationState:1111, index:0, error:1
19:41:14.174 -> A:48, B:36, rotationState:1111, index:0, error:1
19:41:14.452 -> A:62, B:36, rotationState:1101, index:0, error:1
19:41:14.779 -> A:66, B:35, rotationState:101, index:0, error:1
19:41:15.102 -> A:66, B:35, rotationState:101, index:0, error:1
19:41:15.382 -> A:68, B:51, rotationState:100, index:1, error:1
19:41:15.663 -> A:70, B:63, rotationState:0, index:1, error:1
19:41:15.991 -> A:58, B:62, rotationState:0, index:1, error:1
19:41:16.273 -> A:30, B:54, rotationState:10, index:1, error:1
19:41:16.557 -> A:30, B:26, rotationState:1011, index:1, error:1
19:41:16.892 -> A:58, B:24, rotationState:1101, index:1, error:1
19:41:17.178 -> A:64, B:28, rotationState:101, index:1, error:1
19:41:17.466 -> A:68, B:60, rotationState:100, index:2, error:1
19:41:17.754 -> A:60, B:64, rotationState:0, index:2, error:1
19:41:18.083 -> A:41, B:60, rotationState:10, index:2, error:1
19:41:18.363 -> A:36, B:57, rotationState:1010, index:2, error:1
19:41:18.697 -> A:36, B:56, rotationState:1010, index:2, error:1
19:41:18.986 -> A:32, B:42, rotationState:1011, index:2, error:1
19:41:19.270 -> A:44, B:34, rotationState:1111, index:2, error:1
19:41:19.556 -> A:62, B:36, rotationState:1101, index:2, error:1
19:41:19.892 -> A:64, B:36, rotationState:101, index:2, error:1
19:41:20.173 -> A:62, B:48, rotationState:101, index:2, error:1
19:41:20.502 -> A:62, B:64, rotationState:100, index:3, error:1
19:41:20.784 -> A:42, B:62, rotationState:10, index:3, error:1
19:41:21.066 -> A:37, B:58, rotationState:1010, index:3, error:1
19:41:21.391 -> A:34, B:56, rotationState:1010, index:3, error:1
19:41:21.669 -> A:30, B:44, rotationState:1011, index:3, error:1
19:41:21.996 -> A:34, B:30, rotationState:1111, index:3, error:1
19:41:22.281 -> A:52, B:34, rotationState:1111, index:3, error:1
19:41:22.565 -> A:66, B:35, rotationState:1101, index:3, error:1
19:41:22.889 -> A:66, B:41, rotationState:101, index:3, error:1
19:41:23.170 -> A:70, B:63, rotationState:100, index:4, error:1
19:41:23.450 -> A:52, B:60, rotationState:10, index:4, error:1
19:41:23.776 -> A:38, B:56, rotationState:1010, index:4, error:1
19:41:24.056 -> A:36, B:56, rotationState:1010, index:4, error:1
19:41:24.384 -> A:36, B:48, rotationState:1011, index:4, error:1
19:41:24.663 -> A:42, B:36, rotationState:1111, index:4, error:1
19:41:24.991 -> A:54, B:36, rotationState:1101, index:4, error:1
19:41:25.274 -> A:64, B:36, rotationState:101, index:4, error:1
19:41:25.554 -> A:66, B:42, rotationState:101, index:4, error:1
19:41:25.880 -> A:65, B:64, rotationState:100, index:5, error:1
19:41:26.162 -> A:48, B:62, rotationState:10, index:5, error:1
19:41:26.446 -> A:38, B:58, rotationState:1010, index:5, error:1
19:41:26.772 -> A:36, B:58, rotationState:1010, index:5, error:1
19:41:27.055 -> A:38, B:46, rotationState:1011, index:5, error:1
19:41:27.384 -> A:48, B:36, rotationState:1111, index:5, error:1
19:41:27.663 -> A:64, B:36, rotationState:1101, index:5, error:1
19:41:27.940 -> A:66, B:36, rotationState:101, index:5, error:1
19:41:28.266 -> A:64, B:48, rotationState:101, index:5, error:1
19:41:28.545 -> A:60, B:64, rotationState:100, index:6, error:1
19:41:28.871 -> A:42, B:62, rotationState:10, index:6, error:1
19:41:29.192 -> A:38, B:60, rotationState:1010, index:6, error:1
19:41:29.473 -> A:36, B:58, rotationState:1010, index:6, error:1
19:41:29.751 -> A:36, B:44, rotationState:1011, index:6, error:1
19:41:30.076 -> A:46, B:36, rotationState:1111, index:6, error:1
19:41:30.354 -> A:62, B:36, rotationState:1101, index:6, error:1
19:41:30.681 -> A:64, B:36, rotationState:101, index:6, error:1
19:41:30.960 -> A:70, B:62, rotationState:100, index:7, error:1
19:41:31.241 -> A:60, B:64, rotationState:0, index:7, error:1
19:41:31.569 -> A:44, B:62, rotationState:10, index:7, error:1
19:41:31.851 -> A:42, B:62, rotationState:1010, index:7, error:1
19:41:32.180 -> A:44, B:60, rotationState:1010, index:7, error:1
19:41:32.464 -> A:46, B:48, rotationState:1011, index:7, error:1
19:41:32.786 -> A:50, B:46, rotationState:1111, index:7, error:1
19:41:33.065 -> A:65, B:38, rotationState:1101, index:7, error:1
19:41:33.344 -> A:64, B:48, rotationState:101, index:7, error:1
19:41:33.673 -> A:70, B:64, rotationState:100, index:8, error:1
19:41:33.955 -> A:58, B:64, rotationState:0, index:8, error:1
19:41:34.280 -> A:42, B:59, rotationState:10, index:8, error:1
19:41:34.558 -> A:38, B:58, rotationState:1010, index:8, error:1
19:41:34.840 -> A:38, B:47, rotationState:1011, index:8, error:1
19:41:35.171 -> A:39, B:38, rotationState:1111, index:8, error:1
19:41:35.452 -> A:52, B:36, rotationState:1111, index:8, error:1
19:41:35.782 -> A:66, B:36, rotationState:1101, index:8, error:1
19:41:36.065 -> A:64, B:50, rotationState:100, index:9, error:1
19:41:36.348 -> A:70, B:65, rotationState:0, index:9, error:1
19:41:36.673 -> A:52, B:62, rotationState:10, index:9, error:1
19:41:36.952 -> A:38, B:60, rotationState:1010, index:9, error:1
19:41:37.279 -> A:36, B:56, rotationState:1010, index:9, error:1
19:41:37.558 -> A:32, B:38, rotationState:1011, index:9, error:1
19:41:37.886 -> A:46, B:34, rotationState:1111, index:9, error:1
19:41:38.169 -> A:64, B:36, rotationState:1101, index:9, error:1
19:41:38.448 -> A:66, B:36, rotationState:101, index:9, error:1
19:41:38.776 -> A:70, B:64, rotationState:100, index:10, error:1
19:41:39.054 -> A:38, B:58, rotationState:10, index:10, error:1

Laisser un commentaire

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