14 Commits

Author SHA1 Message Date
Alex 48c3ee21a4 upd 2021-09-09 17:57:11 +03:00
Alex e6f45643b9 upd 2021-09-09 17:54:30 +03:00
Alex 96520e1b3b upd 2021-08-24 01:30:02 +03:00
Alex 8196c8e583 v1.4 2021-07-17 23:04:48 +03:00
Alex f4c573bf88 Update Gyver433.h 2021-06-20 22:01:08 +03:00
Alex 060fcbb9ce v1.3 2021-06-20 12:05:51 +03:00
Alex d10b64cbe6 upd 2021-06-17 17:24:48 +03:00
Alex 48e1fa1e98 v1.2 2021-06-17 12:49:46 +03:00
Alex bd9e4b19ae Update README.md 2021-06-14 21:55:30 +03:00
Alex ecfcdf1bf9 v1.1 2021-06-14 21:53:39 +03:00
Alex 1e37f86364 Update README.md 2021-06-14 18:38:06 +03:00
Alex cb09165d3e v1.1 2021-06-14 18:36:58 +03:00
Alex ef9eca0f9f Update Gyver433.h 2021-05-26 01:09:20 +03:00
Alex a06be3a93e Update README.md 2021-05-25 00:05:07 +03:00
15 changed files with 556 additions and 316 deletions
+82 -33
View File
@@ -1,13 +1,16 @@
![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) ![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)
![author](https://img.shields.io/badge/author-AlexGyver-informational.svg) ![author](https://img.shields.io/badge/author-AlexGyver-informational.svg)
# Gyver433_TX # Gyver433
Библиотека для радиомодулей 433 МГц и Arduino Библиотека для радиомодулей 433 МГц и Arduino
- Не использует прерывания и таймеры (кроме нулевого, читает micros()) - Поддержка кривых китайских модулей
- Встроенный CRC контроль целостности - Встроенный CRC контроль целостности
- Ускоренный алгоритм IO для AVR Arduino - Ускоренный алгоритм IO для AVR Arduino
- Асинхронный приём в прерывании
- Супер лёгкая либа, заведётся даже на тини13
### Совместимость ### Совместимость
Совместима со всеми Arduino платформами (используются Arduino-функции) Совместима со всеми Arduino платформами (используются Arduino-функции)
- При подключении прерывания на esp8266 не забудь аттрибут `IRAM_ATTR`
## Содержание ## Содержание
- [Установка](#install) - [Установка](#install)
@@ -19,11 +22,11 @@
<a id="install"></a> <a id="install"></a>
## Установка ## Установка
- Библиотеку можно найти по названию **Gyver433_TX** и установить через менеджер библиотек в: - Библиотеку можно найти по названию **Gyver433** и установить через менеджер библиотек в:
- Arduino IDE - Arduino IDE
- Arduino IDE v2 - Arduino IDE v2
- PlatformIO - PlatformIO
- [Скачать библиотеку](https://github.com/GyverLibs/Gyver433_TX/archive/refs/heads/main.zip) .zip архивом для ручной установки: - [Скачать библиотеку](https://github.com/GyverLibs/Gyver433/archive/refs/heads/main.zip) .zip архивом для ручной установки:
- Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64) - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
- Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32) - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
- Распаковать и положить в *Документы/Arduino/libraries/* - Распаковать и положить в *Документы/Arduino/libraries/*
@@ -33,43 +36,74 @@
<a id="init"></a> <a id="init"></a>
## Инициализация ## Инициализация
```cpp ```cpp
// указать пин // === ПЕРЕДАТЧИК ===
Gyver433_RX rx(2); Gyver433_TX<пин> tx;
Gyver433_TX tx(2); Gyver433_TX<пин, CRC> tx;
// === ПРИЁМНИК ===
Gyver433_RX<пин> rx;
Gyver433_RX<пин, буфер> rx;
Gyver433_RX<пин, буфер, CRC> rx;
// пин: цифровой пин
// буфер: размер буфера в байтах, по умолч. 64
// CRC: проверка целостности данных: G433_CRC8 (надёжный), G433_XOR (лёгкий), G433_NOCRC (отключено). По умолч. G433_CRC8
``` ```
<a id="usage"></a> <a id="usage"></a>
## Использование ## Использование
```cpp ```cpp
// ========= Gyver433_TX ========= // ========= Gyver433_TX =========
void sendData(T &data); // отправить данные любого типа void sendData(T &data); // отправить данные любого типа (CRC добавляется автоматически)
void write(uint8_t* buf, uint8_t size); // отправить массив байт указанного размера (CRC не добавляется)
uint8_t buffer[]; // доступ к буферу приёма
uint8_t byteBuf; // доступ к буферу принятого байта
// ========= Gyver433_RX ========= // ========= Gyver433_RX =========
uint8_t tick(); // неблокирующий приём, вернёт кол-во успешно принятых байт void tickISR(); // тикер приёма для вызова в прерывании по CHANGE
uint8_t tickWait(); // блокирующий приём, вернёт кол-во успешно принятых байт uint8_t tickISRraw(); // ручной приём в прерывании по CHANGE. Вернёт 1 (начало приёма), 2 (принят байт), 3 (конец пакета)
uint16_t tick(); // неблокирующий приём. Вернёт количество успешно принятых байт
uint16_t tickWait(); // блокирующий приём. Вернёт количество успешно принятых байт
bool readData(T &data); // прочитает буфер в любой тип данных (в указанную переменную) bool readData(T &data); // прочитает буфер в любой тип данных (в указанную переменную)
int getSize(); // получить размер принятых данных uint16_t getSize(); // получить размер принятых данных
uint16_t gotData(); // вернёт количество успешно принятых в tickISR() байт (см. пример isr_rx)
uint8_t getRSSI(); // получить качество приёма (процент успешных передач 0.. 100)
uint8_t buffer[]; // доступ к буферу приёма
uint8_t byteBuf; // доступ к буферу принятого байта
// ============= CRC =============
// можно использовать встроенные функции для генерации байта CRC для ручной упаковки пакетов
uint8_t G433_crc8(uint8_t *buffer, uint8_t size); // ручной CRC8
uint8_t G433_crc_xor(uint8_t *buffer, uint8_t size); // ручной CRC XOR
// ====== ДЕФАЙНЫ-НАСТРОЙКИ ======
// вызывать перед подключением библиотеки
#define G433_FAST // [TX] короткая синхронизация для зелёных модулей
#define G433_MEDIUM // [TX] средняя синхронизация при отправке на SYN480R ЧАЩЕ 400мс (активно по умолчанию)
#define G433_SLOW // [TX] длинная синхронизация при отправке на SYN480R РЕЖЕ 400мс
#define G433_SPEED 1000 // [RX/TX] скорость, должна быть одинакова на RX и TX, 100-10000 бит/с, по умолч. 2000 бит/с
#define G433_RSSI_COUNT 8 // [RX] количество успешно принятых пакетов для расчёта RSSI (по умолч. 8)
#define G433_CUT_RSSI // [RX] убрать расчёт RSSI из кода (сэкономит чуть памяти)
``` ```
<a id="example"></a> <a id="example"></a>
## Пример ## Примеры
Остальные примеры смотри в **examples**! Остальные примеры смотри в **examples**!
![scheme](/doc/scheme.jpg)
### Отправка ### Отправка
```cpp ```cpp
// мелкий передатчик 3.6V SYN115
#define G433_BUFSIZE 50 // размер буфера
#define G433_SPEED 2000 // скорость бит/сек (минимальная)
#include <Gyver433.h> #include <Gyver433.h>
Gyver433_TX tx(2); // указали пин Gyver433_TX<2> tx; // указали пин
void setup() { void setup() {
} }
char data[] = "Hello from #xx"; char data[] = "Hello from #xx"; // строка для отправки
byte count = 0; byte count = 0; // счётчик для отправки
void loop() { void loop() {
// добавляем счётчик в строку
data[12] = (count / 10) + '0'; data[12] = (count / 10) + '0';
data[13] = (count % 10) + '0'; data[13] = (count % 10) + '0';
if (++count >= 100) count = 0; if (++count >= 100) count = 0;
@@ -78,35 +112,50 @@ void loop() {
} }
``` ```
### Приём ### Приём в прерывании
```cpp ```cpp
// крупный приёмник 5.0 SYN480R
#define G433_BUFSIZE 50 // размер буфера
#define G433_SPEED 2000 // скорость бит/сек (минимальная)
#include <Gyver433.h> #include <Gyver433.h>
Gyver433_RX rx(2); Gyver433_RX<2, 20> rx; // указали пин и размер буфера
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
attachInterrupt(0, isr, CHANGE); // прерывание пина радио по CHANGE
} }
// спец. тикер вызывается в прерывании
void isr() {
rx.tickISR();
}
void loop() { void loop() {
// tick принимает асинхронно, но может ловить ошибки при загруженном коде if (rx.gotData()) { // если успешно принято больше 0
// tickWait блокирует выполнение, но принимает данные чётко Serial.write(rx.buffer, rx.size); // выводим
if (rx.tickWait()) { Serial.println();
byte buf[64];
rx.readData(buf);
for (byte i = 0; i < rx.size; i++) Serial.write(buf[i]);
} }
delay(50); // имитация загруженного кода
} }
``` ```
## Заметка по модулям:
Передатчики:
- SYN115, маленький чип: 1.8-3.6V, макс. скорость 8000
- FS1000A: 3-12V, макс. скорость 10000
- WL102-341: 2.0-3.6V, макс. скорость 6000
Приёмники:
- SYN480R, крупный чип: 3.3-5.5V
- MX-RM-5V (RF-5V): 5V
- RX470 (WL101-341): 3-5V
<a id="versions"></a> <a id="versions"></a>
## Версии ## Версии
- v1.0 - v1.0
- v1.1 - оптимизация, новый интерфейс, поддержка дешёвых синих модулей, работа в прерывании
- v1.2 - улучшение качества связи, оптимизация работы в прерывании
- v1.3 - добавлен вывод RSSI
- v1.4 - переделан FastIO
- v1.4.1 - убран FastIO, CRC вынесен отдельно
- v2.0 - убран буфер на отправку, убран манчестер, полностью переделан и оптимизирован интерфейс связи
<a id="feedback"></a> <a id="feedback"></a>
## Баги и обратная связь ## Баги и обратная связь
+53
View File
@@ -0,0 +1,53 @@
// демо-пример приёма в прерывании
// SYN480R, крупный чип: 3.3-5.5V
// MX-RM-5V (RF-5V): 5V
// RX470 (WL101-341): 3-5V
// дефайны перед подключением библиотеки
//#define G433_SPEED 2000 // скорость 100-10000 бит/с, по умолч. 2000 бит/с
#include <Gyver433.h>
//Gyver433_RX<пин> rx;
//Gyver433_RX<пин, буфер> rx;
// пин: цифровой пин
// буфер: размер приёмного буфера в байтах. По умолч. 64
#include <Gyver433.h>
Gyver433_RX<2, 20> rx;
void setup() {
Serial.begin(9600);
// взводим прерывания по CHANGE
attachInterrupt(0, isr, CHANGE);
}
// тикер вызывается в прерывании
void isr() {
rx.tickISR();
}
void loop() {
// gotData() вернёт количество удачно принятых байт
if (rx.gotData()) { // если больше 0
// ЧИТАЕМ. СПОСОБ 1
// я знаю, что передатчик отправляет char[15]
char data[15];
// читаем принятые данные в data
// если данные совпадают по размеру - ок
if (rx.readData(data)) Serial.print(data);
else Serial.print("Data error");
// ЧИТАЕМ. СПОСОБ 2
// вывести сырые данные из буфера в порт
//Serial.write(rx.buffer, rx.size);
// выведем также качество соединения
Serial.print(", RSSI: ");
Serial.println(rx.getRSSI());
}
// имитация загруженного кода. Не влияет на приём
delay(50);
}
+33
View File
@@ -0,0 +1,33 @@
// демо-пример отправки
// SYN115, маленький чип: 1.8-3.6V
// FS1000A: 3-12V
// WL102-341: 2.0-3.6V
// дефайны перед подключением библиотеки
//#define G433_FAST // короткая синхронизация для зелёных модулей
//#define G433_MEDIUM // средняя синхронизация для SYN480R при отправке ЧАЩЕ 400мс (активно по умолчанию)
//#define G433_SLOW // длинная синхронизация для SYN480R при отправке РЕЖЕ 400мс
//#define G433_SPEED 2000 // скорость 100-10000 бит/с, по умолч. 2000 бит/с
#include <Gyver433.h>
//Gyver433_RX<пин> tx;
Gyver433_TX<2> tx;
void setup() {
}
char data[] = "Hello from #xx"; // строка для отправки
byte count = 0; // счётчик для отправки
void loop() {
// добавляем счётчик в строку
data[12] = (count / 10) + '0';
data[13] = (count % 10) + '0';
if (++count >= 100) count = 0;
// отправка данных любого типа
tx.sendData(data);
// отправка 10 раз в сек
delay(100);
}
+22
View File
@@ -0,0 +1,22 @@
// обмен сырыми данными без CRC
// отправляет пример raw_tx
// принимаем без прерывания! для примера
//#define G433_SPEED 1000 // скорость 100-10000 бит/с, по умолч. 2000 бит/с
#include <Gyver433.h>
Gyver433_RX<2, 20, G433_NOCRC> rx; // буфер 20 байт
void setup() {
Serial.begin(9600);
}
void loop() {
// этот тикер нужно вызывать как можно чаще
// лучше принимать в прерывании, см. пример demo
if (rx.tick()) {
// выводим сырые байты в порт
Serial.write(rx.buffer, rx.size);
Serial.println();
}
}
+26
View File
@@ -0,0 +1,26 @@
// обмен сырыми данными без CRC
// принимает пример raw_rx
//#define G433_SPEED 1000 // скорость 100-10000 бит/с, по умолч. 2000 бит/с
#include <Gyver433.h>
Gyver433_TX<2, G433_NOCRC> tx;
void setup() {
}
char data[] = "Hello from #xx"; // строка для отправки
byte count = 0; // счётчик для отправки
void loop() {
// добавляем счётчик в строку
data[12] = (count / 10) + '0';
data[13] = (count % 10) + '0';
if (++count >= 100) count = 0;
// отправка данных типа byte*
tx.write((byte*)data, sizeof(data));
// отправка 10 раз в сек
delay(100);
}
-22
View File
@@ -1,22 +0,0 @@
// крупный приёмник 5.0 SYN480R
#define G433_BUFSIZE 50 // размер буфера
#define G433_SPEED 2000 // скорость бит/сек (минимальная)
#include <Gyver433.h>
Gyver433_RX rx(2);
void setup() {
Serial.begin(9600);
}
void loop() {
// tick принимает асинхронно, но может ловить ошибки при загруженном коде
// tickWait блокирует выполнение, но принимает данные чётко
if (rx.tickWait()) {
byte buf[64];
rx.readData(buf);
for (byte i = 0; i < rx.size; i++) Serial.write(buf[i]);
}
}
+4 -11
View File
@@ -1,28 +1,21 @@
// крупный приёмник 5.0V SYN480R // выводим данные на дисплей. Отправляет пример demo_tx
#define G433_BUFSIZE 50 // размер буфера //#define G433_SPEED 1000 // скорость 100-10000 бит/с, по умолч. 1500 бит/с
#define G433_SPEED 2000 // скорость бит/сек (минимальная)
#include <Gyver433.h> #include <Gyver433.h>
Gyver433_RX rx(2); // указали пин Gyver433_RX<2> rx; // указали пин
#include <Wire.h>
#include <LiquidCrystal_I2C.h> #include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f, 16, 2); // или 0x27 LiquidCrystal_I2C lcd(0x3f, 16, 2); // или 0x27
void setup() { void setup() {
Serial.begin(9600);
lcd.init(); lcd.init();
lcd.backlight(); lcd.backlight();
} }
void loop() { void loop() {
// tick принимает асинхронно, но может ловить ошибки при загруженном коде
// tickWait блокирует выполнение, но принимает данные чётко
if (rx.tick()) { if (rx.tick()) {
byte buf[64];
rx.readData(buf); // прочитать в buf
lcd.clear(); lcd.clear();
lcd.home(); lcd.home();
for (byte i = 0; i < rx.size; i++) lcd.write(buf[i]); for (byte i = 0; i < rx.size; i++) lcd.write(rx.buffer[i]);
} }
} }
+11 -12
View File
@@ -1,13 +1,12 @@
// передача структуры данных // приём структуры данных
// крупный приёмник 5.0V SYN480R
#define G433_BUFSIZE 50 // размер буфера //#define G433_SPEED 1000 // скорость 100-10000 бит/с, по умолч. 2000 бит/с
#define G433_SPEED 2000 // скорость бит/сек (минимальная)
#include <Gyver433.h> #include <Gyver433.h>
Gyver433_RX rx(2); // указали пин Gyver433_RX<2, 12> rx; // указали пин и размер буфера
// формат пакета для приёма (такой же как отправляется) // формат пакета для приёма (такой же как отправляется)
struct dataPack { struct DataPack {
byte counter; byte counter;
byte randomNum; byte randomNum;
int analog; int analog;
@@ -19,17 +18,17 @@ void setup() {
} }
void loop() { void loop() {
// tick принимает асинхронно, но может ловить ошибки при загруженном коде
// tickWait блокирует выполнение, но принимает данные чётко
if (rx.tick()) { if (rx.tick()) {
dataPack data; DataPack data; // "буферная" структура
rx.readData(data); // прочитать в buf if (rx.readData(data)) { // переписываем данные в неё
// если данные подходят - выводим
Serial.println("Received:");
Serial.println(data.counter); Serial.println(data.counter);
Serial.println(data.randomNum); Serial.println(data.randomNum);
Serial.println(data.analog); Serial.println(data.analog);
Serial.println(data.time); Serial.println(data.time);
Serial.println(); Serial.println();
} else {
Serial.println("Wrong data");
}
} }
} }
-18
View File
@@ -1,18 +0,0 @@
// мелкий передатчик 3.6V SYN115
#define G433_BUFSIZE 50 // размер буфера
#define G433_SPEED 2000 // скорость бит/сек (минимальная)
#include <Gyver433.h>
Gyver433_TX tx(2); // указали пин
void setup() {
}
char data[] = "Hello from #xx";
byte count = 0;
void loop() {
data[12] = (count / 10) + '0';
data[13] = (count % 10) + '0';
if (++count >= 100) count = 0;
tx.sendData(data);
delay(100);
}
+9 -9
View File
@@ -1,19 +1,19 @@
// передача структуры данных // передача структуры данных
// мелкий передатчик 3.6V SYN115
#define G433_BUFSIZE 50 // размер буфера //#define G433_SPEED 1000 // скорость 100-10000 бит/с, по умолч. 2000 бит/с
#define G433_SPEED 2000 // скорость бит/сек (минимальная) #define G433_SLOW // отправляю раз в секунду на SYN480R
#include <Gyver433.h> #include <Gyver433.h>
Gyver433_TX tx(2); // указали пин Gyver433_TX<2> tx; // указали пин
// формат пакета для отправки // формат пакета для отправки
struct dataPack { struct DataPack {
byte counter; byte counter = 0;
byte randomNum; byte randomNum;
int analog; int analog;
uint32_t time; uint32_t time;
}; };
dataPack data; DataPack data;
void setup() { void setup() {
Serial.begin(9600); Serial.begin(9600);
@@ -24,14 +24,14 @@ void loop() {
data.randomNum = random(256); // случайное число data.randomNum = random(256); // случайное число
data.analog = analogRead(0); // тут ацп data.analog = analogRead(0); // тут ацп
data.time = millis(); // тут миллис data.time = millis(); // тут миллис
tx.sendData(data);
Serial.println("Transmit:"); Serial.println("Transmitted:");
Serial.println(data.counter); Serial.println(data.counter);
Serial.println(data.randomNum); Serial.println(data.randomNum);
Serial.println(data.analog); Serial.println(data.analog);
Serial.println(data.time); Serial.println(data.time);
Serial.println(); Serial.println();
tx.sendData(data);
delay(1000); delay(1000);
} }
+16 -1
View File
@@ -13,14 +13,29 @@ Gyver433_RX KEYWORD1
# Methods and Functions (KEYWORD2) # Methods and Functions (KEYWORD2)
####################################### #######################################
sendData KEYWORD2 sendData KEYWORD2
write KEYWORD2
tick KEYWORD2 tick KEYWORD2
tickWait KEYWORD2 tickWait KEYWORD2
tickISR KEYWORD2
tickISRraw KEYWORD2
readData KEYWORD2 readData KEYWORD2
size KEYWORD2 size KEYWORD2
buffer KEYWORD2
getSize KEYWORD2 getSize KEYWORD2
gotData KEYWORD2
G433_crc8 KEYWORD2
G433_crc_xor KEYWORD2
getRSSI KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)
####################################### #######################################
G433_CLI LITERAL1
G433_CRC8 LITERAL1
G433_XOR LITERAL1
G433_NOCRC LITERAL1
G433_FAST LITERAL1
G433_MEDIUM LITERAL1
G433_SLOW LITERAL1
G433_SPEED LITERAL1 G433_SPEED LITERAL1
G433_BUFSIZE LITERAL1 G433_CUT_RSSI LITERAL1
+1 -1
View File
@@ -1,5 +1,5 @@
name=Gyver433 name=Gyver433
version=1.0 version=2.0
author=AlexGyver <alex@alexgyver.ru> author=AlexGyver <alex@alexgyver.ru>
maintainer=AlexGyver <alex@alexgyver.ru> maintainer=AlexGyver <alex@alexgyver.ru>
sentence=Simple library for 433 MHz radio sentence=Simple library for 433 MHz radio
+41
View File
@@ -0,0 +1,41 @@
#include "G433_crc.h"
void G433_crc8_byte(uint8_t &crc, uint8_t data) {
#if defined (__AVR__)
// резкий алгоритм для AVR
uint8_t counter;
uint8_t buffer;
asm volatile (
"EOR %[crc_out], %[data_in] \n\t"
"LDI %[counter], 8 \n\t"
"LDI %[buffer], 0x8C \n\t"
"_loop_start_%=: \n\t"
"LSR %[crc_out] \n\t"
"BRCC _loop_end_%= \n\t"
"EOR %[crc_out], %[buffer] \n\t"
"_loop_end_%=: \n\t"
"DEC %[counter] \n\t"
"BRNE _loop_start_%="
: [crc_out]"=r" (crc), [counter]"=d" (counter), [buffer]"=d" (buffer)
: [crc_in]"0" (crc), [data_in]"r" (data)
);
#else
// обычный для всех остальных
uint8_t i = 8;
while (i--) {
crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1);
data >>= 1;
}
#endif
}
uint8_t G433_crc8(uint8_t *buffer, uint8_t size) {
uint8_t crc = 0;
for (uint8_t i = 0; i < size; i++) G433_crc8_byte(crc, buffer[i]);
return crc;
}
uint8_t G433_crc_xor(uint8_t *buffer, uint8_t size) {
uint8_t crc = 0;
for (uint8_t i = 0; i < size; i++) crc ^= buffer[i];
return crc;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef G433_crc_h
#define G433_crc_h
#include <Arduino.h>
uint8_t G433_crc8(uint8_t *buffer, uint8_t size); // ручной CRC8
uint8_t G433_crc_xor(uint8_t *buffer, uint8_t size); // ручной CRC XOR
void G433_crc8_byte(uint8_t &crc, uint8_t data); // crc8 один байт
#endif
+225 -183
View File
@@ -3,9 +3,11 @@
Документация: Документация:
GitHub: https://github.com/GyverLibs/Gyver433 GitHub: https://github.com/GyverLibs/Gyver433
Возможности: Возможности:
- Не использует прерывания и таймеры (кроме нулевого, читает micros()) - Поддержка кривых китайских модулей
- Встроенный CRC контроль целостности - Встроенный CRC контроль целостности
- Ускоренный алгоритм IO для AVR Arduino - Ускоренный алгоритм IO для AVR Arduino
- Асинхронный приём в прерывании
- Супер лёгкая либа, заведётся даже на тини13
AlexGyver, alex@alexgyver.ru AlexGyver, alex@alexgyver.ru
https://alexgyver.ru/ https://alexgyver.ru/
@@ -13,247 +15,287 @@
Версии: Версии:
v1.0 - релиз v1.0 - релиз
v1.1 - оптимизация, новый интерфейс, поддержка дешёвых синих модулей, работа в прерывании
v1.2 - улучшение качества связи, оптимизация работы в прерывании
v1.3 - добавлен вывод RSSI
v1.4 - переделан FastIO
v1.4.1 - убран FastIO, CRC вынесен отдельно
v2.0 - убран буфер на отправку, убран манчестер, полностью переделан и оптимизирован интерфейс связи
*/ */
#ifndef Gyver433_h #ifndef _Gyver433_h
#define Gyver433_h #define _Gyver433_h
#include <Arduino.h> #include <Arduino.h>
#include "G433_crc.h"
/* #define TRAINING_TIME_SLOW (500000) // время синхронизации для SLOW_MODE
Передатчик:
Gyver433_TX tx(пин) - создать объект
sendData(data) - отправить, любой тип данных
Приёмник:
Gyver433_RX rx(пин) - создать объект
tick() - вызывать постоянно для чтения. Асинхронный. Вернёт количество принятых байт
tickWait() - тож самое, но блокирует выполнение, принимает более четко
readData(data) - прочитать, любой тип данных
size - количество принятых байтов
*/
// =========================================================================
#ifndef G433_SPEED #ifndef G433_SPEED
#define G433_SPEED 2000 // скорость бит/сек (минимальная) #define G433_SPEED 2000
#endif
#ifndef G433_BUFSIZE
#define G433_BUFSIZE 64 // размер буфера приёма и отправки
#endif #endif
// тайминги интерфейса (компилятор посчитает) #ifndef G433_RSSI_COUNT
#define HIGH_PULSE (1000000ul/G433_SPEED) #define G433_RSSI_COUNT 8
#define LOW_PULSE (HIGH_PULSE/2) #endif
#define START_PULSE (HIGH_PULSE*2)
#define PULSE_HYST (LOW_PULSE/2)
#define START_MIN (START_PULSE-PULSE_HYST)
#define START_MAX (START_PULSE+PULSE_HYST)
#define LOW_MIN (LOW_PULSE-PULSE_HYST)
#define LOW_MAX (LOW_PULSE+PULSE_HYST)
#define HIGH_MIN (HIGH_PULSE-PULSE_HYST)
#define HIGH_MAX (HIGH_PULSE+PULSE_HYST)
// crc // тайминги интерфейса
uint8_t G433_crc(uint8_t *buffer, uint8_t size); #define G433_HIGH (1000000ul / G433_SPEED) // время HIGH
void G433_crc_byte(uint8_t &crc, uint8_t data); #define G433_LOW (G433_HIGH / 2) // время LOW
#define G433_START (G433_HIGH * 2) // стартовый импульс
#define G433_TRAIN (G433_HIGH * 3 / 2) // синхроимпульс
// ============ ПЕРЕДАТЧИК ============ #define G433_WINDOW (G433_HIGH / 4)
#define G433_EDGE_L (G433_LOW - G433_WINDOW)
#define G433_EDGE_LH (G433_HIGH - G433_WINDOW)
#define G433_EDGE_HT (G433_HIGH + G433_WINDOW)
#define G433_EDGE_TS (G433_TRAIN + G433_WINDOW)
#define G433_EDGE_S (G433_START + G433_WINDOW)
// жоский delay для avr
#ifdef AVR
#define G433_DELAY(x) _delay_us(x)
#else
#define G433_DELAY(x) delayMicroseconds(x)
#endif
// режимы CRC
#define G433_NOCRC 0
#define G433_CRC8 1
#define G433_XOR 2
// количество синхроимпульсов
#if defined(G433_FAST)
#define TRAINING_TIME 10000
#elif defined(G433_MEDIUM)
#define TRAINING_TIME 100000
#elif defined(G433_SLOW)
#define TRAINING_TIME (TRAINING_TIME_SLOW)
#else
#define TRAINING_TIME 100000
#endif
// =================================== ПЕРЕДАТЧИК ===================================
template <uint8_t TX_PIN, uint8_t CRC_MODE = G433_CRC8>
class Gyver433_TX { class Gyver433_TX {
public: public:
Gyver433_TX(uint8_t pin) : _pin(pin) { Gyver433_TX() {
#if defined(__AVR__) pinMode(TX_PIN, OUTPUT);
_port_reg = portOutputRegister(digitalPinToPort(pin));
_bit_mask = digitalPinToBitMask(pin);
#endif
pinMode(pin, OUTPUT);
} }
// отправка, блокирующая. Кушает любой тип данных // отправка, блокирующая. Кушает любой тип данных
template <typename T> template <typename T>
void sendData(T &data) { bool sendData(T &data) {
const uint8_t *ptr = (const uint8_t*) &data; uint8_t *ptr = (uint8_t*) &data;
for (uint16_t i = 0; i < sizeof(T); i++) buffer[i] = *ptr++; write(ptr, sizeof(T));
buffer[sizeof(T)] = G433_crc(buffer, sizeof(T)); // CRC последним байтом
bool flag = 0; // флаг дрыга
for (uint8_t i = 0; i < 30; i++) { // 30 импульсов для синхронизации
flag = !flag;
fastDW(flag);
delayMicroseconds(HIGH_PULSE);
} }
fastDW(1); // старт бит
delayMicroseconds(START_PULSE); // старт бит // отправка сырого набора байтов
for (int n = 0; n < sizeof(T) + 1; n++) { // буфер + CRC void write(uint8_t* buf, uint16_t size) {
uint8_t crc;
if (CRC_MODE == G433_CRC8) crc = G433_crc8(buf, size);
else if (CRC_MODE == G433_XOR) crc = G433_crc_xor(buf, size);
// раскачка радио
flag = 0;
for (uint16_t i = 0; i < (TRAINING_TIME / G433_TRAIN / 2) * 2 + 1; i++) {
fastWrite(TX_PIN, flag = !flag);
G433_DELAY(G433_TRAIN);
}
// старт бит
fastWrite(TX_PIN, 0); // старт
G433_DELAY(G433_START); // ждём
fastWrite(TX_PIN, 1); // старт бит
for (uint16_t i = 0; i < size; i++) write(buf[i]); // дата
if (CRC_MODE) write(crc); // CRC
G433_DELAY(G433_TRAIN);
fastWrite(TX_PIN, flag = !flag); // стоп
}
// отправить байт (без старт бита!)
void write(uint8_t data) {
for (uint8_t b = 0; b < 8; b++) { for (uint8_t b = 0; b < 8; b++) {
fastDW(flag); if (data & 1) G433_DELAY(G433_HIGH);
flag = !flag; else G433_DELAY(G433_LOW);
if (bitRead(buffer[n], b)) delayMicroseconds(HIGH_PULSE); fastWrite(TX_PIN, flag = !flag);
else delayMicroseconds(LOW_PULSE); data >>= 1;
} }
} }
fastDW(0); // передача окончена
}
private: private:
void fastDW(bool state) { // быстрый IO
#if defined(__AVR__) void fastWrite(const uint8_t pin, bool val) {
if (state) *_port_reg |= _bit_mask; // HIGH #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
else *_port_reg &= ~_bit_mask; // LOW if (pin < 8) bitWrite(PORTD, pin, val);
else if (pin < 14) bitWrite(PORTB, (pin - 8), val);
else if (pin < 20) bitWrite(PORTC, (pin - 14), val);
#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__)
bitWrite(PORTB, pin, val);
#else #else
digitalWrite(_pin, state); digitalWrite(pin, val);
#endif #endif
} }
uint8_t buffer[G433_BUFSIZE];
const uint8_t _pin; bool flag = 0;
#if defined(__AVR__)
volatile uint8_t *_port_reg;
volatile uint8_t _bit_mask;
#endif
}; };
// =================================== ПРИЁМНИК ===================================
// ============ ПРИЁМНИК ============ template <uint8_t RX_PIN, uint16_t RX_BUF = 64, uint8_t CRC_MODE = G433_CRC8>
class Gyver433_RX { class Gyver433_RX {
public: public:
Gyver433_RX(uint8_t pin){ Gyver433_RX() {
#if defined(__AVR__) pinMode(RX_PIN, INPUT);
_pin_reg = portInputRegister(digitalPinToPort(pin));
_bit_mask = digitalPinToBitMask(pin);
#endif
} }
// неблокирующий приём, вернёт кол-во успешно принятых байт // неблокирующий приём. Вернёт количество успешно принятых байт
uint8_t tick() { uint16_t tick() {
bool newState = fastDR(); // читаем пин if (pinChanged()) tickISR();
if (newState != prevState) { // ловим изменение сигнала return gotData();
uint32_t thisUs = micros();
uint32_t thisPulse = thisUs - tmr;
if (parse == 1) { // в прошлый раз поймали фронт
if (thisPulse > START_MIN && thisPulse < START_MAX) { // старт бит?
parse = 2; // ключ на старт
tmr = thisUs;
byteCount = 0;
bitCount = 0;
size = 0;
for (uint8_t i = 0; i < G433_BUFSIZE; i++) buffer[i] = 0;
} else { // не старт бит
parse = 0;
}
} else if (parse == 2) { // идёт парсинг
if (thisPulse > LOW_MIN && thisPulse < LOW_MAX) { // low бит
// просто пропускаем (в буфере уже нули)
tmr = thisUs;
bitCount++;
if (bitCount == 8) {
bitCount = 0;
byteCount++;
if (byteCount > G433_BUFSIZE) parse = 0; // оверфлоу
}
} else if (thisPulse > HIGH_MIN && thisPulse < HIGH_MAX) { // high бит
bitSet(buffer[byteCount], bitCount); // ставим бит единичку
tmr = thisUs;
bitCount++;
if (bitCount == 8) {
bitCount = 0;
byteCount++;
if (byteCount > G433_BUFSIZE) parse = 0; // оверфлоу
}
} else { // ошибка или конец передачи
tmr = thisUs;
parse = 0;
// проверяем, есть ли данные и целые ли они
if (byteCount > 0 && G433_crc(buffer, byteCount) == 0) {
size = byteCount - 2; // длина даты (минус crc)
return size;
}
else return 0;
}
} }
if (newState && !prevState && parse == 0) { // ловим фронт // блокирующий приём. Вернёт количество успешно принятых байт
parse = 1; // в следующий раз ждём флаг uint16_t tickWait() {
tmr = thisUs; do {
if (pinChanged()) tickISR();
} while (state == 1);
return gotData();
} }
prevState = newState;
// ручной приём в прерывании по CHANGE. Вернёт 1 (начало приёма), 2 (принят байт), 3 (конец пакета)
// принятый байт можно прочитать в byteBuf
uint8_t tickISRraw() {
uint32_t pulse = micros() - tmr; // время импульса
tmr += pulse; // сброс таймера. Равносильно tmr = micros()
if (pulse <= G433_EDGE_L) return parse = 0; // импульс слишком короткий
trains <<= 1; // счётчик train импульсов (0b11111111 << 1)
if (pulse <= G433_EDGE_HT && parse) { // окно LOW/HIGH и идёт парсинг
byteBuf >>= 1; // двигаем байт-буфер
if (pulse > G433_EDGE_LH) byteBuf |= (1 << 7); // пишем бит, если это HIGH
if (!(++bits & 0x7)) return 2; // 2: ПРИНЯТ БАЙТ: собрали байт (каждые 8 бит, 0x7 == 0b111)
} else if (pulse <= G433_EDGE_TS) { // окно START
trains |= 1; // добавляем 1 справа к trains
if (parse) { // был парсинг, а это стоп бит
parse = 0; // стоп машина
return 3; // 3: ПРИНЯТ ПАКЕТ: принят стоп-бит
} }
} else if (pulse <= G433_EDGE_S) { // окно STOP/TRAINING
if (trains == 0xFE) { // было 7 train импульсов (0xFE == 0b11111110)
bits = 0; // прерываем парсинг, если он был
parse = 1; // старт бит, начинаем парсинг
return 1; // 1: СТАРТ ПРИЁМА
}
} else return parse = 0; // слишком длинный импульс, выходим
return 0; return 0;
} }
// блокирующий приём, вернёт кол-во успешно принятых байт // тикер приёма для вызова в прерывании по CHANGE
uint8_t tickWait() { void tickISR() {
do { switch (tickISRraw()) {
tick(); case 1: // СТАРТ БИТ
} while (parse == 2); if (!state) { // старта не было
if (byteCount > 0) { state = 1; // старт
byteCount = 0; bytes = 0; // сброс
} // старт был - ошибка приёма
break;
case 2: // ПРИНЯТ БАЙТ
if (state == 1) { // парсинг идёт
buffer[bytes] = byteBuf; // пишем в буфер
bytes++; // счётчик принятых
if (bytes > sizeof(buffer)) { // буфер переполнен
state = 3; // флаг на чтение
}
}
break;
case 3: // КОНЕЦ ПАКЕТА
if (state == 1) state = 2; // флаг на чтение
break;
}
}
// если пакет прочитан успешно - вернёт количество байт в нём
uint16_t gotData() {
if (state >= 2) { // флаг на чтение
size = 0; // обнуляем размер
if (state != 3 && bytes != 0) { // если буфер не переполнен, проверяем CRC
if (CRC_MODE == G433_CRC8) { // CRC8
if (!G433_crc8(buffer, bytes)) size = bytes - 1;
} else if (CRC_MODE == G433_XOR) { // CRC XOR
if (!G433_crc_xor(buffer, bytes)) size = bytes - 1;
} else size = bytes; // без CRC
}
#ifndef G433_CUT_RSSI
if (!size) errCount++; // принято 0 байт - ошибка
if (++rcCount >= G433_RSSI_COUNT) {
RSSI = 100 - errCount * 100 / G433_RSSI_COUNT;
errCount = rcCount = 0;
}
#endif
state = 0;
return size; return size;
} else return 0; }
return 0;
} }
// прочитает буфер в любой тип данных // прочитает буфер в любой тип данных
template <typename T> template <typename T>
bool readData(T &data) { bool readData(T &data) {
if (sizeof(T) > G433_BUFSIZE) return false; if (sizeof(T) > RX_BUF) return false; // великовато для буфера
if (sizeof(T) != size) return false; // данные не соответствуют
uint8_t *ptr = (uint8_t*) &data; uint8_t *ptr = (uint8_t*) &data;
for (uint16_t i = 0; i < sizeof(T); i++) *ptr++ = buffer[i]; for (uint16_t i = 0; i < sizeof(T); i++) *ptr++ = buffer[i];
return true; return true;
} }
// получить качество приёма (процент успешных передач)
uint8_t getRSSI() {
#ifndef G433_CUT_RSSI
return RSSI;
#endif
}
// получить размер принятых данных // получить размер принятых данных
int getSize() { uint16_t getSize() {
return size; return size;
} }
int size = 0; // размер принятых данных
uint16_t size = 0;
// доступ к буферу
uint8_t buffer[RX_BUF + !!CRC_MODE];
uint8_t byteBuf;
private: private:
bool fastDR() { bool fastRead(const uint8_t pin) {
#if defined(__AVR__) #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
return bool(*_pin_reg & _bit_mask); if (pin < 8) return bitRead(PIND, pin);
else if (pin < 14) return bitRead(PINB, pin - 8);
else if (pin < 20) return bitRead(PINC, pin - 14);
#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__)
return bitRead(PINB, pin);
#else #else
return digitalRead(_pin); return digitalRead(pin);
#endif #endif
return 0;
} }
uint8_t buffer[G433_BUFSIZE];
bool prevState; bool pinChanged() {
uint8_t parse = 0; if (prevBit != fastRead(RX_PIN)) {
uint32_t tmr = 0; prevBit = !prevBit;
uint8_t bitCount = 0, byteCount = 0; return 1;
#if defined(__AVR__) } return 0;
volatile uint8_t *_pin_reg; }
volatile uint8_t _bit_mask;
bool prevBit;
volatile uint8_t state = 0;
volatile uint8_t parse = 0, trains = 0;
volatile uint32_t tmr = 0;
uint8_t bits = 0, bytes = 0;
#ifndef G433_CUT_RSSI
uint8_t errCount = 0, rcCount = 0, RSSI = 100;
#endif #endif
}; };
void G433_crc_byte(uint8_t &crc, uint8_t data) {
#if defined (__AVR__)
// резкий алгоритм для AVR
uint8_t counter;
uint8_t buffer;
asm volatile (
"EOR %[crc_out], %[data_in] \n\t"
"LDI %[counter], 8 \n\t"
"LDI %[buffer], 0x8C \n\t"
"_loop_start_%=: \n\t"
"LSR %[crc_out] \n\t"
"BRCC _loop_end_%= \n\t"
"EOR %[crc_out], %[buffer] \n\t"
"_loop_end_%=: \n\t"
"DEC %[counter] \n\t"
"BRNE _loop_start_%="
: [crc_out]"=r" (crc), [counter]"=d" (counter), [buffer]"=d" (buffer)
: [crc_in]"0" (crc), [data_in]"r" (data)
);
#else
// обычный для всех остальных
uint8_t i = 8;
while (i--) {
crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1);
data >>= 1;
}
#endif
}
uint8_t G433_crc(uint8_t *buffer, uint8_t size) {
uint8_t crc = 0;
for (uint8_t i = 0; i < size; i++) G433_crc_byte(crc, buffer[i]);
return crc;
}
#endif #endif