Термометр с беспроводной передачей данных

от автора

…на ультрадешевых радиомодулях по 5 долларов за пучок.

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


Передатчик с амплитудной модуляцией на частоту 433 МГц, частота стабилизирована ПАВ. Передатчик близкий родственник всяким одно-, двух-, трехтранзисторным жучкам и маячкам, которые делали все, кто когда-либо занимался радиотехникой (много схем на vrtp.ru). Питание на передатчик допускается до 12 В. Вход data фактически открывает транзистор и запускает генерацию, при низком уровне на входе генерация прекращается. Соответственно выставляя Hi и Lo на входе с определенной частотой мы можем передавать данные.

Приемник относится к классу сверхрегенеративных. Нынче такая схема практически полностью вышла из употребления и заменена супергетеродинными, но десятилетия назад на ней выпускали даже промышленные приемники для прослушивания радиоэфира.
Сверхрегенерат это дальнейшее развитие приемника прямого усиления. Во входной контур за счет положительной обратной связи в прерывистом режиме, на частотах десятки кГц, вносится энергия источника питания в виде колебаний той же частоты, на которую настроен контур. Этим компенсируются потери в контуре и улучшается его добротность. На выходе радиомодуля стоит компаратор, выдающий на линию Data Hi или Lo.


Достоинства сверхрегенеративных приемников:

  • Простота и дешевизны конструкции
  • Очень высокая чувствительность
  • Широкая полоса пропускания
  • Автоматическая регулировка усиления

Недостатки, вытекающие из достоинств:

  • Широкая полоса пропускания, в которую всегда что-то попадает. Величина порядке единиц МГц.
  • Высокая чувствительность, хоть передатчик включен, хоть выключен, на выходе приемника всегда есть какой-то шум (см. далее).
  • Сложность в настройке, т.к. сверхрегенеративный каскад выполняет одновременно две-три функции (усилитель, генератор, детектор). Но нам-то уже настраивать ничего не надо.

На обоих модулях оставлено отверстие под антенну. После того как рядом лежащие модули, были наконец разнесены, я понял, что без антенны делать нечего и прицепил к модулям в качестве антенн под руку попавшие крокодильчики с проводками. В таком виде дальность действия собранных устройств составила свыше 10 метров по прямой при питании передатчика от Кроны 9В. Напоминаю, что в качестве антенны может использоваться кусок провода равный примерно ¼ или ½ длины волны, в нашем случае будет 17 или 34 см. Общий провод (GND), если есть возможность, лучше подключить к противовесу, которым может быть металлический корпус или другой кусок провода, направленный в противоположную от антенны сторону.

Эта картинка при удалении передатчика на 10 метров. Несмотря на то, что мощность сигнала визуально сильно уменьшилась, приемный модуль данные получает уверенно.

Теперь непосредственно к передаче данных. Как же наивен я был когда подключил передатчик к UART на одном контроллере, и к UART на другом. Я увидел переданный байт, хотя он и был в окружении десятков лишних! Посмотрите следующую картинку, приемник всегда что-то ловит. И только когда есть устойчивый периодический(!) сигнал, он четко проявляется во всем входящем мусоре. UART передает данные побитно, без разделения каждого бита, если передается 0x00, то это будет низкий уровень на всем протяжении передачи байта, т.е. передатчик будет просто отключен, а если передается 0xff, то это будет высокий уровень, но и при этом приемник вскоре начнет видеть в нем неоднородности и выдавать случайную последовательность.

Т.е. я включаю передатчик, сажу линию данных на питание, передатчик во всю мощь передает несущую, а на приемнике я вижу несколько единиц, а потом мусор 11111111110100101001. Отпускаю линию данных, появляются нули и снова мусор 000000000110101010. Вывод: приемнику нужны периодические перепады уровня.

