[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
|
||||
RTTYClient KEYWORD1
|
||||
MorseClient KEYWORD1
|
||||
PagerClient KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
|
@ -210,6 +211,7 @@ ERR_FRAME_NO_RESPONSE LITERAL1
|
|||
ASCII LITERAL1
|
||||
ASCII_EXTENDED LITERAL1
|
||||
ITA2 LITERAL1
|
||||
BCD LITERAL1
|
||||
ERR_INVALID_RTTY_SHIFT LITERAL1
|
||||
ERR_UNSUPPORTED_ENCODING LITERAL1
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include "protocols/PhysicalLayer.h"
|
||||
#include "protocols/Morse.h"
|
||||
#include "protocols/RTTY.h"
|
||||
#include "protocols/Pager.h"
|
||||
|
||||
// transport layer protocols
|
||||
#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