Introduction: Traffic Detection and Counter Using FG-3+ and Arduino UNO R4 WiFi

About: We are a team of software and hardware engineers with 30 years of combined experience. At FG Sensors our main focus are fluxgate magnetometers, custom hardware, software solutions & research.

Create a low-cost, traffic monitoring system by deploying the FG-3+ fluxgate magnetometer near roads to detect magnetic disturbances caused by passing vehicles. Use the Arduino UNO R4 WiFi to detect vehicles, and optionally timestamp data, and upload real-time traffic analytics to the Arduino Cloud for remote monitoring and historical trend analysis.


Supplies

Step 1: Project Overview

Goal: Detect vehicles by sensing their magnetic disturbances using the FG-3+ near roadways.

Platform: Arduino UNO R4 WiFi for sensor interfacing and optional cloud connectivity.

Functionality: Detect vehicle, with possibility to upgrade system to log detection events with timestamps and send data wirelessly to Arduino Cloud.

Step 2: Hardware Setup

Connect the FG-3+ sensor to Arduino UNO R4 WiFi.

  1. FG-3+ VCC - 5V
  2. FG-3+ GND – GND
  3. FG-3+ OUT – connected to DIGITAL pin 2

Step 3: Firmware Functionality

  1. Sensor Reading: Measures FG-3+ sensor output frequency, which varies proportionally with the magnetic field from 40 kHz to 120 kHz, using a frequency counter.
  2. Signal Filtering: Applies a smoothing algorithm to eliminate false readings and reduce EMI-induced disturbances.
  3. Vehicle Detection: Identifies sudden, significant changes in magnetic field strength corresponding to a passing vehicle.
  4. Visual Feedback: Triggers a car animation on the matrix display each time a vehicle is detected.

Step 4: How the Code Works

1. Sensor Signal Counting

The digital sensor output (frequency signal proportional to magnetic field) is connected to an interrupt pin. Rising edges trigger the ISR sensorDigHandler(), which increments a counter sensorDigCnt when counting is enabled (intEnable flag). This counter accumulates sensor pulses during a fixed measurement window (measureTime ms).

2. Baseline Establishment and Moving Average

The baseline frequency count (representing no vehicle traffic) is initialized at startup by measuring sensor pulses without disturbance. During normal operation, a moving average baseline is continuously updated with factor alpha smoothing to adapt slowly to slow environmental changes.

3. Peak Detection

The main logic checks if the instantaneous sensor counts deviates from the baseline by a threshold (base_line_change). If yes, it enters a peak event state, tracking how long the deviation lasts (peakSamplesCount) and its maximum magnitude (peakValue). When the deviation drops below the threshold or exceeds sample count (N), the peak event ends.

4. Vehicle Detection and Animation Trigger

When a peak event ends, it is treated as a detected vehicle event. The car animation on the matrix display is triggered to visually signal the vehicle passage.

5. Matrix Animation

The animation function playCarAnimation() simulates a car bitmap moving across the LED matrix from right to left with a blocking delay controlling frame speed.

Step 5: Code

#include <Arduino_LED_Matrix.h>

const int sensorDig = 2; // Digital pin 2 for sensor input (interrupt pin)

unsigned int updateRate = 100; // Sensor update rate in ms
unsigned int measureTime = 100; // Sensor measure time in ms

volatile unsigned int intEnable = 0; // Interrupt counter enable flag
volatile unsigned long sensorDigCnt = 0; // Count of sensor signal changes
unsigned long prevMillis = 0; // For timing updates

float base_line = 0.0; // Moving average baseline
const float alpha = 0.1; // Smoothing factor for baseline
const float base_line_change = 10.0; // Threshold for significant change
const int N = 10; // Number of samples for peak event duration

int peakSamplesCount = 0; // Samples counted during peak event
bool inPeakEvent = false; // Flag: tracking a peak event?
float peakValue = 0.0; // Maximum deviation recorded during peak event

int state = 0; // State machine variable

// LED matrix setup
ArduinoLEDMatrix matrix;

const int matrixWidth = 12;
const int matrixHeight = 8;

// Car bitmap 7x5 pixels
const uint8_t carWidth = 7;
const uint8_t carHeight = 5;
const uint8_t carBitmap[carHeight] = {
0b011110, // roof shape
0b1111111, // upper body
0b1111111, // lower body
0b0110110, // wheels
0b0000000 // chassis detail
};

uint8_t frame[matrixHeight][matrixWidth];

void clearFrame() {
for (int y = 0; y < matrixHeight; y++) {
for (int x = 0; x < matrixWidth; x++) {
frame[y][x] = 0;
}
}
}

void drawCar(int xPos) {
clearFrame();
for (int row = 0; row < carHeight; row++) {
for (int col = 0; col < carWidth; col++) {
bool pixelOn = carBitmap[row] & (1 << (carWidth - 1 - col));
int x = xPos + col;
int y = (matrixHeight - carHeight) + row; // bottom aligned
if (pixelOn && x >= 0 && x < matrixWidth && y >= 0 && y < matrixHeight) {
frame[y][x] = 1;
}
}
}
matrix.renderBitmap(frame, matrixHeight, matrixWidth);
}

void playCarAnimation() {
// Animate car moving from right to left
for (int x = matrixWidth; x >= -carWidth; x--) {
drawCar(x);
delay(150); // Simple blocking delay; consider millis() for non-blocking
}
matrix.clear();
}

