Mantenha seu drone bem calibrado com este circuito simples, BMP280 & AHT20. Vamos montar um circuito com estes dois sensores para manter seu drone ou quadcóptero no ar. Serve também para estações meteorológicas e outros robôs.
Este combo de sensores é perfeito para projetos como estações meteorológicas, onde necessita-se saber várias valores de sensores. Por exemplo no caso do nosso combo AHT20 + BMP280: temperatura, umidade, pressão atmosférica e altitude (calculada).
Este combo de sensores também é útil e projetos como drone/quadcóptero, para cálculo de altitude em vôo. O sensor usa a variação de pressão em decorrência da altitude, controlando assim esta variável. Isto possibilita um grande controle sobre a dinâmica de vôo do drone/quadcóptero.

Onde eu compro este sensor? eu tenho um link com desconto especial do Aliexpress, aqui. Sou afiliado, então ganho um percentual com a sua compra. Recomendo comprar do Aliexpress, o preço é imbatível e a entrega é rápida. Eu geralmente recebo minhas encomendas em duas semanas. Outra vantagem é que o imposto é pago no ato da compra, então sem surpresas.
Como os sensores funcionam?
Para manter seu drone bem calibrado, dois sensores estão montados nesta placa: AHT20 e BMP280. O AHT20 é um sensor fabricado pela Asair, cujo datasheet está aqui. Ele mede umidade com precisão de 2% e com erro de +/- 0,3ºC. Tudo isso comunicando com qualquer microcontrolador via i2c.
Já o BMP280 é fabricado pela Bosch. Seu datasheet está aqui; ele é capaz de medir pressão atmosférica entre 300 e 1100 hPa (resolução de 0,16 hPa). É também capaz de medir temperatura entre -40ºC e +85ºC com resolução de 0,01ºC (isto é bem preciso). Eu já falei sobre este sensor antes aqui no blog, neste artigo.
Ambos compartilham o barramento i2c, com apenas dois pinos: SDA e SCL. O endereço do AHT20 é 0x38 e do BMP280 é 0x77. O fato de cada um ter seu endereço evita colisões e faz com que possam compartilhar o barramento com segurança.
O sensor BMP280 ainda pode funcionar via SPI, porém não vamos entrar neste mérito neste artigo. Para que o Arduino (ou ESP32) consiga comunicar com ambos sensores, vamos utilizar bibliotecas da Adafruit. Ambas funcionam dentro do software IDE do Arduino.
Hardware
Como de costume aqui no blog, todos os nossos hardwares e ligações são simples. No caso do artigo de hoje nós vamos precisar da plaquinha com o BMP280+AHT20. Além disso precisaremos de um ESP32 ou Arduino qualquer (já que quase todos suportam protocolo i2c). E finalmente um LED de qualquer cor e um resistor de qualquer valor (entre 220 Ohm e 2200 Ohm).
O diagrama esquemático da nossa montagem está abaixo. Eu fiz a mesma em uma protoboard de 400 pinos, bem pequena. Isto porque estou usando um SeeedStudio Xiao ESP32-C6 numa placa de circuito impreso que eu mesmo fiz. Esta placa permite que eu utilize apenas um pino de cada linha da protoboard.
Visto que todos os onze (11) pinos do ESP32-C6, bem como 5V, 3V3 e GND estão expostos em uma única linha da placa PCI. Veja também que a alimentação do projeto vem pelo próprio cabo USB conectado ao ESP32-C6. Além de energia, este cabo transita informações lidas pelos sensores para o monitor serial da IDE do Arduino.

Tudo que você precisa é uma protoboard e alguns fios jumpers, pode ser destes comprados ou até mesmo feito com fios de telefone rígidos. Eu já usei bastante destes no passado para fazer ligações.
Código/Firmware
Conforme já comentado, fiz o código para nosso exemplo no software IDE do Arduino. As bibliotecas necessárias são do WiFi, de JSON, Wire, SPI, e duas da Adafruit. Ambas são para os sensores, AHT20 e BMP280. O cabeçalho do arquivo fica assim:
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_AHTX0.h>
O código executa as funções de:
- Ler o AHT20
- Buscar pressão atmosférica numa API via internet
- Ler o BMP280 (usando a pressão atmosférica do passo anterior)
- Fazer tudo isso a cada 10 segundos, ligando e desligando um LED a cada ciclo.
O código completo está abaixo, pode copiar e colar na sua IDE do Arduino:
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_AHTX0.h>
Adafruit_BMP280 bmp; // I2C
Adafruit_AHTX0 aht;
long sensorsTime= 0;
float aht20temp;
float aht20hum;
float bmp280temp;
float bmp280pressure;
float bmp280altitude;
float sensorsReadingTime;
const char* weatherapikey= ""; // from OpenWeatherMap
//const char* weatherlanguage= "pt_br"; // OpenWeatherMap
float weathercondition;
long weatherAPITime= 0;
long weatherFetchingTime= 0;
bool startup= true;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
WiFi.begin("ssid", "password");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
pinMode(D0, OUTPUT);
unsigned status;
//status = bmp.begin(BMP280_ADDRESS_ALT, BMP280_CHIPID);
status = bmp.begin();
if (!status) {
Serial.println(F("Could not find a valid BMP280 sensor, check wiring or "
"try a different address!"));
Serial.print("SensorID was: 0x"); Serial.println(bmp.sensorID(),16);
Serial.print(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
Serial.print(" ID of 0x56-0x58 represents a BMP 280,\n");
Serial.print(" ID of 0x60 represents a BME 280.\n");
Serial.print(" ID of 0x61 represents a BME 680.\n");
while (1) delay(10);
}
if (! aht.begin()) {
Serial.println("Could not find AHT? Check wiring");
while (1) delay(10);
}
/* Default settings from datasheet. */
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */
Adafruit_BMP280::SAMPLING_X2, /* Temp. oversampling */
Adafruit_BMP280::SAMPLING_X16, /* Pressure oversampling */
Adafruit_BMP280::FILTER_X16, /* Filtering. */
Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */
}
void loop() {
if((millis() - weatherAPITime > 180000) || startup == true){ // fetch atmospheric pressure via API every 3 minutes
startup= false;
weatherAPITime= millis();
weatherFetchingTime= micros();
getWeather();
weatherFetchingTime= micros() - weatherFetchingTime;
}
if(millis() - sensorsTime > 9998){ // execute one reading on BMP280 and AHT20 every 10 seconds
sensorsTime= millis();
digitalWrite(D0, !digitalRead(D0));
sensorsReadingTime= micros(); // start counting how long the sensors reading takes
sensors_event_t humidity, temp;
aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
aht20temp= temp.temperature;
aht20hum= humidity.relative_humidity;
bmp280temp= bmp.readTemperature();
bmp280pressure= bmp.readPressure();
//bmp280altitude= bmp.readAltitude(1013.25);
bmp280altitude= bmp.readAltitude(weathercondition);
sensorsReadingTime= micros() - sensorsReadingTime;
Serial.print("Temp AHT20: "); Serial.print(aht20temp); Serial.println(" *C");
Serial.print("Umid AHT20: "); Serial.print(aht20hum); Serial.println(" %rH");
Serial.print(F("Temp BMP280 = "));
Serial.print(bmp280temp);
Serial.println(" *C");
Serial.print(F("Pressao BMP280 = "));
Serial.print(bmp280pressure);
Serial.println(" Pa");
Serial.print(F("Altitude BMP280 = "));
Serial.print(bmp280altitude); /* Adjusted to local forecast! */
Serial.println(" m");
Serial.print("Pressao API = ");
Serial.println(weathercondition);
Serial.println("-----");
Serial.print(sensorsReadingTime);
Serial.println(" us");
Serial.print("Weather= ");
Serial.print(weatherFetchingTime);
Serial.println(" us");
Serial.println("-----");
}
}
void getWeather() {
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
String url = "https://api.openweathermap.org/data/2.5/weather?lat=yourlatitude&lon=yourlongitude&appid="
+ String(weatherapikey);
if (http.begin(client, url)) {
int httpCode = http.GET();
if (httpCode == 200) {
DynamicJsonDocument doc(2048);
deserializeJson(doc, http.getString());
//weathercondition = doc["weather"][0]["sea_level"].as<String>();
if (doc["main"]["sea_level"].is<float>()) {
weathercondition = doc["main"]["sea_level"].as<float>();
} else {
// fallback if API doesn't provide sea_level
weathercondition = doc["main"]["pressure"].as<float>();
}
//weathercondition.setCharAt(0, toupper(weathercondition.charAt(0)));
}
http.end();
}
}
Alguns detalhes importantes devem ser levados em conta. Por exemplo inserir o nome e senha da sua rede WiFi. Ou então a API key (chave de API) do OpenWeatherMap. Para criar sua chave, cadastre-se neste site. Outros detalhes são os tempos em que as funções são executadas. Pressão atmosférica somente é buscada na internet a cada três minutos:
if((millis() - weatherAPITime > 180000) || startup == true){ // fetch atmospheric pressure via API every 3 minutes
Já a leitura dos sensores e impressão no monitor serial da IDE do Arduino, são feitos a cada 10 segundos:
if(millis() - sensorsTime > 9998){ // execute one reading on BMP280 and AHT20 every 10 seconds
Outra parte interessante deste código é que ele conta o tempo que as coisas demorar para serem executadas. Eu conto o tempo que os dois sensores demoram para ser lidos. Conto também o tempo que demora para ler a API da pressão atmosférica.

No exemplo da imagem acima, os sensores demoraram 44,9 milisegundos para serem lidos. Já a pressão atmosférica demorou um total de 1,03 segundos.
Seu drone bem calibrado
Conforme vimos no artigo, você pode usar este sensor, que é super preciso, para medir altitude de drones. Ele serve também para outros veículos e modelos/protótipos que voam ou variam sua altitude.
Fiz um vídeo sobre o funcionamento destes sensores, está no início do artigo. Volte lá para assistir e em caso de dúvidas comente logo abaixo. Ou também lá no canal do Youtube do FritzenLab_br. Até a próxima pessoal. Aí fiz mais um vídeo, logo abaixo.





