ATTINY85 - Радиочастотный передатчик (RH ASK)

Я работаю над небольшим радиочастотным датчиком, работающим от аккумулятора. Я использовал код от мистера К. (https://arduino.stackexchange.com/a/55516/65870) в качестве основы и адаптировал его к протоколу RH Ask:

#define F_CPU 8000000  // Это используется задержкой.h библиотека

#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <util/crc16.h>

#define LED_PIN           0

#define TX_PIN            4 //3
#define TX_BAUDRATE       2000
#define TX_MILLIINTERVAL  (1000UL/TX_BAUDRATE)
#define TX_MICROINTERVAL  (1000000UL/TX_BAUDRATE)
#define TX_MAXLEN         12
#define RFRepetitions 3
byte RollingCounter = 0;


// Определение идентификаторов датчиков (для MQTT)
#define SensorIDMoisture 6

// Определение переменных датчика влажности
#define MoistureSensorPinA    A1
#define MoistureSensorPinD    10
#define MoisturePowerOutPin   1
#define RFPowerPin            3 //4

inline void led(const bool onOff) { digitalWrite(LED_PIN, onOff ? HIGH : LOW); }
inline void tx(const bool onOff) { digitalWrite(TX_PIN, onOff ? HIGH : LOW); }

const uint8_t header[8] = { 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x38, 0x2C };
const uint8_t conv4to6[16] = { 0x0D, 0x0E, 0x13, 0x15, 0x16, 0x19, 0x1A, 0x1C,
                               0x23, 0x25, 0x26, 0x29, 0x2A, 0x2C, 0x32, 0x34 };


// Сторожевой таймер
#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


// Важно: время ожидания равно параметру wdt_length, умноженному на значение wdt_loops_max
#define wdt_loops_max 225
#define wdt_length 9 // 9 = 8 секунд

volatile int wdt_loops = wdt_loops_max; // Начните с максимального значения, чтобы иметь измерение при включении системы


void rawSend(const uint8_t * p, uint8_t len) {
  while (len--) {
    const uint8_t val = *p++;
    for (uint8_t mask = 1; mask != 0x40; mask <<= 1) {
      tx(val & mask);
      delayMicroseconds(TX_MICROINTERVAL);
      
// _delay_ms(TX_MILLIONINTERVAL);
    }
  }
}

void send(const uint8_t * buf, const uint8_t len) {
  #define RH_ASK_HEADER_LEN 4
  // Сначала создайте полезную нагрузку ...
  static uint8_t payload[(TX_MAXLEN+3+RH_ASK_HEADER_LEN)*2];
  uint8_t * p = payload;
  uint16_t crc = 0xFFFF;
  uint8_t v = len + 3 + RH_ASK_HEADER_LEN;
  crc = _crc_ccitt_update(crc, v);
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];

  uint8_t _txHeaderTo = 0xff;
  uint8_t _txHeaderFrom = 0xff;
  uint8_t _txHeaderId = 0;
  uint8_t _txHeaderFlags = 0;
  
    // Кодирование заголовков
    crc = _crc_ccitt_update(crc, _txHeaderTo);
    *p++ = conv4to6[_txHeaderTo >> 4];
    *p++ = conv4to6[_txHeaderTo & 0xf];
    crc = _crc_ccitt_update(crc, _txHeaderFrom);
    *p++ = conv4to6[_txHeaderFrom >> 4];
    *p++ = conv4to6[_txHeaderFrom & 0xf];
    crc = _crc_ccitt_update(crc, _txHeaderId);
    *p++ = conv4to6[_txHeaderId >> 4];
    *p++ = conv4to6[_txHeaderId & 0xf];
    crc = _crc_ccitt_update(crc, _txHeaderFlags);
    *p++ = conv4to6[_txHeaderFlags >> 4];
    *p++ = conv4to6[_txHeaderFlags & 0xf];


  for (uint8_t l = len; l--;) {
    v = *buf++;
    crc = _crc_ccitt_update(crc, v);
    *p++ = conv4to6[v >> 4];
    *p++ = conv4to6[v & 0x0F];
  }
  crc = ~crc;
  v = (uint8_t)crc;
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];
  v = (uint8_t)(crc >> 8);
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];

  // Теперь передайте заголовок и полезную нагрузку ...
  rawSend(header, sizeof(header));
  rawSend(payload, (len + 3 + RH_ASK_HEADER_LEN)*2);
  tx(false);

  //Serial.print("CRC: ");Serial.println(crc);
}

ISR(WDT_vect) 
{
  wdt_loops++;
}

