Перейти к публикации
iT4iT.CLUB
Neon

NRF24L01 на ESP8266

Рекомендованные сообщения

в теме про метеостанцию писал про датчик с передатчиком на NRF24L01 и возможность использования с метеостанцией

простой код приемника на esp

// https://aterlux.ru/article/nrf24l01p

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line, для 20x4 - 0x3F

//#include <SPI.h>
//#include "nRF24L01.h"
#include "RF24.h"
int32_t data[5];
RF24 radio(99, 15); // CE, CSN, 99 - это заглушка, чтобы освободить один пин, если передача не предполлагается то можно CE просто подтянуть резистором к питанию, иначе нужно указать вывод

void setup(void)
{
  lcd.init();                  
  lcd.backlight();
  
  radio.begin();
  radio.setAutoAck(true);                // radio.setAutoAck(true);   включение и ли отклучение автоподтверждения
  radio.setChannel(100);
  radio.setDataRate     (RF24_2MBPS);   // скорость обмена RF24_2MBPS, RF24_1MBPS, RF24_250KBPS - максимальная чувствительность на 250 кбитс, NRF24L01 без плюса 250 не умеет
  radio.setPALevel      (RF24_PA_MAX);    // уровень мощности RF24_PA_MIN -18dBm, RF24_PA_LOW -12dBm, RF24_PA_HIGH -6dBM, RF24_PA_MAX 0dBm
  radio.openReadingPipe(1, 0xAABBCCDD11LL); 
  radio.startListening();
}


void loop(void) {
  
  if(radio.available()){                                         // Если в буфере имеются принятые данные, то получаем номер трубы, по которой они пришли, по ссылке на переменную pipe
        radio.read( &data, sizeof(data)); }                      // Читаем данные в массив data и указываем сколько байт читать

     lcd.setCursor(0,0);
     lcd.print("Temp ");
     lcd.print(float(data[3])/100, 2); lcd.print(" "); lcd.print((char)223); lcd.print("C");

     lcd.setCursor(0,1);
     lcd.print("Battery ");
     lcd.print(float(data[0])/1000,3); lcd.print(" v");

     lcd.setCursor(0,2);     // на некоторых 4 строчных дисплеях в 3 и 4 строке ноль сдвинут на 4 символа вправо, поэтому нулевой символ это -4
     lcd.print("Packet N ");
     lcd.print(data[1]);  
        
     delay (100);
}

 

 

код передатчика

 


//  тема на форуме - http://forum.ixbt.com/topic.cgi?id=48:12460&r=lYjQSTixmu#2
//  библиотека для датчика BME280 - https://github.com/sergeyastakhov/BME280 (чтобы не было конфликтов с другими библиотеками, желательно ее разместить в папке скетча)
//  потребление всего модуля в power save - 2.0 мкА(из них NRF24 - 0.9 мкА), 1.1 мкА - atmega168
//  обязательно параллельно батарейке доп емкость около 2200мкф 6.3 в - падение при передаче 10 мВ, ток утечки конденсатора после подформовки менее 100 нА, - http://forum.ixbt.com/topic.cgi?id=48:11468:3073#3073
//  измерение напряжение батарейки с помощью измерения питания atmega
//  еще можно питать атмегу напрямую от лития и измерять ее напряжение, а NRF и BME280 питается от стабилизатора 3.3 вольта с маленькой утечкой, например mcp1700-3302, ток утечки около 1.5 мкА
//  I2C преобразователь уровней не нужен, главное чтобы подтягивающие резисторы I2C питались от 3.3 вольт
// проблема с вотчдогом - http://arduino.ru/forum/apparatnye-voprosy/ne-mogu-vylechit-vatchdog-na-goloi-atmege328r-s-vnutr-taktirovaniem-8-mgts

#include <avr/wdt.h>
#include <avr/sleep.h>
//#include <avr/power.h>

