Пользователь
0,0
рейтинг
28 мая 2014 в 18:08

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

…на ультрадешевых радиомодулях по 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-биты по-умолчанию.
@ks0
карма
13,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (17)

  • +3
    Ещё есть такие же модули, но на другие частоты, например на 315мгц, там эфир значительно чище.
    • +1
      В России ЕМНИП, диапазон 315 МГц не разрешен к применению для бытовых маломощных радиоустройств, соответственно, это означает что серых устройств у нас еще пока меньше, чем белых — так как из Китая 315-мегагерцовые радиогаджеты везут на ура.
      В любом случае, конечно, если взять на себя риски, припарковаться под знаком Остановка запрещена всегда физически проще :)
      • 0
        На 433мгц больше мешаются не маломощные устройства, типа сигнализаций и прочих беспроводных девайсов, сколько радиолюбители из ада, которые пуляют в эфир 50-100Вт на этих частотах. Ещё хуже, если это кросс-бенд, который переодически работает без умолку, да ещё и установлен неподалёку. В моём случае эфир на 433 выглядит как то так.

        Согласен, не правильно решать проблему подобным образом, лучше разобраться с нормальными радио-модулями, которые уже умеют делать избыточное кодирование, контрольную сумму и подтверждение доставки, хотя уйти на запрещённые частоты действительно проще, особенно учитывая то, что при питании от 5В у этих модулей мощность получается меньше 5мВт, а на этих частотах разве что ТВ вещание ведётся, да другие китайские модули общаются, вряд ли оно кому-нибудь помешает.
        • +1
          Ну для передачи одного байта температуры раз в 15 минут в бытовых целях ставить двунаправленные модули типа NRF24 — имхо перебор. Хотя стоят они те же самые копейки, что и эта 433-МГц пара.
          Однако КМК, смысл этой конструкции прежде всего образовательный. Полученный опыт в создании самодельного радиостека передачи данных, пусть даже в примитивном виде, достаточно ценен. Если грамотно все спроектировать прежде всего программно, можно и на таких модулях mesh-сеть построить. А в готовых типа NRF уже все внутри зашито.
          • 0
            Да и NRF+AtTiny13+какой-нибудь датчик (и лучше не один) — удовольствие для истинных ценителей. Тоже поначалу делал на Tiny13 внешние датчики, но потом оказалось, что можно сильно не париться и взять AtMega328. Даже при отправке данных раз в 10 секунд, мега будет делать это пока солнце не потухнет (или сама не сдохнет, что более вероятно), а если всё таки потухнет, то ещё несколько месяцев сможет поработать на ионисторах\аккумуляторах.
      • 0
        Все может быть. Однако в Леруа Мерлен мне повезло купить беспроводной звонок именно на 315 МГц. Вообще-то мне нужна была только более-менее красивая радиокнопка, причем на 433 МГц, так что можете представить мое удивление.
  • 0
    Главный недостаток сверхрегенеративных приёмников — то, что они шумят на той частоте, на которую настроены (плюс-минус полоса пропускания). Я делал схему со второй гармоникой (антенна настроена на удвоенную частоту генерации), и то немного помех вплотную выдавало.
  • +3
    Есть хорошая библиотека VirtualWire которая решает проблемы с шумом, но в один килобайт флеша Attiny13 она вероятно не влезет. Если не влезет, можно использовать контрольную сумму, а для исключения вероятности принятия мусора (по теореме о обезьянах) необходимо отправлять не менее двух-трёх байт информации плюс их контрольную сумму. У вас есть упоминание о CRC8, но вероятно вычисленное для одного байта, так что, вероятность словить валидный шум велика. Например, для получения температуры с нескольких передатчиков, можно использовать последовательность байт: 1) номер передатчика, 2) порядковый номер сообщения, 3) температура, 4) CRC8. Ну и конечно для надёжности отправить это сообщение 100 раз. Теперь получить не валидное сообщение практически нереально — во первых, вероятность получить сообщение из четырёх байт и чтобы четвёртый байт сошёлся с CRC8 первых трёх байт, исчезающе мала, но вспомним про обезьян, тогда во вторых у нас есть номер передатчика, передатчиков у нас скажем 17 штук, если номер не соответствует ни одному из них — значит сообщение мусорное, в третьих у нас есть порядковый номер сообщения и мы вполне можем отслеживать порядковые номера для каждого передатчика, причём даже отслеживать номера пропущенных сообщений (ориентируясь на время), так что, если порядковый номер не соответствует ожидаемому — сообщение мусорное. Ну и наконец можем ориентироваться на температуру, если в июне месяце мы получаем с датчика температуру -91, то либо сообщение мусорное, либо отслеживать температуру уже не имеет ни какого смысла. Итого: добавив пару байт информации и контрольную сумму, мы сводим на нет вероятность получения мусора.
  • +1
    Огромное спасибо за статью! Купил давненько пару приемник-передатчик таких, валяются уже пол-года, один раз полез смотреть как с ними работать, но не довел до конца. Попробую теперь второй заход =)
  • +2
    Такие приемники имеют очень широкую полосу пропускания и АРУ — в итоге всегда что-нибудь находится, что раскачивает выход приемника. Для того, чтобы он начал слушать именно вас, перед посылкой данных обычно дают преамбулу — например 4 импульса меандра с периодом 1-2 мс. Эта посылка загрубляет АРУ в приемнике, и дальше его выход начинает переключаться в соответствии с тем, что передаете вы. Кроме того, за преамбулу удобно зацепиться программно при обработке пакетов приемной стороной.
    И еще — Манчестерское кодирование тут не очень хорошо, так как (а) тракт не дает гарантию неизменной скважности сигнала и (б) скважность, равная двум слишком энергетически затратна для автономного передатчика. Лучше давать узкие опорные импульсы на каждый бит, и заполняющие между ними при передаче, например, единиц — посмотрите физические протоколы keeloq или просто просниффайте обмен RC-switch или бытовых термометров.
    • 0
      Да, на RC-switch все проще — не надо изобретать велосипед. У меня беспроводной датчик влажности/температуры на attiny13 его и использует + ещё может принимать команды от радиопультов.
      Ошибок в передаче данных на RC-switch вообще не встречал, кроме случая, когда сам накосячил и код одной температуры совпал с кодом включения света в комнате :). Вообще RC-switch передает код 3 раза насколько я помню и этого хватает…
  • 0
    Допустим у меня два передатчика? Три передатчика? Как разделять? Захват и контроль среды? Все передатчики (этой серии) работают в мире на одной частоте? Не увидел никаких регуляторов на картинках.
    • 0
      Работают на одной частоте. Проблема нескольких приемников и передатчиков решается программно индивидуальными идентификаторами или разными протоколами.
    • 0
      Да, все на одной частоте. Некоторые ДУ, так вообще ужас — имеют приемник с полосой пропускания 10Мгц — передатчик под них вообще нет необходимости стабилизировать. Так же работает автосигнализация, в старых брелках я вообще не видел никаких элементов стабилизации — схема как радиожучек на 1-м транзисторе и кусочек дорожки в качестве контура… стабильности никакой а ведь работает сволочь.

      Когда используется 2-3 передатчика то нужно избегать коллизий — самый простой способ это задать период передачи разным для каждого из передатчиков — одному раз в 10 минут, другому в 14 минут, третьему 8 минут и т.д. возможно даже варьировать эти интервалы на некоторую случайную величину, главное чтобы они не были кратными.
      • 0
        Кстати да, если передатчиков несколько, то желательно чтобы интервал между передачами и интервал между повторами сообщения в передаче был случайной величиной. В данном случае можно написать генератор псевдослучайных чисел для которого шумом будут данные с ацп, в данном случае температура.
    • 0
      Shrim выше хорошо написал — передавать несколько байт, плюс контрольная сумма.
  • 0
    А Вы не планировали пойти дальше в реализации и добавить передачу данных на облачные сервисы через gsm / wifi / eth канал?
    Нам для Каталога подобные девайсы и участники нужны и востребованы.

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