// Спящий
void system_sleep() {
  cbi(ADCSRA,ADEN);                    // выключить аналого-цифровой преобразователь

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // здесь устанавливается спящий режим
  sleep_enable();

  sleep_mode();                        // Система переходит в спящий режим здесь

  sleep_disable();                     // Система продолжает выполнение здесь, когда время ожидания сторожевого тайм-аута
  sbi(ADCSRA,ADEN);                    // включить аналого-цифровой преобразователь
}

// Настройка для сторожевого
void setup_watchdog(int ii) {
  byte bb;
  int ww;
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<<WDCE);
  ww=bb;

  MCUSR &= ~(1<<WDRF);
  // запустить временную последовательность
  WDTCR |= (1<<WDCE) | (1<<WDE);
  // установить новое значение тайм-аута сторожевого таймера
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
}


void setup()   /*----( SETUP: RUNS ONCE )----*/
{
// pinMode (вывод светодиода, ВЫХОД);
  pinMode(LED_PIN, OUTPUT);
  led(false);

  // Инициализировать
  pinMode(MoistureSensorPinD, INPUT);

  // Инициализация радиочастотного датчика
  //драйвер.инициализация();

  setup_watchdog(wdt_length); // примерно 8 секунд сна

}/*--(end setup )---*/



void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
{
  if(wdt_loops >= wdt_loops_max)
  {
    //Цифровая запись (ledPin, ВЫСОКАЯ);
    led(true);

    
    wdt_loops = 0;
    moisture_measurement();
    
    led(false);
  }  
  system_sleep();


}/* --(end main loop )-- */

void moisture_measurement()
{
  // Активировать
  analogWrite(MoisturePowerOutPin,255);  
  analogWrite(RFPowerPin,255);
  _delay_ms(150);
  

  // Включить радиочастотный передатчик
  

  float MoistureMedian   = 0;
 // значение датчика с плавающей запятой;
  for(int i = 0;i<=100;i++)
  {
    float sensorValue = analogRead(MoistureSensorPinA);
// процент влажности = ( 100 - ( (Значение датчика/1023.00) * 100 ) );
// Датчик влажности += процент влажности;

    MoistureMedian += ( 1 - ( (sensorValue/1023.00) * 1 ) );
    //delay(1);
    _delay_ms(1);
  }

  // Считывание цифровых значений
  bool digitalValue = digitalRead(MoistureSensorPinD);

  // Отключить
  analogWrite(MoisturePowerOutPin,0);

  // Передаваемое значение
  TransmitSensorValue(SensorIDMoisture,MoistureMedian);

  _delay_ms(150);
  // Отключить
  analogWrite(RFPowerPin,0);

}


void MessageEncrypt(int adressIn, int parameterIn, float payloadIn, byte pByteArray[])
{
  pByteArray[0] = (byte)adressIn;

  byte * payloadBArray = (byte *)&payloadIn;
  pByteArray[1] = payloadBArray[0];
  pByteArray[2] = payloadBArray[1];
  pByteArray[3] = payloadBArray[2];
  pByteArray[4] = payloadBArray[3];
    
  RollingCounter = RollingCounter + 1;
  if(RollingCounter > 2)
    RollingCounter = 0;

  pByteArray[5] = RollingCounter;
}

// CRC-8 - на основе формул CRC8 от Dallas / Maxim
//код, выпущенный в соответствии с условиями лицензии GNU GPL 3.0
byte CRC8(const byte *data, byte len) {
  byte crc = 0x00;
  while (len--) {
    byte extract = *data++;
    for (byte tempI = 8; tempI; tempI--) {
      byte sum = (crc ^ extract) & 0x01;
      crc >>= 1;
      if (sum) {
        crc ^= 0x8C;
      }
      extract >>= 1;
    }
  }

  return crc;
}

void TransmitSensorValue(int SensorID, float SensorValue)
{
  byte MessageOut[6];
  MessageEncrypt(SensorID, 1, SensorValue, MessageOut);

    for(int i=0;i<RFRepetitions;i++)
    {
      send((const uint8_t *)&MessageOut, sizeof(MessageOut));
    }  
}

Код работает как во сне, пока я использую 2000 в качестве скорости передачи данных. Как только я использую другую скорость передачи данных (500 или 1000), мой тестовый приемник не получает никаких сообщений.

Есть какие-нибудь идеи, что здесь происходит не так?

Спасибо :)

, 👍1


1 ответ


1

Я нашел проблему. Я использовал 2 батареи 2032 для питания attiny85. Внутренние часы чипа недостаточно точны при работе на напряжении 6 В. Чтобы решить проблему, я извлек 1 батарейку. Альтернативой было бы установить скорость передачи данных передатчика и приемника на разные значения (мне пришлось установить приемник на 520 кадров в секунду, чтобы получать сообщения от передатчика со скоростью 500 кадров в секунду).

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

,