Skip to content

Added UserText OSD support on Rapidfire Module #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 213 additions & 0 deletions lib/rapidfire_SPI/rapidFIRE_SPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// rapidFIRE_SPI.cpp
// Copyright 2023 GOROman.

#include "rapidFIRE_SPI.h"

rapidFIRE_SPI::rapidFIRE_SPI(int pin_SCK, int pin_DATA, int pin_SS, int freq) {
assert(freq < RAPIDFIRE_SPI_MAX_CLOCK);

SPI_pin_SCK = pin_SCK;
SPI_pin_DATA = pin_DATA;
SPI_pin_SS = pin_SS;
SPI_freq = freq;

spi = new SPIClass;
}

rapidFIRE_SPI::~rapidFIRE_SPI() { delete spi; }

RF_RESULT rapidFIRE_SPI::begin() {
// Init SPI
pinMode(SPI_pin_SCK, OUTPUT);
pinMode(SPI_pin_DATA, OUTPUT);
pinMode(SPI_pin_SS, OUTPUT);

// To enable SPI mode, set CS1, CS2, CS3 high, and then within 100-400ms set
// them all low.
digitalWrite(SPI_pin_SCK, HIGH);
digitalWrite(SPI_pin_DATA, HIGH);
digitalWrite(SPI_pin_SS, HIGH);

delay(RAPIDFIRE_SPI_MODE_ENABLE_DELAY);

digitalWrite(SPI_pin_SCK, LOW);
digitalWrite(SPI_pin_DATA, LOW);
digitalWrite(SPI_pin_SS, LOW);

delay(10);

digitalWrite(SPI_pin_SS, HIGH);

return RF_RESULT_OK;
}

RF_RESULT rapidFIRE_SPI::end() { return RF_RESULT_OK; }

RF_RESULT rapidFIRE_SPI::recvCommand(uint8_t *data, size_t len) {
assert(data != NULL);
assert(len != 0);

if (data == NULL) {
return RF_RESULT_ERROR;
}

byte recv_len = 0x00;
byte recv_sum = 0x00;

#if defined(PLATFORM_ESP32)
spi->begin(SPI_pin_SCK, SPI_pin_DATA, -1, SPI_pin_SS);
#else
spi->begin();
spi->pins(SPI_pin_SCK, SPI_pin_DATA, -1, SPI_pin_SS);
#endif

spi->setFrequency(SPI_freq);
spi->setDataMode(SPI_MODE0);
spi->setBitOrder(MSBFIRST);
spi->setHwCs(true);

recv_len = spi->transfer(0);
recv_sum = spi->transfer(0);
for (size_t i = 0; i < len; i++) {
data[i] = spi->transfer(0);
}

spi->end();
spi->setHwCs(false);

byte csum = recv_len;
for (size_t i = 0; i < len; ++i) {
csum += data[i];
}

if (recv_sum != csum) {
// return RF_RESULT_ERROR_CHECKSUM_MISSMATCH; // Checksum missmatch.
}
return RF_RESULT_OK;
}

RF_RESULT rapidFIRE_SPI::sendCommand(uint16_t command, byte *data, size_t len) {
uint8_t csum = (command >> 8) + (command & 0xff) + len;

for (size_t i = 0; i < len; ++i) {
csum += data[i];
}

#if defined(PLATFORM_ESP32)
spi->begin(SPI_pin_SCK, SPI_pin_DATA, -1, SPI_pin_SS);
#else
spi->begin();
spi->pins(SPI_pin_SCK, SPI_pin_DATA, -1, SPI_pin_SS);
#endif

spi->setFrequency(SPI_freq);
spi->setDataMode(SPI_MODE0);
spi->setBitOrder(MSBFIRST);
spi->setHwCs(true);

spi->write16(command);
spi->write(len);
spi->write(csum);
spi->writeBytes(data, len);
spi->end();

spi->setHwCs(false);

return RF_RESULT_OK;
}

RF_RESULT rapidFIRE_SPI::getFirmwareVersion(QUERY_FIRMWARE_VERSION *version) {
assert(version);
if (version == NULL) {
return RF_RESULT_ERROR;
}

RF_RESULT res = sendCommand(RAPIDFIRE_CMD_FIRMWARE_VERSION);
if (res != RF_RESULT_OK) {
return res;
}
byte data[6] = {0};
res = recvCommand(data, sizeof(data));
if (res == RF_RESULT_OK) {
for (int i = 0; i < 3; ++i) {
version->oled[i] = data[0 + i];
version->core[i] = data[3 + i];
}
}

return res;
}