#include "RF24.h"
RF24 radio(9, 10);  // CE_PIN, CSN_PIN
byte count = 0;     // счетчик количества просыпаний
int32_t data[5];    // на данный момент используется int32_t чтобы иметь возможность получить большие числа счетчика передач, для тестирования продолжительности работы от батарейки,
                    // потом можно обычный int
                    // data 0 - данные АЦП или пересчитанное напряжение питания c тремя знаками(в милливольтах, на стороне приемника переменная float деленная на 1000
                    // data 1 - счетчик отправленных пакетов, сбрасывается при снятии питания
                    // data 2 - P атмсомферное в мм, можно умножить на 10 если нужны десятые, тогда на стороне приемника делить на 10 и float
                    // data 3 - Т температура умноженное на 100 на стороне приемника float делить на 100
                    // data 4 - H влажность умноженная на 100
              
#include "BME280.h" // при таком написании компилятор ищет библиотеку в первую очередь в папке скетча
using namespace BME280; 
class CustomI2CProtocol : public I2CProtocol {
  public:
    virtual void init() {
      I2CProtocol::init();
      Wire.setClock(400000);  // частота работы I2C, 400 кГц - максимум для BME280, уже чувствительна к качеству и длине линии
    }                         // реальная частота I2C зависит от регистра и строки в wire.cpp - TWBR = ((F_CPU / frequency) - 16) / 2 и при частоте 8 МГц она ниже, около 250 кГц
                              // https://www.avrfreaks.net/forum/twi-clock-divider?name=PNphpBB2&file=viewtopic&t=79562
}; 
BME280Sensor bme280 = BME280Sensor(new CustomI2CProtocol()); //BME280Sensor bme280 = BME280Sensor(new I2CProtocol()) - I2C работает на частоте по умолчанию - 100 кГц 

ISR (TIMER2_OVF_vect) {                             // из прерывания по таймеру 2 нельзя выходить слишком быстро, поэтому инкрементирование переменной расположим здесь
  count++;
  }
   
void setup() {
  MCUSR = 0;                                        // необходимо при использовании вотчдога для сброса контроллера при зависании на выпонении кода
  wdt_disable();                                    // иначе при первом сбросе вотчдогом, будет установлено минимальное время вотчдога 0.15 с, и мк зависнет на этом цикле
  PORTC |= (1 << PC1);                              // включение светодиода, для индикации запуска мк
  delay(10);

  DDRB = 0b11000001;                                // настраиваем неиспользуемые выводы(и порты) на выход
  DDRC = 0b11111111;
  DDRD = 0b11111111;
  
  //clock_prescale_set(clock_div_2);                  // деление тактовой частоты  для получения 4 МГц для работы atmega168 вплоть до 1.8 вольта
  bme280.init(false);
  bme280.setHumidityMode(Over_1);                   // установка режима измерения влажности, остальные при запуске измерения
  //bme280.setMode(Forced, Over_1, Over_1, Over_1);   // Forced - однократный режим измрения, все оверсемплинги(T, P, Hum) = 1

  PRR = (1<<PRTIM1) | (1<<PRUSART0);                // отключаем неиспользуемую периферию, (1<<PRTIM2) - работает в асинхронном режиме
                                                    // (1<<PRTIM0) - так не работает передача, видимо count использует этот таймер
                                                    // (1<<PRSPI), (1<<PRTWI)- не отключаем,  SPI - для NRF24 и TWI(по другому I2C) - для BME280
  radio.begin();
  radio.setAutoAck(true);                    // подтверждение приема, true - включено, false - отключено
  radio.setRetries   (2, 1);                 // количество попыток передачи если включен режим автоподтверждения приема, первая цифра - задержка между передачами, 1 = 250 мкс, вторая - количество попыток передачи
  radio.setChannel(100);                     // номер канала, можно перед выбором просканировать эфир чтобы выяснить менее зашумленные каналы(использовать скетч scanner из библ. RF24)
  radio.setDataRate     (RF24_2MBPS);        // скорость обмена RF24_2MBPS, RF24_1MBPS, RF24_250KBPS - максимальная чувствительность на 250 кбитс
  radio.setPALevel      (RF24_PA_MAX);       // уровень мощности RF24_PA_MIN -18dBm, RF24_PA_LOW -12dBm, RF24_PA_HIGH -6dBM, RF24_PA_MAX 0dBm
  radio.openWritingPipe (0xAABBCCDD11LL);    // уникальный номер передатчика, на приемнике должен быть такой же
  
    ADCSRA = 0;                              // отключаем АЦП
    ACSR |= (1<<ACD);                        // отключаем компаратор
    ADMUX =  _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);            // настраиваем АЦП на измерение собственного напряжения питания, опорное 1.1 вольт
    DIDR0 = 1;                                                          // отключаем цифровой вход выводов АЦП, он тоже немного потребляет

    ASSR |= (1<<AS2);                         // Асинхронный режим
    TCCR2A = (0<<WGM21)|(0<<WGM20);           //режим normal
    TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20);   // clk/1024
    TCNT2 = 0x00;                             // начальное значение таймера
 // OCR2A = 0x00; OCR2B = 0x00;
    TIMSK2 |= (1 << TOIE2);                   // разрешаем прерывание по переполнению Т2  
    
    SMCR = _BV(SM1) | _BV(SM0);               // Power_Save,  set_sleep_mode(SLEEP_MODE_PWR_SAVE),  set_sleep_mode (SLEEP_MODE_PWR_DOWN) - выбор режима сна
    radio.powerDown();   

    PORTC &= ~_BV(PC1);                       // выключение светодиода 
}                             

