[Pager][WIP] Added Pager files
This commit is contained in:
parent
79f1560d41
commit
05498c2598
5 changed files with 315 additions and 0 deletions
78
examples/Pager/Pager_Transmit/Pager_Transmit.ino
Normal file
78
examples/Pager/Pager_Transmit/Pager_Transmit.ino
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
RadioLib Pager (POCSAG) Transmit Example
|
||||||
|
|
||||||
|
This example sends POCSAG messages using SX1278's
|
||||||
|
FSK modem.
|
||||||
|
|
||||||
|
Other modules that can be used for POCSAG:
|
||||||
|
- SX127x/RFM9x
|
||||||
|
- RF69
|
||||||
|
- SX1231
|
||||||
|
- CC1101
|
||||||
|
- SX126x
|
||||||
|
*/
|
||||||
|
|
||||||
|
// include the library
|
||||||
|
#include <RadioLib.h>
|
||||||
|
|
||||||
|
// SX1278 module is in slot A on the shield
|
||||||
|
SX1278 fsk = RadioShield.ModuleA;
|
||||||
|
|
||||||
|
// create Pager client instance using the FSK module
|
||||||
|
PagerClient pager(&fsk);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// initialize SX1278
|
||||||
|
Serial.print(F("[SX1278] Initializing ... "));
|
||||||
|
// carrier frequency: 434.0 MHz
|
||||||
|
// bit rate: 48.0 kbps
|
||||||
|
// frequency deviation: 50.0 kHz
|
||||||
|
// Rx bandwidth: 125.0 kHz
|
||||||
|
// output power: 13 dBm
|
||||||
|
// current limit: 100 mA
|
||||||
|
// sync word: 0x2D 0x01
|
||||||
|
int state = fsk.beginFSK();
|
||||||
|
if(state == ERR_NONE) {
|
||||||
|
Serial.println(F("success!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(state);
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initalize Pager client
|
||||||
|
Serial.print(F("[Pager] Initializing ... "));
|
||||||
|
// base (center) frequency: 434.0 MHz
|
||||||
|
// speed: 1200 bps
|
||||||
|
state = pager.begin(434.0, 1200);
|
||||||
|
if(state == ERR_NONE) {
|
||||||
|
Serial.println(F("success!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(state);
|
||||||
|
while(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// transmit numeric (BCD) message to the destination pager
|
||||||
|
Serial.print(F("[Pager] Transmitting message ... "));
|
||||||
|
int state = pager.transmit("0123456789*U -()", 0x01234567);
|
||||||
|
// NOTE: Only characters 0123456789*U-() and space
|
||||||
|
// can be sent in a BCD message! To send ASCII
|
||||||
|
// characters, you have to set encoding to ASCII.
|
||||||
|
/*
|
||||||
|
int state = pager.transmit("Hello World!", 0x01234567, ASCII);
|
||||||
|
*/
|
||||||
|
if (state == ERR_NONE) {
|
||||||
|
Serial.println(F("success!"));
|
||||||
|
} else {
|
||||||
|
Serial.print(F("failed, code "));
|
||||||
|
Serial.println(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for a second before transmitting again
|
||||||
|
delay(1000);
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ MQTTClient KEYWORD1
|
||||||
HTTPClient KEYWORD1
|
HTTPClient KEYWORD1
|
||||||
RTTYClient KEYWORD1
|
RTTYClient KEYWORD1
|
||||||
MorseClient KEYWORD1
|
MorseClient KEYWORD1
|
||||||
|
PagerClient KEYWORD1
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# Methods and Functions (KEYWORD2)
|
# Methods and Functions (KEYWORD2)
|
||||||
|
@ -210,6 +211,7 @@ ERR_FRAME_NO_RESPONSE LITERAL1
|
||||||
ASCII LITERAL1
|
ASCII LITERAL1
|
||||||
ASCII_EXTENDED LITERAL1
|
ASCII_EXTENDED LITERAL1
|
||||||
ITA2 LITERAL1
|
ITA2 LITERAL1
|
||||||
|
BCD LITERAL1
|
||||||
ERR_INVALID_RTTY_SHIFT LITERAL1
|
ERR_INVALID_RTTY_SHIFT LITERAL1
|
||||||
ERR_UNSUPPORTED_ENCODING LITERAL1
|
ERR_UNSUPPORTED_ENCODING LITERAL1
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include "protocols/PhysicalLayer.h"
|
#include "protocols/PhysicalLayer.h"
|
||||||
#include "protocols/Morse.h"
|
#include "protocols/Morse.h"
|
||||||
#include "protocols/RTTY.h"
|
#include "protocols/RTTY.h"
|
||||||
|
#include "protocols/Pager.h"
|
||||||
|
|
||||||
// transport layer protocols
|
// transport layer protocols
|
||||||
#include "protocols/TransportLayer.h"
|
#include "protocols/TransportLayer.h"
|
||||||
|
|
184
src/protocols/Pager.cpp
Normal file
184
src/protocols/Pager.cpp
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
#include "Pager.h"
|
||||||
|
|
||||||
|
PagerClient::PagerClient(PhysicalLayer* phy) {
|
||||||
|
_phy = phy;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t PagerClient::begin(float base, uint16_t speed) {
|
||||||
|
// calculate duration of 1 bit in us
|
||||||
|
_bitDuration = (uint32_t)1000000/speed;
|
||||||
|
|
||||||
|
// calculate 24-bit frequency
|
||||||
|
_base = (base * (uint32_t(1) << _phy->getDivExponent())) / _phy->getCrystalFreq();
|
||||||
|
|
||||||
|
// calculate module carrier frequency resolution
|
||||||
|
uint16_t step = round((_phy->getCrystalFreq() * 1000000) / (uint32_t(1) << _phy->getDivExponent()));
|
||||||
|
|
||||||
|
// calculate raw frequency shift
|
||||||
|
_shift = FREQ_SHIFT_HZ/step;
|
||||||
|
|
||||||
|
// set module frequency deviation to 0
|
||||||
|
int16_t state = _phy->setFrequencyDeviation(0);
|
||||||
|
|
||||||
|
return(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t PagerClient::transmit(String& str, uint32_t addr, uint8_t encoding) {
|
||||||
|
return(PagerClient::transmit(str.c_str(), addr, encoding));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t PagerClient::transmit(const char* str, uint32_t addr, uint8_t encoding) {
|
||||||
|
return(PagerClient::transmit((uint8_t*)str, strlen(str), addr, encoding));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding) {
|
||||||
|
// get symbol bit length based on encoding
|
||||||
|
uint8_t symbolLength = 0;
|
||||||
|
if(encoding == BCD) {
|
||||||
|
symbolLength = 4;
|
||||||
|
|
||||||
|
// replace ASCII characters with BCD representations
|
||||||
|
for(size_t i = 0; i < len; i++) {
|
||||||
|
switch(data[i]) {
|
||||||
|
case '*':
|
||||||
|
data[i] = 0x0A;
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
data[i] = 0x0B;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
data[i] = 0x0C;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
data[i] = 0x0D;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
data[i] = 0x0E;
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
data[i] = 0x0F;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data[i] -= '0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(encoding == ASCII) {
|
||||||
|
symbolLength = 7;
|
||||||
|
} else {
|
||||||
|
return(ERR_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get target position in batch (3 LSB from address determine frame position in batch)
|
||||||
|
uint8_t framePos = addr & 0b0000000000000111;
|
||||||
|
|
||||||
|
// get address that will be written into address frame
|
||||||
|
uint16_t frameAddr = (addr & 0b1111111111111000) >> 3;
|
||||||
|
|
||||||
|
// calculate the number of 20-bit data blocks
|
||||||
|
size_t numDataBlocks = (len * symbolLength) / MESSAGE_BITS_LENGTH;
|
||||||
|
if((len * symbolLength) % MESSAGE_BITS_LENGTH > 0) {
|
||||||
|
numDataBlocks += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate number of batches
|
||||||
|
size_t numBatches = (1 + framePos + numDataBlocks) / 16 + 1;
|
||||||
|
if((1 + numDataBlocks) % 16 == 0) {
|
||||||
|
numBatches -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate message length in 32-bit code words
|
||||||
|
size_t msgLen = PREAMBLE_LENGTH + (1 + 16) * numBatches;
|
||||||
|
|
||||||
|
// build the message
|
||||||
|
uint32_t* msg = new uint32_t[msgLen];
|
||||||
|
// TODO: BCD padding?
|
||||||
|
memset(msg, 0x00, msgLen);
|
||||||
|
|
||||||
|
// set preamble
|
||||||
|
for(size_t i = 0; i < PREAMBLE_LENGTH; i++) {
|
||||||
|
msg[i] = PREAMBLE_CODE_WORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set frame synchronization code words
|
||||||
|
for(size_t i = 0; i < numBatches; i++) {
|
||||||
|
msg[PREAMBLE_LENGTH + i*(1 + 16)] = FRAME_SYNC_CODE_WORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set unused code words to idle
|
||||||
|
for(size_t i = 0; i < framePos; i++) {
|
||||||
|
msg[PREAMBLE_LENGTH + 1 + i] = IDLE_CODE_WORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write address code word
|
||||||
|
msg[PREAMBLE_LENGTH + 1 + framePos] = addParity(encodeBCH((addr << 1) | ADDRESS_CODE_WORD));
|
||||||
|
|
||||||
|
// split the data into 20-bit blocks
|
||||||
|
size_t bitPos = MESSAGE_BITS_LENGTH;
|
||||||
|
size_t blockPos = PREAMBLE_LENGTH + 1;
|
||||||
|
for(size_t i = 0; i < len; i++) {
|
||||||
|
// check if the next data symbol fits into the remaining space in current 20-bit block
|
||||||
|
if(bitPos >= symbolLength) {
|
||||||
|
// insert the whole data symbol into current block
|
||||||
|
msg[blockPos] |= (uint32_t)data[i] << (bitPos - symbolLength);
|
||||||
|
bitPos -= symbolLength;
|
||||||
|
} else {
|
||||||
|
// split the symbol between two blocks
|
||||||
|
uint8_t msbPart = data[i];
|
||||||
|
size_t lsbLen = symbolLength - bitPos;
|
||||||
|
msg[blockPos] |= msbPart >> lsbLen;
|
||||||
|
blockPos++;
|
||||||
|
bitPos = MESSAGE_BITS_LENGTH;
|
||||||
|
uint8_t lsbPart = data[i] & ((1 << lsbLen) - 1);
|
||||||
|
msg[blockPos] |= (uint32_t)lsbPart << (bitPos - lsbLen);
|
||||||
|
bitPos -= lsbLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write message code words
|
||||||
|
|
||||||
|
// transmit the message
|
||||||
|
PagerClient::write(msg, msgLen);
|
||||||
|
|
||||||
|
delete[] msg;
|
||||||
|
|
||||||
|
// turn transmitter off
|
||||||
|
_phy->standby();
|
||||||
|
|
||||||
|
return(ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PagerClient::write(uint32_t* data, size_t len) {
|
||||||
|
// write code words from buffer
|
||||||
|
for(size_t i = 0; i < len; i++) {
|
||||||
|
PagerClient::write(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PagerClient::write(uint32_t codeWord) {
|
||||||
|
// write single code word
|
||||||
|
for(uint8_t i = 0; i <= 31; i++) {
|
||||||
|
uint32_t mask = (uint32_t)0x01 << i;
|
||||||
|
if(codeWord & mask) {
|
||||||
|
// send 1
|
||||||
|
uint32_t start = micros();
|
||||||
|
_phy->transmitDirect(_base + _shift);
|
||||||
|
while(micros() - start < _bitDuration);
|
||||||
|
} else {
|
||||||
|
// send 0
|
||||||
|
uint32_t start = micros();
|
||||||
|
_phy->transmitDirect(_base - _shift);
|
||||||
|
while(micros() - start < _bitDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PagerClient::encodeBCH(uint32_t data) {
|
||||||
|
return(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t PagerClient::addParity(uint32_t msg) {
|
||||||
|
return(msg);
|
||||||
|
}
|
50
src/protocols/Pager.h
Normal file
50
src/protocols/Pager.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef _RADIOLIB_PAGER_H
|
||||||
|
#define _RADIOLIB_PAGER_H
|
||||||
|
|
||||||
|
#include "TypeDef.h"
|
||||||
|
#include "PhysicalLayer.h"
|
||||||
|
|
||||||
|
// supported encoding schemes
|
||||||
|
#define ASCII 0
|
||||||
|
#define BCD 1
|
||||||
|
|
||||||
|
#define PREAMBLE_LENGTH 18
|
||||||
|
#define MESSAGE_BITS_LENGTH 20
|
||||||
|
|
||||||
|
#define FREQ_SHIFT_HZ 4500
|
||||||
|
|
||||||
|
#define PREAMBLE_CODE_WORD 0xAAAAAAAA
|
||||||
|
#define FRAME_SYNC_CODE_WORD 0x7CD215D8
|
||||||
|
#define IDLE_CODE_WORD 0x7A89C197
|
||||||
|
#define ADDRESS_CODE_WORD 0
|
||||||
|
#define MESSAGE_CODE_WORD 1
|
||||||
|
|
||||||
|
#define BCH_GENERATOR_POLYNOMIAL 0b11101101001 // x^10 + x^9 + x^8 + x^6 + x^5 + x^3 + 1
|
||||||
|
|
||||||
|
class PagerClient {
|
||||||
|
public:
|
||||||
|
PagerClient(PhysicalLayer* phy);
|
||||||
|
|
||||||
|
// basic methods
|
||||||
|
int16_t begin(float base, uint16_t speed);
|
||||||
|
int16_t transmit(String& str, uint32_t addr, uint8_t encoding = BCD);
|
||||||
|
int16_t transmit(const char* str, uint32_t addr, uint8_t encoding = BCD);
|
||||||
|
int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding = BCD);
|
||||||
|
|
||||||
|
// TODO: add receiving + option to listen to all packets
|
||||||
|
|
||||||
|
private:
|
||||||
|
PhysicalLayer* _phy;
|
||||||
|
|
||||||
|
uint32_t _base;
|
||||||
|
uint16_t _shift;
|
||||||
|
uint16_t _bitDuration;
|
||||||
|
|
||||||
|
void write(uint32_t* data, size_t len);
|
||||||
|
void write(uint32_t b);
|
||||||
|
|
||||||
|
uint32_t encodeBCH(uint32_t data);
|
||||||
|
uint32_t addParity(uint32_t msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue