Pull to refresh

Превращение обычного электрического конвектора в беспроводной

Reading time 7 min
Views 40K

Предыстория


Мой частный дом отапливается при помощи электрических конвекторов. Всё в них хорошо: и лёгкость монтажа и автоматическое управление температурой и режим день/ночь и режим 50% мощности. Но есть и минус — датчик температуры воздуха закреплён прямо на корпусе конвектора, поэтому нагревается и остывает вместе с ним. Из-за этого конвектор включается/выключается гораздо чаще, чем хотелось бы, невозможно установить желаемую температуру воздуха в комнате, т.к. реальная будет ниже градусов на 5, да и на надёжности постоянные переключения реле сказываются негативно. Можно было, конечно, удлинить датчик температуры и отнести его подальше, но это не наш метод. Т.к. я давно занимаюсь беспроводными технологиями и есть наработки, то решил оснастить конвектор беспроводным датчиком температуры. Это позволит разместить его в любом месте комнаты, не тянуть провода, а если нужно, использовать не один, а несколько датчиков и рассчитывать среднюю по комнате температуру. (Под катом картинки)

Переделка


Как я уже сказал, я плотно занимаюсь беспроводкой и имею по этой теме наработки. Для мониторинга различных датчиков как нельзя лучше подходит ZigBee, а качестве ZigBee микроконтроллера я уже давно использую JN5148 фирмы Jennic (куплена NXP). Для быстрого изготовления макетов я сделал себе несколько модулей с этим микроконтроллером.


Схема модуля (кликабельна):
image
В схему модуля включен сам микроконтроллер, внешняя память программ для него (обязательный компонент для JN5148), кварц, конденсаторы по линиям питания, ВЧ часть с антенной. Для быстрого старта нужен только разъём программирования и питание 3.3 В. Платки заказывал в seeedstudio, дёшево и сердито. Для того чтобы быстро что-то сваять отлично подходят.
Датчик температуры тоже был сделан заранее и ждал своего часа.


Схема датчика (кликабельна):

В качестве измерителя температуры использована микросхема TMP102 фирмы Texas Instruments. Микросхема довольно недорогая, измеряет температуру с точностью 0.5 градуса в диапазоне -25..+85, имеет ток потребления 10 мкА в активном режиме и 1 мкА в спящем, очень компактная, а также работает в диапазоне напряжений питания от 1.4 до 3.6 В, что важно при питании от одной литиевой батарейки. В остальном схема датчика отличается от схемы модуля наличием батарейки, делителя для измерения её напряжения, включателя питания и разъёма для программирования.
Чтобы закончить с железом и перейти собственно к переделке, забегу вперёд и скажу, что сначала я хотел только измерять температуру, передавать его конвектору и каким то образом подсовывать её микроконтроллеру вместо его родного датчика. В последствии появилась идея устанавливать температурный порог так же удаленно, с ПК. Для этого я использовал USB свисток с тем же JN5148.

Схема (тоже кликабельна):

Схема свистка включает в себя схему модуля, рассмотренного выше и USB-UART конвертер на микросхеме FT232R, который одновременно является программатором для микроконтроллера.
Теперь перейдём к реверс-инжинирингу. В качестве подопытного использовался конвектор фирмы Ballu мощностью 1000 Вт с электронной системой управления. Разобрав конвектор, я обнаружил 2 платы: силовую и плату управления.

Силовая плата:


Плата управления:


На силовой плате расположен сетевой источник питания, стабилизатор напряжения +5В на L7805, 2 реле, которые включают либо нагрузку 500Вт (50%) либо 1000Вт (100%) и зуммер. Отдельно расположены термопредохранитель и ионизатор воздуха. На плате управления расположен микроконтроллер, кнопки, а также семисегментный индикатор температуры.
Осмотр показал, что для измерения температуры используется полупроводниковый диод, который, как известно, обладает довольно линейной зависимостью прямого падения напряжения от температуры. Диод включен в верхнее плечо делителя напряжения питания, а напряжение с делителя подаётся на вход АЦП микроконтроллера.

