Community project

High-Altitude Telemetry Payload

Abdulaziz Abulxayriyev (Genius.001)

Published August 2, 2026 · Updated August 11, 2026

ESP3210 components6 assembly steps
Remix this project
Photo of High-Altitude Telemetry PayloadGenerated with AI

This high-altitude telemetry payload captures environmental and motion data during balloon or rocket flights up to 18 km. Built around an ESP32 microcontroller, it combines a BME280 environmental sensor, MPU-6050 6-axis IMU, NEO-6M GPS module, UV intensity sensor, and DS18B20 temperature probe to log altitude, pressure, humidity, acceleration, rotation, position, and radiation data to a MicroSD card while transmitting live telemetry via LoRa radio.

This guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for integrating the sensor bus, communication modules, and power system into a flight-ready payload. You'll also get the Arduino firmware that initializes all sensors, manages data logging, and handles LoRa transmission, plus mechanical testing procedures to verify the build before launch.

Wiring diagram

Interactive · read-only
Wiring diagram for High-Altitude Telemetry Payload

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
BME280BME280 I2C breakout1Bosch BME280 environmental sensor on the exact Adafruit 2652 breakout. Supports I2C via SCK/SCL and SDI/SDA, optional SDO address select, and SPI pins when needed.
DFRobot SEN0142 Fermion MPU-6050 6 DOF Sensor BreakoutMPU-6050 I2C breakout1DFRobot SEN0142 MPU-6050 breakout with 3-5 V board input, I2C interface, onboard I2C pull-ups, and i2cdevlib Arduino example coverage.
NEO-6M GPS ModuleNEO-6M GPS1u-blox NEO-6M based GPS receiver module. Outputs NMEA sentences (GGA, RMC, etc.) over UART at 9600 baud by default. Provides latitude, longitude, altitude, speed, and time. The NEO-6M module itself is a 3.3V-class device; many GY-NEO6MV2 breakout boards accept 5V on their VCC header through an onboard regulator, but UART I/O remains 3.3V-domain and must not be driven above 3.6V. Features an on-board patch antenna footprint and an SMA/IPEX connector for external active antenna (preferred for faster lock acquisition). Supply current ~45 mA in acquisition, ~11 mA in tracking.
MicroSD Card ModuleSPI MicroSD logger module (3.3 V logic)1SPI-based microSD card adapter module for SPI-capable microcontrollers. Uses MOSI, MISO, SCK, and CS plus power and ground. Many low-cost modules include a 3.3 V regulator and level shifting for 5 V MCU boards, while bare breakouts should be powered and signalled at 3.3 V.
DS18B20DS18B20 battery temperature probe1Digital temperature sensor using OneWire protocol
4.7k Pull Up Resistor4.7 kΩ14.7kΩ pull-up resistor used in 1-Wire (OneWire) communication buses. Essential passive component that pulls the single data line to 3.3V or 5V when idle. Works with 1-Wire protocol devices (DS18B20, DS18S20, iButton, etc.) to ensure proper bus communication at standard 16.3 kbit/s data rates. Supports both 3.3V and 5V systems.
Ebyte E32-433T20D LoRa UART transceiver433 MHz; use only within locally authorized band plan1433 MHz UART LoRa module. Power it from a clean 3.3 V rail; UART logic is 3.3 V. M0 and M1 are held low for normal transmission; AUX is a module-ready status output.
GUVA-S12SD UV intensity sensor moduleGUVA-S12SD analog UVA sensor1Analog UVA intensity module with 0–1 V typical output for UV index 0–10 when powered at 3.3 V.
Lithium Ion Battery - 7.4V 1000mAh (2S LiPo)2S protected LiPo; select 2200–3000 mAh actual pack17.4V (2S) 1000mAh lithium polymer battery pack with a 25C continuous discharge rate, JST-XH charge plug, and Deans discharge connector; sized for R/C vehicles, robotics, and other portable projects needing a compact 2-cell high-current supply.
MP1584 Buck ConverterSet to 5.0 V before connecting electronics1Adjustable buck (step-down) DC-DC converter, 4.5-28 V in -> 0.8-20 V out, ~3 A. Configured to 5 V to step a 9 V / 12 V supply or battery pack down to the board's 5 V rail.

Assembly

