Драйвера радиомодулей (передатчик)
Напомню, что драйвер это такая программа, которая преобразует нули и единицы устройства в логически цельную информацию, которую может использовать человек.
Например, устройство – мышь нулями и единицами сообщает только куда и насколько оно сместилось в последний раз. А драйвер вначале помещает курсор мыши в середину экрана, а потом, считая суммы перемещений, передвигает по экрану этот курсор и следит за тем, чтобы курсор не уехал за границу экрана.
В данный момент, я считаю, что все устройства этой серии:
https://amperkot.ru/spb/catalog/radiomodul_nrf24l01_pa__lna_s_antennoy-24118355.html
… брак. А сложная настройка c SPI протоколом и прочим нужна только для того, чтобы спрятать этот неприятный факт.
Ниже я попытаюсь объяснить, почему я так думаю.
Разбирать я буду драйвер для этого устройства:
https://amperkot.ru/spb/catalog/radiomoduli_433mhz_peredatchik_mxfs03v_i_priemnik_mx05v-23869814.html
Его минус – очень небольшая дальность, а плюс – простота и естественность работы, которые, в числе прочего, позволяют понять, какие проблемы возникают с физической частью процесса, т.е с передачей информации по воздуху.
Класс – объект, в котором собран список той самой логически доступной информации включает в себя… очень много чего. Потому только часть списка из файла RCSwitch.h:
class RCSwitch {
public:
RCSwitch();
void send(unsigned long Code, unsigned int length); // Отправка данных
void enableReceive(); // Проверка получения информации
void disableReceive(); // Обнуление считанного значения
unsigned long getReceivedValue(); // Получение информации, прочитанной приёмником
void setPulseLength(int nPulseLength); // Установка длины импульса
void setRepeatTransmit(int nRepeatTransmit); // Установка кол-во повторов отправки
// Установка отклонения длины полученного сигнала:
void setReceiveTolerance(int nPercent); void setProtocol(int nProtocol);
private:
void send0(); // Отправка ”0”
void send1(); // Отправка “1”
void sendSync(); /// Отправка паузы после передачи информации
void transmit(int nHighPulses, int nLowPulses);
static void handleInterrupt(); // Обработчик прерывания приходя сигнала на приёмник
// Проверка полученных данных на соответствие какому-то протоколу:
static bool receiveProtocol1(unsigned int changeCount);
static bool receiveProtocol2(unsigned int changeCount);
static bool receiveProtocol3(unsigned int changeCount);
int nTransmitterPin;
int nPulseLength; // Базовая длина сигнала
int nRepeatTransmit; // Сколько раз отправлять информацию
char nProtocol; // Номер протокола передачи
static int nReceiveTolerance; // Допустимое отклонение длины полученного сигнала
static unsigned long nReceivedValue; // Полученное приёмником значение
// Массив задержек между включением и выключением сигнала на приёмнике:
static unsigned int timings[RCSWITCH_MAX_CHANGES];
};
Поскольку тут приём и передача данных, то основные используемые тут функции отправки (send()) и получения информации (getReceivedValue()). Понятно и то, что внутри отправляемое десятичное число преобразуется в его двоичный вид. И эти нули с единицами будут последовательно отправляться в эфир функциями send0() и send1().
Но вначале напишу про 4 параметра настройки внутренней работы.
nPulseLength – базовая длина сигнала. Для первого протокола она 350 микросекунд.
nRepeatTransmit – сколько раз в эфир передавать информацию. По – умолчанию 10. Т.е 10 раз будет послана цифра в надежде на то, что хотя бы один раз приёмник сможет её опознать в фоновых шумах.
nProtocol – номер протокола. В этом драйвере их три. Зачем они не очень понятно. Скорость передачи у них одинаковая. Хотя обычно замедляют передачу для того чтобы увереннее идентифицировать полученные данные. Может быть, так можно различать сигналы от разных устройств.
nReceiveTolerance – процент допустимого отклонения пришедшего сигнала от того, что было послано. По – умолчанию – 60%. Т.е если нулём считается длительность сигнала в 350 микросекунд, то программа решит, что получен ноль, если придёт сигнал длительностью от 140 до 560 микросекунд. Такая дикая неточность компенсируется отправкой кучи нулей и единиц и промежутков между ними. Минимум 12 цифр последовательных промежутков времени должны соответствовать этой погрешности в 60%.
Я подключила к приемнику светодиод и обнаружила, что он мигает почти постоянно. Можно перевернуть приёмник и уже из-за этого он выловит какие-то шумы. Вся очистка информации тут переложена на программный уровень. Приёмники могут быть лучше или хуже, но у всех возникает необходимость какой-то чистки информации. Т.е этим на каком-то уровне (электронном, программном) будут заниматься приёмник любого радиомодуля)
Несколько функций из RCSwitch.cpp:
Установка протокола:
void RCSwitch::setProtocol(int nProtocol) {
this->nProtocol = nProtocol;
if (nProtocol == 1){
this->setPulseLength(350);
}
else if (nProtocol == 2) {
this->setPulseLength(650);
}
else if (nProtocol == 3) {
this->setPulseLength(100);
}
}
Присваивается значение внутренней переменной номер протокола (this->nProtocol) и задаётся величина базового импульса.
Передача сигнала:
void RCSwitch::send(char* sCodeWord) {
for (int nRepeat=0; nRepeat<nRepeatTransmit; nRepeat++) {
int i = 0;
while (sCodeWord[i] != '\0') {
switch(sCodeWord[i]) {
case '0':
this->send0();
break;
case '1':
this->send1();
break;
}
i++;
}
this->sendSync();
}
}
Тоже все просто. В цикле последовательно отправляются в эфир биты информации до пока не встретиться символ конца строки ('\0'). Только повторюсь, что отправка обычно идёт 10 раз (цикл по nRepeat).
Отправка «0»:
void RCSwitch::send0() {
if (this->nProtocol == 1){
this->transmit(1,3);
}
else if (this->nProtocol == 2) {
this->transmit(1,2);
}
else if (this->nProtocol == 3) {
this->transmit(4,11);
}
}
Тут видно, чем отличаются протоколы – разными параметрами функции transmit(), которая использует эти значения для длительности сигнала и его отсутствия:
void RCSwitch::transmit(int nHighPulses, int nLowPulses) {
digitalWrite(this->nTransmitterPin, HIGH);
delayMicroseconds( this->nPulseLength * nHighPulses);
digitalWrite(this->nTransmitterPin, LOW);
delayMicroseconds( this->nPulseLength * nLowPulses);
}
Вот и вся работа с передатчиком. Его надо включить, подождать какое-то количество микросекунд (базовый импульс * параметр функции), потом выключить и опять подождать сколько-то микросекунд. Когда передатчик включен, в эфир идут радиоволны, когда выключен – не идут. Длительность того и другого будет оценивать приёмник.
Отправка ”1”:
void RCSwitch::send1() {
if (this->nProtocol == 1){
this->transmit(3,1);
}
else if (this->nProtocol == 2) {
this->transmit(2,1);
}
else if (this->nProtocol == 3) {
this->transmit(9,6);
}
}
Для первого протокола «0» это сигнал 350*1 микросекунд и его отсутствие 350*3 микросекунд, а единица это сигнал 350*3 микросекунд и пауза 350*1 микросекунд. Для остальных протоколов другие длительности, но действия (включить и выключить) те же
|