I am currently working on an educational project involving magnetic field detection. I designed a PCB using an ATtiny85 microcontroller and an MLX90393 magnetic sensor.
The issue I am facing is that I am having trouble detecting the magnetic field with a satisfactory distance precision. I suspect one specific hardware issue: the CR123A battery is located directly underneath my PCB.
However, I would like to know if it is possible to change some calculations or configurations in my code to improve the detection range and compensate for this, as I have already tried many different approaches without success.
You can find the link for the documentation register of MLX90393 at : https://www.melexis.com/-/media/files/documents/application-notes/mlx90393-application-note-melexis.pdf
I am sharing the schematic of the project and the code below."


/**
Capteur : MLX90393 / Microcontrôleur : ATtiny85
*/
#include <TinyWireM.h>
// ============ CONFIGURATION MATERIEL ============
const int bornierPin = 1;
const int ledPin = 3;
uint8_t sensorAddr = 0x0C;
// ============ BASELINE MAGNÉTIQUE ============
long offX = 0, offY = 0, offZ = 0;
// ============ SEUILS DE DÉTECTION ============
unsigned long seuilDeclenchement = 0;
unsigned long seuilRearmement = 0;
const int FACTEUR_SECURITE = 4;
// Baisse massive du seuil plancher grâce au filtre matériel !
const unsigned long SEUIL_MINIMUM = 1500UL;
// ============ ÉTATS DE DÉTECTION ============
bool mouvementDetecte = false;
unsigned long tempsDebutDetection = 0;
bool timeoutActif = false;
const unsigned long TIMEOUT_BLOCAGE = 10000UL;
// ============ PÉRIODE DE GRÂCE (après init) ============
const unsigned long DUREE_GRACE = 3000UL;
unsigned long tempsFinGrace = 0;
bool periodeGraceActive = true;
// ============ PÉRIODE DE STABILISATION (après grâce) ============
const unsigned long DUREE_STABILISATION = 1000UL;
unsigned long tempsFinStabilisation = 0;
bool periodeStabilisationActive = false;
bool detectionActive = false;
// ============ FONCTION : Lire les données brutes du capteur ============
bool lireCapteur(long &rX, long &rY, long &rZ) {
TinyWireM.beginTransmission(sensorAddr);
TinyWireM.write(0x4E); // Commande RM (Read Measurement)
TinyWireM.endTransmission();
TinyWireM.requestFrom(sensorAddr, (uint8_t)7);
if (TinyWireM.available() >= 7) {
TinyWireM.read(); // Ignorer le status byte
rX = (int16_t)((TinyWireM.read() << 8) | TinyWireM.read());
rY = (int16_t)((TinyWireM.read() << 8) | TinyWireM.read());
rZ = (int16_t)((TinyWireM.read() << 8) | TinyWireM.read());
return true;
}
return false;
}
// ============ FONCTION : Clignoter la LED ============
void clignoter(int nombreFois, int dureeHaut, int dureeBas) {
for (int i = 0; i < nombreFois; i++) {
digitalWrite(ledPin, HIGH);
delay(dureeHaut);
digitalWrite(ledPin, LOW);
delay(dureeBas);
}
}
// ============ SETUP : INITIALISATION ET CALIBRATION ============
void setup() {
pinMode(bornierPin, OUTPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(bornierPin, LOW);
digitalWrite(ledPin, LOW);
TinyWireM.begin();
delay(100);
// --- ÉTAPE 1 : RESET DU CAPTEUR ---
TinyWireM.beginTransmission(sensorAddr);
TinyWireM.write(0xF0);
TinyWireM.endTransmission();
delay(100);
// --- ÉTAPE 2 : CONFIGURATION DU GAIN (Registre 0x60) ---
TinyWireM.beginTransmission(sensorAddr);
TinyWireM.write(0x60);
TinyWireM.write(0xC7); // HALLCONF=0xC + GAIN_SEL=0x7
TinyWireM.write(0x0C);
TinyWireM.write(0x00);
TinyWireM.endTransmission();
delay(10);
// --- ÉTAPE 2 Bis : ACTIVATION DU FILTRE (Registre 0x02) ---
// C'est ce qui permet de ne pas suractiver ! (DIG_FILT = 5)
TinyWireM.beginTransmission(sensorAddr);
TinyWireM.write(0x60);
TinyWireM.write(0x14);
TinyWireM.write(0x00);
TinyWireM.write(0x08);
TinyWireM.endTransmission();
delay(10);
// --- ÉTAPE 3 : ACTIVATION DU MODE LECTURE ---
TinyWireM.beginTransmission(sensorAddr);
TinyWireM.write(0x1E);
TinyWireM.endTransmission();
// --- ÉTAPE 4 : TEMPS DE FUITE ---
clignoter(20, 100, 100);
delay(2000);
// --- ÉTAPE 5 : NETTOYAGE DU TAMPON ---
for (int i = 0; i < 5; i++) {
TinyWireM.beginTransmission(sensorAddr);
TinyWireM.write(0x4E);
TinyWireM.endTransmission();
TinyWireM.requestFrom(sensorAddr, (uint8_t)7);
if (TinyWireM.available() >= 7) {
for (int j = 0; j < 7; j++) TinyWireM.read();
}
delay(40); // Délai ajusté pour le filtre
}
// --- ÉTAPE 6 : PHOTO ABSOLUE ---
long rX, rY, rZ;
if (lireCapteur(rX, rY, rZ)) {
offX = rX;
offY = rY;
offZ = rZ;
}
// --- ÉTAPE 7 : CALIBRATION ---
unsigned long sommeBruit = 0;
const int NB_SAMPLES = 40;
for (int i = 0; i < NB_SAMPLES; i++) {
if (lireCapteur(rX, rY, rZ)) {
long dX = rX - offX;
long dY = rY - offY;
long dZ = rZ - offZ;
sommeBruit += (unsigned long)(dX * dX + dY * dY + dZ * dZ);
}
delay(40);
}
unsigned long moyenneBruit = sommeBruit / NB_SAMPLES;
seuilDeclenchement = moyenneBruit * FACTEUR_SECURITE;
if (seuilDeclenchement < SEUIL_MINIMUM) {
seuilDeclenchement = SEUIL_MINIMUM;
}
seuilRearmement = seuilDeclenchement / 2;
// --- ÉTAPE 8 : CONFIRMATION ---
clignoter(3, 300, 300);
digitalWrite(ledPin, LOW);
// --- ÉTAPE 9 : DÉMARRER LA GRÂCE ---
tempsFinGrace = millis() + DUREE_GRACE;
periodeGraceActive = true;
periodeStabilisationActive = false;
detectionActive = false;
}
void loop() {
// ========== PHASE 1 : PÉRIODE DE GRÂCE ==========
if (periodeGraceActive) {
if (millis() < tempsFinGrace) {
long rX, rY, rZ;
if (lireCapteur(rX, rY, rZ)) {
offX = (offX * 7 + rX) / 8;
offY = (offY * 7 + rY) / 8;
offZ = (offZ * 7 + rZ) / 8;
}
delay(40);
return;
} else {
long rX, rY, rZ;
if (lireCapteur(rX, rY, rZ)) {
offX = rX;
offY = rY;
offZ = rZ;
}
periodeGraceActive = false;
tempsFinStabilisation = millis() + DUREE_STABILISATION;
periodeStabilisationActive = true;
detectionActive = false;
}
}
// ========== PHASE 2 : PÉRIODE DE STABILISATION ==========
if (periodeStabilisationActive && !periodeGraceActive) {
if (millis() < tempsFinStabilisation) {
long rX, rY, rZ;
lireCapteur(rX, rY, rZ);
delay(40);
return;
} else {
periodeStabilisationActive = false;
detectionActive = true;
mouvementDetecte = false;
timeoutActif = false;
}
}
// ========== PHASE 3 : DÉTECTION NORMALE ==========
if (detectionActive) {
long rawX, rawY, rawZ;
if (lireCapteur(rawX, rawY, rawZ)) {
long dx = rawX - offX;
long dy = rawY - offY;
long dz = rawZ - offZ;
unsigned long d2 = (unsigned long)(dx * dx + dy * dy + dz * dz);
// --- SEUIL DE DÉCLENCHEMENT ---
if (d2 > seuilDeclenchement) {
if (!mouvementDetecte) {
digitalWrite(ledPin, HIGH);
digitalWrite(bornierPin, HIGH);
delay(3000);
digitalWrite(ledPin, LOW);
digitalWrite(bornierPin, LOW);
mouvementDetecte = true;
timeoutActif = true;
tempsDebutDetection = millis();
}
}
// --- HYSTÉRÉSIS ---
else if (d2 < seuilRearmement) {
mouvementDetecte = false;
timeoutActif = false;
}
// --- TIMEOUT ---
if (timeoutActif && (millis() - tempsDebutDetection > TIMEOUT_BLOCAGE)) {
offX = rawX;
offY = rawY;
offZ = rawZ;
mouvementDetecte = false;
timeoutActif = false;
clignoter(3, 100, 100);
}
}
}
delay(40); // Synchronisé avec le filtre
}