void setup() {
Serial.begin(115200);
pinMode(sensorDig, INPUT);
attachInterrupt(digitalPinToInterrupt(sensorDig), sensorDigHandler, RISING);
matrix.begin();
matrix.clear();

// Initialize baseline with first measurement
sensorDigCnt = 0;
intEnable = 1;
delay(measureTime);
intEnable = 0;
base_line = sensorDigCnt;
Serial.print("Initial baseline: ");
Serial.println(base_line);

prevMillis = millis();
}

void loop() {
unsigned long currMillis = millis();

switch (state) {
case 1:
// Update baseline (moving average)
base_line = alpha * sensorDigCnt + (1.0 - alpha) * base_line;

// Calculate deviation from baseline
float diff = sensorDigCnt - base_line;

if (!inPeakEvent) {
// Not currently tracking a peak event
if (abs(diff) > base_line_change) {
inPeakEvent = true;
peakSamplesCount = 1;
peakValue = diff;
}
} else {
// Tracking peak event
peakSamplesCount++;
if (abs(diff) > abs(peakValue)) {
peakValue = diff;
}

// End peak detection when signal returns near baseline or max samples reached
if (abs(diff) <= base_line_change || peakSamplesCount > N) {
Serial.print("Peak detected: ");
Serial.println(peakValue);
inPeakEvent = false;
peakSamplesCount = 0;
peakValue = 0.0;

// Trigger car animation on new peak detected
playCarAnimation();
}
}

Serial.print("Sensor= ");
Serial.print(sensorDigCnt);
Serial.print(" Baseline= ");
Serial.println(base_line);

state = 0;
break;
}

if (currMillis - prevMillis >= updateRate) {
sensorDigCnt = 0;
prevMillis = currMillis;
intEnable = 1;
delay(measureTime);
intEnable = 0;
state = 1;
}
}

// Interrupt handler increments counter on rising edge
void sensorDigHandler() {
if (intEnable == 1) {
sensorDigCnt++;
}
}

Step 6: Parameter Tuning

(Parameter // Purpose // How to Tune)


updateRate // How often sensor data is processed (ms) // Lower for faster response but higher CPU usage (100ms is good starting point)
measureTime // Duration during which pulses are counted (ms) // Should be less than updateRate; affects resolution of frequency measurement
alpha // Smoothing factor for moving average baseline //Lower (0.05-0.1) = slower baseline adjustment, more stable; higher = faster adaptation
base_line_change // Threshold to detect significant deviation (counts) // Tune empirically based on noise and vehicle signal amplitude, start with 10
N // Max duration of peak event in samples // Set to cover expected vehicle presence duration; too small might cut event early


  1. To tune, start by observing the sensor values and noise under normal conditions.
  2. Adjust base_line_change so false positives are minimized.
  3. Adjust alpha so baseline smoothly tracks slow changes but ignores brief spikes.
  4. Increase N if vehicle signal is longer or noisier.

Step 7: How to Test in the Lab?

To test in the lab, simply move your keys or a magnet near the sensor—this creates a large enough magnetic disturbance to simulate a vehicle passing by, allowing quick verification of detection functionality.

Notice: Fluxgate magnetometers are extremely sensitive sensors that can detect very small magnetic field changes and are also affected by electromagnetic interference (EMI) in the lab – this can introduce falls triggers.

Step 8: Optional Enhancements

Adding Non-Blocking Animation (Improvement Suggestion)

To avoid blocking sensor processing during animation, change the animation to run incrementally within the loop() using a state machine and millis() for timing:

  1. Store animation state variables: current x-position and last frame time.
  2. On peak detection, set a flag to start animation.
  3. Each loop() iteration, check if animation flag is set and enough time elapsed since last frame.
  4. Update car position, redraw frame, and clear when animation completes.
  5. Meanwhile, sensor counting and peak detection continue running concurrently

This approach allows vehicle detection to keep running without missing interrupts or updates due to delay() blocking.

Step 9: Improving Detection and Logging Algorithm

  1. Optimize detection parameters: prevent false readings
  2. Counting & Logging: Increment vehicle counts per detection event. Timestamp each event for historical data.
  3. Use AI signal processing to identify different vehicle types
  4. Use timestamps combined with geographic data (GPS) for spatial traffic analytics.

Step 10: Connecting System With Arduino Cloud

  1. Cloud Upload: Send real-time counts, logs, and sensor data to Arduino Cloud for remote access and visualization.
  2. Alerts & Analytics: Set thresholds for abnormal traffic flow or unusual magnetic signatures for notifications.
  3. Analyse long-term traffic patterns remotely through Arduino Cloud dashboards.

Step 11: Benefits of This Traffic Counter

  1. Low-Cost & Easy to Deploy: Uses affordable, off-the-shelf components.
  2. Highly Sensitive Detection: FG-3+ fluxgate magnetometer’s precision detects subtle magnetic changes from vehicles.
  3. Scalable: Multiple units can create a wide-area traffic monitoring network.
  4. Wireless & Cloud-Connected: No need for wired infrastructure, with real-time data available anywhere.

This project shows the real-world applicability of fluxgate magnetometer technology, especially the FG-3+ sensor from FG Sensors, in modern smart city solutions like traffic monitoring using Arduino’s versatile and connected platform