Built-in project · Arduino Pro Mini, 3.3 V 8 MHz

Solar sensor node on a 3.3 V Arduino Pro Mini

A 6 V panel charges an 18650 through a TP4056, an MCP1700 makes 3.3 V, and a Pro Mini reports temperature, humidity, the battery and the panel every few seconds. The whole build, a 3.3 V Arduino Pro Mini and 9 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Advanced About 40 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $14.30. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Charging a Li-ion cell from a solar panel
  • Why a low-dropout regulator suits a battery node
  • Measuring a higher voltage with a resistor divider
  • Reading an I2C temperature and humidity sensor
  • Doing decimal math with whole numbers
  • Difficulty: Advanced
  • Time: About 40 minutes
  • Board: Arduino Pro Mini, 3.3 V 8 MHz
  • Parts: 11

How it works

A solar panel feeds a TP4056 charger, which charges a battery. A small regulator turns the battery voltage into a steady 3.3 V for the Pro Mini and the AHT20 temperature and humidity sensor.

The board also watches its own power. Two voltage dividers shrink the battery and the panel voltages to a safe range for A0 and A1. The sketch converts the readings back to millivolts. It calls the node charging when the panel is more than 100 mV above the battery.

Every three seconds it prints both voltages and whether it is charging. If the battery is under 3.5 V, it skips the sensor and says it is waiting for the sun. Otherwise it prints the temperature and humidity.

Parts list

11 parts, plus the jumper wires. They retail for about $14.30; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Li-ion charger, TP4056 pin BAT-Arduino Pro Mini, 3.3 V 8 MHz pin GND
Battery: AA, coin cell, LiPo pin V-Arduino Pro Mini, 3.3 V 8 MHz pin GND
Voltage regulator pin GNDArduino Pro Mini, 3.3 V 8 MHz pin GND
Humidity sensor, AHT20 pin GNDArduino Pro Mini, 3.3 V 8 MHz pin GND
Resistor, 100k Ω (2) pin 2Arduino Pro Mini, 3.3 V 8 MHz pin GND
Resistor, 47k Ω pin 2Arduino Pro Mini, 3.3 V 8 MHz pin GND
Voltage regulator pin OUTArduino Pro Mini, 3.3 V 8 MHz pin VCC
Humidity sensor, AHT20 pin VCCArduino Pro Mini, 3.3 V 8 MHz pin VCC
Resistor, 100k Ω (3) pin 2Arduino Pro Mini, 3.3 V 8 MHz pin A1
Resistor, 47k Ω pin 1Arduino Pro Mini, 3.3 V 8 MHz pin A1
Resistor, 100k Ω (1) pin 2Arduino Pro Mini, 3.3 V 8 MHz pin A0
Resistor, 100k Ω (2) pin 1Arduino Pro Mini, 3.3 V 8 MHz pin A0
Humidity sensor, AHT20 pin SDAArduino Pro Mini, 3.3 V 8 MHz pin A4
Humidity sensor, AHT20 pin SCLArduino Pro Mini, 3.3 V 8 MHz pin A5
Li-ion charger, TP4056 pin IN+Solar panel, 6 V 1 W pin +
Resistor, 100k Ω (3) pin 1Solar panel, 6 V 1 W pin +
Li-ion charger, TP4056 pin IN-Solar panel, 6 V 1 W pin -
Battery: AA, coin cell, LiPo pin V+Li-ion charger, TP4056 pin BAT+
Voltage regulator pin INLi-ion charger, TP4056 pin BAT+
Resistor, 100k Ω (1) pin 1Li-ion charger, TP4056 pin BAT+

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Pro Mini, 3.3 V 8 MHz
// Solar sensor node: a 3.3 V Pro Mini and an AHT20 that live outdoors on a
// 6 V 1 W panel and one 18650 cell.
//
//   the panel      into the TP4056's IN+ and IN-
//   the 18650      on the TP4056's BAT+ and BAT-
//   MCP1700-3.3    IN from BAT+, OUT to the Pro Mini's VCC and the AHT20's VCC
//   A0             the middle of 100 k and 100 k from BAT+ to GND (half the cell)
//   A1             the middle of 100 k and 47 k from the panel's + to GND
//   A4, A5         SDA and SCL to the AHT20
//
// # The power path
//
// The panel is a current source set by the sun: about 0.2 A in full sun,
// whatever the cell wants. The TP4056 passes what it gets into the cell, up
// to 1 A, and stops at 4.2 V. The MCP1700 turns the cell's 3.0 to 4.2 V into
// 3.3 V with 178 mV of dropout and under 2 uA for itself, which is why it and
// not an AMS1117 (a volt of dropout, 5 mA for itself) is on a battery node.
// A Li-ion cell under 3.5 V is nearly empty and close to where the MCP1700
// can no longer make 3.3 V, so there the node stops measuring and waits for
// the sun.
//
// # Measuring its own supply
//
// The ADC's reference is VCC, 3.3 V, so the cell (up to 4.2 V) is halved and
// the panel (up to 7.2 V) is divided by 147/47 before they reach a pin. The
// sums are in millivolts and whole numbers.
//
// # Sleeping
//
// A real node sleeps between readings (LowPower.powerDown in the Rocket
// Scream library, a few microamps) and wakes on the watchdog. Here delay()
// stands in for the sleep, so the reports come at the same pace.

#include <Wire.h>
#include <Adafruit_AHTX0.h>

const int CELL = A0;
const int PANEL = A1;
const long VREF_MV = 3300;
const unsigned long EVERY_MS = 3000;
const long LOW_CELL_MV = 3500;