Исходя из этой схемы измерения, самый простой способ эмулировать датчик температуры — это подавать на АЦП конвектора напряжение с ЦАП, который имеется на борту JN5148. Т.к. напряжение питания (и одновременно опорное АЦП) контроллера в конвекторе составляет 5 В, а опорное у ЦАПа — 2.4, необходимо усилить напряжение с ЦАП при помощи операционника примерно в 2 раза. Исходя из этого рисуем схему эмулятора датчика температуры (кликабельна).


Дополнительно к модулю она включает в себя усилитель на операционнике, преобразователь 5 В — 3.3 В для питания JN5148 и разъём программирования. Дальше изготавливаем плату: утюжим, травим, сверлим, лудим, паяем.




Устанавливаем плату на место и начинаем кодить. Кстати, то что плата управления отключается от силовой платы оказалось очень удобно. На неё достаточно подать +5 В и она может работать полностью автономно, поэтому в конвектор я её устанавливал после полной отладки работы системы.

Программирование


Опытным путём я снял зависимость температуры, измеренной конвектором, от напряжения на входе АЦП.

Видно, что в середине диапазона разница между реальной характеристикой и идеальной составляет примерно 1 градус, поэтому я принял решение записать соответствующие коды ЦАП в массив и в зависимости от температуры брать нужный код из массива и отправлять ЦАПу.

В качестве основы для программирования я использовал шаблон от фирмы Jennic — JN-AN-1123-ZBPro-Application-Template, который можно скачать здесь. В нём реализован весь базовый функционал сети ZigBee, которая работает на основе операционной системы JenOS, собственной разработке фирмы Jennic для её микроконтроллеров. Кому интересно, могут скачать шаблон и посмотреть, я же приведу здесь только самый важный код.
В данной системе представлены все типы устройств сети ZigBee: координатор (конвектор), маршрутизатор (USB свисток) и спящее оконечное устройство (датчик). Начнём с самого простого — с USB свистка. Он занимается тем, что сканит UART на предмет появления байта с компьютера и отправляет принятый байт координатору.
Функция сканирования представляет собой задачу операционной системы, которая запускается один раз в 50 мс. Она проверяет не пришла ли команда и выдаёт все пришедшие команды в очередь сообщений, которая обрабатывается основной задачей.
OS_TASK(APP_CommandScan)
{
	APP_tsEvent sCommandEvent;
	int16 word;

	//Считываем слово из УАРТа
	word=i16Serial_RxChar(E_AHI_UART_0);

	//Если успешно
	if(word != -1)
	{
		//Отправляем системное сообщение с кодом команды
        sCommandEvent.eType = APP_E_EVENT_COMMAND;
        sCommandEvent.sCommand.u8Value = (uint8)word;
        OS_ePostMessage(APP_CommandEvent, &sCommandEvent);
	}

	//Перезапускаем таймер задачи
	OS_eContinueSWTimer(APP_tmrCommandScan, APP_TIME_MS(50), NULL);
}

В основном цикле все пришедшие команды отправляются координатору.
//Пришло системное сообщение о принятии команды по УАРТу
if (APP_E_EVENT_COMMAND == sAppEvent.eType)
{
        //Создаём указатель APDU
        PRIVATE PDUM_thAPduInstance s_hAPduInst = PDUM_INVALID_HANDLE;

        //Форматируем APDU для передачи команды
        s_hAPduInst = PDUM_hAPduAllocateAPduInstance(apduCommand);

        //Записываем в APDU данные для передачи
        PDUM_u16APduInstanceWriteNBO(s_hAPduInst,
							   0,				//Стартовая позиция для записи
							   "b", 			//Строка форматирования, передаём один байт (b)
							   sAppEvent.sCommand.u8Value);

        //Устанавливаем размер APDU равным 1 байту
	PDUM_eAPduInstanceSetPayloadSize(s_hAPduInst, 1);

	//Отправляем данные, с указанием кластера, конечных точек отправителя и приёмника
	u8Status = ZPS_eAplAfUnicastDataReq(s_hAPduInst,
										MYPROFILE_MYCLUSTER_CLUSTER_ID,
										USBSTICK_MYENDPOINT_ENDPOINT,
										THERMOSTAT_MYENDPOINT_ENDPOINT,
										0x0000,  //Адрес получателя (в данном случае координатора)
										ZPS_E_APL_AF_UNSECURE,
										0,
										&u8SeqNum);
}

