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), мой тестовый приемник не получает никаких сообщений.
Есть какие-нибудь идеи, что здесь происходит не так?
Спасибо :)
@MrBen, 👍1
1 ответ
Я нашел проблему. Я использовал 2 батареи 2032 для питания attiny85. Внутренние часы чипа недостаточно точны при работе на напряжении 6 В. Чтобы решить проблему, я извлек 1 батарейку. Альтернативой было бы установить скорость передачи данных передатчика и приемника на разные значения (мне пришлось установить приемник на 520 кадров в секунду, чтобы получать сообщения от передатчика со скоростью 500 кадров в секунду).
Может быть, кто-нибудь сможет повторно использовать мой код для своего проекта.
- 1 МГц Attiny85 RF с библиотекой MANCHESTER
- Радиочастотный пакетный импульс 433 МГц с микросхемой ATTiny85
- Attiny85 с 433 МГц вч, работал отлично нано к нано, не attiny85 к нано
- В чем разница между вариантами RF (wifi, xbee, NRF24L01)
- Связь последовательного порта Digispark
- Радиочастотное дистанционное управление с использованием VirtualWire на ATtiny85, работающем на частоте 8 МГц на внутреннем генераторе
- Декодирование 2.4G RF пульта дистанционного управления?
- Библиотеки I2C для ATTiny85?