6 steps
  1. Prepare the insulated payload and power system

    Use a protected 2S (7.4 V nominal) LiPo battery, not a single 3.7 V cell, because buck_1 is step-down only. Connect battery_1 BAT+ to buck_1 VIN and battery_1 BAT− to buck_1 GND. Before any electronics are attached, measure buck_1 VOUT relative to GND and adjust it to exactly 5.0 V. Feed that 5 V output to the ESP32 DevKit V1 VIN/5V pin and connect its ground to the common ground rail. Keep the battery, regulator, and all cables inside the insulated enclosure; strain-relieve every cable.

    • Tip: Choose a 2200–3000 mAh protected 2S pack with a suitable connector and a charger specifically rated for 2S lithium packs.
    • Tip: Add an inline fuse close to the battery positive lead if available.
    • Tip: Keep the MP1584 away from the BME280 and GPS antenna to reduce heat and electrical noise.
    • Never connect a 3.7 V single-cell LiPo directly to buck_1 expecting 5 V output; this regulator cannot boost voltage.
    • Do not connect the camera or ESP32 until the buck output has been checked with a multimeter.
    • Do not charge a LiPo inside a sealed foam payload or while unattended.
  2. Build the common 3.3 V sensor bus

    With the ESP32 unpowered, join every sensor ground to an ESP32 GND pin. Use the ESP32 3V3 pin for the logic/sensor rail: connect bme280_1 VCC, mpu6050_1 VIN, gps_1 VCC, sd_1 VCC, ds18b20_1 VCC, lora_1 VCC, and guva_1 VCC to 3V3. Connect each corresponding GND pin to GND. This design intentionally runs all listed modules at 3.3 V to protect ESP32 GPIO.

    • Tip: Use short twisted or paired ground/signal wires where practical, especially for I2C and the radio supply.
    • Tip: Add a local electrolytic capacitor near lora_1 VCC/GND if its breakout does not already have adequate decoupling.
    • Never connect the E32 UART pins or the GUVA output to 5 V when attached to the ESP32.
    • Some low-cost MicroSD boards are intended for 5 V Arduino systems. Use a module proven to accept 3.3 V supply and 3.3 V SPI logic.
  3. Wire the shared I2C environmental and motion sensors

    Connect bme280_1 SDA and mpu6050_1 SDA together at ESP32 GPIO21. Connect bme280_1 SCL and mpu6050_1 SCL together at ESP32 GPIO22. Leave the optional interrupt pins unconnected. Mount bme280_1 where it samples outside air through a protected vent and breathable mesh; keep it away from the warm-water container and regulator.

    • Tip: The BME280 must not be sealed in the warm electronics cavity if you need external atmospheric pressure and humidity readings.
    • Tip: Secure mpu6050_1 firmly to the payload structure so its axes do not move relative to the payload.
    • Do not use a BME280 and a second I2C device with the same address unless you deliberately change one address; the MPU-6050 default address is different and is safe on this bus.
    • Protect the BME280 vent from liquid water and direct debris; do not cover the sensor opening with tape.
  4. Wire the GPS, MicroSD, DS18B20, and UV sensor

    Connect gps_1 TX to ESP32 GPIO16 and gps_1 RX to GPIO17. Connect sd_1 MISO to GPIO19, MOSI to GPIO23, SCK to GPIO18, and CS to GPIO13. Connect ds18b20_1 DATA to GPIO4. Install onewire_pullup_1 between the same DS18B20 DATA point and 3V3 (it is not connected to ground). Connect guva_1 OUT to GPIO34, which is input-only and suitable for analog sensing. Tape the DS18B20 probe directly against the battery pack with electrical insulation over the probe and tape.

    • Tip: Use a quality, freshly formatted high-endurance MicroSD card and physically secure it in sd_1.
    • Tip: Mount the GPS patch antenna facing outward with as much sky view as possible, separated from the radio antenna and buck converter.
    • Tip: Place the GUVA-S12SD behind a UV-transparent opening; ordinary glass and many plastics attenuate UV.
    • The 4.7 kΩ onewire_pullup_1 must go from DATA to 3.3 V; connecting it to ground prevents the DS18B20 bus from working.
    • GPIO34 has no internal pull-up; that is correct for the GUVA analog output.
    • NEO-6M devices are subject to their own firmware limits and may cease navigation at high altitude/high dynamics; this project does not override those limits.
  5. Wire the E32 radio and its antenna

    Connect lora_1 TXD to ESP32 GPIO26, lora_1 RXD to GPIO25, and lora_1 AUX to GPIO27. Tie lora_1 M0 and M1 to GND to select normal transparent UART mode. Attach the antenna designed for the exact legally authorized 433 MHz operating frequency before powering the transmitter. Keep the antenna clear of the GPS patch antenna, battery, and conductive foil.

    • Tip: The E32 must share ground with the ESP32; a UART connection alone is not sufficient.
    • Tip: Route the radio antenna vertically outside the foam enclosure and provide mechanical strain relief at the module.
    • Tip: Use the matching E32 settings on the ground-station receiver: air data rate, channel, UART rate, parity, and address must agree.
    • Do not transmit without an antenna attached; it can stress the radio output stage.
    • Confirm that 433 MHz operation, output power, bandwidth, duty cycle, telemetry content, and antenna use are permitted at your launch location before flight.
    • The stated 16.4 cm per side dipole dimension should be verified with a proper 433 MHz antenna design and local regulations; antenna length and feedline arrangement materially affect performance.
  6. Install and test mechanically before flight

    Mount the electronics on a nonconductive plate, secure the MicroSD card, and ensure no bare metal can contact the battery. Place the sealed hot-water container in a separate compartment from the electronics, with an additional waterproof barrier. Ensure the BME280 vent, GUVA window, GPS sky view, and radio antenna path remain unobstructed. Verify the parachute/recovery system is fully independent of the ESP32 firmware. Power the payload from its regulated 5 V input and use Schematik’s Deploy button to load the firmware after the wiring is inspected.

    • Tip: Bench-test for several hours with the complete power system and radio transmission enabled; inspect the CSV log for a new row every two seconds.
    • Tip: Use conformal protection or enclosed mounting appropriate to condensation risk, while leaving the pressure sensor vent and optical sensor window functional.
    • Tip: A separate audible/visual recovery beacon and a manual pre-flight checklist improve recovery robustness.
    • Hot water can leak and burn; use a leak-tested, heat-rated sealed container and never allow it to contact a LiPo battery or electronics.
    • High-altitude balloon launches may require aviation authority approval, radio authorization, and recovery planning. Follow all applicable rules and do not fly near controlled airspace.
    • Do not rely on this telemetry system as the sole safety or recovery system.

