Arduino и модули RF433 нет передачи данных
- Войдите на сайт для отправки комментариев
Вс, 07/02/2021 - 00:15
Пытаюсь сделать метеостанцию с выносным датчиком, передача с него при помощи модулей RF433. Ну смысл не в "железе", а коде.
Написал скетч для термометра на 2х матрицах MAX7219, все работало как было задумано (Скроллировало температуру и влажность туда - сюда). Но не понравилась задержка прокрутки при передаче данных по радиочастоте. Переписал код на 2 дисплея TM1637 и тут возникла проблема - нет передачи данных.
Скетч следующий:
// Подключаем библиотеки:
#include <iarduino_RF433_Transmitter.h> // Подключаем библиотеку для работы с передатчиком FS1000A
#include <SPI.h> // подключаем библиотеку для работы с шиной SPI
#include <Wire.h> // подключаем библиотеку для работы с шиной I2C
#include <sav_button.h> // подключаем библиотеку для работы с кнопками
#include <Adafruit_HTU21DF.h> // подключаем библиотеку для работы с HTU21DF
#include "TM1637Display.h" // подключаем библиотеку для работы с TM1637 (для отладки)
#define CLK_PIN_T 6 // пин TM1637 - температура
#define DIO_PIN_T 7 // пин TM1637 - температура
#define CLK_PIN_H 8 // пин TM1637 - влажность
#define DIO_PIN_H 9 // пин TM1637 - - влажность
#define BT_PIN 2 // Пин подключения кнопки
#define RF_PIN 4 // Пин подключения передатчика
#define CHARGE_PIN 5 // Пин управления полевиком зарядки АКБ
#define RD_WAIT 5000 // Пауза в миллисекундах между опросами датчика
#define LOW_VOLT 174 // Значение низкого напряжения АКБ ~3V
#define HIGH_VOLT 242 // Значение высокого напряжения АКБ ~4.15V
Adafruit_HTU21DF HTU21 = Adafruit_HTU21DF(); // Создаём объект HTU21 для работы с библиотекой Adafruit_HTU21DF
iarduino_RF433_Transmitter radio(RF_PIN); // Создаём объект radio для работы с библиотекой iarduino_RF433, указывая номер вывода к которому подключён передатчик
TM1637Display dispTemp(CLK_PIN_T,DIO_PIN_T); // объявляем переменную для работы с TM1637 (температура)
TM1637Display dispHum(CLK_PIN_H,DIO_PIN_H); // объявляем переменную для работы с TM1637 (влажность)
unsigned long ms, ms1; // переменная таймера
float realTemp, realHum; // переменные для хранения температуры и влажности
uint8_t backTime = 0; // время отображения
uint8_t brLvl = 1 ; // уровень яркости 0-7
uint8_t maxBr = 3 ; // максимальный уровень яркости 0-7
int8_t lTmp, hTmp, hHum; // переменные для разбивки данных по разрядам
uint8_t analog_ref = DEFAULT; // переменная для хранения состояния регистра ADMUX
bool autoBr = true; // флаг включения дисплея
volatile bool brPin = false; // флаг выбора входа АЦП
volatile bool trueValue = false; // переменная для пропуска преобразования АЦП при переключении входов
volatile bool lowVoltage = false; // флаг низкого напряжения
volatile bool charging = false; // флаг включения зарядки АКБ
volatile bool charge = false; // флаг окончания зарядки АКБ
volatile uint8_t countBr = 0, countVolt = 0, countV = 0; // счетчики
volatile uint8_t voltVal = 0; // значение с АЦП для ослеживания пропадания напряжения питания
volatile uint16_t voltValTmp = 0; // значение для временного хранения и усреднения занчений с АЦП
volatile uint8_t brightVal = 0; // значение с АЦП для автоматического изменения яркости
volatile uint16_t brightValTmp = 0; // значение с АЦП для автоматического изменения яркости
const uint8_t SEG_Err[] = {
SEG_A | SEG_D | SEG_E | SEG_F | SEG_G, // E
SEG_E | SEG_G, // r
SEG_E | SEG_G}; // r
uint8_t dataT[] = {0x00, 0x00, 0x00, 0x00}; // массив для вывода данных на дисплей температуры
uint8_t dataH[] = {0x00, 0x00, 0x00, 0x00}; // массив для вывода данных на дисплей влажности
char msg[20];
SButton BUT (BT_PIN,50,1500,0,0);
#include "text_to_disp.h"
#include "ADC.h"
#include "autobright.h"
void setup(){
//Serial.begin(9600);
pinMode(CHARGE_PIN, OUTPUT); // пин включения зарядки на выход
digitalWrite(CHARGE_PIN, LOW); // низкий уровень на пине
dispTemp.setBrightness(brLvl); // устанавливаем яркость
dispHum.setBrightness(brLvl); // устанавливаем яркость
radio.begin(1000); // Инициируем работу передатчика FS1000A, 1000 bit/s
radio.openWritingPipe(5); // Открываем 5 трубу для передачи данных
if (!HTU21.begin()) {
//Serial.println("Couldn't find sensor!"); // для отладки
while(1){
dispTemp.setSegments(SEG_Err); // выводим "Err"
delay(1000);
dispTemp.clear();
dispHum.setSegments(SEG_Err); // выводим "Err"
delay(1000);
dispHum.clear();
}// end while
}// end if
BUT.begin();
ADC_init();
//realTemp = -35.45; // отладка
//realHum = 0; // отладка
}// end setup()
void loop(){
// включаем зарядку АКБ
if (lowVoltage && !charging){
digitalWrite(CHARGE_PIN, HIGH); // высокий уровень на пине
charging = true; // флаг включения зарядки АКБ
}//end if
// включаем зарядку АКБ
if (charging && !charging){
digitalWrite(CHARGE_PIN, HIGH); // высокий уровень на пине
charging = true; // флаг включения зарядки АКБ
}//end if
// выключаем зарядку АКБ
if (charge){
charge = false; // флаг окончания зарядки
digitalWrite(CHARGE_PIN, LOW); // низкий уровень на пине
}// end if
// устанавливаем уровень яркости (при автоматической)
if (millis() - ms > 250){
ms = millis();
if (autoBr) AutoBright();
}// end if
// считываем показания датчика раз в RD_WAIT mc
// передаем данные по радиоканалу
if ((millis() - ms1) > RD_WAIT){
ms1 = millis();
realTemp = HTU21.readTemperature();
//realTemp += 0.1; // отладка
hTmp = realTemp * 100 / 100; // выделяем целые градуса
lTmp = abs(int(realTemp * 100) % 100); // выделяем сотые градуса
//округляем до десятых
if (lTmp % 10 < 5) lTmp = lTmp / 10; // в меньшую сторону
else if (lTmp % 10 >= 5 && lTmp % 10 < 9 && lTmp / 10 < 9) lTmp = lTmp / 10 + 1; // если от 5 до 8 включительно, в большую
else { // если 9, в большую
lTmp = 0; // обнуляем
hTmp++; // увеличиваем десятки
}// end else if
// вводим поправочный коэфф. при темп. от 0 до 80
// влажность с учетом поправочного коэфф.
if (realTemp > 0 && realTemp < 80)realHum = (HTU21.readHumidity() + (25 - realTemp) * -0.15);
else realHum = HTU21.readHumidity();
//realHum += 0.1; // отладка
hHum = round(realHum); // округляем влажность, дробные нам не нужны...
if (hHum > 99) hHum = 99; //
String strMsg = "HTU21"; // сигнатура - данные
strMsg += "="; // разделитель
strMsg += hTmp; // температуру в строку
strMsg += "."; // десятичная точка
strMsg += lTmp; // температуру в строку
strMsg += "="; // разделитель
strMsg += hHum; // присоединяем влажность
strMsg.toCharArray(msg, strMsg.length() + 1); // переводим строку в массив символов + 1 конец строки
radio.write(&msg, sizeof(msg)); // отправляем данные из массива msg указывая сколько байт массива мы хотим отправить
//Serial.print("Температура:"); // для отладки
//Serial.print(realTemp); // для отладки
//Serial.print(" - "); // для отладки
//Serial.print(hTmp); // для отладки
//Serial.print("."); // для отладки
//Serial.println(lTmp); // для отладки
//Serial.print("Влажность:"); // для отладки
//Serial.print(realHum); // для отладки
//Serial.print(" - "); // для отладки
//Serial.println(hHum); // для отладки
//Serial.print("Яркость:"); // для отладки
//Serial.println(brightVal); // для отладки
//Serial.print("Напряжение АКБ:"); // для отладки
//Serial.println(voltVal); // для отладки
WriteToDisplay(); // выводим информацию на дисплеи
}//end if
//обработка кнопки
switch(BUT.Loop()){
case SB_NONE:
break;
case SB_CLICK:
if (!autoBr){
brLvl++;
if (brLvl > 3) brLvl = 0; // яркость ограничиваем 3
dispTemp.setBrightness(brLvl); // устанавливаем яркость температура
dispHum.setBrightness(brLvl); // устанавливаем яркость влажность
WriteToDisplay(); // изменяем яркость (нужно вывести данные для изменения яркости)
}//end if
break;
case SB_LONG_CLICK:
autoBr = !autoBr; // инвертируем флаг
break;
case SB_AUTO_CLICK:
break;
}//end switch(BUT.Loop())
}//end loop
//------------------------------------------------------
// инициализируем АЦП
//
void ADC_init(){
ACSR = 0x00; // сбрасываем регистр компаратора
ACSR |= 1 << ACD; // выключаем питание компаратора
ADCSRA = 0; // Сбрасываем регистр ADCSRA
ADCSRB = 0; // Сбрасываем регистр ADCSRB
ADMUX |= (1 << REFS1) | (1 << REFS0); // Задаем ИОН внутренний 1.1В (для АТМега328)
ADMUX |= (1 << ADLAR); // Меняем порядок записи бит, чтобы можно было читать только 8 бит регистра ADCH
analog_ref = ADMUX; // Запоминаем состояние регистра - из него мы будем формировать маску для смены входного пина вход A0
ADMUX |= (1 << MUX2) | (1 << MUX1); // Выбираем пин A6 для преобразования
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // Устанавливаем предделитель - 32 (биты 1 0 1)
ADCSRA |= (1 << ADATE); // Включаем автоматическое преобразование
ADCSRA |= (1 << ADIE); // Разрешаем прерывания по завершении преобразования
ADCSRA |= (1 << ADEN); // Включаем АЦП
ADCSRA |= (1 << ADSC); // Запускаем преобразование
}// ADC_init()
//----------------------------------------------------------
// обработчик прерывания
// по завершению преобразования АЦП
ISR(ADC_vect) {
if (trueValue) {
uint8_t result = ADCH; // ADLAR=1, Получаем 8-битный результат, 2 младшими битами пренебрегаем (ADCL)
// Если актуальный входной пин A7, то присваиваем значение соответствующей переменной
if (brPin) {
if (countBr < 10){
brightValTmp += result;
countBr++;
}// end if
else{
brightVal = brightValTmp / countBr;
brightValTmp = 0;
countBr = 0;
brPin = false; // выбираем пин
ADMUX = analog_ref; // сбрасываем вход (A0)
ADMUX |= (1 << MUX2) | (1 << MUX1); // Выбираем пин A6 для преобразования
}// end else
}// end if
else {
if (countVolt < 10){ //10 отсчетов для более стабильного результата
voltValTmp += result;
countVolt++;
}// end if
else {
voltVal = voltValTmp / countVolt; // находим среднее значание из количества отсчетов
voltValTmp = 0; // обнуляем временную переменную
countVolt = 0; // обнуляем счетчик
if (voltVal < LOW_VOLT) { // если напряжения меньше LOW_VOLT
countV++; // инкрементируем счетчик
if (countV > 4) { // считываем несколько раз, исключая ошибку
countV = 0; // обнуляем счетчик
lowVoltage = true; // устанавливаем флаг низкого напряжения
}// end if (countV > 4)
}// end if (voltVal < LOW_VOLT)
else if (voltVal >= HIGH_VOLT && charging){
countV++; // инкрементируем счетчик
if (countV > 4) { //
countV = 0; // обнуляем счетчик
charging = false; // флаг зарядки
lowVoltage = false; // флаг низкого напряжения
charge = true; // флаг окончания зарядки
}// end if (countV > 4)
}// end else if
brPin = true; // выбираем пин
ADMUX = analog_ref; // сбрасываем вход
ADMUX |= (1 << MUX2) | (1 << MUX1) | (1 << MUX0); // Выбираем пин A7 для преобразования
}// end else
}// end else
trueValue = false; // Устанавливаем флаг смены входного пина - следующее прерывание пропускаем
}// end if (trueValue)
else {
trueValue = true; // Первый раз пропускаем считывание и устанавливаем флаг на чтение в следующий раз
}// end else
}// ISR(ADC_vect)
//----------------------------------------------
// авторегулировка яркости
void AutoBright(){
uint8_t brLvlAuto = 0;
if (brightVal <= 215){
brLvlAuto = 0;
}// end if
else if (brightVal > 215 && brightVal <= 225){
brLvlAuto = 1;
}//end else if
else if (brightVal > 225 && brightVal <= 235){
brLvlAuto = 2;
}//end else if
else if (brightVal > 235){
brLvlAuto = 3;
}//end else if
dispTemp.setBrightness(brLvlAuto); // устанавливаем яркость
dispHum.setBrightness(brLvlAuto); // устанавливаем яркость
}// end AutoBright()
//==== Выводим данные на дисплеи ====
void WriteToDisplay(){
if (hTmp < 0) dataT[0] = SEG_G; // 1 символ
else dataT[0] = 0;
dataT[1] = dispTemp.encodeDigit(abs(hTmp) / 10); // 2 символ
dataT[2] = dispTemp.encodeDigit(abs(hTmp) % 10) | SEG_H; // 3 символ
dataT[3] = dispTemp.encodeDigit(lTmp); // 4 символ
dispTemp.setSegments(dataT); // выводим на 1 дисплей
dataH[0] = dispTemp.encodeDigit(hHum / 10); // 1 символ
dataH[1] = dispTemp.encodeDigit(hHum % 10); // 2 символ
dataH[2] = SEG_A |SEG_B | SEG_F |SEG_G; // 3 символ (0 - вверху)
dataH[3] = SEG_C | SEG_D | SEG_E | SEG_G; // 4 символ (0 - внизу)
dispHum.setSegments(dataH); // выводим на 2 дисплей
}// end WriteToDisplay()
А вот из примера этой библиотеки код работает, не могу понять, что у меня не так? Причем даже заливаю его в 2 ардуины, меняю местами передатчики, ставлю оба на одну ардуину...
// ПРИМЕР ДЛЯ ПРОВЕРКИ РАБОТЫ ПРИЁМНИКА (MX-RM-5V) И ПЕРЕДАТЧИКА (FS1000A) НА ОДНОЙ ПЛАТЕ ARDUINO
//
// Приёмник подключён к выводу D3
// Передатчик подключён к выводу D4
//
// Алгоритм: Значение массива i отправляется передатчиком, принимается приёмником в массив j и выводится на монитор
// Если приёмник и передатчик работают, то на монитере будут появляться строки Hello World с указанием номера трубы, по которой они получены
//
// Подключаем библиотеку:
#include <iarduino_RF433_Transmitter.h> // Подключаем библиотеку для работы с передатчиком FS1000A
#include <iarduino_RF433_Receiver.h> // Подключаем библиотеку для работы с приёмником MX-RM-5V
// Объявляем объекты, переменные и массивы:
iarduino_RF433_Transmitter radioTX(4); // Создаём объект radioTX для работы с библиотекой iarduino_RF433, указывая номер вывода к которому подключён передатчик
iarduino_RF433_Receiver radioRX(3); // Создаём объект radioRX для работы с библиотекой iarduino_RF433, указывая номер вывода к которому подключён приёмник (можно подключать только к выводам использующим внешние прерывания)
char i[]="Hello World!!!"; // Создаём массив для передачи данных
char j[20]; // Создаём массив для приёма данных
uint8_t k; // Создаём переменную, в которую получим номер трубы, по которой приняты данные
void setup(){
// ===============================
Serial.begin(9600); // Инициируем передачу данных по последовательному порту на скорости 9600 бит/сек
// =============================== ПЕРЕДАТЧИК
radioTX.begin(1000); // Инициируем работу передатчика FS1000A на скорости 1 кбит/сек
// radioTX.setDataRate(i433_1KBPS); // Скорость передачи данных можно изменить вызвав данную функцию в любом месте кода. Параметры (i433_5KBPS, i433_4KBPS, i433_3KBPS, i433_2KBPS, i433_1KBPS, i433_500BPS, i433_100BPS), i433_1KBPS - 1кбит/сек
radioTX.openWritingPipe(5); // Открываем 5 трубу для передачи данных (передатчик может передавать данные только по одной из труб: 0...7). Если повторно вызвать функцию openWritingPipe указав другой номер трубы, то передатчик начнёт передавать данные по вновь указанной трубе
// =============================== ПРИЁМНИК
radioRX.begin(1000); // Инициируем работу приёмника MX-RM-5V (в качестве параметра можно указать скорость ЧИСЛО бит/сек, тогда можно не вызывать функцию setDataRate)
// radioRX.setDataRate(i433_1KBPS); // Скорость приёма данных можно изменить вызвав данную функцию в любом месте кода. Параметры (i433_5KBPS, i433_4KBPS, i433_3KBPS, i433_2KBPS, i433_1KBPS, i433_500BPS, i433_100BPS), i433_1KBPS - 1кбит/сек
radioRX.openReadingPipe (5); // Открываем 5 трубу для приема данных (если вызвать функцию без параметра, то будут открыты все трубы сразу, от 0 до 7)
// radioRX.openReadingPipe (2); // Открываем 2 трубу для приёма данных (таким образом можно прослушивать сразу несколько труб)
// radioRX.closeReadingPipe(2); // Закрываем 2 трубу от приёма данных (если вызвать функцию без параметра, то будут закрыты все трубы сразу, от 0 до 7)
radioRX.startListening (); // Включаем приемник, начинаем прослушивать открытую трубу
// radioRX.stopListening (); // Выключаем приёмник, если потребуется
}
void loop(){
// =============================== ПРИЁМНИК
if(radioRX.available(&k)){ // Если в буфере имеются данные принятые приёмником, то получаем номер трубы в переменную k и ...
radioRX.read(&j, sizeof(j)); // Читаем данные в массив j и указываем сколько байт читать
Serial.print(j); // Выводим полученные данные на монитор
Serial.println((String)" (Pipe="+k+")"); // Выводим номер трубы, по которой эти данные получены. Так можно определить, от кокого передатчика они получены
}
// =============================== ПЕРЕДАТЧИК
radioTX.write(&i, sizeof(i)); // Отправляем данные из массива i указывая сколько байт массива мы хотим отправить
delay(200); // Ждем 200мс
}
Отвечаю себе сам - отключил прерывания в момент передачи, все стало нормально.