Премиальный набор датчика влажности и температуры в корпусе из ореха — Arduino + NeoPixel + TFT-дисплей
Компоненты и материалы
| Компонент | Количество |
|---|---|
| Arduino UNO | 1 |
| Кольцо NeoPixel: WS2812 5050 RGB LED (24 LED) | 1 |
| Датчик температуры и влажности DHT22 | 1 |
| TFT-дисплей 1.44" ST7735R с MicroSD (Adafruit) | 1 |
| Беспаяльная макетная плата | 1 |
| Корпус для 4×AA батареек | 1 |
| Перемычки (набор) | 1 |
Инструменты
- Паяльник
- Клейкий пистолет
О проекте
История создания
Мы построили элегантный монитор влажности и температуры в корпусе из ореха с «ласточкиным хвостом» (dovetail), используя Arduino Uno и компоненты Adafruit. Проект объединяет столярное дело и базовую электронику — идеально для начинающих.
Мы живём в Центральном Техасе, где весной происходят резкие скачки влажности и температуры, что вредно для столярных изделий. Это устройство предупреждает об изменении влажности, позволяя вовремя убрать заготовки в дом или отложить фрезеровку. К тому же, оно выглядит отлично и украшено нашим логотипом!
Мы были новичками в Arduino и учились в процессе — теперь планируем новые проекты.
Видео сборки
Смотрите подробное видео по сборке на нашем канале.
Программный код
#include <Adafruit_NeoPixel.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_GFX.h>
#include <FastLED.h>
#include <SPI.h>
#include <SD.h>
#include <cactus_io_DHT22.h>
// Пины
#define TFT_CS 10
#define TFT_RST 9
#define TFT_DC 8
#define DHT_PIN 2
#define PIXEL_PIN 6
#define PIXEL_NUM 24
#define SD_PIN 4
#define BMP_BUF 20
// Глобальные переменные
int currentColor = 9999;
CRGB leds[PIXEL_NUM];
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
DHT22 dht(DHT_PIN);
int loopDelay = 2000;
void setup() {
Serial.begin(9600);
Serial.println("Starting up.");
dht.begin();
tft.initR(INITR_144GREENTAB);
Serial.print("Initializing SD card.");
if (!SD.begin(SD_PIN)) {
Serial.println("Failed!");
return;
}
tft.fillScreen(ST7735_BLACK);
FastLED.addLeds<NEOPIXEL, PIXEL_PIN>(leds, PIXEL_NUM);
delay(500);
}
void loop() {
dht.readTemperature();
dht.readHumidity();
int f = dht.temperature_F; // °F
int h = dht.humidity; // %
// Иконка градусов
tft.fillCircle(102, 42, 6, ST7735_WHITE);
tft.fillCircle(102, 42, 4, ST7735_BLACK);
drawTemperature(f);
setHumidityColors(h);
Serial.print("Temperature: "); Serial.println(f);
Serial.print("Humidity: "); Serial.print(h); Serial.println("%");
delay(1000);
}
// Отрисовка температуры битмапами с SD-карты
void drawTemperature(int temp) {
int digits = numDigits(temp);
int x1_2 = 62, x2_2 = 32;
int x1_3 = 1, x2_3 = 1, x3_3 = 1;
int y = 38;
char digitStr1[24], digitStr2[24], digitStr3[24];
itoa(temp % 10, digitStr1, 10); strcat(digitStr1, ".bmp");
if (digits == 2) { itoa((temp/10)%10, digitStr2, 10); strcat(digitStr2, ".bmp"); }
if (digits == 3) { itoa((temp/100)%10, digitStr3, 10); strcat(digitStr3, ".bmp"); }
if (digits > 2) {
bmpDraw(digitStr1, x1_3, y);
bmpDraw(digitStr2, x2_3, y);
bmpDraw(digitStr3, x3_3, y);
} else {
bmpDraw(digitStr1, x1_2, y);
bmpDraw(digitStr2, x2_2, y);
}
}
int numDigits(int number) {
return (number > 99) ? 3 : 2;
}
// Загрузка BMP с SD и вывод на TFT
void bmpDraw(char *filename, uint8_t x, uint8_t y) {
File bmpFile;
int bmpWidth, bmpHeight;
uint8_t bmpDepth;
uint32_t bmpImageoffset, rowSize;
uint8_t sdbuffer[3*BMP_BUF];
uint8_t buffidx = sizeof(sdbuffer);
boolean goodBmp = false, flip = true;
int w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0, startTime = millis();
if ((x >= tft.width()) || (y >= tft.height())) return;
if ((bmpFile = SD.open(filename)) == NULL) {
Serial.print("Bitmap file not found!"); return;
}
if (read16(bmpFile) == 0x4D42) { // BMP signature
read32(bmpFile); (void)read32(bmpFile);
bmpImageoffset = read32(bmpFile);
read32(bmpFile);
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
if (read16(bmpFile) == 1) {
bmpDepth = read16(bmpFile);
if ((bmpDepth == 24) && (read32(bmpFile) == 0)) {
goodBmp = true;
rowSize = (bmpWidth * 3 + 3) & ~3;
if (bmpHeight < 0) { bmpHeight = -bmpHeight; flip = false; }
w = bmpWidth; h = bmpHeight;
if ((x+w-1) >= tft.width()) w = tft.width() - x;
if ((y+h-1) >= tft.height()) h = tft.height() - y;
tft.setAddrWindow(x, y, x+w-1, y+h-1);
for (row=0; row= sizeof(sdbuffer)) {
bmpFile.read(sdbuffer, sizeof(sdbuffer)); buffidx = 0;
}
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
tft.pushColor(tft.Color565(r,g,b));
}
}
}
}
}
bmpFile.close();
if (!goodBmp) Serial.println("BMP format not recognized.");
}
uint16_t read16(File f) {
uint16_t result;
((uint8_t*)&result)[0] = f.read();
((uint8_t*)&result)[1] = f.read();
return result;
}
uint32_t read32(File f) {
uint32_t result;
((uint8_t*)&result)[0] = f.read();
((uint8_t*)&result)[1] = f.read();
((uint8_t*)&result)[2] = f.read();
((uint8_t*)&result)[3] = f.read();
return result;
}
// Управление цветами NeoPixel по влажности
void setColors(int r, int g, int b) {
for (int i = 0; i < PIXEL_NUM; i++) {
leds[i].setRGB(r, g, b);
leds[i].fadeLightBy(125);
FastLED.show();
delay(100);
}
}
void setHumidityColors(int humidity) {
// Цветовая шкала влажности:
// 0-17% — Красный
// 18-34% — Оранжевый
// 35-51% — Жёлтый
// 52-68% — Зелёный
// 69-85% — Синий
// 86-100% — Фиолетовый
int cRed[3] = {255,0,0};
int cOrange[3] = {255,90,0};
int cYellow[3] = {255,255,0};
int cGreen[3] = {0,255,0};
int cBlue[3] = {0,175,255};
int cPurple[3] = {255,0,255};
int range = map(humidity, 0, 100, 0, 6);
switch (range) {
case 0: if (currentColor != 0) { setColors(cRed[0],cRed[1],cRed[2]); currentColor=0; } break;
case 1: if (currentColor != 1) { setColors(cOrange[0],cOrange[1],cOrange[2]); currentColor=1; } break;
case 2: if (currentColor != 2) { setColors(cYellow[0],cYellow[1],cYellow[2]); currentColor=2; } break;
case 3: if (currentColor != 3) { setColors(cGreen[0],cGreen[1],cGreen[2]); currentColor=3; } break;
case 4: if (currentColor != 4) { setColors(cBlue[0],cBlue[1],cBlue[2]); currentColor=4; } break;
case 5: if (currentColor != 5) { setColors(cPurple[0],cPurple[1],cPurple[2]); currentColor=5; } break;
}
}
Корпус (CAD)
3D-модель корпуса из ореха с ласточьиным хвостом доступна в разделе Custom parts.
Схемы подключения
Схема выполнена в Fritzing (компоненты подобраны приближенно).
- DHT22: DATA → D2, VCC → 3.3V, GND → GND
- NeoPixel: DIN → D6, VCC → 5V, GND → GND
- TFT ST7735: CS→D10, RST→D9, DC→D8, MOSI→D11, SCK→D13, VCC→5V, GND→GND
- SD-карта: CS→D4, остальные по SPI
Производственный процесс
- Датчики влажности: принцип работы и ключевые применения
- Датчик DHT11: принцип работы и ключевые применения
- Sensirion SVM30: Все-в-одном мультигазовый, влажность и температура модуль для очистителей воздуха и HVAC
- Создание удалённого датчика температуры на Raspberry Pi и Python — пошаговое руководство
- Бесшовный мониторинг температуры и влажности на Raspberry Pi с EzTemp
- Создание многодатчиковой системы мониторинга температуры и света с Raspberry Pi и DS18B20
- Создайте автоматизированную аэропоническую систему с Raspberry Pi и датчиком влажности
- Создайте домашний монитор температуры на Raspberry Pi с MCP9808, InfluxDB и Grafana
- Проверка и калибровка датчика влажности для точных измерений
- Лабораторный чип NASA Goddard обеспечивает мониторинг потока и температуры в реальном времени