Adafruit_AHTX0 aht;
bool sensorOk = false;

long millivolts(int pin, long top, long bottom) {
  long sum = 0;
  for (int i = 0; i < 8; i++) sum += analogRead(pin);
  long mv = sum * VREF_MV / 8 / 1023;
  return mv * (top + bottom) / bottom;
}

// Hundredths, as a number with two places: 372 is 3.72.
void printCenti(long v) {
  Serial.print(v / 100);
  Serial.print('.');
  long frac = v % 100;
  if (frac < 10) Serial.print('0');
  Serial.print(frac);
}

void setup() {
  Serial.begin(57600);
  Serial.println("Mokxi Pro Mini 3.3 V: solar sensor node");
  Wire.begin();
  sensorOk = aht.begin();
  if (!sensorOk) Serial.println("no AHT20: check SDA, SCL and 3.3 V");
}

void loop() {
  long cell = millivolts(CELL, 100, 100);
  long panel = millivolts(PANEL, 100, 47);
  bool charging = panel > cell + 100;

  Serial.print("cell ");
  printCenti(cell / 10);
  Serial.print(" V  panel ");
  printCenti(panel / 10);
  Serial.print(charging ? " V  charging" : " V  not charging");

  if (cell < LOW_CELL_MV) {
    Serial.println("  cell low: waiting for the sun");
  } else if (sensorOk) {
    // One measurement. No events wanted: the whole numbers below are all
    // this sketch needs, and leaving out the floats keeps it small.
    aht.getEvent(NULL, NULL);
    long rh = (aht.getHumidityCentis() + 50) / 100;
    Serial.print("  ");
    printCenti(aht.getTemperatureCentis());
    Serial.print(" C  ");
    Serial.print(rh);
    Serial.println(" %RH");
  } else {
    Serial.println();
  }
  delay(EVERY_MS);
}

Code walkthrough

The key functions of the sketch, and what each one does.

millivolts sketch.ino

Reads a pin eight times, adds the readings and scales them against the 3.3 V reference. That gives the voltage at the pin. It then multiplies by the divider ratio, top plus bottom over bottom, to get the real voltage.

long millivolts(int pin, long top, long bottom) {
  long sum = 0;
  for (int i = 0; i < 8; i++) sum += analogRead(pin);
  long mv = sum * VREF_MV / 8 / 1023;
  return mv * (top + bottom) / bottom;
}

printCenti sketch.ino

Prints a number in hundredths as a decimal, so 372 becomes 3.72. It prints the whole part, a point, then the two decimal digits, adding a leading zero when needed. This avoids floating point.

// Hundredths, as a number with two places: 372 is 3.72.
void printCenti(long v) {
  Serial.print(v / 100);
  Serial.print('.');
  long frac = v % 100;
  if (frac < 10) Serial.print('0');
  Serial.print(frac);
}

loop sketch.ino

Measures the cell and panel, decides if it is charging and prints both. If the cell is under LOW_CELL_MV it only reports that it is low. Otherwise it prints the AHT20 temperature and humidity. Then it waits EVERY_MS.

void loop() {
  long cell = millivolts(CELL, 100, 100);
  long panel = millivolts(PANEL, 100, 47);
  bool charging = panel > cell + 100;

  Serial.print("cell ");
  printCenti(cell / 10);
  Serial.print(" V  panel ");
  printCenti(panel / 10);
  Serial.print(charging ? " V  charging" : " V  not charging");

  if (cell < LOW_CELL_MV) {
    Serial.println("  cell low: waiting for the sun");
  } else if (sensorOk) {
    // One measurement. No events wanted: the whole numbers below are all
    // this sketch needs, and leaving out the floats keeps it small.
    aht.getEvent(NULL, NULL);
    long rh = (aht.getHumidityCentis() + 50) / 100;
    Serial.print("  ");
    printCenti(aht.getTemperatureCentis());
    Serial.print(" C  ");
    Serial.print(rh);
    Serial.println(" %RH");
  } else {
    Serial.println();
  }
  delay(EVERY_MS);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The monitor says no AHT20 This project
    Check SDA on A4 and SCL on A5, and that the sensor gets 3.3 V from the regulator output.
  • Cell reads half of what you expect This project
    A0 sits at the middle of two 100k resistors. The sketch doubles it with the millivolts call, so keep both resistors equal.
  • It never reports charging This project
    The panel voltage on A1 must be more than 100 mV above the cell. Check the 100k and 47k divider values.
  • Measured voltage is far lower than the panel's rating. Solar panel
    A panel is a current source with a limit. Under load its voltage falls, and without full sun the current drops. Open circuit voltage is higher (about 7.2 V) than the working voltage.
  • A battery drains back through the panel at night. Solar panel
    Put a diode in series so current only flows toward the battery. Use a charge controller for batteries. Do not charge a lithium cell straight from a panel.
  • The cell does not charge or gets hot. Li-ion charger
    Use one lithium-ion cell only, never two in series. Set the charge current with the programming resistor (1.2 kohm is 1 A). Use a protected cell.
  • The load behaves badly while charging. Li-ion charger
    Connect the load to OUT on a protection version, not straight to the cell. Wire B+ and B- with the right polarity. Reversed wiring can ruin the cell.
  • The battery is wired backwards, or the voltage is too low or too high for the circuit. A fresh AA is about 1.5 V, a LiPo is 3.7 V nominal and 4.2 V full. Battery: AA
    Match the plus and minus marks. Check the voltage your parts need. Two AAs make 3 V, four make 6 V. Never feed a LiPo to a 3.3 V chip directly at 4.2 V.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.