И такой код есть у нас. Манчестерский. В манчестерском коде биты кодируются перепадом из низкого уровня вверх (пусть будет 1), или из верхнего вниз (пусть будет 0). Соответственно перед началом передачи каждого бита уровень должен быть выставлен в начальное положение, а в середине измениться. Опытным путем выяснено, что оптимальная скорость передачи данных примерно 5-10-20 килобит в секунду. Это позволит добиться достаточно устойчивого приема, и будет использоваться в прототипе устройства, которое я и начну далее описывать.

Традиционно это будет термометр, но теперь с беспроводной передачей данных. По условию задачи на грядке под пленкой лежит передатчик, а в доме приемник, который, так сказать собирается данные с датчиков, ну с одного датчика, и строит гистограмму. Т.е. если я вижу, что ночью там было 0 градусов, я понимаю от чего вся рассада замерзла и остается только отыскать виновника.

Передатчик собран на контроллере Attiny13, с термодатчиком LM335. Термодатчик аналоговый, выдает 100мВ на 1К. Источник опорного напряжения МК используется внутренний 1,1В. Т.к. значения с датчика свыше 3В, то данные на АЦП передаются через делитель. Контроллер просыпается раз в 30 минут, а в режиме отладки раз в 4 секунды, подает питание на термодатчик, включает передатчик, и в течение примерно 1-2 секунд передает текущую температуру тысячу раз. Да! Именно тысячу раз подряд, т.к. даже со 100 подряд передачами я пропускал данные в некоторых сеансах. Современные города, с десятками автомобильных сигналок на 433МГц не самое спокойное место.
Температура передается в виде одного единственного байта с манчестерским кодированием.

0b10101011 на стороне приемника превратиться в 21°С.

Передача байта

void send1() { 	PORT_TX&=~B_TX; 	_delay_us(DELAY_US); 	PORT_TX|=B_TX; 	_delay_us(DELAY_US); }  void send0() { 	PORT_TX|=B_TX; 	_delay_us(DELAY_US); 	PORT_TX&=~B_TX; 	_delay_us(DELAY_US); }  void send(char c) { 	send1(); 	for(uint8_t i=128; i>0; i>>=1) 	{ 		if(c & i) send1(); 		else send0();			 	} 	PORT_TX&=~B_TX; } 

Засыпание после передачи данных до поступления прерывания от сторожевого таймера