Pin assignments

Board wiring reference
PinConnectionType
3V3bme280_1 VCCpower
GNDbme280_1 GNDground
GPIO 21bme280_1 SDAi2c
GPIO 22bme280_1 SCLi2c
3V3mpu6050_1 VINpower
GNDmpu6050_1 GNDground
GPIO 21mpu6050_1 SDAi2c
GPIO 22mpu6050_1 SCLi2c
3V3gps_1 VCCpower
GNDgps_1 GNDground
GPIO 16gps_1 TXuart
GPIO 17gps_1 RXuart
3V3sd_1 VCCpower
GNDsd_1 GNDground
GPIO 19sd_1 MISOspi
GPIO 23sd_1 MOSIspi
GPIO 18sd_1 SCKspi
GPIO 13sd_1 CSspi
3V3ds18b20_1 VCCpower
GNDds18b20_1 GNDground
GPIO 4ds18b20_1 DATAdata
3V3onewire_pullup_1 VCCpower
GNDonewire_pullup_1 GNDground
3V3lora_1 VCCpower
GNDlora_1 GNDground
GPIO 26lora_1 TXDuart
GPIO 25lora_1 RXDuart
GPIO 27lora_1 AUXdigital
GNDlora_1 M0ground
GNDlora_1 M1ground
3V3guva_1 VCCpower
GNDguva_1 GNDground
GPIO 34guva_1 OUTanalog
EXTbattery_1 BAT+MP1584 Buck Converter VINpower
EXTbattery_1 BAT-MP1584 Buck Converter GNDground
VINbuck_1 VOUTpower
GNDbuck_1 GNDground
EXTonewire_pullup_1 DATA_LINEDS18B20 DATAdata

Firmware

ESP32
main.cppDeploy to device
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_BME280.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <TinyGPSPlus.h>


// Forward declarations
void writeMpuRegister(uint8_t reg, uint8_t value);
bool readMpuWord(uint8_t reg, int16_t &value);
bool readImu(float &ax, float &ay, float &az, float &gx, float &gy, float &gz);
void appendLogHeaderIfNeeded();

constexpr int I2C_SDA_PIN = 21;
constexpr int I2C_SCL_PIN = 22;
constexpr int SD_CS_PIN = 13;
constexpr int ONEWIRE_PIN = 4;
constexpr int UV_PIN = 34;
constexpr int GPS_RX_PIN = 16;
constexpr int GPS_TX_PIN = 17;
constexpr int LORA_RX_PIN = 26;
constexpr int LORA_TX_PIN = 25;
constexpr int LORA_AUX_PIN = 27;
constexpr float GPS_TELEMETRY_CEILING_M = 18000.0f;
constexpr uint32_t SAMPLE_INTERVAL_MS = 2000;

Adafruit_BME280 bme;
OneWire oneWire(ONEWIRE_PIN);
DallasTemperature batteryProbe(&oneWire);
TinyGPSPlus gps;
HardwareSerial gpsSerial(2);
HardwareSerial loraSerial(1);

bool bmeReady = false;
bool sdReady = false;
uint32_t lastSampleMs = 0;

void writeMpuRegister(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(0x68);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

bool readMpuWord(uint8_t reg, int16_t &value) {
  Wire.beginTransmission(0x68);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return false;
  if (Wire.requestFrom(0x68, 2) != 2) return false;
  value = (int16_t)((Wire.read() << 8) | Wire.read());
  return true;
}

bool readImu(float &ax, float &ay, float &az, float &gx, float &gy, float &gz) {
  int16_t rawAx, rawAy, rawAz, rawGx, rawGy, rawGz;
  bool ok = readMpuWord(0x3B, rawAx) && readMpuWord(0x3D, rawAy) &&
            readMpuWord(0x3F, rawAz) && readMpuWord(0x43, rawGx) &&
            readMpuWord(0x45, rawGy) && readMpuWord(0x47, rawGz);
  if (!ok) return false;
  ax = rawAx / 2048.0f;  // ±16 g
  ay = rawAy / 2048.0f;
  az = rawAz / 2048.0f;
  gx = rawGx / 16.4f;    // ±2000 degrees/s
  gy = rawGy / 16.4f;
  gz = rawGz / 16.4f;
  return true;
}

void appendLogHeaderIfNeeded() {
  if (!sdReady || SD.exists("/flight_log.csv")) return;
  File file = SD.open("/flight_log.csv", FILE_WRITE);
  if (file) {
    file.println("ms,pressure_hpa,air_temp_c,humidity_pct,baro_alt_m,battery_temp_c,uv_volts,uv_index,ax_g,ay_g,az_g,gx_dps,gy_dps,gz_dps,gps_lat,gps_lon,gps_alt_m,gps_valid");
    file.close();
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  bmeReady = bme.begin(0x76) || bme.begin(0x77);

  // MPU-6050 awake, ±16 g accelerometer, ±2000 degrees/s gyro.
  writeMpuRegister(0x6B, 0x00);
  writeMpuRegister(0x1C, 0x18);
  writeMpuRegister(0x1B, 0x18);

  batteryProbe.begin();
  analogReadResolution(12);
  pinMode(LORA_AUX_PIN, INPUT);
  gpsSerial.begin(9600, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
  loraSerial.begin(9600, SERIAL_8N1, LORA_RX_PIN, LORA_TX_PIN);
  sdReady = SD.begin(SD_CS_PIN);
  appendLogHeaderIfNeeded();

  Serial.println(bmeReady ? "BME280 ready" : "BME280 not detected");
  Serial.println(sdReady ? "SD logger ready" : "SD logger not detected");
}

void loop() {
  while (gpsSerial.available()) gps.encode(gpsSerial.read());
  const uint32_t now = millis();
  if (now - lastSampleMs < SAMPLE_INTERVAL_MS) return;
  lastSampleMs = now;

  float pressure = NAN, airTemp = NAN, humidity = NAN, baroAltitude = NAN;
  if (bmeReady) {
    pressure = bme.readPressure() / 100.0f;
    airTemp = bme.readTemperature();
    humidity = bme.readHumidity();
    baroAltitude = bme.readAltitude(1013.25f);
  }

  batteryProbe.requestTemperatures();
  const float batteryTemp = batteryProbe.getTempCByIndex(0);
  const float uvVolts = analogRead(UV_PIN) * (3.3f / 4095.0f);
  const int uvIndex = constrain((int)lroundf(uvVolts * 10.0f), 0, 15);

  float ax = NAN, ay = NAN, az = NAN, gx = NAN, gy = NAN, gz = NAN;
  readImu(ax, ay, az, gx, gy, gz);

  const bool belowCeiling = isfinite(baroAltitude) && baroAltitude < GPS_TELEMETRY_CEILING_M;
  const bool gpsAllowed = belowCeiling && gps.location.isValid() && gps.location.age() < 5000;
  const double lat = gpsAllowed ? gps.location.lat() : 0.0;
  const double lon = gpsAllowed ? gps.location.lng() : 0.0;
  const double gpsAlt = gpsAllowed && gps.altitude.isValid() ? gps.altitude.meters() : 0.0;

  char line[420];
  snprintf(line, sizeof(line), "%lu,%.2f,%.2f,%.2f,%.2f,%.2f,%.3f,%d,%.2f,%.2f,%.2f,%.1f,%.1f,%.1f,%.6f,%.6f,%.1f,%d",
           (unsigned long)now, pressure, airTemp, humidity, baroAltitude, batteryTemp,
           uvVolts, uvIndex, ax, ay, az, gx, gy, gz, lat, lon, gpsAlt, gpsAllowed ? 1 : 0);

  if (sdReady) {
    File file = SD.open("/flight_log.csv", FILE_APPEND);
    if (file) {
      file.println(line);
      file.close();
    } else {
      sdReady = false;
    }
  }

  // E32 M0/M1 are hard-wired low for transparent normal mode. AUX high means ready.
  if (digitalRead(LORA_AUX_PIN) == HIGH) loraSerial.println(line);
  Serial.println(line);
}

“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik