SubGhz: sending / receiving messages via subghz (#851)
* SubGhz: add worker subghz_txrx * SubGhz: added support for transferring Russian characters and support for backspace in CLI subghz_txrx * SubGhz: refactoring subghz_txrx_worker, added a callback for accepting data in an empty one RX buffer * SubGhz: fix conflict * SubGhz: fix syntax errors * Cli: document string_move usage and its behavior * FuriHal: update subghz api and documentation. Subghz: move chat to subghz cli subcommand. * Subghz: update text in chat cli Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
@@ -22,14 +22,14 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
while(hal_gpio_read(handle->miso));
|
||||
furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
|
||||
|
||||
assert((rx[0].CHIP_RDYn|rx[1].CHIP_RDYn) == 0);
|
||||
assert((rx[0].CHIP_RDYn | rx[1].CHIP_RDYn) == 0);
|
||||
return rx[1];
|
||||
}
|
||||
|
||||
CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) {
|
||||
assert(sizeof(CC1101Status) == 1);
|
||||
uint8_t tx[2] = { reg|CC1101_READ, 0};
|
||||
CC1101Status rx[2] = { 0 };
|
||||
uint8_t tx[2] = {reg | CC1101_READ, 0};
|
||||
CC1101Status rx[2] = {0};
|
||||
|
||||
while(hal_gpio_read(handle->miso));
|
||||
furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
|
||||
@@ -58,8 +58,6 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) {
|
||||
}
|
||||
|
||||
void cc1101_reset(FuriHalSpiBusHandle* handle) {
|
||||
delay_us(1000);
|
||||
delay_us(1000);
|
||||
cc1101_strobe(handle, CC1101_STROBE_SRES);
|
||||
}
|
||||
|
||||
@@ -130,7 +128,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) {
|
||||
while(hal_gpio_read(handle->miso));
|
||||
furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, sizeof(rx), CC1101_TIMEOUT);
|
||||
|
||||
assert((rx[0].CHIP_RDYn|rx[8].CHIP_RDYn) == 0);
|
||||
assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0);
|
||||
}
|
||||
|
||||
uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) {
|
||||
@@ -159,9 +157,14 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si
|
||||
|
||||
// First byte - packet length
|
||||
furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT);
|
||||
*size = buff_rx[1];
|
||||
|
||||
// Check that the packet is placed in the receive buffer
|
||||
if(buff_rx[1] > 64) {
|
||||
*size = 64;
|
||||
} else {
|
||||
*size = buff_rx[1];
|
||||
}
|
||||
furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT);
|
||||
cc1101_flush_rx(handle);
|
||||
|
||||
return *size;
|
||||
}
|
||||
|
@@ -89,22 +89,21 @@ extern "C" {
|
||||
#define CC1101_STATUS_PARTNUM 0x30 /** Chip ID Part Number */
|
||||
#define CC1101_STATUS_VERSION 0x31 /** Chip ID Version */
|
||||
#define CC1101_STATUS_FREQEST 0x32 /** Frequency Offset Estimate from Demodulator */
|
||||
#define CC1101_STATUS_LQI 0x33 /** Demodulator Estimate for Link Quality */
|
||||
#define CC1101_STATUS_LQI 0x33 /** Demodulator Estimate for Link Quality, 7bit-CRC, 6..0-LQI*/
|
||||
#define CC1101_STATUS_RSSI 0x34 /** Received Signal Strength Indication */
|
||||
#define CC1101_STATUS_MARCSTATE 0x35 /** Main Radio Control State Machine State */
|
||||
#define CC1101_STATUS_WORTIME1 0x36 /** High Byte of WOR Time */
|
||||
#define CC1101_STATUS_WORTIME0 0x37 /** Low Byte of WOR Time */
|
||||
#define CC1101_STATUS_PKTSTATUS 0x38 /** Current GDOx Status and Packet Status */
|
||||
#define CC1101_STATUS_VCO_VC_DAC 0x39 /** Current Setting from PLL Calibration Module */
|
||||
#define CC1101_STATUS_TXBYTES 0x3A /** Underflow and Number of Bytes */
|
||||
#define CC1101_STATUS_RXBYTES 0x3B /** Overflow and Number of Bytes */
|
||||
#define CC1101_STATUS_TXBYTES 0x3A /** Underflow and Number of Bytes, 7bit-Underflow, 6..0-Number of Bytes*/
|
||||
#define CC1101_STATUS_RXBYTES 0x3B /** Overflow and Number of Bytes, 7bit-Overflow*, 6..0-Number of Bytes*/
|
||||
#define CC1101_STATUS_RCCTRL1_STATUS 0x3C /** Last RC Oscillator Calibration Result */
|
||||
#define CC1101_STATUS_RCCTRL0_STATUS 0x3D /** Last RC Oscillator Calibration Result */
|
||||
|
||||
/* Some special registers, use CC1101_BURST to read/write data */
|
||||
#define CC1101_PATABLE 0x3E /** PATABLE register number, an 8-byte table that defines the PA control settings */
|
||||
#define CC1101_FIFO 0x3F /** FIFO register nunmber, can be combined with CC1101_WRITE and/or CC1101_BURST */
|
||||
|
||||
#define CC1101_IOCFG_INV (1<<6) /** IOCFG inversion */
|
||||
|
||||
typedef enum {
|
||||
|
273
lib/subghz/subghz_tx_rx_worker.c
Normal file
273
lib/subghz/subghz_tx_rx_worker.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "subghz_tx_rx_worker.h"
|
||||
|
||||
#include <stream_buffer.h>
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "SubGhzTxRxWorker"
|
||||
|
||||
#define GUBGHZ_TXRX_WORKER_BUF_SIZE 2048
|
||||
//you can not set more than 62 because it will not fit into the FIFO CC1101
|
||||
#define GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60
|
||||
|
||||
#define GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40
|
||||
|
||||
struct SubGhzTxRxWorker {
|
||||
FuriThread* thread;
|
||||
StreamBufferHandle_t stream_tx;
|
||||
StreamBufferHandle_t stream_rx;
|
||||
|
||||
volatile bool worker_running;
|
||||
volatile bool worker_stoping;
|
||||
|
||||
SubGhzTxRxWorkerStatus satus;
|
||||
|
||||
uint32_t frequency;
|
||||
|
||||
SubGhzTxRxWorkerCallbackHaveRead callback_have_read;
|
||||
void* context_have_read;
|
||||
};
|
||||
|
||||
bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {
|
||||
furi_assert(instance);
|
||||
bool ret = false;
|
||||
size_t stream_tx_free_byte = xStreamBufferSpacesAvailable(instance->stream_tx);
|
||||
if(size && (stream_tx_free_byte >= size)) {
|
||||
if(xStreamBufferSend(
|
||||
instance->stream_tx, data, size, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) ==
|
||||
size) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) {
|
||||
furi_assert(instance);
|
||||
return xStreamBufferBytesAvailable(instance->stream_rx);
|
||||
}
|
||||
|
||||
size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {
|
||||
furi_assert(instance);
|
||||
size_t len = 0;
|
||||
size_t stream_rx_byte = xStreamBufferBytesAvailable(instance->stream_rx);
|
||||
|
||||
if(stream_rx_byte > 0) {
|
||||
if(stream_rx_byte <= size) {
|
||||
len = xStreamBufferReceive(
|
||||
instance->stream_rx,
|
||||
data,
|
||||
stream_rx_byte,
|
||||
GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
|
||||
} else {
|
||||
len = xStreamBufferReceive(
|
||||
instance->stream_rx, data, size, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void subghz_tx_rx_worker_set_callback_have_read(
|
||||
SubGhzTxRxWorker* instance,
|
||||
SubGhzTxRxWorkerCallbackHaveRead callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
furi_assert(context);
|
||||
instance->callback_have_read = callback;
|
||||
instance->context_have_read = context;
|
||||
}
|
||||
|
||||
bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) {
|
||||
uint8_t timeout = 20;
|
||||
bool ret = false;
|
||||
if(instance->satus != SubGhzTxRxWorkerStatusRx) {
|
||||
furi_hal_subghz_rx();
|
||||
instance->satus = SubGhzTxRxWorkerStatusRx;
|
||||
osDelay(1);
|
||||
}
|
||||
//waiting for reception to complete
|
||||
while(hal_gpio_read(&gpio_cc1101_g0)) {
|
||||
osDelay(1);
|
||||
if(!--timeout) {
|
||||
FURI_LOG_W(TAG, "RX cc1101_g0 timeout");
|
||||
furi_hal_subghz_flush_rx();
|
||||
furi_hal_subghz_rx();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(furi_hal_subghz_rx_pipe_not_empty()) {
|
||||
if(furi_hal_subghz_is_rx_data_crc_valid()) {
|
||||
furi_hal_subghz_read_packet(data, size);
|
||||
ret = true;
|
||||
}
|
||||
furi_hal_subghz_flush_rx();
|
||||
furi_hal_subghz_rx();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) {
|
||||
uint8_t timeout = 40;
|
||||
if(instance->satus != SubGhzTxRxWorkerStatusIDLE) {
|
||||
furi_hal_subghz_idle();
|
||||
}
|
||||
furi_hal_subghz_write_packet(data, size);
|
||||
instance->satus = SubGhzTxRxWorkerStatusTx;
|
||||
|
||||
furi_hal_subghz_tx(); //start send
|
||||
|
||||
while(!hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted
|
||||
osDelay(1);
|
||||
if(!--timeout) {
|
||||
FURI_LOG_W(TAG, "TX !cc1101_g0 timeout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet
|
||||
osDelay(1);
|
||||
if(!--timeout) {
|
||||
FURI_LOG_W(TAG, "TX cc1101_g0 timeout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_hal_subghz_idle();
|
||||
instance->satus = SubGhzTxRxWorkerStatusIDLE;
|
||||
}
|
||||
/** Worker thread
|
||||
*
|
||||
* @param context
|
||||
* @return exit code
|
||||
*/
|
||||
static int32_t subghz_tx_rx_worker_thread(void* context) {
|
||||
SubGhzTxRxWorker* instance = context;
|
||||
FURI_LOG_I(TAG, "Worker start");
|
||||
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_idle();
|
||||
furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync);
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
furi_hal_subghz_set_frequency_and_path(instance->frequency);
|
||||
furi_hal_subghz_flush_rx();
|
||||
|
||||
uint8_t data[GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE] = {0};
|
||||
size_t size_tx = 0;
|
||||
uint8_t size_rx[1] = {0};
|
||||
uint8_t timeout_tx = 0;
|
||||
bool callback_rx = false;
|
||||
|
||||
while(instance->worker_running) {
|
||||
//transmit
|
||||
size_tx = xStreamBufferBytesAvailable(instance->stream_tx);
|
||||
if(size_tx > 0 && !timeout_tx) {
|
||||
timeout_tx = 20; //20ms
|
||||
if(size_tx > GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) {
|
||||
xStreamBufferReceive(
|
||||
instance->stream_tx,
|
||||
&data,
|
||||
GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE,
|
||||
GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
|
||||
subghz_tx_rx_worker_tx(instance, data, GUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE);
|
||||
} else {
|
||||
//todo checking that he managed to write all the data to the TX buffer
|
||||
xStreamBufferReceive(
|
||||
instance->stream_tx, &data, size_tx, GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
|
||||
subghz_tx_rx_worker_tx(instance, data, size_tx);
|
||||
}
|
||||
} else {
|
||||
//recive
|
||||
if(subghz_tx_rx_worker_rx(instance, data, size_rx)) {
|
||||
if(xStreamBufferSpacesAvailable(instance->stream_rx) >= size_rx[0]) {
|
||||
if(instance->callback_have_read &&
|
||||
xStreamBufferBytesAvailable(instance->stream_rx) == 0) {
|
||||
callback_rx = true;
|
||||
}
|
||||
//todo checking that he managed to write all the data to the RX buffer
|
||||
xStreamBufferSend(
|
||||
instance->stream_rx,
|
||||
&data,
|
||||
size_rx[0],
|
||||
GUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF);
|
||||
if(callback_rx) {
|
||||
instance->callback_have_read(instance->context_have_read);
|
||||
callback_rx = false;
|
||||
}
|
||||
} else {
|
||||
//todo RX buffer overflow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(timeout_tx) timeout_tx--;
|
||||
osDelay(1);
|
||||
}
|
||||
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||
furi_hal_subghz_sleep();
|
||||
|
||||
FURI_LOG_I(TAG, "Worker stop");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() {
|
||||
SubGhzTxRxWorker* instance = furi_alloc(sizeof(SubGhzTxRxWorker));
|
||||
|
||||
instance->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(instance->thread, "SubghzTxRxWorker");
|
||||
furi_thread_set_stack_size(instance->thread, 2048);
|
||||
furi_thread_set_context(instance->thread, instance);
|
||||
furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread);
|
||||
instance->stream_tx =
|
||||
xStreamBufferCreate(sizeof(uint8_t) * GUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
|
||||
instance->stream_rx =
|
||||
xStreamBufferCreate(sizeof(uint8_t) * GUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t));
|
||||
|
||||
instance->satus = SubGhzTxRxWorkerStatusIDLE;
|
||||
instance->worker_stoping = true;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
vStreamBufferDelete(instance->stream_tx);
|
||||
vStreamBufferDelete(instance->stream_rx);
|
||||
furi_thread_free(instance->thread);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
furi_assert(!instance->worker_running);
|
||||
bool res = false;
|
||||
xStreamBufferReset(instance->stream_tx);
|
||||
xStreamBufferReset(instance->stream_rx);
|
||||
|
||||
instance->worker_running = true;
|
||||
|
||||
furi_thread_start(instance->thread);
|
||||
|
||||
if(furi_hal_subghz_is_tx_allowed(frequency)) {
|
||||
instance->frequency = frequency;
|
||||
res = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->worker_running);
|
||||
|
||||
instance->worker_running = false;
|
||||
|
||||
furi_thread_join(instance->thread);
|
||||
}
|
||||
|
||||
bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->worker_running;
|
||||
}
|
81
lib/subghz/subghz_tx_rx_worker.h
Normal file
81
lib/subghz/subghz_tx_rx_worker.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi-hal.h>
|
||||
|
||||
typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context);
|
||||
|
||||
typedef struct SubGhzTxRxWorker SubGhzTxRxWorker;
|
||||
|
||||
typedef enum {
|
||||
SubGhzTxRxWorkerStatusIDLE,
|
||||
SubGhzTxRxWorkerStatusTx,
|
||||
SubGhzTxRxWorkerStatusRx,
|
||||
} SubGhzTxRxWorkerStatus;
|
||||
|
||||
/** SubGhzTxRxWorker, add data to transfer
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
* @param data *data
|
||||
* @param size data size
|
||||
* @return bool true if ok
|
||||
*/
|
||||
bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size);
|
||||
|
||||
/** SubGhzTxRxWorker, get available data
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
* @return size_t data size
|
||||
*/
|
||||
size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance);
|
||||
|
||||
/** SubGhzTxRxWorker, read data
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
* @param data *data
|
||||
* @param size max data size, which can be read
|
||||
* @return size_t data size, how much is actually read
|
||||
*/
|
||||
size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size);
|
||||
|
||||
/** Сallback SubGhzTxRxWorker when there is data to read in an empty buffer
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
* @param callback SubGhzTxRxWorkerCallbackHaveRead callback
|
||||
* @param context
|
||||
*/
|
||||
void subghz_tx_rx_worker_set_callback_have_read(
|
||||
SubGhzTxRxWorker* instance,
|
||||
SubGhzTxRxWorkerCallbackHaveRead callback,
|
||||
void* context);
|
||||
|
||||
/** Allocate SubGhzTxRxWorker
|
||||
*
|
||||
* @return SubGhzTxRxWorker*
|
||||
*/
|
||||
SubGhzTxRxWorker* subghz_tx_rx_worker_alloc();
|
||||
|
||||
/** Free SubGhzTxRxWorker
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
*/
|
||||
void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance);
|
||||
|
||||
/** Start SubGhzTxRxWorker
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
* @return bool - true if ok
|
||||
*/
|
||||
bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency);
|
||||
|
||||
/** Stop SubGhzTxRxWorker
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
*/
|
||||
void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance);
|
||||
|
||||
/** Check if worker is running
|
||||
*
|
||||
* @param instance SubGhzTxRxWorker instance
|
||||
* @return bool - true if running
|
||||
*/
|
||||
bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance);
|
Reference in New Issue
Block a user