Колхозим метеостанцию на STM32 (Bluepill, STM32IDE, HAL)

Привет, Хабр!
В этой статье я хочу рассказать о своей небольшой поделке во время карантина — метеостанция на основе STM32 (плата bluepill).

j9bt4ido1rsdjomtelic-lyiw0s.jpeg

Заранее уточню, что устройство собиралось скорее из желания что-нибудь собрать, нежели чем из реальной необходимости собрать метеостанцию. И уже после сборки метеостанции появилось непреодолимое желание расширить ее скудный функционал и значительно переделать.
Принципиальная схема метеостанции:
ryeto_94wuvhnlx6yikzvirevmk.jpeg

В метеостанции я использовал два датчика — АМТ1001 и BMP-180 (плата GY-68). Питается все это от однобаночного Lipo аккумулятора через повышающий до 5В DC-DC преобразователь. Информация с датчиков выводится на дисплей 2004. Выводы настроил следующим образом:
2xvh_gys6fymljc1abmjzpymk_k.jpeg
Подключение датчика температуры и влажности AMT1001

albriijdtppbopoj08sigqfy7ug.jpeg

Выводы датчика:
Красный — VDD (4.75 — 5.25 В)
Черный — GND
Желтый — выход датчика влажности
Белый — термистор
Считывая напряжение на выводах датчика, можно определить температуру и влажность, соотнеся значение напряжения с таблицами из datasheet. Начнем с влажности:

twljbm3bvh9kxhuqn-ahcwvkld0.jpeg

kc1_oyyqohmyssgky6f8drw60ro.jpeg

По графику видно, что зависимость между влажностью и напряжением линейная, а это значит, что значения АЦП можно напрямую преобразовать в значение влажности. Обозначим:
ADC_hum — значение с АЦП;
hum — значение влажности (в %);
V_hum — значение напряжения на датчике.
V_hum = 3.3 В (опорное напряжение АЦП) * ADC_hum / 4095 (кол-во разрядов АЦП).
1% влажности соответствует 0.3/10 = 0.03 В, то 
hum = V_hum / 0.03 = ADC_hum * 0.027
С вычислениями определились, теперь перейдем к коду. Датчик влажности подключен к PA4 (ADC1).

float hum;
uint16_t ADC_hum;

HAL_ADC_Start(&hadc1);                              // запуск АЦП
HAL_ADC_PollForConversion(&hadc1,100);              // ожидание вычисления
ADC_hum = HAL_ADC_GetValue(&hadc1);                 // запись результата в переменную
HAL_ADC_Stop(&hadc1);                               // остановка АЦП

hum = ADC_hum;
hum *= 0.027; 

Теперь рассмотрим термистор — резистор, сопротивление которого меняется от температуры. С помощью АЦП можно измерить напряжение на элементе, но не его сопротивление. Для работы с термистором необходимо добавить резистор на 10 кОм между выводом термистора (белый провод) и землей. В итоге получим вот такой делитель напряжения (термистор внутри подключен к VCC):

bu0f2mgymys8dsp4yfpb05jj74o.jpeg

Теперь мы можем измерить сопротивление термистора, воспользовавшись первым правилом Кирхгофа — сумма токов в узле равна 0. Вспомнив школьный курс физики получим:
Untc/ Rntc = U1/R1
Выводом РА5 мы измеряем напряжение на резисторе 10к. В случае с термистором, его сопротивление меняется не пропорционально изменению температуры, в документации приведена огромная таблица значения сопротивлений транзистора при температуре от -40 до +125. Всю таблицу переносить в микроконтроллер та еще задача, я взял диапазон от 10 до 30 градусов (21 значение).
Для того, чтобы микроконтроллеру не нужно было каждый раз вычислять значение сопротивления, я сделал программу в Mathcad. Полученные результаты я перевел в массив:

int out_temp[21] ={2200, 2255,	2315, 2377, 2433, 2497, 2558, 2618, 2680, 2741, 2801, 2862, 2923, 2983, 3041, 3102, 3166, 3215, 3283, 3336, 3396};


Если АЦП покажет значение, близкое к первому элементу массива — температура 10 градусов, если ко второму — 11, и так далее, до 30 градусов. Значение десятых долей градуса можно примерно вычислить, но можно этого и не делать. Ниже представлен код, который вычисляет десятые (примерное их значение), хотя учитывая технологический разброс полагаться на десятые доли не стоит.

float temp;
uint16_t ADC_temp;
HAL_ADC_Start(&hadc2);                        // запуск АЦП 2
HAL_ADC_PollForConversion(&hadc2,100);        // ожидание вычисления
ADC_temp = HAL_ADC_GetValue(&hadc2);          // запись результата в переменную
HAL_ADC_Stop(&hadc1);                         // остановка АЦП

for (int i=0; i<21; i++)
{
	if (ADC_temp < out_temp[i+1] && ADC_temp > out_temp[i] )
	{
		int x = ADC_temp - out_temp[i];
		int xx = out_temp[i+1] - out_temp[i];
		temp = x;
		temp = temp/xx+i+10;
                break;
	}
}


Дальше я подключил датчик температуры и атмосферного давления GY- 68 (BMP180). Как с ним работать описано тут, код я брал оттуда же.
Для вывода информации я использовал дисплей 2004 с I2C адаптером (про него писал тут)
Соединил все это я следующим примитивным образом — напаял все на макетку, воткнул выключатель в разрыв питания. Датчики и дисплей подключаются через обычную гребенку. Датчики сделал выносными, так как была идея вынуть их за окно, но в таком виде дождя они не переживут.

kx-eg-vg5xa9n4ej2hzt1aoe2da.jpeg

Вот так выглядит датчик:

ihga8tvqbinuyyyzgha6pcb0yt4.jpeg

Корпус печатал на 3д принтере белым ABS (он хорошо шлифуется и на белом цвете меньше видна слоистость).

d_qnbn8acbjw98zviq2hqktf1s4.jpeg

Все материалы выкладываю сюда

© Habrahabr.ru