[Из песочницы] Превращение обычного электрического конвектора в беспроводной
Предыстория Мой частный дом отапливается при помощи электрических конвекторов. Всё в них хорошо: и лёгкость монтажа и автоматическое управление температурой и режим день/ночь и режим 50% мощности. Но есть и минус — датчик температуры воздуха закреплён прямо на корпусе конвектора, поэтому нагревается и остывает вместе с ним. Из-за этого конвектор включается/выключается гораздо чаще, чем хотелось бы, невозможно установить желаемую температуру воздуха в комнате, т.к. реальная будет ниже градусов на 5, да и на надёжности постоянные переключения реле сказываются негативно. Можно было, конечно, удлинить датчик температуры и отнести его подальше, но это не наш метод. Т.к. я давно занимаюсь беспроводными технологиями и есть наработки, то решил оснастить конвектор беспроводным датчиком температуры. Это позволит разместить его в любом месте комнаты, не тянуть провода, а если нужно, использовать не один, а несколько датчиков и рассчитывать среднюю по комнате температуру. (Под катом картинки)Переделка Как я уже сказал, я плотно занимаюсь беспроводкой и имею по этой теме наработки. Для мониторинга различных датчиков как нельзя лучше подходит ZigBee, а качестве ZigBee микроконтроллера я уже давно использую JN5148 фирмы Jennic (куплена NXP). Для быстрого изготовления макетов я сделал себе несколько модулей с этим микроконтроллером.Схема модуля (кликабельна): В схему модуля включен сам микроконтроллер, внешняя память программ для него (обязательный компонент для 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, 0×0000, //Адрес получателя (в данном случае координатора) 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); } } И напоследок, видео работы системы:[embedded content]
Полезные ссылки: Описание операционной системы JenOSОписание стека ZigBee ProОписание функций для работы с периферией JN5148Архив с проектомАрхив со схемами
И ещё, статья опубликована 7 мая, так что всех с днём Радио!