RF_RESULT rapidFIRE_SPI::getRSSI(QUERY_RSSI *rssi) {
assert(rssi);
if (rssi == NULL) {
return RF_RESULT_ERROR;
}
RF_RESULT res = sendCommand(RAPIDFIRE_CMD_RSSI);
if (res != RF_RESULT_OK) {
return res;
}
byte data[8] = {0};
res = recvCommand(data, sizeof(data));
if (res == RF_RESULT_OK) {
rssi->raw_rx1 = data[0] | data[1] << 8;
rssi->raw_rx2 = data[2] | data[3] << 8;
rssi->scaled_rx1 = data[4] | data[5] << 8;
rssi->scaled_rx2 = data[6] | data[7] << 8;
}
return res;
}

RF_RESULT rapidFIRE_SPI::getVoltage(QUERY_VOLTAGE *voltage) {
assert(voltage);
if (voltage == NULL) {
return RF_RESULT_ERROR;
}

RF_RESULT res = sendCommand(RAPIDFIRE_CMD_VOLTAGE);
if (res != RF_RESULT_OK) {
return res;
}
byte data[2] = {0};
res = recvCommand(data, sizeof(data));
if (res == RF_RESULT_OK) {
int temp = data[0] | data[1] << 8;
voltage->voltage = (float)temp / 1000.0f;
}

return res;
}

RF_RESULT rapidFIRE_SPI::buzzer() {
return sendCommand(RAPIDFIRE_CMD_SOUND_BUZZER);
}

RF_RESULT rapidFIRE_SPI::setOSDUserText(char *text) {
return sendCommand(RAPIDFIRE_CMD_SET_OSD_USER_TEXT, (byte *)text,
strlen(text));
}

RF_RESULT rapidFIRE_SPI::setOSDMode(OSDMODE mode) {
byte data[] = {(byte)mode};
return sendCommand(RAPIDFIRE_CMD_SET_OSD_MODE, data, sizeof(data));
}

RF_RESULT rapidFIRE_SPI::setRXModule(RXMODULE module) {
byte data[] = {(byte)module};
return sendCommand(RAPIDFIRE_CMD_SET_RX_MODULE, data, sizeof(data));
}

RF_RESULT rapidFIRE_SPI::setChannel(byte channel) {
byte data[] = {channel};
return sendCommand(RAPIDFIRE_CMD_SET_CHANNEL, data, sizeof(data));
}

RF_RESULT rapidFIRE_SPI::setBand(BAND band) {
byte data[] = {band};
return sendCommand(RAPIDFIRE_CMD_SET_BAND, data, sizeof(data));
}

RF_RESULT rapidFIRE_SPI::setRapidfireMode(RAPIDFIREMODE mode) {
byte data[] = {(byte)mode};
return sendCommand(RAPIDFIRE_CMD_SET_RAPIDFIRE_MODE, data, sizeof(data));
}
105 changes: 105 additions & 0 deletions lib/rapidfire_SPI/rapidFIRE_SPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// rapidFIRE_SPI.h
// Copyright 2023 GOROman.

#pragma once
#include "rapidFIRE_SPI_Protocol.h"
#include <Arduino.h>
#include <SPI.h>

struct QUERY_RSSI {
int16_t raw_rx1;
int16_t raw_rx2;
int16_t scaled_rx1;
int16_t scaled_rx2;
};

struct QUERY_FIRMWARE_VERSION {
byte oled[3]; // rapidFIRE OLED firmware version.
byte core[3]; // rapidFIRE core firmware version.
};

struct QUERY_VOLTAGE {
float voltage; // 4.222 V
};

typedef int RF_RESULT;

enum {
RF_RESULT_OK = 0,
RF_RESULT_ERROR = -1,
RF_RESULT_ERROR_CHECKSUM_MISSMATCH = -2,
};

class SPIClass;

