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.
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.
- 1 × Arduino Pro Mini, 3.3 V 8 MHz
- 1 × Half breadboard
- 1 × Solar panel, 6 V 1 W
- 1 × Li-ion charger, TP4056
- 1 × Battery: AA, coin cell, LiPoIn Handheld Console Kit, Laser Sword Kit, Power Bank Kit.
- 1 × Voltage regulatorIn Power Bank Kit.
- 1 × Humidity sensor, AHT20
- 3 × Resistor, 100k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Resistor, 47k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
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 pin | Connects 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 GND | Arduino Pro Mini, 3.3 V 8 MHz pin GND |
| Humidity sensor, AHT20 pin GND | Arduino Pro Mini, 3.3 V 8 MHz pin GND |
| Resistor, 100k Ω (2) pin 2 | Arduino Pro Mini, 3.3 V 8 MHz pin GND |
| Resistor, 47k Ω pin 2 | Arduino Pro Mini, 3.3 V 8 MHz pin GND |
| Voltage regulator pin OUT | Arduino Pro Mini, 3.3 V 8 MHz pin VCC |
| Humidity sensor, AHT20 pin VCC | Arduino Pro Mini, 3.3 V 8 MHz pin VCC |
| Resistor, 100k Ω (3) pin 2 | Arduino Pro Mini, 3.3 V 8 MHz pin A1 |
| Resistor, 47k Ω pin 1 | Arduino Pro Mini, 3.3 V 8 MHz pin A1 |
| Resistor, 100k Ω (1) pin 2 | Arduino Pro Mini, 3.3 V 8 MHz pin A0 |
| Resistor, 100k Ω (2) pin 1 | Arduino Pro Mini, 3.3 V 8 MHz pin A0 |
| Humidity sensor, AHT20 pin SDA | Arduino Pro Mini, 3.3 V 8 MHz pin A4 |
| Humidity sensor, AHT20 pin SCL | Arduino 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 1 | Solar 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 IN | Li-ion charger, TP4056 pin BAT+ |
| Resistor, 100k Ω (1) pin 1 | Li-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.
// 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 themillivoltscall, 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.