Датчик температуры просыпается один раз в секунду (время, конечно, настраивается), измеряет температуру и напряжение батарейки, отправляет всё конвектору и снова засыпает.
PRIVATE void vSendSensorData()
{
	uint8 u8Status;
	uint8 u8SeqNum;

	//Создаём структуру для измеренных данных
	SensorData NewSensorData;

	//Измеряем температуру и напряжение батарейки
	NewSensorData.TempValue = TempMeasurement();
	NewSensorData.BattValue = BatVoltageMeasurment();

	//МАС адрес считывается один раз при запуске
	//и отправляется для того, чтобы различать несколько датчиков температуры
	uint32 MacH, MacL;
	MacH = MacAddr.u32H;
	MacL = MacAddr.u32L;

	//Создаём указатель APDU
	PRIVATE PDUM_thAPduInstance s_hAPduInst = PDUM_INVALID_HANDLE;

	//Форматируем APDU для передачи температуры
	s_hAPduInst = PDUM_hAPduAllocateAPduInstance(apduTemperature);

	//Записываем в APDU данные для передачи
	PDUM_u16APduInstanceWriteNBO(   s_hAPduInst,
									0,                //Начальная позиция записываемых данных
									"wwbh",			  //Строка форматирования (в данном случае мы
									                  //хотим записать две 32-битных переменных (ww)
									                  //одну 8-битную (b) и одну 16-битную (h)
									MacH,
									MacL,
									NewSensorData.TempValue,
									NewSensorData.BattValue);

	//Устанавливаем общий размер отправляемых данных равным 11 байт
	PDUM_eAPduInstanceSetPayloadSize(s_hAPduInst, 11);

	//Отправляем данные широковещательным пакетом с указанием кластера и конечной точки отправителя
	u8Status = ZPS_eAplAfBroadcastDataReq(s_hAPduInst,
										MYPROFILE_TEMPERATURE_CLUSTER_ID,
										TEMPSENSOR_TEMPSENSORENDPOINT_ENDPOINT,
										0xFF,
										ZPS_E_BROADCAST_ZC_ZR,
										ZPS_E_APL_AF_UNSECURE,
										0 ,
										&u8SeqNum);


}

Координатор в свою очередь определяет от кого пришли данные и если это температура, то устанавливает соответствующее напряжение на выходе ЦАПа, а если это команда с компьютера, то выдаёт импульсы на кнопки установки температуры (эмулирует нажатие).
Функция установки температуры:
void SetTemp(int8 temp)
{
	//Проверяем, что температура не выходит за диапазон
	if(temp > 33) temp = 33;
	else if(temp < 0) temp = 0;
	//Устанавливаем напряжение ЦАП, соответствующее текущей температуре
	vAHI_DacOutput(E_AHI_AP_DAC_1, temp_levels[temp]);
}

Приём данных:
//Если пришли данные с датчика температуры
if(MYPROFILE_TEMPERATURE_CLUSTER_ID == sStackEvent.uEvent.sApsDataIndEvent.u16ClusterId)
    {
        //Считываем
        PDUM_u16APduInstanceReadNBO(sStackEvent.uEvent.sApsDataIndEvent.hAPduInst, 8, "bh", &ReceivedTempSensorData);
        //Устанавливаем температуру
        SetTemp(ReceivedTempSensorData.TempValue);
    }
else
    //Пришла команда с USB свистка
    {
        //Считываем
        PDUM_u16APduInstanceReadNBO(sStackEvent.uEvent.sApsDataIndEvent.hAPduInst, 0, "b", &Command);

        //Выдаём импульс, эмулирующий нажатие на соответствующую кнопку конвектора
        if(Command== '+')
        {
             vAHI_DioSetOutput(0, (1 << PLUS));
             vDelay(50);
             vAHI_DioSetOutput((1 << PLUS), 0);
        }
        if(Command== '-')
        {
            vAHI_DioSetOutput(0, (1 << MINUS));
            vDelay(50);
	    vAHI_DioSetOutput((1 << MINUS), 0);
        }
}


И напоследок, видео работы системы:


Полезные ссылки:
Описание операционной системы JenOS
Описание стека ZigBee Pro
Описание функций для работы с периферией JN5148
Архив с проектом
Архив со схемами

И ещё, статья опубликована 7 мая, так что всех с днём Радио!
Tags:
Hubs:
+31
Comments 25
Comments Comments 25

Articles