Back to Home

Weather station Atmega328 NRF24L01: schematics and code

Article analyzes implementation of weather station on Atmega328 with NRF24L01: circuit design with RGB balancing, software flicker filters, precipitation forecast by least squares on BMP280, communication control. Design errors and optimizations described.

Creating a weather station: Atmega328 + NRF24L01 + code
Advertisement 728x90

Atmega328 Weather Station with NRF24L01: Hardware and Software Implementation

The base station of the weather station is built on the Atmega328 microcontroller with an external 16 MHz resonator. Power supply is 5 V, with a separate AMS1117 3.3 V regulator for the NRF24L01 module. The circuit includes RGB backlighting with brightness balancing: a 100 Ω resistor on the red channel, 200 Ω on green and blue to compensate for the eye's physiological sensitivity and differences in crystal emission.

Power Supply Optimization

In the initial circuit, one bypass capacitor was used for power and one for AREF. This worked but violated recommendations: each VCC-GND pair requires a separate 100 nF capacitor to suppress microcontroller impulse noise. Optimally, three for power plus one on AREF. On the I²C bus, two pairs of pull-up resistors are excessive; one pair of 4.7 kΩ is sufficient.

PCB Layout and Grounding

The board was handmade using the photoresist method with UV solder mask. The bottom layer is a solid GND polygon, vias implemented by soldering wires. Separating analog and digital ground for the photoresistor proved unnecessary: the sensor is low-frequency, signal 0.5–4.5 V, noise in millivolts. A 0.1 µF filter on the ADC input shunts high-frequency interference and compensates for ground spikes in common mode.

Google AdInline article slot
  • Photoresistor vs photodiode: Photoresistor (kΩ–MΩ) provides a voltage signal, robust to noise; photodiode (µA) requires clean AGND.
  • Recommendation: Common ground + 0.1 µF on ADC for photoresistors.

Ground separation is needed for low-current sensors (thermocouples, piezo).

Software Flicker Filtering

Display and backlight brightness are adjusted via PWM based on the photoresistor. ADC noise and thermal noise (±3–4 units) cause flickering. Power supply didn't solve the issue; a 3rd-order median filter and exponential filter were applied.

int rawValue = analogRead(0);
int filteredValue = med.filter(rawValue);
float smoothValue = expF.filter(filteredValue);
int photoresistor = map(smoothValue, 260, 1023, 0, 255);
photoresistor = constrain(photoresistor, 0, 255);
analogWrite(3, 255 - photoresistor);

The filter combination is better than averaging over 10: eliminates spikes without significant reaction delay.

Google AdInline article slot

Precipitation Forecast via Barometer

BMP280 measures pressure every 10 min, averaging over 10 readings. An array of the last 6 values is analyzed using the least squares method for approximation.

if (millis() - lasttime > 600000) {
  lasttime = millis();
  PP = pressure();
  for (byte i = 0; i < 5; i++) {
    pres1_array[i] = pres1_array[i + 1];
  }
  pres1_array[5] = PP;
}

// Least squares
for (byte i = 0; i < 6; i++) {
  sumX += time_array[i];
  sumY += (long)pres1_array[i];
  sumX2 += time_array[i] * time_array[i];
  sumXY += (long)time_array[i] * pres1_array[i];
}
a = (long)6 * sumXY - (long)sumX * sumY;
a = (float)a / (6 * sumX2 - sumX * sumX);
delta = a * 6;
rain = map(delta, -250, 250, 100, -100);

Coefficient a is the slope, delta is the change per hour (Pa). rain from -100 (rising, clear) to +100 (falling, rain).

Communication Control and Timeout

When receiving a packet from the outdoor sensor, lastReceiveTime is recorded. If absent >60 min:

Google AdInline article slot
if (radio.available()) {
  radio.read(&rxData, sizeof(rxData));
  lastReceiveTime = millis();
  dataIsFresh = true;
}
if (lastReceiveTime > 0 && millis() - lastReceiveTime > 3600000) {
  resetWirelessData();
}

resetWirelessData() zeros dataIsFresh, the screen shows dashes.

Atmospheric RGB Backlighting

Color changes over time, mimicking nature: morning — yellow, day — blue, evening — pink, night — purple. Linear interpolation of RGB based on progress within the interval.

float progress = (currentHour - 8 + (float)currentMinute / 60.0) / 3.0;
if (progress < 0.5) {
  float subProgress = progress * 2.0;
  r = 255;
  g = 80 + subProgress * 90;
  b = 0 + subProgress * 50;
} else {
  float subProgress = (progress - 0.5) * 2.0;
  r = 255 - subProgress * 170;
  g = 180 - subProgress * 50;
  b = 60 + subProgress * 195;
}
void day() {
  r = 85; g = 130; b = 255;
}

Phases ensure smoothness without abrupt jumps.

Enclosure and Firmware

Enclosure: ABS + transparent PETG, board sized to the display. Firmware via Arduino as ISP on PLS connectors.

Key Points

  • RGB balancing: 100 Ω on red for visual equalization.
  • Filters: median + exponential better than averaging against flicker.
  • Least squares for pressure: delta ±250 Pa determines rain probability.
  • 60 min timeout for NRF24L01 prevents displaying stale data.
  • Common ground sufficient for photoresistor with 0.1 µF.

— Editorial Team

Advertisement 728x90

Read Next