class rapidFIRE_SPI {
int SPI_pin_SCK = -1;
int SPI_pin_DATA = -1;
int SPI_pin_SS = -1;
int SPI_freq = -1;

SPIClass *spi = NULL;

public:
enum BAND {
BAND_F = 0x01, // - ImmersionRC / FatShark
BAND_R = 0x02, // - RaceBand
BAND_E = 0x03, // - Boscam E
BAND_B = 0x04, // - Boscam B
BAND_A = 0x05, // - Boscam A
BAND_L = 0x06, // - LowRace
BAND_X = 0x07, // - Band X
};

enum RXMODULE {
BOTH = 0x00, // - Both
UPPER = 0x01, // - Upper
LOWER = 0x02, // - Lower
};

enum OSDMODE {
OFF = 0, // - Off
LOCKONLY = 1, // - LockOnly
DEFAULTMODE = 2, // - Default
LOCKANDSTANDARD = 3, // - LockAndStandard
RSSIBARSLITE = 4, // - RSSIBarsLite
RSSIBARS = 5, // - RSSIBars
UNIQUIE_ID = 6, // - Unique ID
INTERNAL_USE = 7, // - Internal use
USERTEXT = 8, // - UserText

};
enum RAPIDFIREMODE {
RAPIDFIRE_1 = 0x00, // - RapidFIRE #1
RAPIDFIRE_2 = 0x01, // - RapidFIRE #2 # Seems to have compatibility issues
// with OSD UserText
LEGACY = 0x02, // - Legacy
};

private:
RF_RESULT recvCommand(uint8_t *data, size_t len);
RF_RESULT sendCommand(uint16_t command, byte *data = NULL, size_t len = 0);

public:
rapidFIRE_SPI(int pin_SCK, int pin_DATA, int pin_SS, int freq = 60000);
~rapidFIRE_SPI();

RF_RESULT begin();
RF_RESULT end();

// Query
RF_RESULT getFirmwareVersion(QUERY_FIRMWARE_VERSION *version);
RF_RESULT getRSSI(QUERY_RSSI *rssi);
RF_RESULT getVoltage(QUERY_VOLTAGE *voltage);

// Action
RF_RESULT buzzer();

// Command
RF_RESULT setOSDUserText(char *text);
RF_RESULT setOSDMode(OSDMODE mode);
RF_RESULT setRXModule(RXMODULE module);
RF_RESULT setChannel(byte channel);
RF_RESULT setBand(BAND band);
RF_RESULT setRapidfireMode(RAPIDFIREMODE mode);
};
65 changes: 65 additions & 0 deletions lib/rapidfire_SPI/rapidFIRE_SPI_Protocol.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// ImmersionRC Rapidfire - SPI Mode Programming
//
// rapidFIRE_SPI_Protocol.h
// Copyright 2023 GOROman.

#pragma once

// The CS1, CS2, CS3 pins are used as the SPI interface. These are normally used
// as a 3-bit binary interface to communicate the goggle selected channel to the
// module. The SPI interface is configured so that the module is the slave
// (allowing several modules to be connected to the same bus), with CPOL = 0,
// CPHA = 0, MSB first. The speed should be limited to a clock rate of about
// 80kHz.

#define RAPIDFIRE_SPI_CS1 1
#define RAPIDFIRE_SPI_CS2 2
#define RAPIDFIRE_SPI_CS3 3

#define RAPIDFIRE_SPI_CPOL 0
#define RAPIDFIRE_SPI_CPHA 0

#define RAPIDFIRE_SPI_BIT SPI_MSBFIRST

#define RAPIDFIRE_SPI_MAX_CLOCK 80000 // 80kHz

#define RAPIDFIRE_SPI_MODE_ENABLE_DELAY 100 // ms

#define RAPIDFIRE_MAX_LENGTH_TEXT \
25 // max length of text to display on rapidfire osd

// SPI Protocol
// ------------
//
// Command Heade
//
// | cmd | dir | len | csum | data0 | ... | |
//
// Where Csum is computed as the 8 - bit checksum of all header bytes, Cmd, Dir,
// Len, and all(optional) data bytes.
//
// Query Header
// | len | csum | data0 | ... | |
//
// Where Csum is computed as the 8-bit checksum of Len, and all data bytes.

// SPI Commands
// ------------
#define RAPIDFIRE_CMD(Cmd, Dir) (Cmd << 8 | Dir)

// Query
#define RAPIDFIRE_CMD_FIRMWARE_VERSION \
RAPIDFIRE_CMD('F', '?') // - Firmware Version, Query
#define RAPIDFIRE_CMD_VOLTAGE RAPIDFIRE_CMD('V', '?') // - Voltage, Query
#define RAPIDFIRE_CMD_RSSI RAPIDFIRE_CMD('R', '?') // - RSSI, Query

// Action
#define RAPIDFIRE_CMD_SOUND_BUZZER RAPIDFIRE_CMD('S', '>') // - Buzzer

// Command
#define RAPIDFIRE_CMD_SET_OSD_USER_TEXT RAPIDFIRE_CMD('T', '=')
#define RAPIDFIRE_CMD_SET_OSD_MODE RAPIDFIRE_CMD('O', '=')
#define RAPIDFIRE_CMD_SET_RX_MODULE RAPIDFIRE_CMD('M', '=')
#define RAPIDFIRE_CMD_SET_CHANNEL RAPIDFIRE_CMD('C', '=')
#define RAPIDFIRE_CMD_SET_BAND RAPIDFIRE_CMD('B', '=')
#define RAPIDFIRE_CMD_SET_RAPIDFIRE_MODE RAPIDFIRE_CMD('D', '=')
Loading