From 76937429dedcc03467b5b5f001b2cc94d18be45e Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Sun, 18 Jun 2023 18:26:01 +0200 Subject: [PATCH 1/7] separate preamble --- src/protocols/Pager/Pager.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 1416247a4..cf4944c0d 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -125,7 +125,7 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t } // calculate message length in 32-bit code words - size_t msgLen = RADIOLIB_PAGER_PREAMBLE_LENGTH + (1 + RADIOLIB_PAGER_BATCH_LEN) * numBatches; + size_t msgLen = (1 + RADIOLIB_PAGER_BATCH_LEN) * numBatches; #if defined(RADIOLIB_STATIC_ONLY) uint32_t msg[RADIOLIB_STATIC_ARRAY_SIZE]; @@ -136,33 +136,28 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t // build the message memset(msg, 0x00, msgLen*sizeof(uint32_t)); - // set preamble - for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { - msg[i] = RADIOLIB_PAGER_PREAMBLE_CODE_WORD; - } - - // start by setting everything after preamble to idle - for(size_t i = RADIOLIB_PAGER_PREAMBLE_LENGTH; i < msgLen; i++) { + // start by setting everything to idle + for(size_t i = 0; i < msgLen; i++) { msg[i] = RADIOLIB_PAGER_IDLE_CODE_WORD; } // set frame synchronization code words for(size_t i = 0; i < numBatches; i++) { - msg[RADIOLIB_PAGER_PREAMBLE_LENGTH + i*(1 + RADIOLIB_PAGER_BATCH_LEN)] = RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD; + msg[i*(1 + RADIOLIB_PAGER_BATCH_LEN)] = RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD; } // write address code word - msg[RADIOLIB_PAGER_PREAMBLE_LENGTH + 1 + framePos] = RadioLibBCHInstance.encode(frameAddr); + msg[1 + framePos] = RadioLibBCHInstance.encode(frameAddr); // write the data as 20-bit code blocks if(len > 0) { int8_t remBits = 0; uint8_t dataPos = 0; for(size_t i = 0; i < numDataBlocks + numBatches - 1; i++) { - uint8_t blockPos = RADIOLIB_PAGER_PREAMBLE_LENGTH + 1 + framePos + 1 + i; + uint8_t blockPos = 1 + framePos + 1 + i; // check if we need to skip a frame sync marker - if(((blockPos - (RADIOLIB_PAGER_PREAMBLE_LENGTH + 1)) % RADIOLIB_PAGER_BATCH_LEN) == 0) { + if(((blockPos - 1) % RADIOLIB_PAGER_BATCH_LEN) == 0) { blockPos++; i++; } @@ -222,6 +217,11 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t } } + // transmit the preamble + for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { + PagerClient::write(RADIOLIB_PAGER_PREAMBLE_CODE_WORD); + } + // transmit the message PagerClient::write(msg, msgLen); From 5e700978ba56081267aa8f28931dc6e5beeea8d0 Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Mon, 19 Jun 2023 13:25:29 +0200 Subject: [PATCH 2/7] pocsag message struct --- src/protocols/Pager/Pager.cpp | 16 ++++++++++++++ src/protocols/Pager/Pager.h | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index cf4944c0d..7bc381e76 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -65,6 +65,22 @@ int16_t PagerClient::transmit(const char* str, uint32_t addr, uint8_t encoding, } int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding, uint8_t function) { + PagerMessage_t message; + message.addr = addr; + message.func = function; + message.data = data; + message.data_len = len; + message.encoding = encoding; + return(PagerClient::transmit(message)); +} + +int16_t PagerClient::transmit(PagerMessage_t &message) { + uint32_t addr = message.addr; + uint8_t function = message.func; + uint8_t* data = message.data; + size_t len = message.data_len; + uint8_t encoding = message.encoding; + if(addr > RADIOLIB_PAGER_ADDRESS_MAX) { return(RADIOLIB_ERR_INVALID_ADDRESS_WIDTH); } diff --git a/src/protocols/Pager/Pager.h b/src/protocols/Pager/Pager.h index ca84f7331..6f84debab 100644 --- a/src/protocols/Pager/Pager.h +++ b/src/protocols/Pager/Pager.h @@ -55,6 +55,38 @@ // the maximum allowed address (2^22 - 1) #define RADIOLIB_PAGER_ADDRESS_MAX (2097151) +/*! + \struct PagerMessage_t + \brief Structure to save one message. +*/ +struct PagerMessage_t { + + /*! + \brief Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved. + */ + uint32_t addr; + + /*! + \brief function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding + */ + uint8_t func; + + /*! + \brief Binary data that will be transmitted. + */ + uint8_t* data; + + /*! + \brief Length of binary data to transmit (in bytes). + */ + size_t data_len; + + /*! + \brief Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD. + */ + uint8_t encoding; +}; + /*! \class PagerClient \brief Client for Pager communication. @@ -119,6 +151,13 @@ class PagerClient { */ int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD, uint8_t function = RADIOLIB_PAGER_FUNC_AUTO); + /*! + \brief Message transmit method. + \param message Message object that will be transmitted. + \returns \ref status_codes + */ + int16_t transmit(PagerMessage_t &message); + #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) /*! \brief Start reception of POCSAG packets. From 2bfc30644538b40f6dfd9aa051b02e456808ab6e Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Mon, 19 Jun 2023 14:18:09 +0200 Subject: [PATCH 3/7] send batch by batch, separate out sync word --- src/protocols/Pager/Pager.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 7bc381e76..3919cb5b0 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -157,23 +157,18 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { msg[i] = RADIOLIB_PAGER_IDLE_CODE_WORD; } - // set frame synchronization code words - for(size_t i = 0; i < numBatches; i++) { - msg[i*(1 + RADIOLIB_PAGER_BATCH_LEN)] = RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD; - } - // write address code word - msg[1 + framePos] = RadioLibBCHInstance.encode(frameAddr); + msg[framePos] = RadioLibBCHInstance.encode(frameAddr); // write the data as 20-bit code blocks if(len > 0) { int8_t remBits = 0; uint8_t dataPos = 0; for(size_t i = 0; i < numDataBlocks + numBatches - 1; i++) { - uint8_t blockPos = 1 + framePos + 1 + i; + uint8_t blockPos = framePos + 1 + i; // check if we need to skip a frame sync marker - if(((blockPos - 1) % RADIOLIB_PAGER_BATCH_LEN) == 0) { + if((blockPos % RADIOLIB_PAGER_BATCH_LEN) == 0) { blockPos++; i++; } @@ -238,8 +233,13 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { PagerClient::write(RADIOLIB_PAGER_PREAMBLE_CODE_WORD); } - // transmit the message - PagerClient::write(msg, msgLen); + for(size_t i = 0; i < numBatches; i++) { + // transmit the frame synchronization word + PagerClient::write(RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD); + + // transmit the message + PagerClient::write(msg+i*RADIOLIB_PAGER_BATCH_LEN, RADIOLIB_PAGER_BATCH_LEN); + } #if !defined(RADIOLIB_STATIC_ONLY) delete[] msg; From 66765511dd2ac06516bb2ab2af5c97da473f1a2b Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Tue, 20 Jun 2023 18:30:08 +0200 Subject: [PATCH 4/7] rewrite pager transmit function --- src/protocols/Pager/Pager.cpp | 72 ++++++++++++++--------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 3919cb5b0..8a26025c7 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -76,19 +76,19 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t int16_t PagerClient::transmit(PagerMessage_t &message) { uint32_t addr = message.addr; - uint8_t function = message.func; - uint8_t* data = message.data; - size_t len = message.data_len; - uint8_t encoding = message.encoding; - if(addr > RADIOLIB_PAGER_ADDRESS_MAX) { return(RADIOLIB_ERR_INVALID_ADDRESS_WIDTH); } + uint32_t addr_h = message.addr >> 3; + uint32_t addr_l = message.addr & 0x07; + uint8_t* data = message.data; + size_t len = message.data_len; if(((data == NULL) && (len > 0)) || ((data != NULL) && (len == 0))) { return(RADIOLIB_ERR_INVALID_PAYLOAD); } + uint8_t encoding = message.encoding; // get symbol bit length based on encoding uint8_t symbolLength = 0; if(encoding == RADIOLIB_PAGER_BCD) { @@ -102,6 +102,7 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { } + uint8_t function = message.func; // Automatically set function bits based on given encoding if (function == RADIOLIB_PAGER_FUNC_AUTO) { if(encoding == RADIOLIB_PAGER_BCD) { @@ -123,10 +124,7 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { } // get target position in batch (3 LSB from address determine frame position in batch) - uint8_t framePos = 2*(addr & 0x07); - - // get address that will be written into address frame - uint32_t frameAddr = ((addr >> 3) << RADIOLIB_PAGER_ADDRESS_POS) | (function << RADIOLIB_PAGER_FUNC_BITS_POS); + uint8_t framePosAddr = 2*addr_l; // calculate the number of 20-bit data blocks size_t numDataBlocks = (len * symbolLength) / RADIOLIB_PAGER_MESSAGE_BITS_LENGTH; @@ -135,13 +133,13 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { } // calculate number of batches - size_t numBatches = (1 + framePos + numDataBlocks) / RADIOLIB_PAGER_BATCH_LEN + 1; - if((1 + numDataBlocks) % RADIOLIB_PAGER_BATCH_LEN == 0) { - numBatches -= 1; + size_t numBatches = (1 + framePosAddr + numDataBlocks) / RADIOLIB_PAGER_BATCH_LEN; + if((1 + framePosAddr + numDataBlocks) % RADIOLIB_PAGER_BATCH_LEN > 0) { + numBatches += 1; } // calculate message length in 32-bit code words - size_t msgLen = (1 + RADIOLIB_PAGER_BATCH_LEN) * numBatches; + size_t msgLen = RADIOLIB_PAGER_BATCH_LEN * numBatches; #if defined(RADIOLIB_STATIC_ONLY) uint32_t msg[RADIOLIB_STATIC_ARRAY_SIZE]; @@ -149,29 +147,22 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { uint32_t* msg = new uint32_t[msgLen]; #endif - // build the message - memset(msg, 0x00, msgLen*sizeof(uint32_t)); - // start by setting everything to idle for(size_t i = 0; i < msgLen; i++) { msg[i] = RADIOLIB_PAGER_IDLE_CODE_WORD; } + // construct address frame + uint32_t frameAddr = (addr_h << RADIOLIB_PAGER_ADDRESS_POS) | (function << RADIOLIB_PAGER_FUNC_BITS_POS); + // write address code word - msg[framePos] = RadioLibBCHInstance.encode(frameAddr); + msg[framePosAddr] = RadioLibBCHInstance.encode(frameAddr); // write the data as 20-bit code blocks - if(len > 0) { int8_t remBits = 0; uint8_t dataPos = 0; - for(size_t i = 0; i < numDataBlocks + numBatches - 1; i++) { - uint8_t blockPos = framePos + 1 + i; - - // check if we need to skip a frame sync marker - if((blockPos % RADIOLIB_PAGER_BATCH_LEN) == 0) { - blockPos++; - i++; - } + for(size_t i = 0; i < numDataBlocks; i++) { + uint8_t blockPos = framePosAddr + 1 + i; // mark this as a message code word msg[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1); @@ -188,8 +179,19 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { int8_t symbolPos = RADIOLIB_PAGER_CODE_WORD_LEN - 1 - symbolLength - remBits; while(symbolPos > (RADIOLIB_PAGER_FUNC_BITS_POS - symbolLength)) { + uint8_t symbol; + if(dataPos < len) { + symbol = data[dataPos++]; + } else if(dataPos >= len) { + // we ran out of message symbols + // in BCD mode, pad the rest of the code word with spaces (0xC) + if(encoding == RADIOLIB_PAGER_BCD) { + symbol = ' '; + } else { + break; + } + } // for BCD, encode the symbol - uint8_t symbol = data[dataPos++]; if(encoding == RADIOLIB_PAGER_BCD) { symbol = encodeBCD(symbol); } @@ -200,21 +202,6 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { msg[blockPos] |= (uint32_t)symbol << symbolPos; symbolPos -= symbolLength; - // check if we ran out of message symbols - if(dataPos >= len) { - // in BCD mode, pad the rest of the code word with spaces (0xC) - if(encoding == RADIOLIB_PAGER_BCD) { - uint8_t numSteps = (symbolPos - RADIOLIB_PAGER_FUNC_BITS_POS + symbolLength)/symbolLength; - for(uint8_t i = 0; i < numSteps; i++) { - symbol = encodeBCD(' '); - symbol = Module::reflect(symbol, 8); - symbol >>= (8 - symbolLength); - msg[blockPos] |= (uint32_t)symbol << symbolPos; - symbolPos -= symbolLength; - } - } - break; - } } // ensure the parity bits are not set due to overflow @@ -226,7 +213,6 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { // do the FEC msg[blockPos] = RadioLibBCHInstance.encode(msg[blockPos]); } - } // transmit the preamble for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { From e53652fed073a40818e278599dcd419fb3039c26 Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Tue, 20 Jun 2023 20:02:14 +0200 Subject: [PATCH 5/7] encode message data in separate function --- src/protocols/Pager/Pager.cpp | 123 +++++++++++++++++++--------------- src/protocols/Pager/Pager.h | 2 + 2 files changed, 70 insertions(+), 55 deletions(-) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 8a26025c7..0f969dc56 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -74,6 +74,73 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t return(PagerClient::transmit(message)); } +int16_t PagerClient::encodeData(uint32_t* buf, uint8_t* data, size_t len, uint8_t encoding) { + // get symbol bit length based on encoding + uint8_t symbolLength = (encoding == RADIOLIB_PAGER_BCD)?4:7; + // calculate the number of 20-bit data blocks + size_t numDataBlocks = (len * symbolLength) / RADIOLIB_PAGER_MESSAGE_BITS_LENGTH; + if((len * symbolLength) % RADIOLIB_PAGER_MESSAGE_BITS_LENGTH > 0) { + numDataBlocks += 1; + } + + // write the data as 20-bit code blocks + int8_t remBits = 0; + uint8_t dataPos = 0; + for(uint8_t blockPos = 0; blockPos < numDataBlocks; blockPos++) { + + // mark this as a message code word + buf[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1); + + // first insert the remainder from previous code word (if any) + if(remBits > 0) { + // this doesn't apply to BCD messages, so no need to check that here + uint8_t prev = Module::reflect(data[dataPos - 1], 8); + prev >>= 1; + buf[blockPos] |= (uint32_t)prev << (RADIOLIB_PAGER_CODE_WORD_LEN - 1 - remBits); + } + + // set all message symbols until we overflow to the next code word or run out of message symbols + int8_t symbolPos = RADIOLIB_PAGER_CODE_WORD_LEN - 1 - symbolLength - remBits; + while(symbolPos > (RADIOLIB_PAGER_FUNC_BITS_POS - symbolLength)) { + + uint8_t symbol; + if(dataPos < len) { + symbol = data[dataPos++]; + } else if(dataPos >= len) { + // we ran out of message symbols + // in BCD mode, pad the rest of the code word with spaces (0xC) + if(encoding == RADIOLIB_PAGER_BCD) { + symbol = ' '; + } else { + break; + } + } + // for BCD, encode the symbol + if(encoding == RADIOLIB_PAGER_BCD) { + symbol = encodeBCD(symbol); + } + symbol = Module::reflect(symbol, 8); + symbol >>= (8 - symbolLength); + + // insert the next message symbol + buf[blockPos] |= (uint32_t)symbol << symbolPos; + symbolPos -= symbolLength; + + } + + // ensure the parity bits are not set due to overflow + buf[blockPos] &= ~(RADIOLIB_PAGER_BCH_BITS_MASK); + + // save the number of overflown bits + remBits = RADIOLIB_PAGER_FUNC_BITS_POS - symbolPos - symbolLength; + + // do the FEC + buf[blockPos] = RadioLibBCHInstance.encode(buf[blockPos]); + } + + return(RADIOLIB_ERR_NONE); +} + int16_t PagerClient::transmit(PagerMessage_t &message) { uint32_t addr = message.addr; if(addr > RADIOLIB_PAGER_ADDRESS_MAX) { @@ -158,61 +225,7 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { // write address code word msg[framePosAddr] = RadioLibBCHInstance.encode(frameAddr); - // write the data as 20-bit code blocks - int8_t remBits = 0; - uint8_t dataPos = 0; - for(size_t i = 0; i < numDataBlocks; i++) { - uint8_t blockPos = framePosAddr + 1 + i; - - // mark this as a message code word - msg[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1); - - // first insert the remainder from previous code word (if any) - if(remBits > 0) { - // this doesn't apply to BCD messages, so no need to check that here - uint8_t prev = Module::reflect(data[dataPos - 1], 8); - prev >>= 1; - msg[blockPos] |= (uint32_t)prev << (RADIOLIB_PAGER_CODE_WORD_LEN - 1 - remBits); - } - - // set all message symbols until we overflow to the next code word or run out of message symbols - int8_t symbolPos = RADIOLIB_PAGER_CODE_WORD_LEN - 1 - symbolLength - remBits; - while(symbolPos > (RADIOLIB_PAGER_FUNC_BITS_POS - symbolLength)) { - - uint8_t symbol; - if(dataPos < len) { - symbol = data[dataPos++]; - } else if(dataPos >= len) { - // we ran out of message symbols - // in BCD mode, pad the rest of the code word with spaces (0xC) - if(encoding == RADIOLIB_PAGER_BCD) { - symbol = ' '; - } else { - break; - } - } - // for BCD, encode the symbol - if(encoding == RADIOLIB_PAGER_BCD) { - symbol = encodeBCD(symbol); - } - symbol = Module::reflect(symbol, 8); - symbol >>= (8 - symbolLength); - - // insert the next message symbol - msg[blockPos] |= (uint32_t)symbol << symbolPos; - symbolPos -= symbolLength; - - } - - // ensure the parity bits are not set due to overflow - msg[blockPos] &= ~(RADIOLIB_PAGER_BCH_BITS_MASK); - - // save the number of overflown bits - remBits = RADIOLIB_PAGER_FUNC_BITS_POS - symbolPos - symbolLength; - - // do the FEC - msg[blockPos] = RadioLibBCHInstance.encode(msg[blockPos]); - } + encodeData(msg+framePosAddr+1, data, len, encoding); // transmit the preamble for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { diff --git a/src/protocols/Pager/Pager.h b/src/protocols/Pager/Pager.h index 6f84debab..95ca14671 100644 --- a/src/protocols/Pager/Pager.h +++ b/src/protocols/Pager/Pager.h @@ -158,6 +158,8 @@ class PagerClient { */ int16_t transmit(PagerMessage_t &message); + int16_t encodeData(uint32_t* buf, uint8_t* data, size_t len, uint8_t encoding); + #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) /*! \brief Start reception of POCSAG packets. From e95739d5abfed276da862b1952dc40e647c84329 Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Tue, 20 Jun 2023 21:44:51 +0200 Subject: [PATCH 6/7] add PagerMessage class --- src/protocols/Pager/Pager.cpp | 207 ++++++++++++++++++++-------------- src/protocols/Pager/Pager.h | 91 +++++++++------ 2 files changed, 181 insertions(+), 117 deletions(-) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 0f969dc56..7956eb684 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -19,6 +19,98 @@ static void PagerClientReadBit(void) { } #endif +PagerMessage::PagerMessage(uint32_t address, uint8_t function, uint8_t* data, size_t data_len, uint8_t encoding) + : address(address) + , function(function) + , data(data) + , data_len(data_len) + , encoding(encoding) + {} + +PagerMessage::PagerMessage(uint32_t address, uint8_t* data, size_t data_len, uint8_t encoding) + : address(address) + , data(data) + , data_len(data_len) + , encoding(encoding) + { + // Automatically set function bits based on given encoding + if(encoding == RADIOLIB_PAGER_BCD) { + function = RADIOLIB_PAGER_FUNC_BITS_NUMERIC; + } else if(encoding == RADIOLIB_PAGER_ASCII) { + function = RADIOLIB_PAGER_FUNC_BITS_ALPHA; + } + if(data_len == 0) { + function = RADIOLIB_PAGER_FUNC_BITS_TONE; + } + } + +uint32_t PagerMessage::getAddr_h() { + return address >> 3; +} + +uint32_t PagerMessage::getAddr_l() { + return address & 0x07; +} + +// get target position in batch (3 LSB from address determine frame position in batch) +uint8_t PagerMessage::getAddrFrameNr() { + return 2*getAddr_l(); +} + +uint8_t PagerMessage::getFirstDataFrameNr() { + return getAddrFrameNr()+1; +} + +// get symbol bit length based on encoding +uint8_t PagerMessage::getSymbolLength() { + if(encoding == RADIOLIB_PAGER_BCD) { + return 4; + } else if(encoding == RADIOLIB_PAGER_ASCII) { + return 7; + } else { + // invalid + return 0; + } +} + +// calculate the number of 20-bit data blocks +size_t PagerMessage::getNumDataBlocks() { + size_t numDataBlocks = (data_len * getSymbolLength()) / RADIOLIB_PAGER_MESSAGE_BITS_LENGTH; + if((data_len * getSymbolLength()) % RADIOLIB_PAGER_MESSAGE_BITS_LENGTH > 0) { + numDataBlocks += 1; + } + return numDataBlocks; +} + +// calculate number of batches needed +size_t PagerMessage::getNumBatches(){ + size_t numBatches = (getAddrFrameNr() + 1 + getNumDataBlocks()) / RADIOLIB_PAGER_BATCH_LEN; + if((getAddrFrameNr() + 1 + getNumDataBlocks()) % RADIOLIB_PAGER_BATCH_LEN > 0) { + numBatches += 1; + } + return numBatches; +} + +uint16_t PagerMessage::validate() { + if(address > RADIOLIB_PAGER_ADDRESS_MAX) { + return(RADIOLIB_ERR_INVALID_ADDRESS_WIDTH); + } + + if(((data == NULL) && (data_len > 0)) || ((data != NULL) && (data_len == 0))) { + return(RADIOLIB_ERR_INVALID_PAYLOAD); + } + + if (getSymbolLength() <= 0) { + return(RADIOLIB_ERR_INVALID_ENCODING); + } + + if (function > RADIOLIB_PAGER_FUNC_BITS_ALPHA) { + return(RADIOLIB_ERR_INVALID_FUNCTION); + } + + return(RADIOLIB_ERR_NONE); +} + PagerClient::PagerClient(PhysicalLayer* phy) { phyLayer = phy; #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) @@ -58,35 +150,36 @@ int16_t PagerClient::sendTone(uint32_t addr) { int16_t PagerClient::transmit(String& str, uint32_t addr, uint8_t encoding, uint8_t function) { return(PagerClient::transmit(str.c_str(), addr, encoding, function)); } +int16_t PagerClient::transmit(String& str, uint32_t addr, uint8_t encoding) { + return(PagerClient::transmit(str.c_str(), addr, encoding)); +} #endif int16_t PagerClient::transmit(const char* str, uint32_t addr, uint8_t encoding, uint8_t function) { return(PagerClient::transmit((uint8_t*)str, strlen(str), addr, encoding, function)); } +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, uint8_t function) { - PagerMessage_t message; - message.addr = addr; - message.func = function; - message.data = data; - message.data_len = len; - message.encoding = encoding; + PagerMessage message(addr, function, data, len, encoding); + return(PagerClient::transmit(message)); +} +int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding) { + PagerMessage message(addr, data, len, encoding); return(PagerClient::transmit(message)); } -int16_t PagerClient::encodeData(uint32_t* buf, uint8_t* data, size_t len, uint8_t encoding) { - // get symbol bit length based on encoding - uint8_t symbolLength = (encoding == RADIOLIB_PAGER_BCD)?4:7; - // calculate the number of 20-bit data blocks - size_t numDataBlocks = (len * symbolLength) / RADIOLIB_PAGER_MESSAGE_BITS_LENGTH; - if((len * symbolLength) % RADIOLIB_PAGER_MESSAGE_BITS_LENGTH > 0) { - numDataBlocks += 1; - } + +int16_t PagerClient::encodeData(uint32_t* buf, PagerMessage &message) { + uint8_t symbolLength = message.getSymbolLength(); + uint8_t* data = message.getData(); // write the data as 20-bit code blocks int8_t remBits = 0; uint8_t dataPos = 0; - for(uint8_t blockPos = 0; blockPos < numDataBlocks; blockPos++) { + for(uint8_t blockPos = 0; blockPos < message.getNumDataBlocks(); blockPos++) { // mark this as a message code word buf[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1); @@ -104,19 +197,19 @@ int16_t PagerClient::encodeData(uint32_t* buf, uint8_t* data, size_t len, uint8_ while(symbolPos > (RADIOLIB_PAGER_FUNC_BITS_POS - symbolLength)) { uint8_t symbol; - if(dataPos < len) { + if(dataPos < message.getDataLen()) { symbol = data[dataPos++]; - } else if(dataPos >= len) { + } else { // if(dataPos >= message.getDataLen()) { // we ran out of message symbols // in BCD mode, pad the rest of the code word with spaces (0xC) - if(encoding == RADIOLIB_PAGER_BCD) { + if(message.getEncoding() == RADIOLIB_PAGER_BCD) { symbol = ' '; } else { break; } } // for BCD, encode the symbol - if(encoding == RADIOLIB_PAGER_BCD) { + if(message.getEncoding() == RADIOLIB_PAGER_BCD) { symbol = encodeBCD(symbol); } symbol = Module::reflect(symbol, 8); @@ -141,72 +234,15 @@ int16_t PagerClient::encodeData(uint32_t* buf, uint8_t* data, size_t len, uint8_ return(RADIOLIB_ERR_NONE); } -int16_t PagerClient::transmit(PagerMessage_t &message) { - uint32_t addr = message.addr; - if(addr > RADIOLIB_PAGER_ADDRESS_MAX) { - return(RADIOLIB_ERR_INVALID_ADDRESS_WIDTH); - } - uint32_t addr_h = message.addr >> 3; - uint32_t addr_l = message.addr & 0x07; +int16_t PagerClient::transmit(PagerMessage &message) { - uint8_t* data = message.data; - size_t len = message.data_len; - if(((data == NULL) && (len > 0)) || ((data != NULL) && (len == 0))) { - return(RADIOLIB_ERR_INVALID_PAYLOAD); - } - - uint8_t encoding = message.encoding; - // get symbol bit length based on encoding - uint8_t symbolLength = 0; - if(encoding == RADIOLIB_PAGER_BCD) { - symbolLength = 4; - - } else if(encoding == RADIOLIB_PAGER_ASCII) { - symbolLength = 7; - - } else { - return(RADIOLIB_ERR_INVALID_ENCODING); - - } - - uint8_t function = message.func; - // Automatically set function bits based on given encoding - if (function == RADIOLIB_PAGER_FUNC_AUTO) { - if(encoding == RADIOLIB_PAGER_BCD) { - function = RADIOLIB_PAGER_FUNC_BITS_NUMERIC; - - } else if(encoding == RADIOLIB_PAGER_ASCII) { - function = RADIOLIB_PAGER_FUNC_BITS_ALPHA; - - } else { - return(RADIOLIB_ERR_INVALID_ENCODING); - - } - if(len == 0) { - function = RADIOLIB_PAGER_FUNC_BITS_TONE; - } - } - if (function > RADIOLIB_PAGER_FUNC_BITS_ALPHA) { - return(RADIOLIB_ERR_INVALID_FUNCTION); - } - - // get target position in batch (3 LSB from address determine frame position in batch) - uint8_t framePosAddr = 2*addr_l; - - // calculate the number of 20-bit data blocks - size_t numDataBlocks = (len * symbolLength) / RADIOLIB_PAGER_MESSAGE_BITS_LENGTH; - if((len * symbolLength) % RADIOLIB_PAGER_MESSAGE_BITS_LENGTH > 0) { - numDataBlocks += 1; - } - - // calculate number of batches - size_t numBatches = (1 + framePosAddr + numDataBlocks) / RADIOLIB_PAGER_BATCH_LEN; - if((1 + framePosAddr + numDataBlocks) % RADIOLIB_PAGER_BATCH_LEN > 0) { - numBatches += 1; + uint16_t err = message.validate(); + if (err) { + return(err); } // calculate message length in 32-bit code words - size_t msgLen = RADIOLIB_PAGER_BATCH_LEN * numBatches; + size_t msgLen = RADIOLIB_PAGER_BATCH_LEN * message.getNumBatches(); #if defined(RADIOLIB_STATIC_ONLY) uint32_t msg[RADIOLIB_STATIC_ARRAY_SIZE]; @@ -220,19 +256,20 @@ int16_t PagerClient::transmit(PagerMessage_t &message) { } // construct address frame - uint32_t frameAddr = (addr_h << RADIOLIB_PAGER_ADDRESS_POS) | (function << RADIOLIB_PAGER_FUNC_BITS_POS); + uint32_t frameAddr = (message.getAddr_h() << RADIOLIB_PAGER_ADDRESS_POS) | (message.getFunction() << RADIOLIB_PAGER_FUNC_BITS_POS); // write address code word - msg[framePosAddr] = RadioLibBCHInstance.encode(frameAddr); + msg[message.getAddrFrameNr()] = RadioLibBCHInstance.encode(frameAddr); - encodeData(msg+framePosAddr+1, data, len, encoding); + uint32_t* msg_data_ptr = msg+message.getFirstDataFrameNr(); + encodeData(msg_data_ptr, message); // transmit the preamble for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { PagerClient::write(RADIOLIB_PAGER_PREAMBLE_CODE_WORD); } - for(size_t i = 0; i < numBatches; i++) { + for(size_t i = 0; i < message.getNumBatches(); i++) { // transmit the frame synchronization word PagerClient::write(RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD); diff --git a/src/protocols/Pager/Pager.h b/src/protocols/Pager/Pager.h index 95ca14671..2cbabe9c3 100644 --- a/src/protocols/Pager/Pager.h +++ b/src/protocols/Pager/Pager.h @@ -50,7 +50,6 @@ #define RADIOLIB_PAGER_FUNC_BITS_TONE (0b01) #define RADIOLIB_PAGER_FUNC_BITS_ACTIVATION (0b10) #define RADIOLIB_PAGER_FUNC_BITS_ALPHA (0b11) -#define RADIOLIB_PAGER_FUNC_AUTO 0xFF // the maximum allowed address (2^22 - 1) #define RADIOLIB_PAGER_ADDRESS_MAX (2097151) @@ -59,32 +58,57 @@ \struct PagerMessage_t \brief Structure to save one message. */ -struct PagerMessage_t { - - /*! - \brief Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved. - */ - uint32_t addr; - - /*! - \brief function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding - */ - uint8_t func; - - /*! - \brief Binary data that will be transmitted. - */ - uint8_t* data; - - /*! - \brief Length of binary data to transmit (in bytes). - */ - size_t data_len; - - /*! - \brief Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD. - */ - uint8_t encoding; +class PagerMessage { + public: + /*! + \brief Default constructor. + \param data Binary data that will be transmitted. + \param len Length of binary data to transmit (in bytes). + \param addr Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved. + \param encoding Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD. + \param function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding + */ + explicit PagerMessage(uint32_t address, uint8_t function, uint8_t* data, size_t data_len, uint8_t encoding); + explicit PagerMessage(uint32_t address, uint8_t* data, size_t data_len, uint8_t encoding); + + uint32_t getAddr_h(); + uint32_t getAddr_l(); + uint8_t getAddrFrameNr(); + uint8_t getFirstDataFrameNr(); + uint8_t getSymbolLength(); + uint8_t getFunction() { return function; }; + uint8_t* getData() { return data; }; + size_t getDataLen() { return data_len; }; + uint8_t getEncoding() { return encoding; }; + size_t getNumDataBlocks(); + size_t getNumBatches(); + uint16_t validate(); + + private: + /*! + \brief Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved. + */ + uint32_t address; + + /*! + \brief function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding + */ + uint8_t function; + + /*! + \brief Binary data that will be transmitted. + */ + uint8_t* data; + + /*! + \brief Length of binary data to transmit (in bytes). + */ + size_t data_len; + + /*! + \brief Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD. + */ + uint8_t encoding; }; /*! @@ -127,7 +151,8 @@ class PagerClient { \param function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding \returns \ref status_codes */ - int16_t transmit(String& str, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD, uint8_t function = RADIOLIB_PAGER_FUNC_AUTO); + int16_t transmit(String& str, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD); + int16_t transmit(String& str, uint32_t addr, uint8_t encoding, uint8_t function); #endif /*! @@ -138,7 +163,8 @@ class PagerClient { \param function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding \returns \ref status_codes */ - int16_t transmit(const char* str, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD, uint8_t function = RADIOLIB_PAGER_FUNC_AUTO); + int16_t transmit(const char* str, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD); + int16_t transmit(const char* str, uint32_t addr, uint8_t encoding, uint8_t function); /*! \brief Binary transmit method. Will transmit arbitrary binary data. @@ -149,16 +175,17 @@ class PagerClient { \param function bits (NUMERIC, TONE, ACTIVATION, ALPHANUMERIC). Allowed values 0 to 3. Defaults to auto select by specified encoding \returns \ref status_codes */ - int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD, uint8_t function = RADIOLIB_PAGER_FUNC_AUTO); + int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD); + int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding, uint8_t function); /*! \brief Message transmit method. \param message Message object that will be transmitted. \returns \ref status_codes */ - int16_t transmit(PagerMessage_t &message); + int16_t transmit(PagerMessage &message); - int16_t encodeData(uint32_t* buf, uint8_t* data, size_t len, uint8_t encoding); + int16_t encodeData(uint32_t* buf, PagerMessage &message); #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) /*! From 39eea86ce2f5ae44098b525bf811307a8d56da09 Mon Sep 17 00:00:00 2001 From: Hendrik Langer Date: Tue, 20 Jun 2023 23:44:26 +0200 Subject: [PATCH 7/7] transmitMulti --- src/protocols/Pager/Pager.cpp | 105 +++++++++++++++++++++++----------- src/protocols/Pager/Pager.h | 10 +++- 2 files changed, 82 insertions(+), 33 deletions(-) diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 7956eb684..ac1e79f38 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -44,6 +44,10 @@ PagerMessage::PagerMessage(uint32_t address, uint8_t* data, size_t data_len, uin } } +PagerMessage::PagerMessage(uint32_t address, const char* string, uint8_t encoding) + : PagerMessage(address, (uint8_t*)string, strlen(string), encoding) + {} + uint32_t PagerMessage::getAddr_h() { return address >> 3; } @@ -171,10 +175,47 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t return(PagerClient::transmit(message)); } +int16_t PagerClient::transmitBuffer(uint32_t* buf, size_t num_words) { + size_t num_batches = num_words / RADIOLIB_PAGER_BATCH_LEN; + + // transmit the preamble + for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { + PagerClient::write(RADIOLIB_PAGER_PREAMBLE_CODE_WORD); + } + + for(size_t i = 0; i < num_batches; i++) { + // transmit the frame synchronization word + PagerClient::write(RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD); + + // transmit the batch + PagerClient::write(buf+i*RADIOLIB_PAGER_BATCH_LEN, RADIOLIB_PAGER_BATCH_LEN); + } + + // turn transmitter off + phyLayer->standby(); + + return(RADIOLIB_ERR_NONE); +} + +int16_t PagerClient::encodeMessage(uint32_t* buf, PagerMessage &message) { + // calculate message length in 32-bit code words + size_t msgLen = RADIOLIB_PAGER_BATCH_LEN * message.getNumBatches(); + + // start by setting everything to idle + for(size_t i = 0; i < msgLen; i++) { + buf[i] = RADIOLIB_PAGER_IDLE_CODE_WORD; + } + + // construct address frame + uint32_t frameAddr = (message.getAddr_h() << RADIOLIB_PAGER_ADDRESS_POS) | (message.getFunction() << RADIOLIB_PAGER_FUNC_BITS_POS); + + // write address code word + buf[message.getAddrFrameNr()] = RadioLibBCHInstance.encode(frameAddr); + -int16_t PagerClient::encodeData(uint32_t* buf, PagerMessage &message) { uint8_t symbolLength = message.getSymbolLength(); uint8_t* data = message.getData(); + uint32_t* buf_data_ptr = buf+message.getFirstDataFrameNr(); // write the data as 20-bit code blocks int8_t remBits = 0; @@ -182,14 +223,14 @@ int16_t PagerClient::encodeData(uint32_t* buf, PagerMessage &message) { for(uint8_t blockPos = 0; blockPos < message.getNumDataBlocks(); blockPos++) { // mark this as a message code word - buf[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1); + buf_data_ptr[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1); // first insert the remainder from previous code word (if any) if(remBits > 0) { // this doesn't apply to BCD messages, so no need to check that here uint8_t prev = Module::reflect(data[dataPos - 1], 8); prev >>= 1; - buf[blockPos] |= (uint32_t)prev << (RADIOLIB_PAGER_CODE_WORD_LEN - 1 - remBits); + buf_data_ptr[blockPos] |= (uint32_t)prev << (RADIOLIB_PAGER_CODE_WORD_LEN - 1 - remBits); } // set all message symbols until we overflow to the next code word or run out of message symbols @@ -216,22 +257,23 @@ int16_t PagerClient::encodeData(uint32_t* buf, PagerMessage &message) { symbol >>= (8 - symbolLength); // insert the next message symbol - buf[blockPos] |= (uint32_t)symbol << symbolPos; + buf_data_ptr[blockPos] |= (uint32_t)symbol << symbolPos; symbolPos -= symbolLength; } // ensure the parity bits are not set due to overflow - buf[blockPos] &= ~(RADIOLIB_PAGER_BCH_BITS_MASK); + buf_data_ptr[blockPos] &= ~(RADIOLIB_PAGER_BCH_BITS_MASK); // save the number of overflown bits remBits = RADIOLIB_PAGER_FUNC_BITS_POS - symbolPos - symbolLength; // do the FEC - buf[blockPos] = RadioLibBCHInstance.encode(buf[blockPos]); + buf_data_ptr[blockPos] = RadioLibBCHInstance.encode(buf_data_ptr[blockPos]); } - return(RADIOLIB_ERR_NONE); + return(msgLen); + } int16_t PagerClient::transmit(PagerMessage &message) { @@ -250,42 +292,41 @@ int16_t PagerClient::transmit(PagerMessage &message) { uint32_t* msg = new uint32_t[msgLen]; #endif - // start by setting everything to idle - for(size_t i = 0; i < msgLen; i++) { - msg[i] = RADIOLIB_PAGER_IDLE_CODE_WORD; - } + encodeMessage(msg, message); - // construct address frame - uint32_t frameAddr = (message.getAddr_h() << RADIOLIB_PAGER_ADDRESS_POS) | (message.getFunction() << RADIOLIB_PAGER_FUNC_BITS_POS); + transmitBuffer(msg, msgLen); - // write address code word - msg[message.getAddrFrameNr()] = RadioLibBCHInstance.encode(frameAddr); + #if !defined(RADIOLIB_STATIC_ONLY) + delete[] msg; + #endif - uint32_t* msg_data_ptr = msg+message.getFirstDataFrameNr(); - encodeData(msg_data_ptr, message); + return(RADIOLIB_ERR_NONE); +} - // transmit the preamble - for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) { - PagerClient::write(RADIOLIB_PAGER_PREAMBLE_CODE_WORD); +#if !defined(RADIOLIB_STATIC_ONLY) +int16_t PagerClient::transmitMulti(PagerMessage messages[], size_t num_messages) { + size_t total_words = 0; + for (int i = 0; i < num_messages; i++) { + if (messages[i].validate() == RADIOLIB_ERR_NONE) { + total_words += messages[i].getNumBatches() * RADIOLIB_PAGER_BATCH_LEN; + } else { + uint16_t error = messages[i].validate(); + return(error); + } } + uint32_t* buffer = new uint32_t[total_words]; - for(size_t i = 0; i < message.getNumBatches(); i++) { - // transmit the frame synchronization word - PagerClient::write(RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD); - - // transmit the message - PagerClient::write(msg+i*RADIOLIB_PAGER_BATCH_LEN, RADIOLIB_PAGER_BATCH_LEN); + int pos = 0; + for (int i = 0; i < num_messages; i++) { + pos += encodeMessage(buffer+pos, messages[i]); } - #if !defined(RADIOLIB_STATIC_ONLY) - delete[] msg; - #endif - - // turn transmitter off - phyLayer->standby(); + transmitBuffer(buffer, total_words); + delete[] buffer; return(RADIOLIB_ERR_NONE); } +#endif #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) int16_t PagerClient::startReceive(uint32_t pin, uint32_t addr, uint32_t mask) { diff --git a/src/protocols/Pager/Pager.h b/src/protocols/Pager/Pager.h index 2cbabe9c3..d2ae32000 100644 --- a/src/protocols/Pager/Pager.h +++ b/src/protocols/Pager/Pager.h @@ -70,6 +70,8 @@ class PagerMessage { */ explicit PagerMessage(uint32_t address, uint8_t function, uint8_t* data, size_t data_len, uint8_t encoding); explicit PagerMessage(uint32_t address, uint8_t* data, size_t data_len, uint8_t encoding); + explicit PagerMessage(uint32_t address, const char* string, uint8_t encoding); + PagerMessage() {}; uint32_t getAddr_h(); uint32_t getAddr_l(); @@ -185,7 +187,13 @@ class PagerClient { */ int16_t transmit(PagerMessage &message); - int16_t encodeData(uint32_t* buf, PagerMessage &message); + int16_t transmitBuffer(uint32_t* buf, size_t num_words); + + #if !defined(RADIOLIB_STATIC_ONLY) + int16_t transmitMulti(PagerMessage messages[], size_t num_messages); + #endif + + int16_t encodeMessage(uint32_t* buf, PagerMessage &message); #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) /*!