void loop() {
    if ( count >= 8 ) {        // передавая один раз в 64 секунды = 8
      radio_();
      TCNT2 = 0x00;
      }   
    while(ASSR&(1<<TCN2UB)){;} //wait to TCNT update!!! 
    TCNT2 = 0x00;                                    
    sleep_enable();  //SMCR |= _BV(SE); 
    sleep_cpu ();                                             
}

void radio_() {
  wdt_enable(WDTO_1S); 
  radio.powerUp();
  data[1]++;
  
  ADCSRA |= _BV(ADEN);                            // включаем АЦП
   
  Measurement measurement = bme280.readMeasurement();                     // опрос датчика и расчет параметров  
  data[2] = (measurement.getPressure32()*1000/133322);
  data[3] = (measurement.getTemperature()*100);
  data[4] = (measurement.getHumidity()*100);
  bme280.setMode(Forced, Over_1, Over_1);           // запуск однократного измерения и установка оверсемплингов T, P, без влажности
  //bme280.setMode(Forced, Over_1, Over_1, Over_1); // запуск однократного измерения и установка оверсемплингов T, P, Hum
    
  ADCSRA |= _BV(ADSC);                            // запуск конверсии               
  while (bit_is_set(ADCSRA,ADSC));                // измерение
  uint8_t low  = ADCL;                            // сначала читаем ADCL - это расблокирует чтение ADCH  
  uint8_t high = ADCH;                           
  int volt = (high<<8) | low;                     // данные АЦП
  data[0] = (1135494/volt);                       // 1135494 подобрано эксперементально и в общем случае равно 1100 милливольт опорное * 1024, но опорное не всегда 1100 мВ
    
  ADCSRA = 0;                                     // отключаем АЦП          
  
  // ******************************************** // код до этого места примерно 1700 мкс, убираем в файле RF24.cpp задержку delay(5) и добавляем перед отправкой данных 
                                                  // задержку в 2.3 мс, чтобы в сумме задежка получилась 4 мс, это минимальная задержка от команды powerUp() до write()
                                                  // при которой стабильно работает большой модуль на RF24, smd модуль работает и при меньшей задержки, т.к. разные кварцы
                   
  clock_prescale_set(clock_div_256);              // на время задержки снижаем частоту контроллера до минимально возможной, для снижения энергопотребления                                                                  
  delayMicroseconds(10);                          // на этой частоте для получения 2.3 миллисекунд нужно указать задержку в 256 раз меньше ~ 9 мкс                                                 
                                                  // в этом режиме потребляет только atmega, которая работает на частоте 1/256 от номанальной
  clock_prescale_set(clock_div_1);                // перед отправкой данных снова устанавливаем исходную частоту работы контроллера                                                
  radio.write(&data, sizeof(data));             
  radio.powerDown();                         
  count = 0;
  wdt_disable();
} 

 

Изменено пользователем Neon
  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Пожалуйста, войдите для комментирования

Вы сможете оставить комментарий после входа



Войти сейчас

  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу.

×
×
  • Создать...