2
.vscode/arduino.json
vendored
2
.vscode/arduino.json
vendored
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"board": "bsfrance:avr:lora32u4",
|
"board": "bsfrance:avr:lora32u4",
|
||||||
"sketch": "crossbow/crossbow.ino",
|
"sketch": "crossbow/crossbow.ino",
|
||||||
"port": "COM5",
|
"port": "COM7",
|
||||||
"output": "../build"
|
"output": "../build"
|
||||||
}
|
}
|
||||||
19
.vscode/settings.json
vendored
19
.vscode/settings.json
vendored
@@ -10,7 +10,24 @@
|
|||||||
"__locale": "c",
|
"__locale": "c",
|
||||||
"string": "c",
|
"string": "c",
|
||||||
"ios": "cpp",
|
"ios": "cpp",
|
||||||
"locale": "cpp"
|
"locale": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"__bit_reference": "cpp",
|
||||||
|
"__functional_base": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"vector": "cpp"
|
||||||
},
|
},
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"**/build": true
|
"**/build": true
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -47,6 +47,7 @@ CRC is computed using `crc8_dvb_s2` method. Initial CRC value for each frame CRC
|
|||||||
| 0100 | 0x4 | Set receiver configuration | TX -> RX | no used |
|
| 0100 | 0x4 | Set receiver configuration | TX -> RX | no used |
|
||||||
| 0101 | 0x5 | PING frame, uses 9 byte payload | TX -> RX | 4 |
|
| 0101 | 0x5 | PING frame, uses 9 byte payload | TX -> RX | 4 |
|
||||||
| 0110 | 0x6 | PONG frame, the same payload as PING | RX -> TX | 4 |
|
| 0110 | 0x6 | PONG frame, the same payload as PING | RX -> TX | 4 |
|
||||||
|
| 0111 | 0x7 | `BIND` frame, transmitted by TX only during binding | TX -> RX | 4 |
|
||||||
|
|
||||||
### `RC_DATA` frame format
|
### `RC_DATA` frame format
|
||||||
|
|
||||||
@@ -82,6 +83,15 @@ Total length of `RC_DATA` payload is 9 bytes
|
|||||||
**TX** sends `PING` frame with curent `micros`. If **RX** receives `PING` frame, it respons
|
**TX** sends `PING` frame with curent `micros`. If **RX** receives `PING` frame, it respons
|
||||||
its payload as `PONG` frame.
|
its payload as `PONG` frame.
|
||||||
|
|
||||||
|
### `BIND` frame format
|
||||||
|
|
||||||
|
| Byte | Description |
|
||||||
|
| ---- | ---- |
|
||||||
|
| 1 | Bind key byte 0 |
|
||||||
|
| 2 | Bind key byte 1 |
|
||||||
|
| 3 | Bind key byte 2 |
|
||||||
|
| 4 | Bind key byte 3 |
|
||||||
|
|
||||||
# RSSI
|
# RSSI
|
||||||
|
|
||||||
1. Receiver RSSI for the last received packet is injected as channel 11
|
1. Receiver RSSI for the last received packet is injected as channel 11
|
||||||
@@ -117,6 +127,20 @@ That mean the following:
|
|||||||
* On F3 or F7 boards flight controller has to be configured not to use inverted SBUS (refer to flight controller docs)
|
* On F3 or F7 boards flight controller has to be configured not to use inverted SBUS (refer to flight controller docs)
|
||||||
* On F4 flight controllers inverios has to be configured only when using dedicated SBUS serial port
|
* On F4 flight controllers inverios has to be configured only when using dedicated SBUS serial port
|
||||||
|
|
||||||
|
# Manual
|
||||||
|
|
||||||
|
## Binding
|
||||||
|
|
||||||
|
After flashing TX and RX, binding is required.
|
||||||
|
|
||||||
|
1. Power up TX module
|
||||||
|
1. Navigate using button #1 to "Bind" option
|
||||||
|
1. Long press button #2 to enter _Bind Mode_
|
||||||
|
1. Power up RX
|
||||||
|
1. RX LED flashes quickly when in bind mode
|
||||||
|
1. After RX receives bind packet, LED goes to constanly _ON_ state
|
||||||
|
1. When RX LED is solid _ON_, leave bind mode by long pressing button #2
|
||||||
|
|
||||||
# TX module connection diagram
|
# TX module connection diagram
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
* DEVICE_MODE_TX
|
* DEVICE_MODE_TX
|
||||||
* DEVICE_MODE_RX
|
* DEVICE_MODE_RX
|
||||||
*/
|
*/
|
||||||
#define DEVICE_MODE_TX
|
// #define DEVICE_MODE_TX
|
||||||
// #define DEVICE_MODE_RX
|
#define DEVICE_MODE_RX
|
||||||
|
|
||||||
#define FEATURE_TX_OLED
|
#define FEATURE_TX_OLED
|
||||||
// #define FORCE_TX_WITHOUT_INPUT
|
// #define FORCE_TX_WITHOUT_INPUT
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef CONFIG_H
|
#ifndef CONFIG_H
|
||||||
#define CONFIG_H
|
#define CONFIG_H
|
||||||
|
|
||||||
#define DEVICE_MODE TX
|
#define DEVICE_MODE_TX
|
||||||
|
|
||||||
#define FEATURE_TX_OLED
|
#define FEATURE_TX_OLED
|
||||||
#define FEATURE_TX_INPUT_SBUS
|
#define FEATURE_TX_INPUT_SBUS
|
||||||
|
|||||||
@@ -100,9 +100,12 @@ void onQspSuccess(QspConfiguration_t *qsp, TxDeviceState_t *txDeviceState, RxDev
|
|||||||
* RX module hops to next channel after frame has been received
|
* RX module hops to next channel after frame has been received
|
||||||
*/
|
*/
|
||||||
#ifdef DEVICE_MODE_RX
|
#ifdef DEVICE_MODE_RX
|
||||||
radioNode.hopFrequency(true, radioNode.lastReceivedChannel, millis());
|
if (!platformNode.isBindMode) {
|
||||||
radioNode.failedDwellsCount = 0; // We received a frame, so we can just reset this counter
|
//We do not hop frequency in bind mode!
|
||||||
LoRa.receive(); //Put radio back into receive mode
|
radioNode.hopFrequency(true, radioNode.lastReceivedChannel, millis());
|
||||||
|
radioNode.failedDwellsCount = 0; // We received a frame, so we can just reset this counter
|
||||||
|
LoRa.receive(); //Put radio back into receive mode
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Store the last timestamp when frame was received
|
//Store the last timestamp when frame was received
|
||||||
@@ -123,6 +126,20 @@ void onQspSuccess(QspConfiguration_t *qsp, TxDeviceState_t *txDeviceState, RxDev
|
|||||||
qsp->forcePongFrame = true;
|
qsp->forcePongFrame = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case QSP_FRAME_BIND:
|
||||||
|
#ifdef DEVICE_MODE_RX
|
||||||
|
if (platformNode.isBindMode) {
|
||||||
|
platformNode.bindKey[0] = qsp->payload[0];
|
||||||
|
platformNode.bindKey[1] = qsp->payload[1];
|
||||||
|
platformNode.bindKey[2] = qsp->payload[2];
|
||||||
|
platformNode.bindKey[3] = qsp->payload[3];
|
||||||
|
|
||||||
|
platformNode.saveBindKey(platformNode.bindKey);
|
||||||
|
platformNode.leaveBindMode();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
case QSP_FRAME_PONG:
|
case QSP_FRAME_PONG:
|
||||||
txDeviceState->roundtrip = qsp->payload[0];
|
txDeviceState->roundtrip = qsp->payload[0];
|
||||||
txDeviceState->roundtrip += (uint32_t) qsp->payload[1] << 8;
|
txDeviceState->roundtrip += (uint32_t) qsp->payload[1] << 8;
|
||||||
@@ -145,6 +162,16 @@ void onQspFailure(QspConfiguration_t *qsp, TxDeviceState_t *txDeviceState, RxDev
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//I do not get function pointers to object methods, no way...
|
||||||
|
int getRcChannel_wrapper(uint8_t channel) {
|
||||||
|
return platformNode.getRcChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Same here, wrapper just works
|
||||||
|
void setRcChannel_wrapper(uint8_t channel, int value, int offset) {
|
||||||
|
platformNode.setRcChannel(channel, value, offset);
|
||||||
|
}
|
||||||
|
|
||||||
void setup(void)
|
void setup(void)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_SERIAL
|
#ifdef DEBUG_SERIAL
|
||||||
@@ -153,11 +180,16 @@ void setup(void)
|
|||||||
|
|
||||||
qsp.onSuccessCallback = onQspSuccess;
|
qsp.onSuccessCallback = onQspSuccess;
|
||||||
qsp.onFailureCallback = onQspFailure;
|
qsp.onFailureCallback = onQspFailure;
|
||||||
|
qsp.rcChannelGetCallback = getRcChannel_wrapper;
|
||||||
|
qsp.setRcChannelCallback = setRcChannel_wrapper;
|
||||||
|
|
||||||
#ifdef DEVICE_MODE_RX
|
#ifdef DEVICE_MODE_RX
|
||||||
platformNode.platformState = DEVICE_STATE_FAILSAFE;
|
platformNode.platformState = DEVICE_STATE_FAILSAFE;
|
||||||
#else
|
#else
|
||||||
platformNode.platformState = DEVICE_STATE_OK;
|
platformNode.platformState = DEVICE_STATE_OK;
|
||||||
|
|
||||||
|
txInput.setRcChannelCallback = setRcChannel_wrapper;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
radioNode.init(LORA_SS_PIN, LORA_RST_PIN, LORA_DI0_PIN, onReceive);
|
radioNode.init(LORA_SS_PIN, LORA_RST_PIN, LORA_DI0_PIN, onReceive);
|
||||||
@@ -172,10 +204,16 @@ void setup(void)
|
|||||||
* Prepare Serial1 for S.Bus processing
|
* Prepare Serial1 for S.Bus processing
|
||||||
*/
|
*/
|
||||||
Serial1.begin(100000, SERIAL_8E2);
|
Serial1.begin(100000, SERIAL_8E2);
|
||||||
|
|
||||||
|
platformNode.enterBindMode();
|
||||||
|
LoRa.receive(); //TODO this probably should be moved somewhere....
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEVICE_MODE_TX
|
#ifdef DEVICE_MODE_TX
|
||||||
|
|
||||||
|
randomSeed(analogRead(A4));
|
||||||
|
platformNode.seed();
|
||||||
|
|
||||||
#ifdef FEATURE_TX_OLED
|
#ifdef FEATURE_TX_OLED
|
||||||
oled.init();
|
oled.init();
|
||||||
oled.page(TX_PAGE_INIT);
|
oled.page(TX_PAGE_INIT);
|
||||||
@@ -202,18 +240,11 @@ void setup(void)
|
|||||||
button0.start();
|
button0.start();
|
||||||
button1.start();
|
button1.start();
|
||||||
|
|
||||||
|
platformNode.loadBindKey(platformNode.bindKey);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup salt bind key
|
|
||||||
*/
|
|
||||||
platformNode.bindKey[0] = 0x12;
|
|
||||||
platformNode.bindKey[1] = 0x0a;
|
|
||||||
platformNode.bindKey[2] = 0x36;
|
|
||||||
platformNode.bindKey[3] = 0xa7;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t currentSequenceIndex = 0;
|
uint8_t currentSequenceIndex = 0;
|
||||||
@@ -248,6 +279,11 @@ int8_t getFrameToTransmit(QspConfiguration_t *qsp) {
|
|||||||
|
|
||||||
#ifdef DEVICE_MODE_TX
|
#ifdef DEVICE_MODE_TX
|
||||||
int8_t getFrameToTransmit(QspConfiguration_t *qsp) {
|
int8_t getFrameToTransmit(QspConfiguration_t *qsp) {
|
||||||
|
|
||||||
|
if (platformNode.isBindMode) {
|
||||||
|
return QSP_FRAME_BIND;
|
||||||
|
}
|
||||||
|
|
||||||
int8_t retVal = txSendSequence[currentSequenceIndex];
|
int8_t retVal = txSendSequence[currentSequenceIndex];
|
||||||
|
|
||||||
currentSequenceIndex++;
|
currentSequenceIndex++;
|
||||||
@@ -267,13 +303,24 @@ int8_t getFrameToTransmit(QspConfiguration_t *qsp) {
|
|||||||
void loop(void)
|
void loop(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static uint32_t nextKey = millis();
|
||||||
|
|
||||||
uint32_t currentMillis = millis();
|
uint32_t currentMillis = millis();
|
||||||
|
|
||||||
#ifdef DEVICE_MODE_RX
|
#ifdef DEVICE_MODE_RX
|
||||||
|
|
||||||
|
//Make sure to leave bind mode when binding is done
|
||||||
|
if (platformNode.isBindMode && millis() > platformNode.bindModeExitMillis) {
|
||||||
|
platformNode.leaveBindMode();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine handles resync of TX/RX while hoppping frequencies
|
* This routine handles resync of TX/RX while hoppping frequencies
|
||||||
|
* When not in bind mode. Bind mode is single frequency operation
|
||||||
*/
|
*/
|
||||||
radioNode.handleChannelDwell();
|
if (!platformNode.isBindMode) {
|
||||||
|
radioNode.handleChannelDwell();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect the moment when radio module stopped transmittig and put it
|
* Detect the moment when radio module stopped transmittig and put it
|
||||||
@@ -305,13 +352,14 @@ void loop(void)
|
|||||||
serialRestartMillis = currentMillis;
|
serialRestartMillis = currentMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
radioNode.handleTxDoneState(true);
|
radioNode.handleTxDoneState(!platformNode.isBindMode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
radioNode.readAndDecode(
|
radioNode.readAndDecode(
|
||||||
&qsp,
|
&qsp,
|
||||||
&rxDeviceState,
|
&rxDeviceState,
|
||||||
&txDeviceState
|
&txDeviceState,
|
||||||
|
platformNode.bindKey
|
||||||
);
|
);
|
||||||
|
|
||||||
bool transmitPayload = false;
|
bool transmitPayload = false;
|
||||||
@@ -361,6 +409,18 @@ void loop(void)
|
|||||||
case QSP_FRAME_RC_DATA:
|
case QSP_FRAME_RC_DATA:
|
||||||
encodeRcDataPayload(&qsp, PLATFORM_CHANNEL_COUNT);
|
encodeRcDataPayload(&qsp, PLATFORM_CHANNEL_COUNT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case QSP_FRAME_BIND:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Key to be transmitted is stored in EEPROM
|
||||||
|
* During binding different key is used
|
||||||
|
*/
|
||||||
|
uint8_t key[4];
|
||||||
|
platformNode.loadBindKey(key);
|
||||||
|
|
||||||
|
encodeBindPayload(&qsp, key);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
transmitPayload = true;
|
transmitPayload = true;
|
||||||
@@ -405,7 +465,13 @@ void loop(void)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case QSP_FRAME_RX_HEALTH:
|
case QSP_FRAME_RX_HEALTH:
|
||||||
encodeRxHealthPayload(&qsp, &rxDeviceState, radioNode.rssi, radioNode.snr);
|
encodeRxHealthPayload(
|
||||||
|
&qsp,
|
||||||
|
&rxDeviceState,
|
||||||
|
radioNode.rssi,
|
||||||
|
radioNode.snr,
|
||||||
|
(platformNode.platformState == DEVICE_STATE_FAILSAFE)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,7 +483,7 @@ void loop(void)
|
|||||||
if (currentMillis > sbusTime) {
|
if (currentMillis > sbusTime) {
|
||||||
platformNode.setRcChannel(RSSI_CHANNEL - 1, rxDeviceState.indicatedRssi, 0);
|
platformNode.setRcChannel(RSSI_CHANNEL - 1, rxDeviceState.indicatedRssi, 0);
|
||||||
|
|
||||||
sbusPreparePacket(sbusPacket, false, (platformNode.platformState == DEVICE_STATE_FAILSAFE));
|
sbusPreparePacket(sbusPacket, false, (platformNode.platformState == DEVICE_STATE_FAILSAFE), getRcChannel_wrapper);
|
||||||
Serial1.write(sbusPacket, SBUS_PACKET_LENGTH);
|
Serial1.write(sbusPacket, SBUS_PACKET_LENGTH);
|
||||||
sbusTime = currentMillis + SBUS_UPDATE_RATE;
|
sbusTime = currentMillis + SBUS_UPDATE_RATE;
|
||||||
}
|
}
|
||||||
@@ -434,7 +500,7 @@ void loop(void)
|
|||||||
|
|
||||||
if (transmitPayload)
|
if (transmitPayload)
|
||||||
{
|
{
|
||||||
radioNode.handleTx(&qsp);
|
radioNode.handleTx(&qsp, platformNode.bindKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEVICE_MODE_TX
|
#ifdef DEVICE_MODE_TX
|
||||||
@@ -502,11 +568,18 @@ void loop(void)
|
|||||||
platformNode.nextLedUpdate = currentMillis + 200;
|
platformNode.nextLedUpdate = currentMillis + 200;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
platformNode.nextLedUpdate = currentMillis + 200;
|
|
||||||
if (platformNode.platformState == DEVICE_STATE_FAILSAFE) {
|
if (platformNode.isBindMode) {
|
||||||
digitalWrite(LED_BUILTIN, HIGH);
|
platformNode.nextLedUpdate = currentMillis + 50;
|
||||||
} else {
|
|
||||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||||
|
} else {
|
||||||
|
platformNode.nextLedUpdate = currentMillis + 200;
|
||||||
|
|
||||||
|
if (platformNode.platformState == DEVICE_STATE_FAILSAFE) {
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
} else {
|
||||||
|
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -520,7 +593,6 @@ void onReceive(int packetSize)
|
|||||||
* If not reading, then we might start
|
* If not reading, then we might start
|
||||||
*/
|
*/
|
||||||
if (radioNode.bytesToRead == NO_DATA_TO_READ) {
|
if (radioNode.bytesToRead == NO_DATA_TO_READ) {
|
||||||
|
|
||||||
if (packetSize >= MIN_PACKET_SIZE && packetSize <= MAX_PACKET_SIZE) {
|
if (packetSize >= MIN_PACKET_SIZE && packetSize <= MAX_PACKET_SIZE) {
|
||||||
//We have a packet candidate that might contain a valid QSP packet
|
//We have a packet candidate that might contain a valid QSP packet
|
||||||
radioNode.bytesToRead = packetSize;
|
radioNode.bytesToRead = packetSize;
|
||||||
|
|||||||
@@ -6,6 +6,38 @@ PlatformNode::PlatformNode(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if new bind key was generated
|
||||||
|
*/
|
||||||
|
void PlatformNode::seed(void) {
|
||||||
|
uint8_t val;
|
||||||
|
|
||||||
|
val = EEPROM.read(EEPROM_ADDRESS_BIND_KEY_SEEDED);
|
||||||
|
|
||||||
|
if (val != 0xf1) {
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_0, random(1, 255)); //Yes, from 1 to 254
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_1, random(1, 255)); //Yes, from 1 to 254
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_2, random(1, 255)); //Yes, from 1 to 254
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_3, random(1, 255)); //Yes, from 1 to 254
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_KEY_SEEDED, 0xf1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlatformNode::loadBindKey(uint8_t key[]) {
|
||||||
|
key[0] = EEPROM.read(EEPROM_ADDRESS_BIND_0);
|
||||||
|
key[1] = EEPROM.read(EEPROM_ADDRESS_BIND_1);
|
||||||
|
key[2] = EEPROM.read(EEPROM_ADDRESS_BIND_2);
|
||||||
|
key[3] = EEPROM.read(EEPROM_ADDRESS_BIND_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlatformNode::saveBindKey(uint8_t key[]) {
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_0, key[0]);
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_1, key[1]);
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_2, key[2]);
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_3, key[3]);
|
||||||
|
EEPROM.write(EEPROM_ADDRESS_BIND_KEY_SEEDED, 0xf1);
|
||||||
|
}
|
||||||
|
|
||||||
int PlatformNode::getRcChannel(uint8_t channel) {
|
int PlatformNode::getRcChannel(uint8_t channel) {
|
||||||
if (channel < PLATFORM_TOTAL_CHANNEL_COUNT) {
|
if (channel < PLATFORM_TOTAL_CHANNEL_COUNT) {
|
||||||
return _channels[channel];
|
return _channels[channel];
|
||||||
@@ -18,4 +50,29 @@ void PlatformNode::setRcChannel(uint8_t channel, int value, int offset) {
|
|||||||
if (channel < PLATFORM_TOTAL_CHANNEL_COUNT) {
|
if (channel < PLATFORM_TOTAL_CHANNEL_COUNT) {
|
||||||
_channels[channel] = value + offset;
|
_channels[channel] = value + offset;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlatformNode::enterBindMode(void) {
|
||||||
|
isBindMode = true;
|
||||||
|
|
||||||
|
// Set temporary bind mode
|
||||||
|
bindKey[0] = 0xf1;
|
||||||
|
bindKey[1] = 0x1e;
|
||||||
|
bindKey[2] = 0x07;
|
||||||
|
bindKey[3] = 0x42;
|
||||||
|
|
||||||
|
radioNode.set(
|
||||||
|
0, // Minimum power
|
||||||
|
125000, // 125kHz bandwidth
|
||||||
|
6, // low spreading factor, we do not need high RX sensitivity
|
||||||
|
5, // same for coding rate
|
||||||
|
868000000 //Fixed frequency while binding
|
||||||
|
);
|
||||||
|
bindModeExitMillis = millis() + 1000; //This happens only on RX
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlatformNode::leaveBindMode(void) {
|
||||||
|
isBindMode = false;
|
||||||
|
loadBindKey(bindKey);
|
||||||
|
radioNode.reset();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
#include "radio_node.h"
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
#ifndef PLATFORM_NODE_H
|
#ifndef PLATFORM_NODE_H
|
||||||
#define PLATFORM_NODE_H
|
#define PLATFORM_NODE_H
|
||||||
@@ -9,11 +11,22 @@
|
|||||||
#define PLATFORM_CHANNEL_COUNT 10
|
#define PLATFORM_CHANNEL_COUNT 10
|
||||||
#define PLATFORM_DEFAULT_CHANNEL_VALUE 1000
|
#define PLATFORM_DEFAULT_CHANNEL_VALUE 1000
|
||||||
|
|
||||||
|
extern RadioNode radioNode;
|
||||||
|
|
||||||
enum deviceStates {
|
enum deviceStates {
|
||||||
DEVICE_STATE_OK,
|
DEVICE_STATE_OK,
|
||||||
DEVICE_STATE_FAILSAFE,
|
DEVICE_STATE_FAILSAFE,
|
||||||
DEVICE_STATE_UNDETERMINED
|
DEVICE_STATE_UNDETERMINED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum platformConfigMemoryLayout {
|
||||||
|
EEPROM_ADDRESS_BIND_KEY_SEEDED = 0x00,
|
||||||
|
EEPROM_ADDRESS_BIND_0,
|
||||||
|
EEPROM_ADDRESS_BIND_1,
|
||||||
|
EEPROM_ADDRESS_BIND_2,
|
||||||
|
EEPROM_ADDRESS_BIND_3,
|
||||||
|
PLATFORM_CONFIG_LAST_BYTE
|
||||||
|
};
|
||||||
|
|
||||||
class PlatformNode {
|
class PlatformNode {
|
||||||
|
|
||||||
@@ -21,9 +34,16 @@ class PlatformNode {
|
|||||||
PlatformNode(void);
|
PlatformNode(void);
|
||||||
int getRcChannel(uint8_t channel);
|
int getRcChannel(uint8_t channel);
|
||||||
void setRcChannel(uint8_t channel, int value, int offset);
|
void setRcChannel(uint8_t channel, int value, int offset);
|
||||||
|
void enterBindMode(void);
|
||||||
|
void leaveBindMode(void);
|
||||||
|
void seed(void);
|
||||||
|
void loadBindKey(uint8_t key[]);
|
||||||
|
void saveBindKey(uint8_t key[]);
|
||||||
uint8_t bindKey[4];
|
uint8_t bindKey[4];
|
||||||
uint32_t nextLedUpdate = 0;
|
uint32_t nextLedUpdate = 0;
|
||||||
uint8_t platformState = DEVICE_STATE_UNDETERMINED;
|
uint8_t platformState = DEVICE_STATE_UNDETERMINED;
|
||||||
|
bool isBindMode = false;
|
||||||
|
uint32_t bindModeExitMillis;
|
||||||
private:
|
private:
|
||||||
volatile int _channels[PLATFORM_TOTAL_CHANNEL_COUNT];
|
volatile int _channels[PLATFORM_TOTAL_CHANNEL_COUNT];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
|
|
||||||
void qspDecodeRcDataFrame(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceSate) {
|
void qspDecodeRcDataFrame(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceSate) {
|
||||||
|
|
||||||
platformNode.setRcChannel(0, (uint16_t) (((uint16_t) qsp->payload[0] << 2) & 0x3fc) | ((qsp->payload[1] >> 6) & 0x03), 1000);
|
qsp->setRcChannelCallback(0, (uint16_t) (((uint16_t) qsp->payload[0] << 2) & 0x3fc) | ((qsp->payload[1] >> 6) & 0x03), 1000);
|
||||||
platformNode.setRcChannel(1, (uint16_t) (((uint16_t) qsp->payload[1] << 4) & 0x3f0) | ((qsp->payload[2] >> 4) & 0x0F), 1000);
|
qsp->setRcChannelCallback(1, (uint16_t) (((uint16_t) qsp->payload[1] << 4) & 0x3f0) | ((qsp->payload[2] >> 4) & 0x0F), 1000);
|
||||||
platformNode.setRcChannel(2, (uint16_t) (((uint16_t) qsp->payload[2] << 6) & 0x3c0) | ((qsp->payload[3] >> 2) & 0x3F), 1000);
|
qsp->setRcChannelCallback(2, (uint16_t) (((uint16_t) qsp->payload[2] << 6) & 0x3c0) | ((qsp->payload[3] >> 2) & 0x3F), 1000);
|
||||||
platformNode.setRcChannel(3, (uint16_t) (((uint16_t) qsp->payload[3] << 8) & 0x300) | ((qsp->payload[4]) & 0xFF), 1000);
|
qsp->setRcChannelCallback(3, (uint16_t) (((uint16_t) qsp->payload[3] << 8) & 0x300) | ((qsp->payload[4]) & 0xFF), 1000);
|
||||||
platformNode.setRcChannel(4, ((int) qsp->payload[5]) << 2, 1000);
|
qsp->setRcChannelCallback(4, ((int) qsp->payload[5]) << 2, 1000);
|
||||||
platformNode.setRcChannel(5, ((int) qsp->payload[6]) << 2, 1000);
|
qsp->setRcChannelCallback(5, ((int) qsp->payload[6]) << 2, 1000);
|
||||||
platformNode.setRcChannel(6, ((int) ((qsp->payload[7] >> 4) & 0b00001111)) << 6, 1000);
|
qsp->setRcChannelCallback(6, ((int) ((qsp->payload[7] >> 4) & 0b00001111)) << 6, 1000);
|
||||||
platformNode.setRcChannel(7, ((int) (qsp->payload[7] & 0b00001111)) << 6, 1000);
|
qsp->setRcChannelCallback(7, ((int) (qsp->payload[7] & 0b00001111)) << 6, 1000);
|
||||||
platformNode.setRcChannel(8, ((int) ((qsp->payload[8] >> 4) & 0b00001111)) << 6, 1000);
|
qsp->setRcChannelCallback(8, ((int) ((qsp->payload[8] >> 4) & 0b00001111)) << 6, 1000);
|
||||||
platformNode.setRcChannel(9, ((int) (qsp->payload[8] & 0b00001111)) << 6, 1000);
|
qsp->setRcChannelCallback(9, ((int) (qsp->payload[8] & 0b00001111)) << 6, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get10bitHighShift(uint8_t channel) {
|
uint8_t get10bitHighShift(uint8_t channel) {
|
||||||
@@ -41,7 +41,7 @@ void qspComputeCrc(QspConfiguration_t *qsp, uint8_t dataByte)
|
|||||||
qsp->crc = crc8_dvb_s2(qsp->crc, dataByte);
|
qsp->crc = crc8_dvb_s2(qsp->crc, dataByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
void encodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceState, uint8_t rssi, uint8_t snr) {
|
void encodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceState, uint8_t rssi, uint8_t snr, bool isFailsafe) {
|
||||||
qsp->payload[0] = rssi;
|
qsp->payload[0] = rssi;
|
||||||
qsp->payload[1] = snr;
|
qsp->payload[1] = snr;
|
||||||
qsp->payload[2] = rxDeviceState->rxVoltage;
|
qsp->payload[2] = rxDeviceState->rxVoltage;
|
||||||
@@ -50,7 +50,7 @@ void encodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceSta
|
|||||||
|
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
|
|
||||||
if (platformNode.platformState == DEVICE_STATE_FAILSAFE) {
|
if (isFailsafe) {
|
||||||
flags |= 0x01 << 0;
|
flags |= 0x01 << 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ void encodeRcDataPayload(QspConfiguration_t *qsp, uint8_t noOfChannels)
|
|||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < noOfChannels; i++)
|
for (uint8_t i = 0; i < noOfChannels; i++)
|
||||||
{
|
{
|
||||||
int cV = constrain(platformNode.getRcChannel(i), 1000, 2000) - 1000;
|
int cV = constrain(qsp->rcChannelGetCallback(i), 1000, 2000) - 1000;
|
||||||
|
|
||||||
uint16_t channelValue10 = cV & 0x03ff;
|
uint16_t channelValue10 = cV & 0x03ff;
|
||||||
uint8_t channelValue8 = (cV >> 2) & 0xff;
|
uint8_t channelValue8 = (cV >> 2) & 0xff;
|
||||||
@@ -133,10 +133,10 @@ void qspClearPayload(QspConfiguration_t *qsp)
|
|||||||
/**
|
/**
|
||||||
* Init CRC with salt based on 4 byte bind key
|
* Init CRC with salt based on 4 byte bind key
|
||||||
*/
|
*/
|
||||||
void qspInitCrc(QspConfiguration_t *qsp) {
|
void qspInitCrc(QspConfiguration_t *qsp, uint8_t bindKey[]) {
|
||||||
qsp->crc = 0;
|
qsp->crc = 0;
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
qspComputeCrc(qsp, platformNode.bindKey[i]);
|
qspComputeCrc(qsp, bindKey[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,8 @@ void qspDecodeIncomingFrame(
|
|||||||
QspConfiguration_t *qsp,
|
QspConfiguration_t *qsp,
|
||||||
uint8_t incomingByte,
|
uint8_t incomingByte,
|
||||||
RxDeviceState_t *rxDeviceState,
|
RxDeviceState_t *rxDeviceState,
|
||||||
TxDeviceState_t *txDeviceState
|
TxDeviceState_t *txDeviceState,
|
||||||
|
uint8_t bindKey[]
|
||||||
) {
|
) {
|
||||||
static uint8_t frameId;
|
static uint8_t frameId;
|
||||||
static uint8_t payloadLength;
|
static uint8_t payloadLength;
|
||||||
@@ -153,7 +154,7 @@ void qspDecodeIncomingFrame(
|
|||||||
|
|
||||||
if (qsp->protocolState == QSP_STATE_IDLE)
|
if (qsp->protocolState == QSP_STATE_IDLE)
|
||||||
{
|
{
|
||||||
qspInitCrc(qsp);
|
qspInitCrc(qsp, bindKey);
|
||||||
qspClearPayload(qsp);
|
qspClearPayload(qsp);
|
||||||
receivedPayload = 0;
|
receivedPayload = 0;
|
||||||
qsp->frameDecodingStartedAt = millis();
|
qsp->frameDecodingStartedAt = millis();
|
||||||
@@ -201,9 +202,15 @@ void qspDecodeIncomingFrame(
|
|||||||
/**
|
/**
|
||||||
* Encode frame is corrent format and write to hardware
|
* Encode frame is corrent format and write to hardware
|
||||||
*/
|
*/
|
||||||
void qspEncodeFrame(QspConfiguration_t *qsp, uint8_t buffer[], uint8_t *size, uint8_t radioChannel) {
|
void qspEncodeFrame(
|
||||||
|
QspConfiguration_t *qsp,
|
||||||
|
uint8_t buffer[],
|
||||||
|
uint8_t *size,
|
||||||
|
uint8_t radioChannel,
|
||||||
|
uint8_t bindKey[]
|
||||||
|
) {
|
||||||
//Salt CRC with bind key
|
//Salt CRC with bind key
|
||||||
qspInitCrc(qsp);
|
qspInitCrc(qsp, bindKey);
|
||||||
|
|
||||||
//Write frame type and length
|
//Write frame type and length
|
||||||
// We are no longer sending payload length, so 4 bits are now free for other usages
|
// We are no longer sending payload length, so 4 bits are now free for other usages
|
||||||
@@ -230,4 +237,13 @@ void encodePingPayload(QspConfiguration_t *qsp, uint32_t currentMicros) {
|
|||||||
qsp->payload[3] = (currentMicros >> 24) & 255;
|
qsp->payload[3] = (currentMicros >> 24) & 255;
|
||||||
|
|
||||||
qsp->payloadLength = qspFrameLengths[QSP_FRAME_PING];
|
qsp->payloadLength = qspFrameLengths[QSP_FRAME_PING];
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodeBindPayload(QspConfiguration_t *qsp, uint8_t bindKey[]) {
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < qspFrameLengths[QSP_FRAME_PING]; i++) {
|
||||||
|
qsp->payload[i] = bindKey[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
qsp->payloadLength = qspFrameLengths[QSP_FRAME_PING];
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
#include "radio_node.h"
|
|
||||||
#include "platform_node.h"
|
|
||||||
|
|
||||||
extern PlatformNode platformNode;
|
|
||||||
|
|
||||||
void qspDecodeRcDataFrame(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceSate);
|
void qspDecodeRcDataFrame(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceSate);
|
||||||
void decodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceState);
|
void decodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceState);
|
||||||
@@ -11,15 +7,17 @@ void decodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceSta
|
|||||||
uint8_t get10bitHighShift(uint8_t channel);
|
uint8_t get10bitHighShift(uint8_t channel);
|
||||||
uint8_t get10bitLowShift(uint8_t channel);
|
uint8_t get10bitLowShift(uint8_t channel);
|
||||||
void qspComputeCrc(QspConfiguration_t *qsp, uint8_t dataByte);
|
void qspComputeCrc(QspConfiguration_t *qsp, uint8_t dataByte);
|
||||||
void encodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceState, uint8_t rssi, uint8_t snr);
|
void encodeRxHealthPayload(QspConfiguration_t *qsp, RxDeviceState_t *rxDeviceState, uint8_t rssi, uint8_t snr, bool isFailsafe);
|
||||||
void encodeRcDataPayload(QspConfiguration_t *qsp, uint8_t noOfChannels);
|
void encodeRcDataPayload(QspConfiguration_t *qsp, uint8_t noOfChannels);
|
||||||
void qspDecodeIncomingFrame(
|
void qspDecodeIncomingFrame(
|
||||||
QspConfiguration_t *qsp,
|
QspConfiguration_t *qsp,
|
||||||
uint8_t incomingByte,
|
uint8_t incomingByte,
|
||||||
RxDeviceState_t *rxDeviceState,
|
RxDeviceState_t *rxDeviceState,
|
||||||
TxDeviceState_t *txDeviceState
|
TxDeviceState_t *txDeviceState,
|
||||||
|
uint8_t bindKey[]
|
||||||
);
|
);
|
||||||
void qspClearPayload(QspConfiguration_t *qsp);
|
void qspClearPayload(QspConfiguration_t *qsp);
|
||||||
void qspEncodeFrame(QspConfiguration_t *qsp, uint8_t buffer[], uint8_t *size, uint8_t radioChannel);
|
void qspEncodeFrame(QspConfiguration_t *qsp, uint8_t buffer[], uint8_t *size, uint8_t radioChannel, uint8_t bindKey[]);
|
||||||
|
|
||||||
void encodePingPayload(QspConfiguration_t *qsp, uint32_t currentMicros);
|
void encodePingPayload(QspConfiguration_t *qsp, uint32_t currentMicros);
|
||||||
|
void encodeBindPayload(QspConfiguration_t *qsp, uint8_t bindKey[]);
|
||||||
@@ -17,6 +17,16 @@ RadioNode::RadioNode(void) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioNode::reset(void) {
|
||||||
|
set(
|
||||||
|
loraTxPower,
|
||||||
|
loraBandwidth,
|
||||||
|
loraSpreadingFactor,
|
||||||
|
loraCodingRate,
|
||||||
|
getFrequencyForChannel(getChannel())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void RadioNode::init(uint8_t ss, uint8_t rst, uint8_t di0, void(*callback)(int)) {
|
void RadioNode::init(uint8_t ss, uint8_t rst, uint8_t di0, void(*callback)(int)) {
|
||||||
/*
|
/*
|
||||||
* Setup hardware
|
* Setup hardware
|
||||||
@@ -31,11 +41,7 @@ void RadioNode::init(uint8_t ss, uint8_t rst, uint8_t di0, void(*callback)(int))
|
|||||||
while (true);
|
while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Configure LoRa module
|
reset();
|
||||||
LoRa.setSignalBandwidth(loraBandwidth);
|
|
||||||
LoRa.setSpreadingFactor(loraSpreadingFactor);
|
|
||||||
LoRa.setCodingRate4(loraCodingRate);
|
|
||||||
LoRa.setTxPower(loraTxPower);
|
|
||||||
LoRa.enableCrc();
|
LoRa.enableCrc();
|
||||||
|
|
||||||
//Setup ISR callback and start receiving
|
//Setup ISR callback and start receiving
|
||||||
@@ -65,7 +71,8 @@ uint32_t RadioNode::getChannelEntryMillis(void) {
|
|||||||
void RadioNode::readAndDecode(
|
void RadioNode::readAndDecode(
|
||||||
QspConfiguration_t *qsp,
|
QspConfiguration_t *qsp,
|
||||||
RxDeviceState_t *rxDeviceState,
|
RxDeviceState_t *rxDeviceState,
|
||||||
TxDeviceState_t *txDeviceState
|
TxDeviceState_t *txDeviceState,
|
||||||
|
uint8_t bindKey[]
|
||||||
) {
|
) {
|
||||||
uint8_t tmpBuffer[MAX_PACKET_SIZE];
|
uint8_t tmpBuffer[MAX_PACKET_SIZE];
|
||||||
/*
|
/*
|
||||||
@@ -75,7 +82,7 @@ void RadioNode::readAndDecode(
|
|||||||
LoRa.read(tmpBuffer, bytesToRead);
|
LoRa.read(tmpBuffer, bytesToRead);
|
||||||
|
|
||||||
for (int i = 0; i < bytesToRead; i++) {
|
for (int i = 0; i < bytesToRead; i++) {
|
||||||
qspDecodeIncomingFrame(qsp, tmpBuffer[i], rxDeviceState, txDeviceState);
|
qspDecodeIncomingFrame(qsp, tmpBuffer[i], rxDeviceState, txDeviceState, bindKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
//After reading, flush radio buffer, we have no need for whatever might be over there
|
//After reading, flush radio buffer, we have no need for whatever might be over there
|
||||||
@@ -141,7 +148,7 @@ void RadioNode::handleTxDoneState(bool hop) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioNode::handleTx(QspConfiguration_t *qsp) {
|
void RadioNode::handleTx(QspConfiguration_t *qsp, uint8_t bindKey[]) {
|
||||||
|
|
||||||
if (!canTransmit) {
|
if (!canTransmit) {
|
||||||
return;
|
return;
|
||||||
@@ -152,11 +159,28 @@ void RadioNode::handleTx(QspConfiguration_t *qsp) {
|
|||||||
|
|
||||||
LoRa.beginPacket();
|
LoRa.beginPacket();
|
||||||
//Prepare packet
|
//Prepare packet
|
||||||
qspEncodeFrame(qsp, tmpBuffer, &size, getChannel());
|
qspEncodeFrame(qsp, tmpBuffer, &size, getChannel(), bindKey);
|
||||||
//Sent it to radio in one SPI transaction
|
//Sent it to radio in one SPI transaction
|
||||||
LoRa.write(tmpBuffer, size);
|
LoRa.write(tmpBuffer, size);
|
||||||
LoRa.endPacketAsync();
|
LoRa.endPacketAsync();
|
||||||
|
|
||||||
//Set state to be able to detect the moment when TX is done
|
//Set state to be able to detect the moment when TX is done
|
||||||
radioState = RADIO_STATE_TX;
|
radioState = RADIO_STATE_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RadioNode::set(
|
||||||
|
uint8_t power,
|
||||||
|
long bandwidth,
|
||||||
|
uint8_t spreadingFactor,
|
||||||
|
uint8_t codingRate,
|
||||||
|
long frequency
|
||||||
|
) {
|
||||||
|
LoRa.sleep();
|
||||||
|
|
||||||
|
LoRa.setTxPower(power);
|
||||||
|
LoRa.setSignalBandwidth(bandwidth);
|
||||||
|
LoRa.setCodingRate4(codingRate);
|
||||||
|
LoRa.setFrequency(frequency);
|
||||||
|
|
||||||
|
LoRa.idle();
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "qsp.h"
|
|
||||||
|
|
||||||
#define RADIO_FREQUENCY_MIN 868000000
|
#define RADIO_FREQUENCY_MIN 868000000
|
||||||
#define RADIO_FREQUENCY_MAX 870000000
|
#define RADIO_FREQUENCY_MAX 870000000
|
||||||
#define RADIO_FREQUENCY_RANGE (RADIO_FREQUENCY_MAX-RADIO_FREQUENCY_MIN)
|
#define RADIO_FREQUENCY_RANGE (RADIO_FREQUENCY_MAX-RADIO_FREQUENCY_MIN)
|
||||||
@@ -13,6 +10,8 @@
|
|||||||
#ifndef RADIO_NODE_H
|
#ifndef RADIO_NODE_H
|
||||||
#define RADIO_NODE_H
|
#define RADIO_NODE_H
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "qsp.h"
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
|
|
||||||
class RadioNode {
|
class RadioNode {
|
||||||
@@ -25,13 +24,22 @@ class RadioNode {
|
|||||||
void readAndDecode(
|
void readAndDecode(
|
||||||
QspConfiguration_t *qsp,
|
QspConfiguration_t *qsp,
|
||||||
RxDeviceState_t *rxDeviceState,
|
RxDeviceState_t *rxDeviceState,
|
||||||
TxDeviceState_t *txDeviceState
|
TxDeviceState_t *txDeviceState,
|
||||||
|
uint8_t bindKey[]
|
||||||
);
|
);
|
||||||
uint8_t getChannel(void);
|
uint8_t getChannel(void);
|
||||||
uint32_t getChannelEntryMillis(void);
|
uint32_t getChannelEntryMillis(void);
|
||||||
void handleChannelDwell(void);
|
void handleChannelDwell(void);
|
||||||
void handleTxDoneState(bool hop);
|
void handleTxDoneState(bool hop);
|
||||||
void handleTx(QspConfiguration_t *qsp);
|
void handleTx(QspConfiguration_t *qsp, uint8_t bindKey[]);
|
||||||
|
void set(
|
||||||
|
uint8_t power,
|
||||||
|
long bandwidth,
|
||||||
|
uint8_t spreadingFactor,
|
||||||
|
uint8_t codingRate,
|
||||||
|
long frequency
|
||||||
|
);
|
||||||
|
void reset(void);
|
||||||
volatile int8_t bytesToRead = -1;
|
volatile int8_t bytesToRead = -1;
|
||||||
volatile uint8_t radioState = RADIO_STATE_RX;
|
volatile uint8_t radioState = RADIO_STATE_RX;
|
||||||
uint8_t rssi = 0;
|
uint8_t rssi = 0;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "variables.h"
|
|
||||||
#include "sbus.h"
|
#include "sbus.h"
|
||||||
|
|
||||||
#define SBUS_MIN_OFFSET 173
|
#define SBUS_MIN_OFFSET 173
|
||||||
@@ -31,7 +30,7 @@ int mapSbusToChannel(int in) {
|
|||||||
return (((long) in - 173l) * 1020l / 1638l) + 990;
|
return (((long) in - 173l) * 1020l / 1638l) + 990;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sbusPreparePacket(uint8_t packet[], bool isSignalLoss, bool isFailsafe){
|
void sbusPreparePacket(uint8_t packet[], bool isSignalLoss, bool isFailsafe, int (* rcChannelGetCallback)(uint8_t)) {
|
||||||
|
|
||||||
int output[SBUS_CHANNEL_NUMBER];
|
int output[SBUS_CHANNEL_NUMBER];
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ void sbusPreparePacket(uint8_t packet[], bool isSignalLoss, bool isFailsafe){
|
|||||||
* 173-1811 with middle at 992 S.BUS protocol requires
|
* 173-1811 with middle at 992 S.BUS protocol requires
|
||||||
*/
|
*/
|
||||||
for (uint8_t i = 0; i < SBUS_CHANNEL_NUMBER; i++) {
|
for (uint8_t i = 0; i < SBUS_CHANNEL_NUMBER; i++) {
|
||||||
output[i] = mapChannelToSbus(platformNode.getRcChannel(i));
|
output[i] = mapChannelToSbus(rcChannelGetCallback(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t stateByte = 0x00;
|
uint8_t stateByte = 0x00;
|
||||||
@@ -115,18 +114,18 @@ void SbusInput::recoverStuckFrames(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sbusToChannels(byte buffer[]) {
|
void SbusInput::sbusToChannels(byte buffer[]) {
|
||||||
|
|
||||||
platformNode.setRcChannel(0, mapSbusToChannel((buffer[1] | buffer[2]<<8) & 0x07FF), 0);
|
setRcChannelCallback(0, mapSbusToChannel((buffer[1] | buffer[2]<<8) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(1, mapSbusToChannel((buffer[2]>>3 | buffer[3]<<5) & 0x07FF), 0);
|
setRcChannelCallback(1, mapSbusToChannel((buffer[2]>>3 | buffer[3]<<5) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(2, mapSbusToChannel((buffer[3]>>6 | buffer[4]<<2 | buffer[5]<<10) & 0x07FF), 0);
|
setRcChannelCallback(2, mapSbusToChannel((buffer[3]>>6 | buffer[4]<<2 | buffer[5]<<10) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(3, mapSbusToChannel((buffer[5]>>1 | buffer[6]<<7) & 0x07FF), 0);
|
setRcChannelCallback(3, mapSbusToChannel((buffer[5]>>1 | buffer[6]<<7) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(4, mapSbusToChannel((buffer[6]>>4 | buffer[7]<<4) & 0x07FF), 0);
|
setRcChannelCallback(4, mapSbusToChannel((buffer[6]>>4 | buffer[7]<<4) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(5, mapSbusToChannel((buffer[7]>>7 | buffer[8]<<1 |buffer[9]<<9) & 0x07FF), 0);
|
setRcChannelCallback(5, mapSbusToChannel((buffer[7]>>7 | buffer[8]<<1 |buffer[9]<<9) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(6, mapSbusToChannel((buffer[9]>>2 | buffer[10]<<6) & 0x07FF), 0);
|
setRcChannelCallback(6, mapSbusToChannel((buffer[9]>>2 | buffer[10]<<6) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(7, mapSbusToChannel((buffer[10]>>5 | buffer[11]<<3) & 0x07FF), 0);
|
setRcChannelCallback(7, mapSbusToChannel((buffer[10]>>5 | buffer[11]<<3) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(8, mapSbusToChannel((buffer[12] | buffer[13]<<8) & 0x07FF), 0);
|
setRcChannelCallback(8, mapSbusToChannel((buffer[12] | buffer[13]<<8) & 0x07FF), 0);
|
||||||
platformNode.setRcChannel(9, mapSbusToChannel((buffer[13]>>3 | buffer[14]<<5) & 0x07FF), 0);
|
setRcChannelCallback(9, mapSbusToChannel((buffer[13]>>3 | buffer[14]<<5) & 0x07FF), 0);
|
||||||
|
|
||||||
//We use only 10 channels, so the reset can be just ignored
|
//We use only 10 channels, so the reset can be just ignored
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,15 +19,17 @@ class SbusInput : public TxInput
|
|||||||
void loop(void);
|
void loop(void);
|
||||||
bool isReceiving(void);
|
bool isReceiving(void);
|
||||||
void recoverStuckFrames(void);
|
void recoverStuckFrames(void);
|
||||||
|
void (* setRcChannelCallback)(uint8_t channel, int value, int offset);
|
||||||
private:
|
private:
|
||||||
HardwareSerial &_serial;
|
HardwareSerial &_serial;
|
||||||
uint32_t _frameDecodingStartedAt = 0;
|
uint32_t _frameDecodingStartedAt = 0;
|
||||||
uint32_t _frameDecodingEndedAt = 0 ;
|
uint32_t _frameDecodingEndedAt = 0 ;
|
||||||
uint8_t _protocolState = SBUS_DECODING_STATE_IDLE;
|
uint8_t _protocolState = SBUS_DECODING_STATE_IDLE;
|
||||||
void sbusRead(void);
|
void sbusRead(void);
|
||||||
|
void sbusToChannels(byte buffer[]);
|
||||||
};
|
};
|
||||||
|
|
||||||
void sbusPreparePacket(uint8_t packet[], bool isSignalLoss, bool isFailsafe);
|
void sbusPreparePacket(uint8_t packet[], bool isSignalLoss, bool isFailsafe, int (* rcChannelGetCallback)(uint8_t));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,18 @@ void TxOled::loop() {
|
|||||||
//Second button has notthing to do over here
|
//Second button has notthing to do over here
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TX_PAGE_BIND:
|
||||||
|
if (button1.getState() == TACTILE_STATE_LONG_PRESS) {
|
||||||
|
|
||||||
|
if (!platformNode.isBindMode) {
|
||||||
|
platformNode.enterBindMode();
|
||||||
|
} else {
|
||||||
|
platformNode.leaveBindMode();
|
||||||
|
}
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case TX_PAGE_STATS:
|
case TX_PAGE_STATS:
|
||||||
//Second button refreshes this page
|
//Second button refreshes this page
|
||||||
if (button1.getState() == TACTILE_STATE_SHORT_PRESS) {
|
if (button1.getState() == TACTILE_STATE_SHORT_PRESS) {
|
||||||
@@ -93,7 +105,12 @@ void TxOled::renderPageBind() {
|
|||||||
_display.clear();
|
_display.clear();
|
||||||
_display.draw1x2String(0, 0, "Bind");
|
_display.draw1x2String(0, 0, "Bind");
|
||||||
|
|
||||||
snprintf(buf, OLED_COL_COUNT, "Bind?");
|
if (platformNode.isBindMode) {
|
||||||
|
snprintf(buf, OLED_COL_COUNT, "Binding!!");
|
||||||
|
} else {
|
||||||
|
snprintf(buf, OLED_COL_COUNT, "Bind?");
|
||||||
|
}
|
||||||
|
|
||||||
_display.draw1x2String(0, 4, buf);
|
_display.draw1x2String(0, 4, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
#include "tactile.h"
|
#include "tactile.h"
|
||||||
#include "radio_node.h"
|
#include "radio_node.h"
|
||||||
|
#include "platform_node.h"
|
||||||
|
|
||||||
extern RadioNode radioNode;
|
extern RadioNode radioNode;
|
||||||
|
extern PlatformNode platformNode;
|
||||||
extern RxDeviceState_t rxDeviceState;
|
extern RxDeviceState_t rxDeviceState;
|
||||||
extern TxDeviceState_t txDeviceState;
|
extern TxDeviceState_t txDeviceState;
|
||||||
extern Tactile button0;
|
extern Tactile button0;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "radio_node.h"
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -34,7 +33,8 @@
|
|||||||
#define QSP_FRAME_SET_RX_CONFIG 0x4
|
#define QSP_FRAME_SET_RX_CONFIG 0x4
|
||||||
#define QSP_FRAME_PING 0x5
|
#define QSP_FRAME_PING 0x5
|
||||||
#define QSP_FRAME_PONG 0x6
|
#define QSP_FRAME_PONG 0x6
|
||||||
#define QSP_FRAME_COUNT 0x7
|
#define QSP_FRAME_BIND 0x7
|
||||||
|
#define QSP_FRAME_COUNT 0x8
|
||||||
|
|
||||||
static const uint8_t qspFrameLengths[QSP_FRAME_COUNT] = {
|
static const uint8_t qspFrameLengths[QSP_FRAME_COUNT] = {
|
||||||
9, //QSP_FRAME_RC_DATA
|
9, //QSP_FRAME_RC_DATA
|
||||||
@@ -44,6 +44,7 @@ static const uint8_t qspFrameLengths[QSP_FRAME_COUNT] = {
|
|||||||
0, //QSP_FRAME_SET_RX_CONFIG -> Not used
|
0, //QSP_FRAME_SET_RX_CONFIG -> Not used
|
||||||
4, //QSP_FRAME_PING
|
4, //QSP_FRAME_PING
|
||||||
4, //QSP_FRAME_PONG
|
4, //QSP_FRAME_PONG
|
||||||
|
4 //QSP_FRAME_BIND
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RX_ADC_PIN_1 A0
|
#define RX_ADC_PIN_1 A0
|
||||||
@@ -99,6 +100,8 @@ struct QspConfiguration_t {
|
|||||||
uint32_t anyFrameRecivedAt = 0;
|
uint32_t anyFrameRecivedAt = 0;
|
||||||
void (* onSuccessCallback)(QspConfiguration_t*, TxDeviceState_t*, RxDeviceState_t*, uint8_t receivedChannel);
|
void (* onSuccessCallback)(QspConfiguration_t*, TxDeviceState_t*, RxDeviceState_t*, uint8_t receivedChannel);
|
||||||
void (* onFailureCallback)(QspConfiguration_t*, TxDeviceState_t*, RxDeviceState_t*);
|
void (* onFailureCallback)(QspConfiguration_t*, TxDeviceState_t*, RxDeviceState_t*);
|
||||||
|
int (* rcChannelGetCallback)(uint8_t);
|
||||||
|
void (* setRcChannelCallback)(uint8_t channel, int value, int offset);
|
||||||
bool forcePongFrame = false;
|
bool forcePongFrame = false;
|
||||||
uint32_t frameDecodingStartedAt = 0;
|
uint32_t frameDecodingStartedAt = 0;
|
||||||
uint32_t lastTxSlotTimestamp = 0;
|
uint32_t lastTxSlotTimestamp = 0;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 115 KiB |
Reference in New Issue
Block a user