Skip to content
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
7 changes: 6 additions & 1 deletion emulator/iSkipper/examples/capture/capture.ino
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ void setup()
void loop()
{
char tmp[50];
iClickerPacket r;
iClickerPacket r;

Serial.println("Sending welcome message");
clicker.sendWelcomePacket("_0+2-2=x_?", QuestionModes::MULTIPLE_NUMERIC, 5);
clicker.setChannel(iClickerChannels::AA);
clicker.startPromiscuous(CHANNEL_SEND, recvPacketHandler);

//see if there is a pending packet, check if its an answer packet
while (recvBuf.pull(&r) && r.type == PACKET_ANSWER) {
Expand Down
70 changes: 69 additions & 1 deletion emulator/iSkipper/iClickerEmulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,62 @@ void iClickerEmulator::acknowledgeAnswer(iClickerAnswerPacket* packet, bool acce
_radio.send(ack_payload, 5, false);
}

// Returns 0 on failure, otherwise converts the ascii character `c`
// to the equivilent fucky-iClicker-text-encoding
uint8_t asciiToiClickerEncoding(char c) {
if (c >= 'a' && c <= 'z') {
return (c - 'a') + 0x8B;
} else if (c >= 'A' && c <= 'Z') {
return (c - 'A') + 0x8B;
} else if (c >= '1' && c <= '9') {
return (c - '1') + 0x81;
}
switch (c) {
case '0': return 0x8A;
case '-': return 0xA5;
case '+': return 0xA6;
case '=': return 0xA7;
case '?': return 0xA8;
case '_': return 0xA9;
default: return 0;
}
}

void iClickerEmulator::sendWelcomePacket(char* msg, QuestionMode mode, uint16_t num_questions) {
uint8_t welcome_payload[WELCOME_PACKET_SIZE];
memset(welcome_payload, 0, WELCOME_PACKET_SIZE);

if (msg != NULL) {
size_t length = strlen(msg);
// Welcome message can't be longer than 8 chars
if (length > 8) {
length = 8;
}

for (size_t i = 0; i < length; i++) {
uint8_t mapped_character = asciiToiClickerEncoding(msg[i]);
welcome_payload[i] = mapped_character;
}
}

// set the magic bytes which tell what question mode to put iClicker2s in
welcome_payload[8] = mode.welcome_bytes[0];
welcome_payload[11] = mode.welcome_bytes[1];
// num_questions only matters in the MULTIPLE_* modes
uint8_t* num_question_bytes = (uint8_t*) &num_questions;
welcome_payload[9] = num_question_bytes[0];
welcome_payload[10] = num_question_bytes[1];
// compute the checksum
uint8_t checksum = 0;
for (int i = 0; i < WELCOME_PACKET_SIZE - 1; i++) {
checksum += welcome_payload[i];
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this just be done with the computeChecksum method?

welcome_payload[WELCOME_PACKET_SIZE - 1] = checksum;

configureRadio(CHANNEL_RECV_WELCOME, WELCOME_RECV_SYNC_ADDR);
_radio.send(welcome_payload, 16, true);
}


void iClickerEmulator::setChannel(iClickerChannel chan)
{
Expand All @@ -242,8 +298,20 @@ iClickerChannel iClickerEmulator::getChannel()

void iClickerEmulator::configureRadio(iClickerChannelType type, const uint8_t *syncaddr)
{
int syncAddrLength = 0;
switch (type) {
case CHANNEL_SEND:
syncAddrLength = SEND_SYNC_ADDR_LEN;
break;
case CHANNEL_RECV:
syncAddrLength = RECV_SYNC_ADDR_LEN;
break;
case CHANNEL_RECV_WELCOME:
syncAddrLength = WELCOME_RECV_SYNC_ADDR_LEN;
break;
}
// set the correct sync addr and len
_radio.setSyncAddr(syncaddr, type == CHANNEL_SEND ? SEND_SYNC_ADDR_LEN : RECV_SYNC_ADDR_LEN);
_radio.setSyncAddr(syncaddr, syncAddrLength);

// put radio on the correct freq and packet size
_radio.setChannelType(type);
Expand Down
40 changes: 40 additions & 0 deletions emulator/iSkipper/iClickerEmulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,44 @@ enum iClickerAnswer : uint8_t
NUM_ANSWER_CHOICES,
};

struct QuestionMode
{
// magic bytes used in the welcome packet for this
// question mode
uint8_t welcome_bytes[2];
};

namespace QuestionModes {
const QuestionMode MULTIPLE_CHOICE =
{
{0x92, 0x62}
};

const QuestionMode NUMERIC =
{
{0x93, 0x63}
};

const QuestionMode ALPHANUMERIC =
{
{0x9B, 0x6B}
};

const QuestionMode MULTIPLE_NUMERIC =
{
{0x94, 0x64}
};

const QuestionMode MULTIPLE_ALPHANUMERIC =
{
{0x9C, 0x6C}
};
};

#define WELCOME_RECV_SYNC_ADDR_LEN 6
const uint8_t WELCOME_RECV_SYNC_ADDR[WELCOME_RECV_SYNC_ADDR_LEN] =
{0x55, 0x55, 0x55, 0x36, 0x36, 0x36};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine you see the base station's firmware transmitting this leading 0x55 . Remember 0x55 = 0b01010101.
This is used to help the receiver learn the channel, not to actually try to trigger on using the sync address functionality. You can not reliable assume it will be received {0x55, 0x55, 0x55}. The sync address should just be {0x36, 0x36, 0x36}. Also remember when calling _radio.send(msg) the RFM69 is automatically creating the packet to transmit as {0x55, 0x55, ..., 0x55} + sync_addr + msg, as it automatically prepends the 0x55 preamble and the sync address currently configured.

This actually bring up a problem with the way thehttps://github.com//pull/12. Trying to receive the ack by triggering using the sync address functionality on the preamble ({0x55, 0x55, 0x55}) likely will not work reliably in certain situations. I think we should use the first two bytes of the encodedID instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as it automatically prepends the 0x55 preamble

The RFM69HCW's datasheet is really confusing me here, the only configurable part of the preamble seems to be RegPreambleMsb and RegPreambleLsb which defaults to 0x03, so is the preamble hardcoded to just be 0x55? Is this just an industry convention or something?

Trying to receive the ack by triggering using the sync address functionality on the preamble ({0x55, 0x55, 0x55}) likely will not work reliably in certain situations. I think we should use the first two bytes of the encodedID instead.

The datasheet does mention the following:

  • A preamble (0x55 or 0xAA) of 12 bits is required for synchronization (from the RxReady interrupt)

So only 1.5 bytes are required to trigger the preamble detecting part of the RFM69 and the rest should be safe to use as a sync word? Also won't this present a problem for promiscuously snooping on all ACKs? What do we use as the sync word there?

Copy link
Owner

@wizard97 wizard97 Jun 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the preamble hardcoded to just be 0x55? Is this just an industry convention or something?

Yeah, the datasheet is a bit confusing... so the preamble is an alternating series of 1 and 0s. So both 0b01010101 (0x55) as well as 0b10101010 (0xaa) would work.

So only 1.5 bytes are required to trigger the preamble detecting part of the RFM69 and the rest should be safe to use as a sync word?

1.5 bytes is only the minimum. I'm not sure if with this particular chipset a longer preamble helps after the first 1.5 bytes. After reading the datasheet more carefully it looks like it is mostly being used to lock on to the bitrate of the transmission. Remember this is supposed to be a general purpose FSK transceivers, so they designed it to be compatible with other transceivers. Some more advanced FSK receivers could use this preamble to do additional things.

I think the best solution is just avoid using preamble bytes for the sync word unless we absolutely have to. For example with answer submission the sync word is {0x85, 0x85, 0x85} not {0x55, 0x55, 0x55, 0x85, 0x85, 0x85,}. We do not need to use sync bytes in this case.

Also won't this present a problem for promiscuously snooping on all ACKs? What do we use as the sync word there?

Yeah, this is a problem with trying to support promisoucMode for acks. See #13



// Encoded answer choice is sent as follows:
/*
Expand Down Expand Up @@ -111,6 +149,8 @@ class iClickerEmulator
bool begin(iClickerChannel chan);
bool submitAnswer(uint8_t id[ICLICKER_ID_LEN], iClickerAnswer ans,
bool withAck=false, uint32_t timeout=DEFAULT_ACK_TIMEOUT, bool waitClear = true);
void sendWelcomePacket(char* msg, QuestionMode mode=QuestionModes::MULTIPLE_CHOICE,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const char* so a vanilla C string works.

uint16_t num_questions=0);
void acknowledgeAnswer(iClickerAnswerPacket* packet, bool accept=true);

void startPromiscuous(iClickerChannelType chanType, void (*cb)(iClickerPacket *));
Expand Down
18 changes: 15 additions & 3 deletions emulator/iSkipper/iClickerRadio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ bool iClickerRadio::initialize(uint8_t freqBand)
/* 0x37 */ { REG_PACKETCONFIG1, RF_PACKET1_FORMAT_FIXED | RF_PACKET1_DCFREE_OFF | RF_PACKET1_CRC_OFF | RF_PACKET1_CRCAUTOCLEAR_ON | RF_PACKET1_ADRSFILTERING_OFF },
/* 0x38 */ { REG_PAYLOADLENGTH, PAYLOAD_LENGTH_SEND }, // in variable length mode: the max frame size, not used in TX
///* 0x39 */ { REG_NODEADRS, nodeID }, // turned off because we're not using address filtering
/* 0x3C */ { REG_FIFOTHRESH, RF_FIFOTHRESH_TXSTART_FIFOTHRESH | RF_FIFOTHRESH_TXSTART_FIFOTHRESH_IC }, // TX on FIFO not empty
/* 0x3C */ { REG_FIFOTHRESH, RF_FIFOTHRESH_TXSTART_FIFONOTEMPTY | RF_FIFOTHRESH_VALUE }, // TX on FIFO not empty
/* 0x3D */ { REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_1BIT | RF_PACKET2_AUTORXRESTART_OFF | RF_PACKET2_AES_OFF }, // RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent)
//for BR-19200: /* 0x3D */ { REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_NONE | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF }, // RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent)
/* 0x6F */ { REG_TESTDAGC, RF_DAGC_IMPROVED_LOWBETA0 }, // run DAGC continuously in RX mode for Fading Margin Improvement, recommended default for AfcLowBetaOn=0
Expand Down Expand Up @@ -126,8 +126,20 @@ void iClickerRadio::setChannelType(iClickerChannelType_t chanType)
// dont change it if allready correct
if (_chanType != chanType) {
_chanType = chanType;
setFrequency(_chanType == CHANNEL_SEND ? _chan.send : _chan.recv);
setPayloadLength(_chanType == CHANNEL_SEND ? PAYLOAD_LENGTH_SEND : PAYLOAD_LENGTH_RECV, false);
switch (chanType) {
case CHANNEL_SEND:
setFrequency(_chan.send);
setPayloadLength(PAYLOAD_LENGTH_SEND, false);
break;
case CHANNEL_RECV:
setFrequency(_chan.recv);
setPayloadLength(PAYLOAD_LENGTH_RECV, false);
break;
case CHANNEL_RECV_WELCOME:
setFrequency(_chan.recv);
setPayloadLength(WELCOME_PACKET_SIZE, false);
break;
}
}
}

Expand Down
3 changes: 0 additions & 3 deletions emulator/iSkipper/iClickerRadio.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
#include "iClickerRadioIncludes.h"
#include "RFM69.h"

// threshold for triggerins fifo transmit interrupt
#define RF_FIFOTHRESH_TXSTART_FIFOTHRESH_IC 0x04


//RegSyncValue1-8 for sending is:
// 0x85, 0x85, 0x85, 0, 0...
Expand Down
2 changes: 2 additions & 0 deletions emulator/iSkipper/iClickerRadioIncludes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
//packet length
#define PAYLOAD_LENGTH_SEND 0x05
#define PAYLOAD_LENGTH_RECV 0x05
#define WELCOME_PACKET_SIZE 13

typedef enum iClickerChannelType
{
CHANNEL_SEND = 0, //act as a clicker
CHANNEL_RECV = 1, // act as base station
CHANNEL_RECV_WELCOME = 2 // act as base station sending a welcome message
} iClickerChannelType_t;

/*
Expand Down