if(debug_mode) { 	cli(); 	wdt_reset(); 	WDTCR=1<<WDCE | 1<<WDTIE; 	WDTCR=1<<WDP3 | 1<<WDTIE;	// 4 секунды 	sei(); 	MCUCR|=1<<SE | 1<<SM1;		// Режим сна Power-down 	sleep_cpu(); } else for(uint8_t i=0; i<SLEEP_TIME; i++) { 	cli(); 	wdt_reset(); 	WDTCR=1<<WDCE | 1<<WDTIE; 	WDTCR=1<<WDP3 | 1<<WDP0 | 1<<WDTIE;	// 8 секунд 	sei(); 	MCUCR|=1<<SE | 1<<SM1; 	sleep_cpu(); } 

Питание осуществляется через ту же самую Крону 9В, замеренное китайским тестером потребление в активном режиме 6мА, в режиме сна 0,2мА. Не могу не уделить внимание схеме стабилизатора напряжения. Контроллер же от 9В просто умрет, а 5В и менее будет мало для передатчика, поэтому понадобился преобразователь питания. Повышающий импульсный, возможно стал бы вносить помехи, и я с ним экспериментировать не стал. Проще было поставить понижающий линейный, типа классической 7805. Но неожиданно я увидел, что у нее собственное потребление 6 мА. Это значит, что пока контроллер спит, она сама сожрет всю батарейку. Погуглив, я нашел малопотребляющие преобразователи LP2950, MCP1700…. Но поблизости их не было, а тут подвернулась вот такая схема на паре транзисторов 7002. Номиналы резисторов пришлось уменьшить даже не до мегаом, а до сотен килоом, иначе выходное напряжение никак не поднималось выше 3 с небольшим вольт. Теперь оно стало примерно 4,6В, что вполне комфортно для контроллера.

Схема передатчика, как не надо делать!

Приемник собран на базе Attiny85 с дисплеем от Nokia 5110. Верхняя строка отведена под индикацию текущей температуры, нижняя показывает наибольшую и наименьшую. Центральная область 84х32 отображает гистограмму от 0 до 32С. Все что свыше или ниже просто обрезается.

Прием ведется непрерывно. При переходе на линии приема с Lo в Hi делается попытка принять 8 бит данных. В первоначальном варианте прием бит выглядел примерно так: т.к. первый переход с Lo в Hi это середина стартового бита 1, то от него отмеряется время немногим меньше длительности бита, фиксируется значение на входе, а чуть погодя, когда наступает время прихода второй половины бита, снова фиксируется значение на порту. Таким образом определяется переход к верхнему или нижнему уровню. Дальше видно, что это сработало плохо.

Схема приемника

Для отсеивания мусора я ожидаю три посылки подряд, и если они совпадают, то это и будет текущая температура. При этом она отображается и на 0,5 сек. отображается индикатор приема. Раз в 15 минут гистограмма сдвигается, а если в течение 45 минут не было получено новой температуры, то индикация текущей температуры гаснет, а гистограмма продолжит сдвигаться с пустой колонкой. Если бы char в настройках GCC не оказался unsigned, то все заработало бы с пол пинка, но странные глюки до ночи выклевывали мне мозг. А по утру включив приемник, при выключенном передатчике, я в течение получаса словил помеху, получив -91 градус.
Тогда я не растерялся и сделал ожидание четырех посылок подряд. И словил ложную посылку опять в течение получаса. Я сделал ожидание двух подряд одинаковых байт и подтверждение их CRC8. И через часок опять словил ложную температуру, все согласно теореме о бесконечных обезьянах.
«Полдюжины обезьян, будь у них пишущие машинки и одна-другая вечность в запасе, создадут все книги, которые хранятся сегодня в Британском музее»
И, наконец, вернувшись к тройной посылке, но изменив алгоритм приема отдельных битов, я вроде успокоился, хотя понимаю, что обезьяны не дремлют.

В измененном алгоритме при приеме каждого бита делается несколько промежуточных чтений порта и если происходит несвоевременный переход уровней, то прием прерывается.

Было

int8_t read_bit() { 	_delay_us(160);			// ожидаем середины бита 	char rx=PIN_RX & B_RX;	// после этого ждем переход 	for(uint8_t i=0; i<6; i++) 	{ 		if((PIN_RX & B_RX) != rx) 		{			 			if(rx) return 0; 			else return 1; 		} 	} 	return -1; } 

Стало

int8_t read_bit() { 	char rx=PIN_RX & B_RX; 	for(uint8_t i=0; i<5; i++) 	{ 		if((PIN_RX & B_RX) != rx) return -1;	// несвоевременный переход 	} 	_delay_us(40);	// здесь граница бит и может быть переход  	rx=PIN_RX & B_RX; 	for(uint8_t i=0; i<5; i++) 	{ 		if((PIN_RX & B_RX) != rx) return -1;	// несвоевременный переход 	} 	 	rx=PIN_RX & B_RX;  	for(uint8_t i=0; i<6; i++)	// здесь ожидаем переход вверх или вниз 	{ 		if((PIN_RX & B_RX) != rx) 		{			 			if(rx) return 0; 			else return 1; 		} 	} 	return -1; } 

Несмотря на то, что для серьезного применения в качестве модулей передачи данных эти радиомодули вряд ли годятся, но для сбора статистики, с последующие обработкой и отбраковкой явно недостоверных данных вполне.

Т.к. устройства носят лишь демонстрационную функцию, то печатные платы я намеренно не выкладываю, тем более их стоит переразвести для исправления багов.
Исходный криворукий Си-код и hex файлы в архиве. Fuse-биты по-умолчанию.

ссылка на оригинал статьи http://habrahabr.ru/post/224459/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *