Init fork from Stuart Robinson's repo

This commit is contained in:
2024-10-03 14:30:13 +03:00
commit 9395706524
201 changed files with 45709 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,166 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 22/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h'
file. The pins to access the lora device need to be defined in the 'Settings.h' file also.
There is a printout of the valid packets received in HEX format. Thus the program can be used to receive
and record non-ASCII packets. The LED will flash for each packet received. To keep up with fast transfers
only the first bytes defined by constant BytesToPrint in the Settings.h file are printed.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#include <SPI.h>
#include <SX128XLT.h>
#include "Settings.h"
SX128XLT LoRa;
uint8_t RXPacketL; //stores length of packet received
int16_t PacketRSSI; //stores RSSI of received packet
int8_t PacketSNR; //stores signal to noise ratio of received packet
void loop()
{
RXPacketL = LoRa.receiveSXBuffer(0, 60000, WAIT_RX); //returns 0 if packet error of some sort, timeout set at 60secs\60000mS
digitalWrite(LED1, HIGH); //something has happened
printSeconds();
PacketRSSI = LoRa.readPacketRSSI();
PacketSNR = LoRa.readPacketSNR();
if (RXPacketL == 0)
{
packet_is_Error();
}
else
{
packet_is_OK();
}
digitalWrite(LED1, LOW);
Serial.println();
}
void packet_is_OK()
{
printReceptionDetails();
Serial.print(RXPacketL);
Serial.print(F(" bytes > "));
if (RXPacketL > BytesToPrint)
{
LoRa.printSXBufferHEX(0, (BytesToPrint - 1));
}
else
{
LoRa.printSXBufferHEX(0, (RXPacketL - 1));
}
}
void packet_is_Error()
{
uint16_t IRQStatus;
IRQStatus = LoRa.readIrqStatus(); //get the IRQ status
if (IRQStatus & IRQ_RX_TIMEOUT)
{
Serial.print(F(" RXTimeout"));
}
else
{
Serial.print(F(" PacketError"));
printReceptionDetails();
Serial.print(F(" Length,"));
Serial.print(LoRa.readRXPacketL()); //get the real packet length
Serial.print(F(",IRQreg,"));
Serial.print(IRQStatus, HEX);
}
}
void printReceptionDetails()
{
Serial.print(F(" RSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm,SNR,"));
Serial.print(PacketSNR);
Serial.print(F("dB "));
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void printSeconds()
{
float secs;
secs = ( (float) millis() / 1000);
Serial.print(secs, 3);
}
void setup()
{
pinMode(LED1, OUTPUT);
led_Flash(2, 125);
Serial.begin(115200);
Serial.println(F("221_LoRa_Packet_Monitor Starting"));
SPI.begin();
//SPI beginTranscation is normally part of library routines, but if it is disabled in library
//a single instance is needed here, so uncomment the program line below
//SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
Serial.println(F("LoRa Device found"));
led_Flash(2, 125);
delay(1000);
}
else
{
Serial.println(F("No LoRa device responding"));
while (1)
{
led_Flash(50, 50);
}
}
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println();
LoRa.printModemSettings();
Serial.println();
LoRa.printOperatingSettings();
Serial.println();
Serial.print(F("Packet monitor ready"));
Serial.println();
}

View File

@ -0,0 +1,31 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 06/11/21
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
//******* Setup hardware pin definitions here ! ***************
//These are the pin definitions for one of my own boards, the Easy Pro Mini,
//be sure to change the definitions to match your own setup.
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
const uint8_t BytesToPrint = 16; //number of bytes of packet to print

View File

@ -0,0 +1,162 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - The program listens for incoming packets using the FLRC settings in the 'Settings.h'
file. The pins to access the lora device need to be defined in the 'Settings.h' file also.
There is a printout of the valid packets received in HEX format. Thus the program can be used to receive
and record non-ASCII packets. The LED will flash for each packet received. To keep up with fast transfers
only the first bytes defined by constant BytesToPrint in the Settings.h file are printed.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#include <SPI.h>
#include <SX128XLT.h>
#include "Settings.h"
SX128XLT LoRa;
uint8_t RXPacketL; //stores length of packet received
int16_t PacketRSSI; //stores RSSI of received packet
void loop()
{
RXPacketL = LoRa.receiveSXBuffer(0, 60000, WAIT_RX); //returns 0 if packet error of some sort, timeout set at 60secs\60000mS
digitalWrite(LED1, HIGH); //something has happened
printSeconds();
PacketRSSI = LoRa.readPacketRSSI();
if (RXPacketL == 0)
{
packet_is_Error();
}
else
{
packet_is_OK();
}
digitalWrite(LED1, LOW);
Serial.println();
}
void packet_is_OK()
{
printReceptionDetails();
Serial.print(RXPacketL);
Serial.print(F(" bytes > "));
if (RXPacketL > BytesToPrint)
{
LoRa.printSXBufferHEX(0, (BytesToPrint - 1));
}
else
{
LoRa.printSXBufferHEX(0, (RXPacketL - 1));
}
}
void packet_is_Error()
{
uint16_t IRQStatus;
IRQStatus = LoRa.readIrqStatus(); //get the IRQ status
if (IRQStatus & IRQ_RX_TIMEOUT)
{
Serial.print(F(" RXTimeout"));
}
else
{
Serial.print(F(" PacketError"));
printReceptionDetails();
Serial.print(F(" Length,"));
Serial.print(LoRa.readRXPacketL()); //get the real packet length
Serial.print(F(",IRQreg,"));
Serial.print(IRQStatus, HEX);
}
}
void printReceptionDetails()
{
Serial.print(F(" RSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm "));
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void printSeconds()
{
float secs;
secs = ( (float) millis() / 1000);
Serial.print(secs, 3);
}
void setup()
{
pinMode(LED1, OUTPUT);
led_Flash(2, 125);
Serial.begin(115200);
Serial.println(F("221_LoRa_Packet_Monitor Starting"));
SPI.begin();
//SPI beginTranscation is normally part of library routines, but if it is disabled in library
//a single instance is needed here, so uncomment the program line below
//SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
Serial.println(F("LoRa Device found"));
led_Flash(2, 125);
delay(1000);
}
else
{
Serial.println(F("No LoRa device responding"));
while (1)
{
led_Flash(50, 50);
}
}
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println();
LoRa.printModemSettings();
Serial.println();
LoRa.printOperatingSettings();
Serial.println();
Serial.print(F("Packet monitor ready"));
Serial.println();
}

View File

@ -0,0 +1,34 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 06/11/21
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
//******* Setup hardware pin definitions here ! ***************
//These are the pin definitions for one of my own boards, the Easy Pro Mini,
//be sure to change the definitions to match your own setup.
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword, needs to match transmitter
const uint8_t BytesToPrint = 32; //number of bytes of packet to print

View File

@ -0,0 +1,103 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 14/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a program that transfers a file from an Arduinos SD card to a folder on a
PC that is connected to the Arduino via a Serial port. The transfer process uses the YModem protocol
and the PC receives the file using the Tera Term Serial terminal program.
This program was run on an Arduino DUE, with Serial2 used as the transfer port to the PC.
Instructions for using the program are to be found here;
https://stuartsprojects.github.io/2021/12/28/Arduino-to-PC-File-Transfers.html
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#include <SPI.h>
#include <SD.h>
#include "YModem.h"
File root;
const uint16_t SDCS = 30;
const uint16_t LED1 = 8; //LED indicator etc. on during transfers
char FileName[] = "$50SATL.JPG"; //file length 63091 bytes, file CRC 0x59CE
uint16_t transferNumber = 0;;
uint32_t filelength;
uint32_t bytestransfered;
void loop()
{
transferNumber++;
digitalWrite(LED1, HIGH);
Serial.print(F("Start transfer "));
Serial.print(transferNumber);
Serial.print(F(" "));
Serial.println(FileName);
Serial.flush();
digitalWrite(LED1, HIGH);
bytestransfered = yModemSend(FileName, 1, 1); //transfer filename with waitForReceiver and batchMode options set
if (bytestransfered > 0)
{
Serial.print(F("YModem transfer completed "));
Serial.print(bytestransfered);
Serial.println(F(" bytes sent"));
}
else
{
Serial.println(F("YModem transfer FAILED"));
}
Serial.println();
digitalWrite(LED1, LOW);
Serial.println();
delay(10000);
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
Serial.begin(115200);
Serial2.begin(115200); //Serial port used for file transfers
Serial.println();
Serial.println(F(__FILE__));
Serial.flush();
Serial.println("Using Serial2 for YModem comms @ 115200 baud.");
Serial.println("Initializing SD card...");
if (SD.begin(SDCS))
{
Serial.println(F("SD Card initialized."));
}
else
{
Serial.println(F("SD Card failed, or not present."));
while (1) led_Flash(100, 25);
}
}

View File

@ -0,0 +1,264 @@
//29/12/21 Working for repeat transfer - source of this version not clear
// Code below taken from https://gist.github.com/zonque/0ae2dc8cedbcdbd9b933
// file xymodem-mini.c
// MarkD modifcations
// - uses 128 byte packets for low RAM devices
// - supports batch upload of several files at once
//StuartsProjects - hard coded for Serial2
//080222 - added timeouts
#define X_SOH 0x01
#define X_STX 0x02
#define X_ACK 0x06
#define X_NAK 0x15
#define X_EOT 0x04
const uint32_t transfertimeoutmS = 5000;
const uint8_t ackerrorlimit = 16;
struct yModemPacket {
uint8_t start;
uint8_t block;
uint8_t block_neg;
uint8_t payload[128];
uint16_t crc;
} __attribute__((packed));
#define CRC_POLY 0x1021
static uint16_t crc_update(uint16_t crc_in, int incr)
{
uint16_t _xor = crc_in >> 15;
uint16_t _out = crc_in << 1;
if (incr)
_out++;
if (_xor)
_out ^= CRC_POLY;
return _out;
}
static uint16_t crc16(const uint8_t *data, uint16_t size)
{
uint16_t crc, i;
for (crc = 0; size > 0; size--, data++)
for (i = 0x80; i; i >>= 1)
crc = crc_update(crc, *data & i);
for (i = 0; i < 16; i++)
crc = crc_update(crc, 0);
return crc;
}
static uint16_t swap16(uint16_t in)
{
return (in >> 8) | ((in & 0xff) << 8);
}
// Main YModem code.
// filename is pointer to null terminated string
// set waitForReceiver to 1 so that the upload begins when TeraTerm is ready
// set batchMode to 0 when sending 1 file
// set batchMode to 1 for each file sent apart from the last one.
static int yModemSend(const char *filename, int waitForReceiver, int batchMode )
{
uint32_t numBytesStillToSend = 0;
uint16_t numBytesThisPacket = 0;
uint8_t skip_payload;
uint8_t doNextBlock;
uint8_t answer = 0;
char spfBuff[16];
struct yModemPacket yPacket;
uint32_t filebytescopied = 0;
uint32_t startmS;
uint8_t ackerrors = 0;
File srcFile = SD.open(filename, O_RDONLY);
if (srcFile < 0)
{
Serial.println("Open error");
return 0;
}
numBytesStillToSend = srcFile.size();
if (numBytesStillToSend == 0)
{
Serial.println("SD file error");
return 0;
}
//convert the size of the file to an ASCII representation for header packet
sprintf(spfBuff, "%ld", numBytesStillToSend);
// wait here for the receiving device to respond
if ( waitForReceiver )
{
Serial.print("Waiting for receiver ping ... ");
while ( Serial2.available() ) Serial2.read();
startmS = millis();
do
{
if ( Serial2.available() ) answer = Serial2.read();
}
while ((answer != 'C') && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if (answer != 'C')
{
Serial.println("Timeout starting YModem transfer");
return 0;
}
else
{
Serial.println("done");
}
}
Serial.print("YModem Sending ");
Serial.print(filename);
Serial.print(" ");
Serial.print(spfBuff);
Serial.println(" bytes");
yPacket.start = X_SOH;
yPacket.block = 0;
// copy the filename into the payload - fill remainder of payload with 0x00
strncpy((char *) yPacket.payload, filename, sizeof(yPacket.payload));
// insert the file size in bytes as ASCII after the NULL of the filename string
strcpy( (char *)(yPacket.payload) + strlen(filename) + 1 , spfBuff );
// first pass - don't read any file data as it will overwrite the file details packet
skip_payload = 1;
while (numBytesStillToSend > 0)
{
doNextBlock = 0;
// if this isn't the 1st pass, then read a block of up to 128 bytes from the file
if (skip_payload == 0)
{
numBytesThisPacket = min(numBytesStillToSend, sizeof(yPacket.payload));
srcFile.read(yPacket.payload, numBytesThisPacket);
filebytescopied = filebytescopied + numBytesThisPacket;
if (numBytesThisPacket < sizeof(yPacket.payload)) {
// pad out the rest of the payload block with 0x1A
memset(yPacket.payload + numBytesThisPacket, 0x1A, sizeof(yPacket.payload) - numBytesThisPacket);
}
}
yPacket.block_neg = 0xff - yPacket.block;
// calculate and insert the CRC16 checksum into the packet
yPacket.crc = swap16(crc16(yPacket.payload, sizeof(yPacket.payload)));
// send the whole packet to the receiver - will block here
startmS = millis();
Serial2.write( (uint8_t*)&yPacket, sizeof(yPacket));
// wait for the receiver to send back a response to the packet
while ((!Serial2.available()) && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if ( ((uint32_t) (millis() - startmS) >= transfertimeoutmS))
{
Serial.println("Timeout waiting YModem response");
return 0;
}
answer = Serial2.read();
switch (answer) {
case X_NAK:
// something went wrong - send the same packet again?
Serial.print("N");
ackerrors++;
break;
case X_ACK:
// got ACK to move to the next block of data
Serial.print(".");
doNextBlock = 1;
break;
default:
// unknown response
Serial.print("?");
ackerrors++;
break;
}
if (ackerrors >= ackerrorlimit)
{
Serial.println("Ack error limit reached");
return 0;
}
// need to handle the 'C' response after the initial file details packet has been sent
if (skip_payload == 1) {
startmS = millis();
while ((!Serial2.available()) && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if ( ((uint32_t) (millis() - startmS) >= transfertimeoutmS))
{
Serial.println("Timeout waiting YModem response");
return 0;
}
answer = Serial2.read();
if (answer == 'C') {
// good - start sending the data in the next transmission
skip_payload = 0;
} else {
// error? send the file details packet again?
doNextBlock = 0;
}
}
// move on to the next block of data
if (doNextBlock == 1) {
yPacket.block++;
numBytesStillToSend = numBytesStillToSend - numBytesThisPacket;
}
}
// all done - send the end of transmission code
Serial2.write( X_EOT );
// need to send EOT again for YMODEM
startmS = millis();
while ((!Serial2.available()) && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if ( ((uint32_t) (millis() - startmS) >= transfertimeoutmS))
{
Serial.println("Timeout waiting YModem response");
return 0;
}
answer = Serial2.read();
Serial2.write( X_EOT );
if (batchMode == 0) {
// and then a packet full of NULL seems to terminate the process
// and make TeraTerm close the receive dialog box
yPacket.block = 0;
yPacket.block_neg = 0xff - yPacket.block;
memset(yPacket.payload, 0x00, sizeof(yPacket.payload) );
yPacket.crc = swap16(crc16(yPacket.payload, sizeof(yPacket.payload)));
Serial2.write( (uint8_t*)&yPacket, sizeof(yPacket));
}
Serial.println("done");
srcFile.close();
return filebytescopied;
}

View File

@ -0,0 +1,202 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a program that simulates the transfer of a file using data transfer (DT)
packet functions from the SX128X library. No SD cards are needed for the simulation. The file length
used to simulate the transfer is defined by DTFileSize in the DTSettings.h file. Use with matching
receiver program 232_Data_Transfer_Test_Receiver.ino.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The file open requests to the remote receiver, each segement sent and
the remote file close will all keep transmitting until a valid acknowledge comes from the receiver.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty.
The transmitter sends the sequence of segments in order. If the sequence fails for some reason, the receiver
will return a NACK packet to the transmitter requesting the segment sequence it was expecting.
Details of the packet identifiers, header and data lengths and formats used are in the file;
'Data transfer packet definitions.md' in the \SX128X_examples\DataTransfer\ folder.
The transfers can be carried out using LoRa packets, max segment size (defined by DTSegmentSize) is 245 bytes
for LoRa, or FLRC packets where 117 is maximum segment size.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "DTSettings.h" //LoRa settings etc.
#include <arrayRW.h>
SX128XLT LoRa; //create an SX128XLT library instance called LoRa
#define PRINTSEGMENTNUM //enable this define to print segment numbers
//#define DEBUG
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
#include "DTLibrarySIM.h"
char DTFileName[] = "/Simulate.JPG"; //file name to simulate sending
void loop()
{
Serial.println(("Transfer started"));
do
{
DTStartmS = millis();
//opens the local file to send and sets up transfer parameters
if (startFileTransfer(DTFileName, sizeof(DTFileName), DTSendAttempts))
{
Serial.print(DTFileName);
Serial.println(F(" opened OK on remote"));
printLocalFileDetails();
Serial.println();
NoAckCount = 0;
}
else
{
Serial.print(DTFileName);
Serial.println(F(" Error opening remote file - restart transfer"));
DTFileTransferComplete = false;
continue;
}
delay(packetdelaymS);
if (!sendSegments())
{
Serial.println();
Serial.println(F("**********************************************************"));
Serial.println(F("Error - Segment write with no file open - Restart received"));
Serial.println(F("**********************************************************"));
Serial.println();
continue;
}
if (endFileTransfer(DTFileName, sizeof(DTFileName))) //send command to close remote file
{
DTSendmS = millis() - DTStartmS; //record time taken for transfer
Serial.print(DTFileName);
Serial.println(F(" closed OK on remote"));
beginarrayRW(DTheader, 4);
DTDestinationFileLength = arrayReadUint32();
Serial.print(F("Acknowledged remote destination file length "));
Serial.println(DTDestinationFileLength);
DTFileTransferComplete = true;
}
else
{
DTFileTransferComplete = false;
Serial.println(F("ERROR send close remote destination file failed - program halted"));
}
}
while (!DTFileTransferComplete);
Serial.print(F("NoAckCount "));
Serial.println( NoAckCount);
Serial.println();
DTsendSecs = (float) DTSendmS / 1000;
Serial.print(F("Transmit time "));
Serial.print(DTsendSecs, 3);
Serial.println(F("secs"));
Serial.print(F("Transmit rate "));
Serial.print( (DTFileSize * 8) / (DTsendSecs), 0 );
Serial.println(F("bps"));
Serial.println(("Transfer finished"));
Serial.println(("Program halted"));
while (1);
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
setDTLED(LED1); //setup LED pin for data transfer indicator
Serial.begin(115200);
Serial.println();
Serial.println(F(__FILE__));
Serial.flush();
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
Serial.println(F("LoRa device error"));
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
LoRa.printOperatingSettings();
Serial.println();
LoRa.printModemSettings();
Serial.println();
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Serial.println(F("Payload CRC disabled"));
}
else
{
Serial.println(F("Payload CRC enabled"));
}
DTFileTransferComplete = false;
Serial.println(F("File transfer ready"));
Serial.println();
}

View File

@ -0,0 +1,973 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
uint8_t RXPacketL; //length of received packet
uint8_t RXPacketType; //type of received packet, segment write, ACK, NACK etc
uint16_t RXErrors; //count of packets received with error
uint8_t RXFlags; //DTflags byte in header, could be used to control actions in TX and RX
uint8_t RXHeaderL; //length of header
uint8_t RXDataarrayL; //length of data array\segment
int16_t PacketRSSI; //stores RSSI of received packet
int8_t PacketSNR; //stores signal to noise ratio of received packet
uint16_t TXNetworkID; //this is used to store the 'network' number, receiver must have the same networkID
uint16_t TXArrayCRC; //should contain CRC of data array sent
uint8_t TXPacketL; //length of transmitted packet
uint16_t AckCount; //keep a count of acks that are received within timeout period
uint16_t NoAckCount; //keep a count of acks not received within timeout period
uint16_t LocalPayloadCRC; //for calculating the local data array CRC
uint16_t DTDestinationFileCRC; //CRC of file received
uint16_t DTSourceFileCRC; //CRC of returned of the remote saved file
uint32_t DTDestinationFileLength; //length of file written on this destination
uint32_t DTSourceFileLength; //length of file at source
uint32_t DTStartmS; //used for timeing transfers
bool DTFileOpened; //bool to record when file has been opened
uint16_t DTSegment = 0; //current segment number
uint16_t DTSegmentNext; //next segment expected
uint16_t DTReceivedSegments; //count of segments received
uint16_t DTSegmentLast; //last segment processed
uint8_t DTLastSegmentSize; //size of the last segment
uint16_t DTNumberSegments; //number of segments for a file transfer
uint16_t DTSentSegments; //count of segments sent
bool DTFileTransferComplete; //bool to flag file transfer complete
uint32_t DTSendmS; //used for timing transfers
float DTsendSecs; //seconds to transfer a file
char DTfilenamebuff[DTfilenamesize];
int DTLED = -1; //pin number for indicator LED, if -1 then not used
bool sendFile(char *DTFileName, uint8_t namelength);
bool sendFileSegment(uint16_t segnum, uint8_t segmentsize);
bool startFileTransfer(char *buff, uint8_t filenamesize, uint8_t attempts);
bool endFileTransfer(char *buff, uint8_t filenamesize);
void build_DTFileOpenHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize);
void build_DTSegmentHeader(uint8_t *header, uint8_t headersize, uint8_t datalen, uint16_t segnum);
void build_DTFileCloseHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize);
void printLocalFileDetails();
bool sendSegments();
void printheader(uint8_t *hdr, uint8_t hdrsize);
void printSeconds();
void printAckBrief();
void printAckDetails();
void printdata(uint8_t *dataarray, uint8_t arraysize);
void printACKdetail();
void printPacketHex();
void printPacketRSSI();
void printPacketDetails();
void readHeaderDT();
void printSourceFileDetails();
void printDestinationFileDetails();
bool processFileClose();
bool processFileOpen(uint8_t *buff, uint8_t filenamesize);
bool processSegmentWrite();
bool processPacket(uint8_t packettype);
bool receiveaPacketDT();
uint16_t getNumberSegments(uint32_t filesize, uint8_t segmentsize);
uint8_t getLastSegmentSize(uint32_t filesize, uint8_t segmentsize);
void setDTLED(int8_t pinnumber);
uint8_t DTheader[16]; //header array
uint8_t DTdata[245]; //data/segment array
bool sendFile(char *DTFileName, uint8_t namelength)
{
//**************************************************************************************************************
// Start filesend process
// This routine allows the file transfer to be run with a function call of sendFile(FileName, sizeof(FileName));
//**************************************************************************************************************
do
{
DTStartmS = millis();
//opens the local file to send and sets up transfer parameters
if (startFileTransfer(DTFileName, namelength, DTSendAttempts))
{
Serial.print(DTFileName);
Serial.println(F(" opened OK on remote"));
printLocalFileDetails();
Serial.println();
NoAckCount = 0;
}
else
{
Serial.print(DTFileName);
Serial.println(F(" Error opening remote file - restart transfer"));
DTFileTransferComplete = false;
continue;
}
delay(packetdelaymS);
if (!sendSegments())
{
Serial.println();
Serial.println(F("**********************************************************"));
Serial.println(F("Error - Segment write with no file open - Restart received"));
Serial.println(F("**********************************************************"));
Serial.println();
continue;
}
if (endFileTransfer(DTFileName, namelength)) //send command to close remote file
{
DTSendmS = millis() - DTStartmS; //record time taken for transfer
Serial.print(DTFileName);
Serial.println(F(" closed OK on remote"));
beginarrayRW(DTheader, 4);
DTDestinationFileLength = arrayReadUint32();
Serial.print(F("Acknowledged remote destination file length "));
Serial.println(DTDestinationFileLength);
if (DTDestinationFileLength != DTSourceFileLength)
{
Serial.println(F("ERROR - file lengths do not match"));
}
else
{
Serial.println(F("File lengths match"));
}
DTFileTransferComplete = true;
}
else
{
DTFileTransferComplete = false;
Serial.println(F("ERROR send close remote destination file failed - program halted"));
}
}
while (!DTFileTransferComplete);
Serial.print(F("NoAckCount "));
Serial.println( NoAckCount);
Serial.println();
DTsendSecs = (float) DTSendmS / 1000;
Serial.print(F("Transmit time "));
Serial.print(DTsendSecs, 3);
Serial.println(F("secs"));
Serial.print(F("Transmit rate "));
Serial.print( (DTDestinationFileLength * 8) / (DTsendSecs), 0 );
Serial.println(F("bps"));
Serial.println(("Transfer finished"));
return true;
}
bool sendFileSegment(uint16_t segnum, uint8_t segmentsize)
{
//****************************************************************
//Send file segment as payload in a packet
//****************************************************************
uint8_t ValidACK;
build_DTSegmentHeader(DTheader, DTSegmentWriteHeaderL, segmentsize, segnum);
#ifdef PRINTSEGMENTNUM
//Serial.print(F("Segment,"));
Serial.println(segnum);
#endif
#ifdef DEBUG
printheader(DTheader, DTSegmentWriteHeaderL);
Serial.print(F(" "));
printdata(DTdata, segmentsize); //print segment size of data array only
#endif
do
{
digitalWrite(DTLED, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTSegmentWriteHeaderL, (uint8_t *) DTdata, segmentsize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(DTLED, LOW);
if (TXPacketL == 0) //if there has been an error TXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
ValidACK = LoRa.waitACKDT(DTheader, DTSegmentWriteHeaderL, ACKsegtimeoutmS);
RXPacketType = DTheader[0];
if (ValidACK > 0)
{
if (RXPacketType == DTSegmentWriteNACK)
{
DTSegment = DTheader[4] + (DTheader[5] << 8); //load what the segment number should be
RXHeaderL = DTheader[2];
Serial.println();
Serial.println(F("************************************"));
Serial.print(F("Received restart request at segment "));
Serial.println(DTSegment);
printheader(DTheader, RXHeaderL);
Serial.println();
Serial.print(F("Seek to file location "));
Serial.println(DTSegment * DTSegmentSize);
Serial.println(F("************************************"));
Serial.println();
Serial.flush();
}
//ack is valid, segment was acknowledged if here
if (RXPacketType == DTStartNACK)
{
Serial.println(F("Received DTStartNACK "));
return false;
}
if (RXPacketType == DTSegmentWriteACK)
{
AckCount++;
#ifdef DEBUG
printAckBrief();
//printAckDetails()
#endif
DTSegment++; //increase value for next segment
return true;
}
}
else
{
NoAckCount++;
Serial.print(F("Error no ACK received "));
Serial.println(NoAckCount);
if (NoAckCount > NoAckCountLimit)
{
Serial.println(F("ERROR NoACK limit reached"));
return false;
}
}
} while (ValidACK == 0);
return true;
}
bool startFileTransfer(char *buff, uint8_t filenamesize, uint8_t attempts)
{
//*********************************************************************
//Start file transfer, simulate open local file first then remote file.
//*********************************************************************
uint8_t ValidACK;
Serial.print(F("Start file transfer "));
Serial.println(buff);
DTSourceFileLength = DTFileSize; //get the file length
if (DTSourceFileLength == 0)
{
Serial.print(F("Error opening local file "));
Serial.println(buff);
return false;
}
DTNumberSegments = getNumberSegments(DTSourceFileLength, DTSegmentSize);
DTLastSegmentSize = getLastSegmentSize(DTSourceFileLength, DTSegmentSize);
build_DTFileOpenHeader(DTheader, DTFileOpenHeaderL, filenamesize, DTSourceFileLength, DTSourceFileCRC, DTSegmentSize);
LocalPayloadCRC = LoRa.CRCCCITT((uint8_t *) buff, filenamesize, 0xFFFF);
do
{
Serial.println(F("Send open remote file request"));
digitalWrite(DTLED, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTFileOpenHeaderL, (uint8_t *) buff, filenamesize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(DTLED, LOW);
#ifdef DEBUG
Serial.print(F("Send attempt "));
Serial.println(DTSendAttempts - attempts + 1);
#endif
attempts--;
TXNetworkID = LoRa.getTXNetworkID(TXPacketL); //get the networkID appended to packet
TXArrayCRC = LoRa.getTXPayloadCRC(TXPacketL); //get the payload CRC appended to packet
#ifdef DEBUG
Serial.print(F("TXNetworkID,0x"));
Serial.print(TXNetworkID, HEX); //get the NetworkID of the packet just sent, its placed at the packet end
Serial.print(F(",TXarrayCRC,0x"));
Serial.println(TXArrayCRC, HEX); //get the CRC of the data array just sent, its placed at the packet end
Serial.println();
#endif
if (TXPacketL == 0) //if there has been a send and ack error, TXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
Serial.print(F("Wait ACK "));
ValidACK = LoRa.waitACKDT(DTheader, DTFileOpenHeaderL, ACKopentimeoutmS);
RXPacketType = DTheader[0];
if ((ValidACK > 0) && (RXPacketType == DTFileOpenACK))
{
#ifdef DEBUG
printPacketHex();
#endif
}
else
{
NoAckCount++;
Serial.println(F("No ACK received"));
Serial.println(NoAckCount);
if (NoAckCount > NoAckCountLimit)
{
Serial.println(F("ERROR NoACK limit reached"));
return false;
}
#ifdef DEBUG
printACKdetail();
Serial.print(F("ACKPacket "));
printPacketHex();
#endif
Serial.println();
}
Serial.println();
}
while ((ValidACK == 0) && (attempts != 0));
if (attempts == 0)
{
return false;
}
return true;
}
bool endFileTransfer(char *buff, uint8_t filenamesize)
{
//****************************************************************
//End file transfer, close local file first then remote file.
//****************************************************************
uint8_t ValidACK;
build_DTFileCloseHeader(DTheader, DTFileCloseHeaderL, filenamesize, DTSourceFileLength, DTSourceFileCRC, DTSegmentSize);
do
{
printSeconds();
Serial.println(F("Send close remote file"));
digitalWrite(DTLED, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTFileCloseHeaderL, (uint8_t *) buff, filenamesize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(DTLED, LOW);
TXNetworkID = LoRa.getTXNetworkID(TXPacketL);
TXArrayCRC = LoRa.getTXPayloadCRC(TXPacketL);
#ifdef DEBUG
Serial.print(F("TXNetworkID,0x"));
Serial.print(TXNetworkID, HEX); //get the NetworkID of the packet just sent, its placed at the packet end
Serial.print(F(",TXarrayCRC,0x"));
Serial.println(TXArrayCRC, HEX); //get the CRC of the data array just sent, its placed at the packet end
Serial.println();
#endif
if (TXPacketL == 0) //if there has been a send and ack error, RXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
ValidACK = LoRa.waitACKDT(DTheader, DTFileCloseHeaderL, ACKclosetimeoutmS);
RXPacketType = DTheader[0];
if ((ValidACK > 0) && (RXPacketType == DTFileCloseACK))
{
#ifdef DEBUG
printPacketHex();
#endif
}
else
{
NoAckCount++;
Serial.print(F("Error no ACK received "));
Serial.println(NoAckCount);
if (NoAckCount > NoAckCountLimit)
{
Serial.println(F("ERROR NoACK limit reached"));
return false;
}
#ifdef DEBUG
Serial.println();
Serial.print(F("ACKPacket "));
printPacketHex();
Serial.println();
#endif
}
}
while (ValidACK == 0);
return true;
}
void build_DTFileOpenHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize)
{
//this builds the header buffer for the filename to send
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTFileOpen); //byte 0, write the packet type
arrayWriteUint8(0); //byte 1, initial DTflags byte, not used here
arrayWriteUint8(headersize); //byte 2, write length of header
arrayWriteUint8(datalength); //byte 3, write length of dataarray
arrayWriteUint32(filelength); //byte 4,5,6,7, write the file length
arrayWriteUint16(filecrc); //byte 8, 9, write file CRC
arrayWriteUint8(segsize); //byte 10, segment size
arrayWriteUint8(0); //byte 11, unused byte
endarrayRW();
}
void build_DTSegmentHeader(uint8_t *header, uint8_t headersize, uint8_t datalen, uint16_t segnum)
{
//this builds the header buffer for a segment transmit
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTSegmentWrite); //write the packet type
arrayWriteUint8(0); //initial DTflags byte, not used here
arrayWriteUint8(headersize); //write length of header
arrayWriteUint8(datalen); //write length of data array
arrayWriteUint16(segnum); //write the DTsegment number
endarrayRW();
}
void build_DTFileCloseHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize)
{
//this builds the header buffer for the filename passed
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTFileClose); //byte 0, write the packet type
arrayWriteUint8(0); //byte 1, initial DTflags byte, not used here
arrayWriteUint8(headersize); //byte 2, write length of header
arrayWriteUint8(datalength); //byte 3, write length of dataarray
arrayWriteUint32(filelength); //byte 4,5,6,7, write the file length
arrayWriteUint16(filecrc); //byte 8, 9, write file CRC
arrayWriteUint8(segsize); //byte 10, segment size
arrayWriteUint8(0); //byte 11, unused byte
endarrayRW();
}
void printLocalFileDetails()
{
Serial.print(F("Source file length is "));
Serial.print(DTSourceFileLength);
Serial.println(F(" bytes"));
Serial.print(F("Segment Size "));
Serial.println(DTSegmentSize);
Serial.print(F("Number segments "));
Serial.println(DTNumberSegments);
Serial.print(F("Last segment size "));
Serial.println(DTLastSegmentSize);
}
bool sendSegments()
{
//start the file transfer at segment 0
DTSegment = 0;
DTSentSegments = 0;
while (DTSegment < (DTNumberSegments - 1))
{
#ifdef DEBUG
printSeconds();
#endif
if (sendFileSegment(DTSegment, DTSegmentSize))
{
DTSentSegments++;
}
else
{
Serial.println(F("ERROR in sendFileSegment"));
Serial.println();
return false;
}
delay(packetdelaymS);
};
printSeconds();
Serial.println(F("Last segment"));
if (!sendFileSegment(DTSegment, DTLastSegmentSize))
{
Serial.println(F("ERROR in sendFileSegment"));
return false;
}
return true;
}
void printheader(uint8_t *hdr, uint8_t hdrsize)
{
Serial.print(F("HeaderBytes,"));
Serial.print(hdrsize);
Serial.print(F(" "));
printarrayHEX(hdr, hdrsize);
}
void printSeconds()
{
float secs;
secs = ( (float) millis() / 1000);
Serial.print(secs, 2);
Serial.print(F(" "));
}
void printdata(uint8_t *dataarray, uint8_t arraysize)
{
Serial.print(F("DataBytes,"));
Serial.print(arraysize);
Serial.print(F(" "));
printarrayHEX((uint8_t *) dataarray, 16); //There is a lot of data to print so only print first 16 bytes
}
void printAckDetails()
{
PacketRSSI = LoRa.readPacketRSSI();
PacketSNR = LoRa.readPacketSNR();
Serial.print(F("AckCount,"));
Serial.print(AckCount);
Serial.print(F(",NoAckCount,"));
Serial.print(NoAckCount);
Serial.print(F(",AckRSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm,AckSNR,"));
Serial.print(PacketSNR);
Serial.print(F("dB"));
Serial.println();
}
void printAckBrief()
{
PacketRSSI = LoRa.readPacketRSSI();
Serial.print(F(",AckRSSI,"));
Serial.print(PacketRSSI);
Serial.println(F("dBm"));
}
void printACKdetail()
{
Serial.print(F("ACKDetail"));
Serial.print(F(",RXNetworkID,0x"));
Serial.print(LoRa.getRXNetworkID(RXPacketL), HEX);
Serial.print(F(",RXPayloadCRC,0x"));
Serial.print(LoRa.getRXPayloadCRC(RXPacketL), HEX);
Serial.print(F(",RXPacketL,"));
Serial.print(RXPacketL);
Serial.print(F(" "));
LoRa.printReliableStatus();
Serial.println();
}
void printPacketHex()
{
RXPacketL = LoRa.readRXPacketL();
Serial.print(RXPacketL);
Serial.print(F(" bytes > "));
if (RXPacketL > 0)
{
LoRa.printSXBufferHEX(0, RXPacketL - 1);
}
}
void printPacketDetails()
{
PacketRSSI = LoRa.readPacketRSSI();
PacketSNR = LoRa.readPacketSNR();
Serial.print(F(" RSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm"));
#ifdef DEBUG
Serial.print(F(",SNR,"));
Serial.print(PacketSNR);
Serial.print(F("dBm,RXOKCount,"));
Serial.print(DTReceivedSegments);
Serial.print(F(",RXErrs,"));
Serial.print(RXErrors);
Serial.print(F(" RX"));
printheader(DTheader, RXHeaderL);
#endif
}
void printPacketRSSI()
{
PacketRSSI = LoRa.readPacketRSSI();
Serial.print(F(" RSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm"));
}
void readHeaderDT()
{
//the first 6 bytes of the header contain the important stuff, so load it up
//so we can decide what to do next.
beginarrayRW(DTheader, 0); //start buffer read at location 0
RXPacketType = arrayReadUint8(); //load the packet type
RXFlags = arrayReadUint8(); //initial DTflags byte, not used here
RXHeaderL = arrayReadUint8(); //load the header length
RXDataarrayL = arrayReadUint8(); //load the datalength
DTSegment = arrayReadUint16(); //load the segment number
}
void printSourceFileDetails()
{
Serial.print(DTfilenamebuff);
Serial.print(F(" Source file length is "));
Serial.print(DTSourceFileLength);
Serial.println(F(" bytes"));
Serial.print(DTfilenamebuff);
}
void printDestinationFileDetails()
{
Serial.print(F("Destination file length is "));
Serial.print(DTDestinationFileLength);
Serial.println(F(" bytes"));
if (DTDestinationFileLength != DTSourceFileLength)
{
Serial.println(F("ERROR - file lengths do not match"));
}
else
{
Serial.println(F("File lengths match"));
}
}
bool processFileClose()
{
//***********************************************************************************************
// Code for closing local SD file and sending request to remote
//***********************************************************************************************
Serial.print(F("File close for "));
Serial.println((char*) DTfilenamebuff);
if (DTFileOpened) //check if file has been opened, close it if it is
{
if (1) //simulate file exists
{
Serial.print(F("Transfer time "));
Serial.print(millis() - DTStartmS);
Serial.print(F("mS"));
Serial.println();
Serial.println(F("File closed"));
Serial.println();
DTFileOpened = false;
DTDestinationFileLength = DTFileSize;
beginarrayRW(DTheader, 4); //start writing to array at location 12
arrayWriteUint32(DTDestinationFileLength); //write file length of file just written just written to ACK header
arrayWriteUint16(DTDestinationFileCRC); //write CRC of file just written to ACK header
printDestinationFileDetails();
}
}
delay(ACKdelaymS);
#ifdef DEBUG
Serial.println(F("Sending ACK"));
#endif
DTheader[0] = DTFileCloseACK;
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTFileCloseHeaderL, TXpower);
digitalWrite(DTLED, LOW);
Serial.println();
Serial.println();
return true;
}
bool processFileOpen(uint8_t *buff, uint8_t filenamesize)
{
//*************************************************************************
// Code for simulating opening local SD file and sending request to remote
//*************************************************************************
beginarrayRW(DTheader, 4); //start buffer read at location 4
DTSourceFileLength = arrayReadUint32(); //load the file length of the remote file being sent
DTSourceFileCRC = arrayReadUint16(); //load the CRC of the source file being sent
memset(DTfilenamebuff, 0, DTfilenamesize); //clear DTfilenamebuff to all 0s
memcpy(DTfilenamebuff, buff, filenamesize); //copy received DTdata into DTfilenamebuff
Serial.print(F("File Open request for "));
Serial.print((char*) DTfilenamebuff);
Serial.println();
printSourceFileDetails();
if (1) //simulate open file for write at beginning, delete if it exists
{
Serial.print((char*) DTfilenamebuff);
Serial.println(F(" File Opened OK"));
Serial.println(F("Waiting transfer"));
DTSegmentNext = 0; //since file is opened the next sequence should be the first
DTFileOpened = true;
DTStartmS = millis();
}
else
{
Serial.print((char*) DTfilenamebuff);
Serial.println(F(" File Open fail"));
DTFileOpened = false;
return false;
}
DTStartmS = millis();
delay(ACKdelaymS);
#ifdef DEBUG
Serial.println(F("Sending ACK"));
#endif
DTheader[0] = DTFileOpenACK; //set the ACK packet type
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTFileOpenHeaderL, TXpower);
digitalWrite(DTLED, LOW);
DTSegmentNext = 0; //after file open, segment 0 is next
return true;
}
bool processSegmentWrite()
{
//***********************************************************************************************
// Code for dealing with segment writes - checks that the sequence of segment writes is correct
//***********************************************************************************************
if (!DTFileOpened)
{
//something is wrong, have received a request to write a segment but there is no file opened
//need to reject the segment write with a restart NACK
Serial.println();
Serial.println(F("***************************************************"));
Serial.println(F("Error - Segment write with no file open - send NACK"));
Serial.println(F("***************************************************"));
Serial.println();
DTheader[0] = DTStartNACK;
delay(ACKdelaymS);
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTStartHeaderL, TXpower);
digitalWrite(DTLED, LOW);
return false;
}
if (DTSegment == DTSegmentNext)
{
#ifdef PRINTSEGMENTNUM
//Serial.print(F("Segment,"));
Serial.println(DTSegment);
#endif
#ifdef DEBUG
Serial.print(F(",Bytes,"));
Serial.print(RXDataarrayL);
//printPacketDetails();
printPacketRSSI();
Serial.println(F(" SendACK"));
#endif
DTheader[0] = DTSegmentWriteACK;
delay(ACKdelaymS);
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTSegmentWriteHeaderL, TXpower);
digitalWrite(DTLED, LOW);
DTReceivedSegments++;
DTSegmentLast = DTSegment; //so we can tell if sequece has been received twice
DTSegmentNext = DTSegment + 1;
return true;
}
if (DTSegment == DTSegmentLast)
{
Serial.print(F("ERROR segment "));
Serial.print(DTSegment);
Serial.print(F(" already received "));
//printPacketDetails();
printPacketRSSI();
DTheader[0] = DTSegmentWriteACK;
Serial.print(F(" Send ACK"));
delay(ACKdelaymS);
delay(DuplicatedelaymS);
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTSegmentWriteHeaderL, TXpower);
digitalWrite(DTLED, LOW);
return true;
}
if (DTSegment != DTSegmentNext )
{
Serial.print(F(" ERROR Received Segment "));
Serial.print(DTSegment);
Serial.print(F(" expected "));
Serial.print(DTSegmentNext);
Serial.print(F(" "));
//printPacketDetails();
printPacketRSSI();
DTheader[0] = DTSegmentWriteNACK;
DTheader[4] = lowByte(DTSegmentNext);
DTheader[5] = highByte(DTSegmentNext);
Serial.print(F(" Send NACK for segment "));
Serial.print(DTSegmentNext);
delay(ACKdelaymS);
delay(DuplicatedelaymS);
digitalWrite(DTLED, HIGH);
Serial.println();
Serial.println();
Serial.println(F("*****************************************"));
Serial.print(F("Transmit restart request for segment "));
Serial.println(DTSegmentNext);
printheader(DTheader, RXHeaderL);
Serial.println();
Serial.println(F("*****************************************"));
Serial.println();
Serial.flush();
LoRa.sendACKDT(DTheader, DTSegmentWriteHeaderL, TXpower);
digitalWrite(DTLED, LOW);
return false;
}
return true;
}
bool processPacket(uint8_t packettype)
{
//***********************************************************************************************
// Decide what to do with an incoming packet
//***********************************************************************************************
if (packettype == DTSegmentWrite)
{
processSegmentWrite();
return true;
}
if (packettype == DTFileOpen)
{
processFileOpen(DTdata, RXDataarrayL);
return true;
}
if (packettype == DTFileClose)
{
processFileClose();
return true;
}
return true;
}
bool receiveaPacketDT()
{
//******************************
//Receive Data transfer packets
//******************************
RXPacketType = 0;
RXPacketL = LoRa.receiveDT(DTheader, HeaderSizeMax, (uint8_t *) DTdata, DataSizeMax, NetworkID, RXtimeoutmS, WAIT_RX);
digitalWrite(DTLED, HIGH);
#ifdef DEBUG
printSeconds();
#endif
if (RXPacketL > 0)
{
//if the LT.receiveDT() returns a value > 0 for RXPacketL then packet was received OK
//then only action payload if destinationNode = thisNode
readHeaderDT(); //get the basic header details into global variables RXPacketType etc
processPacket(RXPacketType); //process and act on the packet
digitalWrite(DTLED, LOW);
return true;
}
else
{
//if the LoRa.receiveDT() function detects an error RXOK is 0
Serial.print(F("PacketError"));
RXErrors++;
printPacketDetails();
#ifdef DEBUG
LoRa.printReliableStatus();
LoRa.printIrqStatus();
#endif
Serial.println();
digitalWrite(DTLED, LOW);
return false;
}
}
uint16_t getNumberSegments(uint32_t filesize, uint8_t segmentsize)
{
uint16_t segments;
segments = filesize / segmentsize;
if ((filesize % segmentsize) > 0)
{
segments++;
}
return segments;
}
uint8_t getLastSegmentSize(uint32_t filesize, uint8_t segmentsize)
{
uint8_t lastsize;
lastsize = filesize % segmentsize;
if (lastsize == 0)
{
lastsize = segmentsize;
}
return lastsize;
}
void setDTLED(int8_t pinnumber)
{
if (pinnumber >= 0)
{
DTLED = pinnumber;
pinMode(pinnumber, OUTPUT);
}
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after packet actioned and ack sent
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t NoAckCountLimit = 250; //if no NoAckCount exceeds this value - restart transfer
const uint32_t DuplicatedelaymS = 10; //ms delay if there has been an duplicate segment or command receipt
const uint32_t packetdelaymS = 0; //mS delay between transmitted packets
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 7 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t DTfilenamesize = 32; //size of DTfilename buffer
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
const uint8_t DTSendAttempts = 10; //number of attempts sending a packet before a restart
const uint32_t DTFileSize = 65535; //size of file to simulate
#ifdef USELORA
const uint8_t DTSegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t DTSegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,134 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a program that simulates the transfer of a file using data transfer (DT)
packet functions from the SX128X library. No SD cards are needed for the simulation. Use with matching
receiver program 231_Data_Transfer_Test_Transmitter.ino.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The remote file open request, the segements sent and the remote file close
will be transmitted until a valid acknowledge comes from the receiver.
Each DT packet contains a variable length header array and a variable length data array as the payload.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty. The receiver will not accept packets that dont have the appropriate NetworkID or payload CRC
at the end of the packet.
The transmitter sends a sequence of segments in order and the receiver keeps track of the sequence. If
the sequence fails for some reason, the receiver will return a NACK packet to the transmitter requesting
the segment sequence it was expecting.
The transfers can be carried out using LoRa packets, max segment size (defined by DTSegmentSize) is 245 bytes
for LoRa, or FLRC packets where 117 is maximum segment size.
Details of the packet identifiers, header and data lengths and formats used are in the file
Data_transfer_packet_definitions.md in the \SX128X_examples\DataTransfer\ folder.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "DTSettings.h" //LoRa settings etc.
#include <arrayRW.h>
SX128XLT LoRa; //create an SX128XLT library instance called LoRa
#define PRINTSEGMENTNUM //enable this define to print segment numbers
//#define DEBUG
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
#include "DTLibrarySIM.h"
void loop()
{
receiveaPacketDT();
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
setDTLED(LED1); //setup LED pin for data transfer indicator
Serial.begin(115200);
Serial.println();
Serial.println(F(__FILE__));
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
Serial.println(F("LoRa device error"));
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
LoRa.printOperatingSettings();
Serial.println();
LoRa.printModemSettings();
Serial.println();
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Serial.println(F("Payload CRC disabled"));
}
else
{
Serial.println(F("Payload CRC enabled"));
}
DTSegmentNext = 0;
DTFileOpened = false;
Serial.println(F("File transfer receiver ready"));
Serial.println();
}

View File

@ -0,0 +1,972 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
uint8_t RXPacketL; //length of received packet
uint8_t RXPacketType; //type of received packet, segment write, ACK, NACK etc
uint16_t RXErrors; //count of packets received with error
uint8_t RXFlags; //DTflags byte in header, could be used to control actions in TX and RX
uint8_t RXHeaderL; //length of header
uint8_t RXDataarrayL; //length of data array\segment
int16_t PacketRSSI; //stores RSSI of received packet
int8_t PacketSNR; //stores signal to noise ratio of received packet
uint16_t TXNetworkID; //this is used to store the 'network' number, receiver must have the same networkID
uint16_t TXArrayCRC; //should contain CRC of data array sent
uint8_t TXPacketL; //length of transmitted packet
uint16_t AckCount; //keep a count of acks that are received within timeout period
uint16_t NoAckCount; //keep a count of acks not received within timeout period
uint16_t LocalPayloadCRC; //for calculating the local data array CRC
uint16_t DTDestinationFileCRC; //CRC of file received
uint16_t DTSourceFileCRC; //CRC of returned of the remote saved file
uint32_t DTDestinationFileLength; //length of file written on this destination
uint32_t DTSourceFileLength; //length of file at source
uint32_t DTStartmS; //used for timeing transfers
bool DTFileOpened; //bool to record when file has been opened
uint16_t DTSegment = 0; //current segment number
uint16_t DTSegmentNext; //next segment expected
uint16_t DTReceivedSegments; //count of segments received
uint16_t DTSegmentLast; //last segment processed
uint8_t DTLastSegmentSize; //size of the last segment
uint16_t DTNumberSegments; //number of segments for a file transfer
uint16_t DTSentSegments; //count of segments sent
bool DTFileTransferComplete; //bool to flag file transfer complete
uint32_t DTSendmS; //used for timing transfers
float DTsendSecs; //seconds to transfer a file
char DTfilenamebuff[DTfilenamesize];
int DTLED = -1; //pin number for indicator LED, if -1 then not used
bool sendFile(char *DTFileName, uint8_t namelength);
bool sendFileSegment(uint16_t segnum, uint8_t segmentsize);
bool startFileTransfer(char *buff, uint8_t filenamesize, uint8_t attempts);
bool endFileTransfer(char *buff, uint8_t filenamesize);
void build_DTFileOpenHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize);
void build_DTSegmentHeader(uint8_t *header, uint8_t headersize, uint8_t datalen, uint16_t segnum);
void build_DTFileCloseHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize);
void printLocalFileDetails();
bool sendSegments();
void printheader(uint8_t *hdr, uint8_t hdrsize);
void printSeconds();
void printAckBrief();
void printAckDetails();
void printdata(uint8_t *dataarray, uint8_t arraysize);
void printACKdetail();
void printPacketHex();
void printPacketRSSI();
void printPacketDetails();
void readHeaderDT();
void printSourceFileDetails();
void printDestinationFileDetails();
bool processFileClose();
bool processFileOpen(uint8_t *buff, uint8_t filenamesize);
bool processSegmentWrite();
bool processPacket(uint8_t packettype);
bool receiveaPacketDT();
uint16_t getNumberSegments(uint32_t filesize, uint8_t segmentsize);
uint8_t getLastSegmentSize(uint32_t filesize, uint8_t segmentsize);
void setDTLED(int8_t pinnumber);
uint8_t DTheader[16]; //header array
uint8_t DTdata[245]; //data/segment array
bool sendFile(char *DTFileName, uint8_t namelength)
{
//**************************************************************************************************************
// Start filesend process
// This routine allows the file transfer to be run with a function call of sendFile(FileName, sizeof(FileName));
//**************************************************************************************************************
do
{
DTStartmS = millis();
//opens the local file to send and sets up transfer parameters
if (startFileTransfer(DTFileName, namelength, DTSendAttempts))
{
Serial.print(DTFileName);
Serial.println(F(" opened OK on remote"));
printLocalFileDetails();
Serial.println();
NoAckCount = 0;
}
else
{
Serial.print(DTFileName);
Serial.println(F(" Error opening remote file - restart transfer"));
DTFileTransferComplete = false;
continue;
}
delay(packetdelaymS);
if (!sendSegments())
{
Serial.println();
Serial.println(F("**********************************************************"));
Serial.println(F("Error - Segment write with no file open - Restart received"));
Serial.println(F("**********************************************************"));
Serial.println();
continue;
}
if (endFileTransfer(DTFileName, namelength)) //send command to close remote file
{
DTSendmS = millis() - DTStartmS; //record time taken for transfer
Serial.print(DTFileName);
Serial.println(F(" closed OK on remote"));
beginarrayRW(DTheader, 4);
DTDestinationFileLength = arrayReadUint32();
Serial.print(F("Acknowledged remote destination file length "));
Serial.println(DTDestinationFileLength);
if (DTDestinationFileLength != DTSourceFileLength)
{
Serial.println(F("ERROR - file lengths do not match"));
}
else
{
Serial.println(F("File lengths match"));
}
DTFileTransferComplete = true;
}
else
{
DTFileTransferComplete = false;
Serial.println(F("ERROR send close remote destination file failed - program halted"));
}
}
while (!DTFileTransferComplete);
Serial.print(F("NoAckCount "));
Serial.println( NoAckCount);
Serial.println();
DTsendSecs = (float) DTSendmS / 1000;
Serial.print(F("Transmit time "));
Serial.print(DTsendSecs, 3);
Serial.println(F("secs"));
Serial.print(F("Transmit rate "));
Serial.print( (DTDestinationFileLength * 8) / (DTsendSecs), 0 );
Serial.println(F("bps"));
Serial.println(("Transfer finished"));
return true;
}
bool sendFileSegment(uint16_t segnum, uint8_t segmentsize)
{
//****************************************************************
//Send file segment as payload in a packet
//****************************************************************
uint8_t ValidACK;
build_DTSegmentHeader(DTheader, DTSegmentWriteHeaderL, segmentsize, segnum);
#ifdef PRINTSEGMENTNUM
//Serial.print(F("Segment,"));
Serial.println(segnum);
#endif
#ifdef DEBUG
printheader(DTheader, DTSegmentWriteHeaderL);
Serial.print(F(" "));
printdata(DTdata, segmentsize); //print segment size of data array only
#endif
do
{
digitalWrite(DTLED, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTSegmentWriteHeaderL, (uint8_t *) DTdata, segmentsize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(DTLED, LOW);
if (TXPacketL == 0) //if there has been an error TXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
ValidACK = LoRa.waitACKDT(DTheader, DTSegmentWriteHeaderL, ACKsegtimeoutmS);
RXPacketType = DTheader[0];
if (ValidACK > 0)
{
if (RXPacketType == DTSegmentWriteNACK)
{
DTSegment = DTheader[4] + (DTheader[5] << 8); //load what the segment number should be
RXHeaderL = DTheader[2];
Serial.println();
Serial.println(F("************************************"));
Serial.print(F("Received restart request at segment "));
Serial.println(DTSegment);
printheader(DTheader, RXHeaderL);
Serial.println();
Serial.print(F("Seek to file location "));
Serial.println(DTSegment * DTSegmentSize);
Serial.println(F("************************************"));
Serial.println();
Serial.flush();
}
//ack is valid, segment was acknowledged if here
if (RXPacketType == DTStartNACK)
{
Serial.println(F("Received DTStartNACK "));
return false;
}
if (RXPacketType == DTSegmentWriteACK)
{
AckCount++;
#ifdef DEBUG
printAckBrief();
//printAckDetails()
#endif
DTSegment++; //increase value for next segment
return true;
}
}
else
{
NoAckCount++;
Serial.print(F("Error no ACK received "));
Serial.println(NoAckCount);
if (NoAckCount > NoAckCountLimit)
{
Serial.println(F("ERROR NoACK limit reached"));
return false;
}
}
} while (ValidACK == 0);
return true;
}
bool startFileTransfer(char *buff, uint8_t filenamesize, uint8_t attempts)
{
//*********************************************************************
//Start file transfer, simulate open local file first then remote file.
//*********************************************************************
uint8_t ValidACK;
Serial.print(F("Start file transfer "));
Serial.println(buff);
DTSourceFileLength = DTFileSize; //get the file length
if (DTSourceFileLength == 0)
{
Serial.print(F("Error opening local file "));
Serial.println(buff);
return false;
}
DTNumberSegments = getNumberSegments(DTSourceFileLength, DTSegmentSize);
DTLastSegmentSize = getLastSegmentSize(DTSourceFileLength, DTSegmentSize);
build_DTFileOpenHeader(DTheader, DTFileOpenHeaderL, filenamesize, DTSourceFileLength, DTSourceFileCRC, DTSegmentSize);
LocalPayloadCRC = LoRa.CRCCCITT((uint8_t *) buff, filenamesize, 0xFFFF);
do
{
Serial.println(F("Send open remote file request"));
digitalWrite(DTLED, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTFileOpenHeaderL, (uint8_t *) buff, filenamesize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(DTLED, LOW);
#ifdef DEBUG
Serial.print(F("Send attempt "));
Serial.println(DTSendAttempts - attempts + 1);
#endif
attempts--;
TXNetworkID = LoRa.getTXNetworkID(TXPacketL); //get the networkID appended to packet
TXArrayCRC = LoRa.getTXPayloadCRC(TXPacketL); //get the payload CRC appended to packet
#ifdef DEBUG
Serial.print(F("TXNetworkID,0x"));
Serial.print(TXNetworkID, HEX); //get the NetworkID of the packet just sent, its placed at the packet end
Serial.print(F(",TXarrayCRC,0x"));
Serial.println(TXArrayCRC, HEX); //get the CRC of the data array just sent, its placed at the packet end
Serial.println();
#endif
if (TXPacketL == 0) //if there has been a send and ack error, TXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
Serial.print(F("Wait ACK "));
ValidACK = LoRa.waitACKDT(DTheader, DTFileOpenHeaderL, ACKopentimeoutmS);
RXPacketType = DTheader[0];
if ((ValidACK > 0) && (RXPacketType == DTFileOpenACK))
{
#ifdef DEBUG
printPacketHex();
#endif
}
else
{
NoAckCount++;
Serial.println(F("No ACK received"));
Serial.println(NoAckCount);
if (NoAckCount > NoAckCountLimit)
{
Serial.println(F("ERROR NoACK limit reached"));
return false;
}
#ifdef DEBUG
printACKdetail();
Serial.print(F("ACKPacket "));
printPacketHex();
#endif
Serial.println();
}
Serial.println();
}
while ((ValidACK == 0) && (attempts != 0));
if (attempts == 0)
{
return false;
}
return true;
}
bool endFileTransfer(char *buff, uint8_t filenamesize)
{
//****************************************************************
//End file transfer, close local file first then remote file.
//****************************************************************
uint8_t ValidACK;
build_DTFileCloseHeader(DTheader, DTFileCloseHeaderL, filenamesize, DTSourceFileLength, DTSourceFileCRC, DTSegmentSize);
do
{
printSeconds();
Serial.println(F("Send close remote file"));
digitalWrite(DTLED, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTFileCloseHeaderL, (uint8_t *) buff, filenamesize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(DTLED, LOW);
TXNetworkID = LoRa.getTXNetworkID(TXPacketL);
TXArrayCRC = LoRa.getTXPayloadCRC(TXPacketL);
#ifdef DEBUG
Serial.print(F("TXNetworkID,0x"));
Serial.print(TXNetworkID, HEX); //get the NetworkID of the packet just sent, its placed at the packet end
Serial.print(F(",TXarrayCRC,0x"));
Serial.println(TXArrayCRC, HEX); //get the CRC of the data array just sent, its placed at the packet end
Serial.println();
#endif
if (TXPacketL == 0) //if there has been a send and ack error, RXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
ValidACK = LoRa.waitACKDT(DTheader, DTFileCloseHeaderL, ACKclosetimeoutmS);
RXPacketType = DTheader[0];
if ((ValidACK > 0) && (RXPacketType == DTFileCloseACK))
{
#ifdef DEBUG
printPacketHex();
#endif
}
else
{
NoAckCount++;
Serial.print(F("Error no ACK received "));
Serial.println(NoAckCount);
if (NoAckCount > NoAckCountLimit)
{
Serial.println(F("ERROR NoACK limit reached"));
return false;
}
#ifdef DEBUG
Serial.println();
Serial.print(F("ACKPacket "));
printPacketHex();
Serial.println();
#endif
}
}
while (ValidACK == 0);
return true;
}
void build_DTFileOpenHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize)
{
//this builds the header buffer for the filename to send
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTFileOpen); //byte 0, write the packet type
arrayWriteUint8(0); //byte 1, initial DTflags byte, not used here
arrayWriteUint8(headersize); //byte 2, write length of header
arrayWriteUint8(datalength); //byte 3, write length of dataarray
arrayWriteUint32(filelength); //byte 4,5,6,7, write the file length
arrayWriteUint16(filecrc); //byte 8, 9, write file CRC
arrayWriteUint8(segsize); //byte 10, segment size
arrayWriteUint8(0); //byte 11, unused byte
endarrayRW();
}
void build_DTSegmentHeader(uint8_t *header, uint8_t headersize, uint8_t datalen, uint16_t segnum)
{
//this builds the header buffer for a segment transmit
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTSegmentWrite); //write the packet type
arrayWriteUint8(0); //initial DTflags byte, not used here
arrayWriteUint8(headersize); //write length of header
arrayWriteUint8(datalen); //write length of data array
arrayWriteUint16(segnum); //write the DTsegment number
endarrayRW();
}
void build_DTFileCloseHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize)
{
//this builds the header buffer for the filename passed
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTFileClose); //byte 0, write the packet type
arrayWriteUint8(0); //byte 1, initial DTflags byte, not used here
arrayWriteUint8(headersize); //byte 2, write length of header
arrayWriteUint8(datalength); //byte 3, write length of dataarray
arrayWriteUint32(filelength); //byte 4,5,6,7, write the file length
arrayWriteUint16(filecrc); //byte 8, 9, write file CRC
arrayWriteUint8(segsize); //byte 10, segment size
arrayWriteUint8(0); //byte 11, unused byte
endarrayRW();
}
void printLocalFileDetails()
{
Serial.print(F("Source file length is "));
Serial.print(DTSourceFileLength);
Serial.println(F(" bytes"));
Serial.print(F("Segment Size "));
Serial.println(DTSegmentSize);
Serial.print(F("Number segments "));
Serial.println(DTNumberSegments);
Serial.print(F("Last segment size "));
Serial.println(DTLastSegmentSize);
}
bool sendSegments()
{
//start the file transfer at segment 0
DTSegment = 0;
DTSentSegments = 0;
while (DTSegment < (DTNumberSegments - 1))
{
#ifdef DEBUG
printSeconds();
#endif
if (sendFileSegment(DTSegment, DTSegmentSize))
{
DTSentSegments++;
}
else
{
Serial.println(F("ERROR in sendFileSegment"));
Serial.println();
return false;
}
delay(packetdelaymS);
};
printSeconds();
Serial.println(F("Last segment"));
if (!sendFileSegment(DTSegment, DTLastSegmentSize))
{
Serial.println(F("ERROR in sendFileSegment"));
return false;
}
return true;
}
void printheader(uint8_t *hdr, uint8_t hdrsize)
{
Serial.print(F("HeaderBytes,"));
Serial.print(hdrsize);
Serial.print(F(" "));
printarrayHEX(hdr, hdrsize);
}
void printSeconds()
{
float secs;
secs = ( (float) millis() / 1000);
Serial.print(secs, 2);
Serial.print(F(" "));
}
void printdata(uint8_t *dataarray, uint8_t arraysize)
{
Serial.print(F("DataBytes,"));
Serial.print(arraysize);
Serial.print(F(" "));
printarrayHEX((uint8_t *) dataarray, 16); //There is a lot of data to print so only print first 16 bytes
}
void printAckDetails()
{
PacketRSSI = LoRa.readPacketRSSI();
PacketSNR = LoRa.readPacketSNR();
Serial.print(F("AckCount,"));
Serial.print(AckCount);
Serial.print(F(",NoAckCount,"));
Serial.print(NoAckCount);
Serial.print(F(",AckRSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm,AckSNR,"));
Serial.print(PacketSNR);
Serial.print(F("dB"));
Serial.println();
}
void printAckBrief()
{
PacketRSSI = LoRa.readPacketRSSI();
Serial.print(F(",AckRSSI,"));
Serial.print(PacketRSSI);
Serial.println(F("dBm"));
}
void printACKdetail()
{
Serial.print(F("ACKDetail"));
Serial.print(F(",RXNetworkID,0x"));
Serial.print(LoRa.getRXNetworkID(RXPacketL), HEX);
Serial.print(F(",RXPayloadCRC,0x"));
Serial.print(LoRa.getRXPayloadCRC(RXPacketL), HEX);
Serial.print(F(",RXPacketL,"));
Serial.print(RXPacketL);
Serial.print(F(" "));
LoRa.printReliableStatus();
Serial.println();
}
void printPacketHex()
{
RXPacketL = LoRa.readRXPacketL();
Serial.print(RXPacketL);
Serial.print(F(" bytes > "));
if (RXPacketL > 0)
{
LoRa.printSXBufferHEX(0, RXPacketL - 1);
}
}
void printPacketDetails()
{
PacketRSSI = LoRa.readPacketRSSI();
PacketSNR = LoRa.readPacketSNR();
Serial.print(F(" RSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm"));
#ifdef DEBUG
Serial.print(F(",SNR,"));
Serial.print(PacketSNR);
Serial.print(F("dBm,RXOKCount,"));
Serial.print(DTReceivedSegments);
Serial.print(F(",RXErrs,"));
Serial.print(RXErrors);
Serial.print(F(" RX"));
printheader(DTheader, RXHeaderL);
#endif
}
void printPacketRSSI()
{
PacketRSSI = LoRa.readPacketRSSI();
Serial.print(F(" RSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm"));
}
void readHeaderDT()
{
//the first 6 bytes of the header contain the important stuff, so load it up
//so we can decide what to do next.
beginarrayRW(DTheader, 0); //start buffer read at location 0
RXPacketType = arrayReadUint8(); //load the packet type
RXFlags = arrayReadUint8(); //initial DTflags byte, not used here
RXHeaderL = arrayReadUint8(); //load the header length
RXDataarrayL = arrayReadUint8(); //load the datalength
DTSegment = arrayReadUint16(); //load the segment number
}
void printSourceFileDetails()
{
Serial.print(DTfilenamebuff);
Serial.print(F(" Source file length is "));
Serial.print(DTSourceFileLength);
Serial.println(F(" bytes"));
Serial.print(DTfilenamebuff);
}
void printDestinationFileDetails()
{
Serial.print(F("Destination file length is "));
Serial.print(DTDestinationFileLength);
Serial.println(F(" bytes"));
if (DTDestinationFileLength != DTSourceFileLength)
{
Serial.println(F("ERROR - file lengths do not match"));
}
else
{
Serial.println(F("File lengths match"));
}
}
bool processFileClose()
{
//***********************************************************************************************
// Code for closing local SD file and sending request to remote
//***********************************************************************************************
Serial.print(F("File close for "));
Serial.println((char*) DTfilenamebuff);
if (DTFileOpened) //check if file has been opened, close it if it is
{
if (1) //simulate file exists
{
Serial.print(F("Transfer time "));
Serial.print(millis() - DTStartmS);
Serial.print(F("mS"));
Serial.println();
Serial.println(F("File closed"));
Serial.println();
DTFileOpened = false;
DTDestinationFileLength = DTFileSize;
beginarrayRW(DTheader, 4); //start writing to array at location 12
arrayWriteUint32(DTDestinationFileLength); //write file length of file just written just written to ACK header
arrayWriteUint16(DTDestinationFileCRC); //write CRC of file just written to ACK header
printDestinationFileDetails();
}
}
delay(ACKdelaymS);
#ifdef DEBUG
Serial.println(F("Sending ACK"));
#endif
DTheader[0] = DTFileCloseACK;
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTFileCloseHeaderL, TXpower);
digitalWrite(DTLED, LOW);
Serial.println();
Serial.println();
return true;
}
bool processFileOpen(uint8_t *buff, uint8_t filenamesize)
{
//*************************************************************************
// Code for simulating opening local SD file and sending request to remote
//*************************************************************************
beginarrayRW(DTheader, 4); //start buffer read at location 4
DTSourceFileLength = arrayReadUint32(); //load the file length of the remote file being sent
DTSourceFileCRC = arrayReadUint16(); //load the CRC of the source file being sent
memset(DTfilenamebuff, 0, DTfilenamesize); //clear DTfilenamebuff to all 0s
memcpy(DTfilenamebuff, buff, filenamesize); //copy received DTdata into DTfilenamebuff
Serial.print(F("File Open request for "));
Serial.print((char*) DTfilenamebuff);
Serial.println();
printSourceFileDetails();
if (1) //simulate open file for write at beginning, delete if it exists
{
Serial.print((char*) DTfilenamebuff);
Serial.println(F(" File Opened OK"));
Serial.println(F("Waiting transfer"));
DTSegmentNext = 0; //since file is opened the next sequence should be the first
DTFileOpened = true;
DTStartmS = millis();
}
else
{
Serial.print((char*) DTfilenamebuff);
Serial.println(F(" File Open fail"));
DTFileOpened = false;
return false;
}
DTStartmS = millis();
delay(ACKdelaymS);
#ifdef DEBUG
Serial.println(F("Sending ACK"));
#endif
DTheader[0] = DTFileOpenACK; //set the ACK packet type
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTFileOpenHeaderL, TXpower);
digitalWrite(DTLED, LOW);
DTSegmentNext = 0; //after file open, segment 0 is next
return true;
}
bool processSegmentWrite()
{
//***********************************************************************************************
// Code for dealing with segment writes - checks that the sequence of segment writes is correct
//***********************************************************************************************
if (!DTFileOpened)
{
//something is wrong, have received a request to write a segment but there is no file opened
//need to reject the segment write with a restart NACK
Serial.println();
Serial.println(F("***************************************************"));
Serial.println(F("Error - Segment write with no file open - send NACK"));
Serial.println(F("***************************************************"));
Serial.println();
DTheader[0] = DTStartNACK;
delay(ACKdelaymS);
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTStartHeaderL, TXpower);
digitalWrite(DTLED, LOW);
return false;
}
if (DTSegment == DTSegmentNext)
{
#ifdef PRINTSEGMENTNUM
//Serial.print(F("Segment,"));
Serial.println(DTSegment);
#endif
#ifdef DEBUG
Serial.print(F(",Bytes,"));
Serial.print(RXDataarrayL);
//printPacketDetails();
printPacketRSSI();
Serial.println(F(" SendACK"));
#endif
DTheader[0] = DTSegmentWriteACK;
delay(ACKdelaymS);
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTSegmentWriteHeaderL, TXpower);
digitalWrite(DTLED, LOW);
DTReceivedSegments++;
DTSegmentLast = DTSegment; //so we can tell if sequece has been received twice
DTSegmentNext = DTSegment + 1;
return true;
}
if (DTSegment == DTSegmentLast)
{
Serial.print(F("ERROR segment "));
Serial.print(DTSegment);
Serial.print(F(" already received "));
//printPacketDetails();
printPacketRSSI();
DTheader[0] = DTSegmentWriteACK;
Serial.print(F(" Send ACK"));
delay(ACKdelaymS);
delay(DuplicatedelaymS);
digitalWrite(DTLED, HIGH);
LoRa.sendACKDT(DTheader, DTSegmentWriteHeaderL, TXpower);
digitalWrite(DTLED, LOW);
return true;
}
if (DTSegment != DTSegmentNext )
{
Serial.print(F(" ERROR Received Segment "));
Serial.print(DTSegment);
Serial.print(F(" expected "));
Serial.print(DTSegmentNext);
Serial.print(F(" "));
//printPacketDetails();
printPacketRSSI();
DTheader[0] = DTSegmentWriteNACK;
DTheader[4] = lowByte(DTSegmentNext);
DTheader[5] = highByte(DTSegmentNext);
Serial.print(F(" Send NACK for segment "));
Serial.print(DTSegmentNext);
delay(ACKdelaymS);
delay(DuplicatedelaymS);
digitalWrite(DTLED, HIGH);
Serial.println();
Serial.println();
Serial.println(F("*****************************************"));
Serial.print(F("Transmit restart request for segment "));
Serial.println(DTSegmentNext);
printheader(DTheader, RXHeaderL);
Serial.println();
Serial.println(F("*****************************************"));
Serial.println();
Serial.flush();
LoRa.sendACKDT(DTheader, DTSegmentWriteHeaderL, TXpower);
digitalWrite(DTLED, LOW);
return false;
}
return true;
}
bool processPacket(uint8_t packettype)
{
//***********************************************************************************************
// Decide what to do with an incoming packet
//***********************************************************************************************
if (packettype == DTSegmentWrite)
{
processSegmentWrite();
return true;
}
if (packettype == DTFileOpen)
{
processFileOpen(DTdata, RXDataarrayL);
return true;
}
if (packettype == DTFileClose)
{
processFileClose();
return true;
}
return true;
}
bool receiveaPacketDT()
{
//******************************
//Receive Data transfer packets
//******************************
RXPacketType = 0;
RXPacketL = LoRa.receiveDT(DTheader, HeaderSizeMax, (uint8_t *) DTdata, DataSizeMax, NetworkID, RXtimeoutmS, WAIT_RX);
digitalWrite(DTLED, HIGH);
#ifdef DEBUG
printSeconds();
#endif
if (RXPacketL > 0)
{
//if the LT.receiveDT() returns a value > 0 for RXPacketL then packet was received OK
//then only action payload if destinationNode = thisNode
readHeaderDT(); //get the basic header details into global variables RXPacketType etc
processPacket(RXPacketType); //process and act on the packet
digitalWrite(DTLED, LOW);
return true;
}
else
{
//if the LoRa.receiveDT() function detects an error RXOK is 0
Serial.print(F("PacketError"));
RXErrors++;
printPacketDetails();
#ifdef DEBUG
LoRa.printReliableStatus();
LoRa.printIrqStatus();
#endif
Serial.println();
digitalWrite(DTLED, LOW);
return false;
}
}
uint16_t getNumberSegments(uint32_t filesize, uint8_t segmentsize)
{
uint16_t segments;
segments = filesize / segmentsize;
if ((filesize % segmentsize) > 0)
{
segments++;
}
return segments;
}
uint8_t getLastSegmentSize(uint32_t filesize, uint8_t segmentsize)
{
uint8_t lastsize;
lastsize = filesize % segmentsize;
if (lastsize == 0)
{
lastsize = segmentsize;
}
return lastsize;
}
void setDTLED(int8_t pinnumber)
{
if (pinnumber >= 0)
{
DTLED = pinnumber;
pinMode(pinnumber, OUTPUT);
}
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after packet actioned and ack sent
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t NoAckCountLimit = 250; //if no NoAckCount exceeds this value - restart transfer
const uint32_t DuplicatedelaymS = 10; //ms delay if there has been an duplicate segment or command receipt
const uint32_t packetdelaymS = 0; //mS delay between transmitted packets
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 7 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t DTfilenamesize = 32; //size of DTfilename buffer
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
const uint8_t DTSendAttempts = 10; //number of attempts sending a packet before a restart
const uint32_t DTFileSize = 65535; //size of file to simulate
#ifdef USELORA
const uint8_t DTSegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t DTSegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,189 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a program that transfers a file using data transfer (DT) packet functions
from the SX128X library to send a file from the SD card on one Arduino to the SD card on another Arduino.
Arduino DUEs were used for the test and this example transfers an JPG image.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The file open requests to the remote receiver, each segement sent and
the remote file close will all keep transmitting until a valid acknowledge comes from the receiver.
Use this transmitter with the matching receiver program, 234_SDfile_Transfer_Receiver.ino.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty.
The transmitter sends the sequence of segments in order. If the sequence fails for some reason, the receiver
will return a NACK packet to the transmitter requesting the segment sequence it was expecting.
Details of the packet identifiers, header and data lengths and formats used are in the file;
'Data transfer packet definitions.md' in the \SX128X_examples\DataTransfer\ folder.
The transfer can be carried out using LoRa or FLRC packets, max segment size (defined by DTSegmentSize) is
245 bytes for LoRa and 117 bytes for FLRC.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "DTSettings.h" //LoRa settings etc.
SX128XLT LoRa; //create an SX128XLT library instance called LoRa, required by SDtransfer.h
#define ENABLEMONITOR //enable monitor prints
#define PRINTSEGMENTNUM //enable this define to print segment numbers
#define ENABLEFILECRC //enable this define to uses and show file CRCs
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
//#define DEBUG //see additional debug info
//#define SDLIB //define SDLIB for SD.h or SDFATLIB for SDfat.h
#define SDFATLIB
#include <DTSDlibrary.h> //library of SD functions
#include <SDtransfer.h> //library of data transfer functions
//choice of files to send
//char FileName[] = "/$50SATL.JPG"; //file length 63091 bytes, file CRC 0x59CE
char FileName[] = "/$50SATS.JPG"; //file length 6880 bytes, file CRC 0x0281
//char FileName[] = "/$50SATT.JPG"; //file length 1068 bytes, file CRC 0x6A02
void loop()
{
uint32_t filelength;
#ifdef ENABLEMONITOR
Monitorport.println(F("Transfer started"));
#endif
filelength = SDsendFile(FileName, sizeof(FileName));
if (filelength)
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Transfer finished"));
#endif
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Transfer failed"));
Monitorport.println();
#endif
}
delay(15000);
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
SDsetLED(LED1); //setup LED pin for data transfer indicator
#ifdef ENABLEMONITOR
Monitorport.begin(115200);
Monitorport.println();
Monitorport.println(F(__FILE__));
Monitorport.flush();
#endif
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("LoRa device error"));
#endif
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
#ifdef ENABLEMONITOR
Monitorport.println();
Monitorport.print(F("Initializing SD card..."));
#endif
if (DTSD_initSD(SDCS))
{
#ifdef ENABLEMONITOR
Monitorport.println(F("SD Card initialized."));
#endif
}
else
{
Monitorport.println(F("SD Card failed, or not present."));
while (1) led_Flash(100, 25);
}
#ifdef ENABLEMONITOR
Monitorport.println();
#endif
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Payload CRC disabled"));
#endif
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Payload CRC enabled"));
#endif
}
SDDTFileTransferComplete = false;
#ifdef ENABLEMONITOR
Monitorport.println(F("SDfile transfer ready"));
Monitorport.println();
#endif
}

View File

@ -0,0 +1,64 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define SDCS 30
#define Monitorport Serial //Port where serial prints go
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after packet actioned and ack sent
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t DuplicatedelaymS = 10; //ms delay if there has been an duplicate segment or command receipt
const uint32_t FunctionDelaymS = 0; //delay between functions such as open file, send segments etc
const uint32_t PacketDelaymS = 1000; //mS delay between transmitted packets such as DTInfo etc
const uint8_t StartAttempts = 2; //number of attempts to start transfer before a fail
const uint8_t SendAttempts = 5; //number of attempts carrying out a process before a restart
const uint32_t NoAckCountLimit = 250; //if no NoAckCount exceeds this value - restart transfer
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 7 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t Maxfilenamesize = 32; //size of DTfilename buffer
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
#ifdef USELORA
const uint8_t SegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t SegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,164 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 10/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a test program for the use of a data transfer (DT) packet to send a file
from the SD card on one Arduino to the SD card on another Arduino, Arduino DUEs were used for the test.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The remote file open request, the segements sent and the remote file close
will be transmitted until a valid acknowledge comes from the receiver. Use with the matching transmitter
program, 233_LoRa_SDfile_Transfer_Transmitter.ino.
Each DT packet contains a variable length header array and a variable length data array as the payload.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty. The receiver will not accept packets that dont have the appropriate NetworkID or payload CRC
at the end of the packet.
The transmitter sends a sequence of segments in order and the receiver keeps track of the sequence. If
the sequence fails for some reason, the receiver will return a NACK packet to the transmitter requesting
the segment sequence it was expecting.
The transfer can be carried out using LoRa or FLRC packets, max segment size (defined by DTSegmentSize) is
245 bytes for LoRa and 117 bytes for FLRC.
Details of the packet identifiers, header and data lengths and formats used are in the file
Data_transfer_packet_definitions.md in the \SX128X_examples\DataTransfer\ folder.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "DTSettings.h" //LoRa settings etc.
SX128XLT LoRa; //create an SX128XLT library instance called LoRa, required by SDtransfer.h
//#define SDLIB //define SDLIB for SD.h or SDFATLIB for SDfat.h
#define SDFATLIB
#define ENABLEMONITOR //enable monitor prints
#define PRINTSEGMENTNUM
#define ENABLEFILECRC //enable this define to uses and show file CRCs
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
//#define DEBUG //see additional debug info
#include <DTSDlibrary.h> //library of SD functions
#include <SDtransfer.h> //library of data transfer functions
void loop()
{
SDreceiveaPacketDT();
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
SDsetLED(LED1); //setup LED pin for data transfer indicator
#ifdef ENABLEMONITOR
Monitorport.begin(115200);
Monitorport.println();
Monitorport.println(F(__FILE__));
#endif
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("LoRa device error"));
#endif
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
#ifdef ENABLEMONITOR
Monitorport.println();
Monitorport.print(F("Initializing SD card..."));
#endif
if (DTSD_initSD(SDCS))
{
Monitorport.println(F("SD Card initialized."));
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("SD Card failed, or not present."));
#endif
while (1) led_Flash(100, 50);
}
#ifdef ENABLEMONITOR
Monitorport.println();
#endif
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Monitorport.println(F("Payload CRC disabled"));
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Payload CRC enabled"));
#endif
}
SDDTSegmentNext = 0;
SDDTFileOpened = false;
#ifdef ENABLEMONITOR
Monitorport.println(F("SDfile transfer receiver ready"));
Monitorport.println();
#endif
}

View File

@ -0,0 +1,64 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define SDCS 30
#define Monitorport Serial //Port where serial prints go
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after packet actioned and ack sent
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t DuplicatedelaymS = 10; //ms delay if there has been an duplicate segment or command receipt
const uint32_t NoAckCountLimit = 250; //if no NoAckCount exceeds this value - restart transfer
const uint32_t FunctionDelaymS = 0; //delay between functions such as open file, send segments etc
const uint32_t PacketDelaymS = 1000; //mS delay between transmitted packets such as DTInfo etc
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 7 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t Maxfilenamesize = 32; //size of DTfilename buffer
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
const uint8_t SendAttempts = 10; //number of attempts sending a packet or attempting a process before a restart of transfer
const uint8_t StartAttempts = 10; //number of attempts sending the file
#ifdef USELORA
const uint8_t SegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t SegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,846 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a simulation test program for the use of a data transfer (DT) packet to send
the contents of a memory array (DTsendarray) on one Arduino to the SD card as a file on another Arduino,
Arduino DUEs were used for the test. For this example the memory array is first loaded from an SD file.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The remote file open request, the segements sent and the remote file close
will be transmitted until a valid acknowledge comes from the receiver. Use with the matching receiver
program, 234_SDfile_Transfer_Receiver.ino or 236_SDfile_Transfer_ReceiverIRQ.ino.
Each DT packet contains a variable length header array and a variable length data array as the payload.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty. The receiver will not accept packets that dont have the appropriate NetworkID or payload CRC
at the end of the packet.
The transfer can be carried out using LoRa or FLRC packets, max segment size (defined by DTSegmentSize) is
245 bytes for LoRa and 117 bytes for FLRC.
Details of the packet identifiers, header and data lengths and formats used are in the file
Data_transfer_packet_definitions.md in the \SX128X_examples\DataTransfer\ folder.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "Settings.h" //LoRa settings etc.
#include <arrayRW.h>
#include "Variables.h"
SX128XLT LoRa; //create an SX128XLT library instance called LoRa
#include <SdFat.h>
SdFat SD;
File dataFile; //name the file instance needed for SD library routines
uint8_t DTheader[16]; //header array
uint8_t DTdata[245]; //data/segment array
uint8_t DTsendarray[0x10000]; //create a global array to hold data to transfer
uint8_t *ptrDTsendarray; //create a global pointer to the array to send, so all functions have access
//choice of files to send
char DTfilenamebuff[] = "/$50SATS.JPG"; //file length 6880 bytes, file CRC 0x0281
//char DTfilenamebuff[] = "/$50SATT.JPG"; //file length 1068 bytes, file CRC 0x6A02
//#define DEBUG //enable define to see more detail for data transfer operation
//#define DEBUGSDLIB //enable define to see more detail for SD operation
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
void loop()
{
DTLocalArrayLength = moveFileArray(DTfilenamebuff, DTsendarray, sizeof(DTsendarray)); //move the file to a global array to be sent
if (DTLocalArrayLength == 0)
{
Serial.println("ERROR moving local file to array - program halted");
while (1);
}
doArrayTransfer(DTsendarray);
Serial.println("Array transfer complete - program halted");
while (1);
}
void printSeconds()
{
float secs;
secs = ( (float) millis() / 1000);
Serial.print(secs, 3);
Serial.print(F(" "));
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
//**********************************
// Start Code for transfer of array
//**********************************
bool doArrayTransfer(uint8_t *ptrarray)
{
ptrDTsendarray = ptrarray; //set the global ptr for the the array to send
DTLocalArrayCRC = LoRa.CRCCCITT(ptrDTsendarray, DTLocalArrayLength, 0xFFFF);
DTStartmS = millis();
do
{
if (startArrayTransfer(DTfilenamebuff, sizeof(DTfilenamebuff))) //sends the DTsendarray to a remote filename
{
Serial.print(DTfilenamebuff);
Serial.println(" opened OK on remote");
DTNumberSegments = getNumberSegments(DTLocalArrayLength, DTSegmentSize);
DTLastSegmentSize = getLastSegmentSize(DTLocalArrayLength, DTSegmentSize);
printLocalFileDetails();
Serial.println();
NoAckCount = 0;
}
else
{
Serial.print(DTfilenamebuff);
Serial.println("Error opening remote file - restart transfer");
DTFileTransferComplete = false;
continue;
}
delay(packetdelaymS);
if (!sendSegments())
{
Serial.println();
Serial.println(F("**********************************************************"));
Serial.println(F("Error - Segment write with no file open - Restart received"));
Serial.println(F("**********************************************************"));
Serial.println();
continue;
}
if (endArrayTransfer(DTfilenamebuff, sizeof(DTfilenamebuff))) //send command to close remote file
{
//the header returned from file close contains a 16bit CRC of the file saved on the remotes SD
Serial.print(DTfilenamebuff);
Serial.println(" closed OK on remote");
beginarrayRW(DTheader, 4);
DTRemoteFileLength = arrayReadUint32();
DTRemoteFileCRC = arrayReadUint16();
Serial.print(F("Acknowledged remote file length "));
Serial.println(DTRemoteFileLength);
Serial.print(F("Acknowledged remote file CRC 0x"));
Serial.println(DTRemoteFileCRC, HEX);
}
else
{
DTFileTransferComplete = false;
Serial.println(F("ERROR send close remote file failed - program halted"));
}
Serial.print(F("NoAckCount "));
Serial.println( NoAckCount);
Serial.print(F("Total file transmit time "));
Serial.print(millis() - DTStartmS);
Serial.println(F("mS"));
DTFileTransferComplete = true;
}
while (!DTFileTransferComplete);
return true;
}
uint16_t getNumberSegments(uint32_t arraysize, uint8_t segmentsize)
{
uint16_t segments;
segments = arraysize / segmentsize;
if ((arraysize % segmentsize) > 0)
{
segments++;
}
return segments;
}
uint8_t getLastSegmentSize(uint32_t arraysize, uint8_t segmentsize)
{
uint8_t lastsize;
lastsize = arraysize % segmentsize;
if (lastsize == 0)
{
lastsize = segmentsize;
}
return lastsize;
}
bool startArrayTransfer(char *buff, uint8_t filenamesize)
{
uint8_t ValidACK;
Serial.print(F("Start Array transfer "));
Serial.print(DTLocalArrayLength);
Serial.println(F(" bytes"));
build_DTFileOpenHeader(DTheader, DTFileOpenHeaderL, filenamesize, DTLocalArrayLength, DTLocalArrayCRC, DTSegmentSize);
LocalPayloadCRC = LoRa.CRCCCITT((uint8_t *) buff, filenamesize, 0xFFFF);
do
{
Serial.println(F("Transmit open remote file request"));
digitalWrite(LED1, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTFileOpenHeaderL, (uint8_t *) buff, filenamesize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(LED1, LOW);
TXNetworkID = LoRa.getTXNetworkID(TXPacketL); //get the networkID appended to packet
TXArrayCRC = LoRa.getTXPayloadCRC(TXPacketL); //get the payload CRC appended to packet
#ifdef DEBUG
Serial.print(F("TXNetworkID,0x"));
Serial.print(TXNetworkID, HEX); //get the NetworkID of the packet just sent, its placed at the packet end
Serial.print(F(",TXarrayCRC,0x"));
Serial.println(TXArrayCRC, HEX); //get the CRC of the data array just sent, its placed at the packet end
Serial.println();
#endif
if (TXPacketL == 0) //if there has been a send and ack error, TXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
Serial.print(F("Wait ACK "));
ValidACK = LoRa.waitACKDT(DTheader, DTFileOpenHeaderL, ACKtimeoutDTmS);
RXPacketType = DTheader[0];
if ((ValidACK > 0) && (RXPacketType == DTFileOpenACK))
{
Serial.println(F(" - Valid ACK received"));
#ifdef DEBUG
printPacketHex();
#endif
}
else
{
NoAckCount++;
Serial.println(F("No Valid ACK received"));
#ifdef DEBUG
printACKdetail();
Serial.print(F("ACKPacket "));
printPacketHex();
#endif
Serial.println();
}
Serial.println();
}
while (ValidACK == 0);
return true;
}
void build_DTFileOpenHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize)
{
//this builds the header buffer for the filename passed
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTFileOpen); //byte 0, write the packet type
arrayWriteUint8(0); //byte 1, initial DTflags byte, not used here
arrayWriteUint8(headersize); //byte 2, write length of header
arrayWriteUint8(datalength); //byte 3, write length of dataarray
arrayWriteUint32(filelength); //byte 4,5,6,7, write the file length
arrayWriteUint16(filecrc); //byte 8, 9, write dataarray (filename) CRC
arrayWriteUint8(segsize); //byte 10, segment size
arrayWriteUint8(0); //byte 11, unused byte
endarrayRW();
}
bool sendSegments()
{
//start the array transfer at segment 0
DTSegment = 0;
DTSentSegments = 0;
DTarraylocation = 0; //ensure at first position in array
while (DTSegment < (DTNumberSegments - 1))
{
printSeconds();
if (sendArraySegment(DTSegment, DTSegmentSize))
{
Serial.println();
//DTSegment++;
DTSentSegments++;
}
else
{
Serial.println(F("ERROR in sendArraySegment"));
Serial.println();
return false;
}
delay(packetdelaymS);
};
Serial.println("Last segment");
if (!sendArraySegment(DTSegment, DTLastSegmentSize))
{
Serial.println(F("ERROR in sendArraySegment"));
return false;
}
return true;
}
bool sendArraySegment(uint16_t segnum, uint8_t segmentsize)
{
uint8_t ValidACK;
uint8_t index;
uint8_t tempdata;
for (index = 0; index < segmentsize; index++)
{
tempdata = ptrDTsendarray[DTarraylocation];
DTdata[index] = tempdata;
DTarraylocation++;
}
build_DTSegmentHeader(DTheader, DTSegmentWriteHeaderL, segmentsize, segnum);
Serial.print(F("Send Segment,"));
Serial.print(segnum);
Serial.print(F(" "));
printheader(DTheader, DTSegmentWriteHeaderL);
Serial.println();
do
{
digitalWrite(LED1, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTSegmentWriteHeaderL, (uint8_t *) DTdata, segmentsize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(LED1, LOW);
if (TXPacketL == 0) //if there has been a send TXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
ValidACK = LoRa.waitACKDT(DTheader, DTSegmentWriteHeaderL, ACKtimeoutDTmS);
RXPacketType = DTheader[0];
if (ValidACK > 0)
{
if (RXPacketType == DTSegmentWriteNACK)
{
DTSegment = DTheader[4] + (DTheader[5] << 8); //load what the segment should be
RXHeaderL = DTheader[2];
Serial.println();
Serial.println(F("************************************"));
Serial.print(F("Received restart request at segment "));
Serial.println(DTSegment);
printheader(DTheader, RXHeaderL);
Serial.println();
Serial.print(F("Seek to file location "));
Serial.println(DTSegment * DTSegmentSize);
Serial.println(F("************************************"));
Serial.println();
Serial.flush();
DTarraylocation = (DTSegment * DTSegmentSize);
}
//ack is valid, segment was acknowledged if here
if (RXPacketType == DTStartNACK)
{
Serial.println(F("Received DTStartNACK "));
return false;
}
if (RXPacketType == DTSegmentWriteACK)
{
readACKHeader();
AckCount++;
printPacketDetails();
DTSegment++; //increase value for next segment
return true;
}
}
else
{
NoAckCount++;
Serial.print(F("Error No Ack "));
Serial.print(F("NoAckCount,"));
Serial.print(NoAckCount);
LoRa.printReliableStatus();
Serial.println();
}
} while (ValidACK == 0);
return true;
}
void build_DTSegmentHeader(uint8_t *header, uint8_t headersize, uint8_t datalen, uint16_t segnum)
{
//this builds the header buffer for the a segment transmit
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTSegmentWrite); //write the packet type
arrayWriteUint8(0); //initial DTflags byte, not used here
arrayWriteUint8(headersize); //write length of header
arrayWriteUint8(datalen); //write length of data array
arrayWriteUint16(segnum); //write the DTsegment number
endarrayRW();
}
void printPacketDetails()
{
PacketRSSI = LoRa.readPacketRSSI();
PacketSNR = LoRa.readPacketSNR();
Serial.print(F("AckCount,"));
Serial.print(AckCount);
Serial.print(F(",NoAckCount,"));
Serial.print(NoAckCount);
Serial.print(F(",AckRSSI,"));
Serial.print(PacketRSSI);
Serial.print(F("dBm,AckSNR,"));
Serial.print(PacketSNR);
Serial.print(F("dB"));
Serial.println();
}
void printLocalFileDetails()
{
Serial.print(DTfilenamebuff);
Serial.print(F(" LocalFilelength is "));
Serial.print(DTLocalFileLength);
Serial.println(F(" bytes"));
Serial.print(DTfilenamebuff);
Serial.print(F(" Array to send CRC is 0x"));
Serial.println(DTLocalArrayCRC, HEX);
Serial.print(F("DTSegmentSize "));
Serial.println(DTSegmentSize);
Serial.print(F("Number Segments "));
Serial.println(DTNumberSegments);
Serial.print(F("DTLastSegmentSize "));
Serial.println(DTLastSegmentSize);
}
void printPacketHex()
{
RXPacketL = LoRa.readRXPacketL();
Serial.print(RXPacketL);
Serial.print(F(" bytes > "));
if (RXPacketL > 0)
{
LoRa.printSXBufferHEX(0, RXPacketL - 1);
}
}
void printACKdetail()
{
Serial.print(F("ACKDetail"));
Serial.print(F(",RXNetworkID,0x"));
Serial.print(LoRa.getRXNetworkID(RXPacketL), HEX);
Serial.print(F(",RXPayloadCRC,0x"));
Serial.print(LoRa.getRXPayloadCRC(RXPacketL), HEX);
Serial.print(F(",RXPacketL,"));
Serial.print(RXPacketL);
Serial.print(F(" "));
LoRa.printReliableStatus();
Serial.println();
}
void printdata(uint8_t *dataarray, uint8_t arraysize)
{
Serial.print(F("DataBytes,"));
Serial.print(arraysize);
Serial.print(F(" "));
printarrayHEX((uint8_t *) dataarray, arraysize);
}
void printheader(uint8_t *hdr, uint8_t hdrsize)
{
Serial.print(F("HeaderBytes,"));
Serial.print(hdrsize);
Serial.print(F(" "));
printarrayHEX(hdr, hdrsize);
}
void readACKHeader()
{
//the first 6 bytes of the segment write header contain the important stuff, so load it up
//so we can decide what to do next.
beginarrayRW(DTheader, 0); //start buffer read at location 0
RXPacketType = arrayReadUint8(); //load the packet type
RXFlags = arrayReadUint8(); //initial DTflags byte, not used here
RXHeaderL = arrayReadUint8(); //load the header length
RXDataarrayL = arrayReadUint8(); //load the datalength
DTSegment = arrayReadUint16(); //load the segment number
}
bool endArrayTransfer(char *buff, uint8_t filenamesize)
{
uint8_t ValidACK;
Serial.print(F("End file transfer "));
Serial.println(buff);
DTSD_closeFile();
build_DTFileCloseHeader(DTheader, DTFileCloseHeaderL, filenamesize, DTLocalArrayLength, DTLocalArrayCRC, DTSegmentSize);
do
{
Serial.println(F("Transmit close remote file request"));
digitalWrite(LED1, HIGH);
TXPacketL = LoRa.transmitDT(DTheader, DTFileCloseHeaderL, (uint8_t *) buff, filenamesize, NetworkID, TXtimeoutmS, TXpower, WAIT_TX);
digitalWrite(LED1, LOW);
TXNetworkID = LoRa.getTXNetworkID(TXPacketL);
TXArrayCRC = LoRa.getTXPayloadCRC(TXPacketL);
#ifdef DEBUG
Serial.print(F("TXNetworkID,0x"));
Serial.print(TXNetworkID, HEX); //get the NetworkID of the packet just sent, its placed at the packet end
Serial.print(F(",TXarrayCRC,0x"));
Serial.println(TXArrayCRC, HEX); //get the CRC of the data array just sent, its placed at the packet end
Serial.println();
#endif
if (TXPacketL == 0) //if there has been a send and ack error, RXPacketL returns as 0
{
Serial.println(F("Transmit error"));
}
Serial.print(F("Wait ACK "));
ValidACK = LoRa.waitACKDT(DTheader, DTFileCloseHeaderL, ACKtimeoutDTmS);
RXPacketType = DTheader[0];
if ((ValidACK > 0) && (RXPacketType == DTFileCloseACK))
{
Serial.println(F(" - Valid ACK received"));
#ifdef DEBUG
printPacketHex();
#endif
}
else
{
NoAckCount++;
Serial.println(F("No Valid ACK received"));
printACKdetail();
#ifdef DEBUG
Serial.print(F("ACKPacket "));
printPacketHex();
#endif
Serial.println();
}
Serial.println();
}
while (ValidACK == 0);
return true;
}
void build_DTFileCloseHeader(uint8_t *header, uint8_t headersize, uint8_t datalength, uint32_t filelength, uint16_t filecrc, uint8_t segsize)
{
//this builds the header buffer for the filename passed
beginarrayRW(header, 0); //start writing to array at location 0
arrayWriteUint8(DTFileClose); //byte 0, write the packet type
arrayWriteUint8(0); //byte 1, initial DTflags byte, not used here
arrayWriteUint8(headersize); //byte 2, write length of header
arrayWriteUint8(datalength); //byte 3, write length of dataarray
arrayWriteUint32(filelength); //byte 4,5,6,7, write the file length
arrayWriteUint16(filecrc); //byte 8, 9, write dataarray (filename) CRC
arrayWriteUint8(segsize); //byte 10, segment size
arrayWriteUint8(0); //byte 11, unused byte
endarrayRW();
}
//*********************************
// Start Code for dealing with SD
//*********************************
uint32_t moveFileArray(char *filenamebuff, uint8_t *buff, uint32_t buffsize)
{
uint32_t index;
ptrDTsendarray = buff; //assign passed array ptr to global ptr
DTLocalFileLength = DTSD_getFileSize(filenamebuff); //get the file length
if (DTLocalFileLength == 0)
{
Serial.print(F("Error - opening local file "));
Serial.println(filenamebuff);
return 0;
}
if (DTLocalFileLength > buffsize)
{
Serial.println(filenamebuff);
Serial.print(F("Error - file length of "));
Serial.print(DTLocalFileLength);
Serial.print(F(" bytes exceeds array length of "));
Serial.print(buffsize);
Serial.println(F(" bytes"));
return 0;
}
DTSD_openFileRead(filenamebuff);
Serial.print(F("Opened local file "));
Serial.print(filenamebuff);
Serial.print(F(" "));
Serial.print(DTLocalFileLength);
Serial.println(F(" bytes"));
DTLocalFileCRC = DTSD_fileCRCCCITT(DTLocalFileLength); //get file CRC from position 0 to end
Serial.print(F("DTLocalFileCRC 0x"));
Serial.println(DTLocalFileCRC, HEX);
//now tranfer SD file to global array
dataFile.seek(0); //ensure at first position in file
for (index = 0; index < DTLocalFileLength; index++)
{
buff[index] = dataFile.read();
}
Serial.println(F("DTsendarray loaded from SD"));
Serial.print(F("Last written location "));
Serial.println(index);
Serial.print(F("First 16 bytes of array to send "));
LoRa.printHEXPacket(buff, 16);
Serial.println();
DTarraylocation = 0;
return DTLocalFileLength;
}
bool DTSD_initSD(uint8_t CSpin)
{
if (SD.begin(CSpin))
{
return true;
}
else
{
return false;
}
}
void DTSD_printDirectory()
{
dataFile = SD.open("/");
Serial.println("Card directory");
SD.ls("/", LS_R);
}
uint32_t DTSD_openFileRead(char *buff)
{
uint32_t filesize;
dataFile = SD.open(buff);
filesize = dataFile.size();
dataFile.seek(0);
return filesize;
}
void DTSD_closeFile()
{
dataFile.close(); //close local file
}
uint16_t DTSD_fileCRCCCITT(uint32_t fsize)
{
//does a CRC calculation on the file open via dataFile
uint32_t index;
uint16_t CRCcalc;
uint8_t j, filedata;
CRCcalc = 0xFFFF; //start value for CRC16
dataFile.seek(0); //be sure at start of file position
for (index = 0; index < fsize; index++)
{
filedata = dataFile.read();
#ifdef DEBUGSDLIB
Serial.print(F(" 0x"));
Serial.print(filedata, HEX);
#endif
CRCcalc ^= (((uint16_t) filedata ) << 8);
for (j = 0; j < 8; j++)
{
if (CRCcalc & 0x8000)
CRCcalc = (CRCcalc << 1) ^ 0x1021;
else
CRCcalc <<= 1;
}
}
#ifdef DEBUGSDLIB
Serial.print(F(" {DEBUGSDLIB} "));
Serial.print(index);
Serial.print(F(" Bytes checked - CRC "));
Serial.println(CRCcalc, HEX);
#endif
return CRCcalc;
}
uint16_t DTSD_getNumberSegments(uint32_t filesize, uint8_t segmentsize)
{
uint16_t segments;
segments = filesize / segmentsize;
if ((filesize % segmentsize) > 0)
{
segments++;
}
return segments;
}
uint8_t DTSD_getLastSegmentSize(uint32_t filesize, uint8_t segmentsize)
{
uint8_t lastsize;
lastsize = filesize % segmentsize;
if (lastsize == 0)
{
lastsize = segmentsize;
}
return lastsize;
}
void DTSD_seekFileLocation(uint32_t position)
{
dataFile.seek(position); //seek to position in file
return;
}
uint32_t DTSD_getFileSize(char *buff)
{
uint32_t filesize;
if (!SD.exists(buff))
{
return 0;
}
dataFile = SD.open(buff);
filesize = dataFile.size();
dataFile.close();
return filesize;
}
//*******************************
// End Code for dealing with SD
//*******************************
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
Serial.begin(115200);
while (!Serial); // wait for serial port to connect. Needed for native USB
Serial.println();
Serial.println(F("235_Array_Transfer_Transmitter starting"));
Serial.flush();
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
Serial.println(F("Device error"));
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
Serial.print(F("Initializing SD card..."));
if (DTSD_initSD(SDCS))
{
Serial.println(F("Card initialized."));
}
else
{
Serial.println(F("Card failed, or not present."));
while (1) led_Flash(100, 25);
}
Serial.println();
DTSD_printDirectory();
Serial.println();
Serial.println();
Serial.println(F("Array transfer ready"));
Serial.println();
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Serial.println(F("Payload CRC disabled"));
}
else
{
Serial.println(F("Payload CRC enabled"));
}
DTFileTransferComplete = false;
}

View File

@ -0,0 +1,50 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/03/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done
#define LED1 8 //LED used to indicate transmission
#define SDCS 30
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after packet actioned and ack sent
const uint32_t ACKtimeoutDTmS = 100; //mS to wait for receiving an ACK and re-trying TX
const uint32_t packetdelaymS = 0; //mS delay between transmitted packets
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
#ifdef USELORA
const uint8_t DTSegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t DTSegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,35 @@
uint8_t RXPacketType; //type of received packet, segment write, ACK, NACK etc
uint8_t RXPacketL; //length of received packet
uint16_t RXErrors; //count of packets received with error
uint8_t RXFlags; //DTflags byte in header, could be used to control actions in TX and RX
uint8_t RXHeaderL; //length of header
uint8_t RXDataarrayL; //length of data array\segment
int16_t PacketRSSI; //stores RSSI of received packet
int8_t PacketSNR; //stores signal to noise ratio of received packet
uint16_t TXNetworkID; //this is used to identify a transaction, receiver must have the same DTnetworkID
uint16_t TXArrayCRC; //should contain CRC of data array sent
uint8_t TXPacketL; //length of transmitted packet
uint16_t LocalPayloadCRC; //for calculating the local data array CRC
uint16_t DTLocalFileCRC; //CRC of file being transferred
uint16_t DTLocalArrayCRC; //CRC of array being transferred
uint32_t DTLocalFileLength; //length of file to transfer
uint32_t DTLocalArrayLength; //length of array to send
uint16_t DTSegment; //current segment number
uint16_t DTNumberSegments; //number of segments for a file transfer
uint8_t DTLastSegmentSize; //size of the last segment
uint16_t DTSegmentNext; //next segment to send\receive
uint16_t DTReceivedSegments; //count of segments received
uint16_t DTSegmentLast; //last segment to send\receive
uint16_t DTSentSegments; //count of segments sent
uint32_t DTarraylocation; //a global variable giving the location in the array last written to
uint16_t DTRemoteFileCRC; //CRC of returned of the remote saved file
uint32_t DTRemoteFileLength; //filelength returned of the remote saved file
uint32_t DTStartmS; //used for timeing transfers
uint16_t AckCount; //keep a track of acks that are received within timeout period
uint16_t NoAckCount; //keep a track of acks not received within timeout period
bool DTFileIsOpen; //bool to record if file open or closed
bool DTFileTransferComplete; //bool to flag file transfer complete

View File

@ -0,0 +1,191 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/02/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a program that transfers a file using data transfer (DT) packet functions
from the SX128X library to send a file from the SD card on one Arduino to the SD card on another Arduino.
Arduino DUEs were used for the test and this example transfers an JPG image.
This program uses routines that do not need to use the DIO1 pin on the LoRa device which is usually used
to indicate RXDONE or TXDONE.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The file open requests to the remote receiver, each segement sent and
the remote file close will all keep transmitting until a valid acknowledge comes from the receiver.
Use this transmitter with the matching receiver program, 234_SDfile_Transfer_Receiver.ino.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty.
The transmitter sends the sequence of segments in order. If the sequence fails for some reason, the receiver
will return a NACK packet to the transmitter requesting the segment sequence it was expecting.
Details of the packet identifiers, header and data lengths and formats used are in the file;
'Data transfer packet definitions.md' in the \SX128X_examples\DataTransfer\ folder.
The transfer can be carried out using LoRa or FLRC packets, max segment size (defined by DTSegmentSize) is
245 bytes for LoRa and 117 bytes for FLRC.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "DTSettings.h" //LoRa settings etc.
SX128XLT LoRa; //create an SX128XLT library instance called LoRa, required by SDtransfer.h
#define ENABLEMONITOR //enable monitor prints
#define PRINTSEGMENTNUM //enable this define to print segment numbers
#define ENABLEFILECRC //enable this define to uses and show file CRCs
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
//#define SDLIB //define SDLIB for SD.h or SDFATLIB for SDfat.h
#define SDFATLIB
#include <DTSDlibrary.h> //library of SD functions
#include <SDtransferIRQ.h> //library of data transfer functions
//choice of files to send
//char FileName[] = "/$50SATL.JPG"; //file length 63091 bytes, file CRC 0x59CE
char FileName[] = "/$50SATS.JPG"; //file length 6880 bytes, file CRC 0x0281
//char FileName[] = "/$50SATT.JPG"; //file length 1068 bytes, file CRC 0x6A02
void loop()
{
uint32_t filelength;
#ifdef ENABLEMONITOR
Monitorport.println(F("Transfer started"));
#endif
filelength = SDsendFile(FileName, sizeof(FileName));
if (filelength)
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Transfer finished"));
#endif
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Transfer failed"));
Monitorport.println();
#endif
}
delay(15000);
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
SDsetLED(LED1); //setup LED pin for data transfer indicator
#ifdef ENABLEMONITOR
Monitorport.begin(115200);
Monitorport.println();
Monitorport.println(F(__FILE__));
Monitorport.flush();
#endif
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("LoRa device error"));
#endif
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
#ifdef ENABLEMONITOR
Monitorport.println();
Monitorport.print(F("Initializing SD card..."));
#endif
if (DTSD_initSD(SDCS))
{
#ifdef ENABLEMONITOR
Monitorport.println(F("SD Card initialized."));
#endif
}
else
{
Monitorport.println(F("SD Card failed, or not present."));
while (1) led_Flash(100, 25);
}
#ifdef ENABLEMONITOR
Monitorport.println();
#endif
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Payload CRC disabled"));
#endif
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Payload CRC enabled"));
#endif
}
SDDTFileTransferComplete = false;
#ifdef ENABLEMONITOR
Monitorport.println(F("SDfile transfer ready"));
Monitorport.println();
#endif
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/02/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define LED1 8 //LED used to indicate transmission
#define SDCS 30
#define Monitorport Serial //Port where serial prints go
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after packet actioned and ack sent
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t DuplicatedelaymS = 10; //ms delay if there has been an duplicate segment or command receipt
const uint32_t FunctionDelaymS = 0; //delay between functions such as open file, send segments etc
const uint32_t PacketDelaymS = 1000; //mS delay between transmitted packets such as DTInfo etc
const uint8_t StartAttempts = 2; //number of attempts to start transfer before a fail
const uint8_t SendAttempts = 5; //number of attempts carrying out a process before a restart
const uint32_t NoAckCountLimit = 250; //if no NoAckCount exceeds this value - restart transfer
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 7 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t Maxfilenamesize = 32; //size of DTfilename buffer
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
#ifdef USELORA
const uint8_t SegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t SegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,165 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/02/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
/*******************************************************************************************************
Program Operation - This is a test program for the use of a data transfer (DT) packet to send a file
from the SD card on one Arduino to the SD card on another Arduino, Arduino DUEs were used for the test.
This program uses routines that do not need to use the DIO1 pin on the LoRa device which is usually used
to indicate RXDONE or TXDONE.
DT packets can be used for transfering large amounts of data in a sequence of packets or segments,
in a reliable and resiliant way. The remote file open request, the segements sent and the remote file close
will be transmitted until a valid acknowledge comes from the receiver. Use with the matching transmitter
program, 233_LoRa_SDfile_Transfer_Transmitter.ino.
Each DT packet contains a variable length header array and a variable length data array as the payload.
On transmission the NetworkID and CRC of the payload are appended to the end of the packet by the library
routines. The use of a NetworkID and CRC ensures that the receiver can validate the packet to a high degree
of certainty. The receiver will not accept packets that dont have the appropriate NetworkID or payload CRC
at the end of the packet.
The transmitter sends a sequence of segments in order and the receiver keeps track of the sequence. If
the sequence fails for some reason, the receiver will return a NACK packet to the transmitter requesting
the segment sequence it was expecting.
The transfer can be carried out using LoRa or FLRC packets, max segment size (defined by DTSegmentSize) is
245 bytes for LoRa and 117 bytes for FLRC.
Details of the packet identifiers, header and data lengths and formats used are in the file
Data_transfer_packet_definitions.md in the \SX128X_examples\DataTransfer\ folder.
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "DTSettings.h" //LoRa settings etc.
SX128XLT LoRa; //create an SX128XLT library instance called LoRa, required by SDtransfer.h
//#define SDLIB //define SDLIB for SD.h or SDFATLIB for SDfat.h
#define SDFATLIB
#define ENABLEMONITOR //enable monitor prints
#define PRINTSEGMENTNUM
#define ENABLEFILECRC //enable this define to uses and show file CRCs
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
#include <DTSDlibrary.h> //library of SD functions
#include <SDtransferIRQ.h> //library of data transfer functions
void loop()
{
SDreceiveaPacketDT();
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(LED1, HIGH);
delay(delaymS);
digitalWrite(LED1, LOW);
delay(delaymS);
}
}
void setup()
{
pinMode(LED1, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
SDsetLED(LED1); //setup LED pin for data transfer indicator
#ifdef ENABLEMONITOR
Monitorport.begin(115200);
Monitorport.println();
Monitorport.println(F(__FILE__));
#endif
SPI.begin();
if (LoRa.begin(NSS, NRESET, RFBUSY, LORA_DEVICE))
{
led_Flash(2, 125);
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("LoRa device error"));
#endif
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Serial.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Serial.println(F("Using FLRC packets"));
#endif
#ifdef ENABLEMONITOR
Monitorport.println();
Monitorport.print(F("Initializing SD card..."));
#endif
if (DTSD_initSD(SDCS))
{
Monitorport.println(F("SD Card initialized."));
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("SD Card failed, or not present."));
#endif
while (1) led_Flash(100, 50);
}
#ifdef ENABLEMONITOR
Monitorport.println();
#endif
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Monitorport.println(F("Payload CRC disabled"));
}
else
{
#ifdef ENABLEMONITOR
Monitorport.println(F("Payload CRC enabled"));
#endif
}
SDDTSegmentNext = 0;
SDDTFileOpened = false;
#ifdef ENABLEMONITOR
Monitorport.println(F("SDfile transfer receiver ready"));
Monitorport.println();
#endif
}

View File

@ -0,0 +1,61 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 12/02/22
This program is supplied as is, it is up to the user of the program to decide if the program is
suitable for the intended purpose and free from errors.
*******************************************************************************************************/
#define NSS 10 //select pin on LoRa device
#define NRESET 9 //reset pin on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define LED1 8 //LED used to indicate transmission
#define SDCS 30
#define Monitorport Serial //Port where serial prints go
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
//******* Setup LoRa Test Parameters Here ! ***************
const uint32_t Frequency = 2445000000; //frequency of transmissions
const uint32_t Offset = 0; //offset frequency for calibration purposes
const int8_t TXpower = 10; //LoRa transmit power
//******* Setup LoRa modem parameters here ! ***************
const uint8_t Bandwidth = LORA_BW_1600; //LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate
//******* Setup FLRC modem parameters here ! ***************
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs
//const uint8_t BandwidthBitRate = FLRC_BR_0_260_BW_0_3; //FLRC 260kbps
const uint8_t CodingRate = FLRC_CR_1_0; //FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT
const uint32_t Syncword = 0x01234567; //FLRC uses syncword
const uint32_t TXtimeoutmS = 5000; //mS to wait for TX to complete
const uint32_t RXtimeoutmS = 60000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 10; //ms delay after packet actioned and ack sent
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 250; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t DuplicatedelaymS = 10; //ms delay if there has been an duplicate segment or command receipt
const uint32_t NoAckCountLimit = 250; //if no NoAckCount exceeds this value - restart transfer
const uint32_t FunctionDelaymS = 0; //delay between functions such as open file, send segments etc
const uint32_t PacketDelaymS = 1000; //mS delay between transmitted packets such as DTInfo etc
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 7 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t Maxfilenamesize = 32; //size of DTfilename buffer
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
const uint8_t SendAttempts = 10; //number of attempts sending a packet or attempting a process before a restart of transfer
const uint8_t StartAttempts = 10; //number of attempts sending the file
#ifdef USELORA
const uint8_t SegmentSize = 245; //number of bytes in each segment, 245 is maximum value for LoRa
#endif
#ifdef USEFLRC
const uint8_t SegmentSize = 117; //number of bytes in each segment, 117 is maximum value for FLRC
#endif

View File

@ -0,0 +1,340 @@
## Data Transfer packet Definitions
0xA0 Request to TX, DTSegmentWrite, Header Length 6, Data length Max, 245
0xA1 ACK to RX, DTSegmentWriteACK, Header Length 6, Data length Max, 245
0xA2 NACK to RX, DTSegmentWriteNACK, Header Length 6, Data length Max, 245
0xA4 Request to TX, DTFileOpen, Header Length 12, Data length Max, 239
0xA5 ACK to RX, DTFileOpenACK, Header Length 12, Data length Max, 239
0xA6 NACK to RX, DTFileOpenNACK, Header Length 12, Data length Max, 239
0xA8 Request to TX, DTFileClose, Header Length 12, Data length Max, 239
0xA9 ACK to RX, DTFileCloseACK, Header Length 12, Data length Max, 239
0xAA NACK to RX, DTFileCloseNACK, Header Length 12, Data length Max, 239
0xAC Request to TX, DTFileSeek, Header Length 9, Data length Max, 242
0xAD ACK to RX, DTFileSeekACK, Header Length 9, Data length Max, 242
0xAE NACK to RX, DTFileSeekNACK, Header Length 9, Data length Max, 242
0xB0 Request to TX, DTStart, Header Length 6, Data length Max, 245
0xB1 ACK to RX, DTStartACK, Header Length 6, Data length Max, 245
0xB2 NACK to RX, DTStartNACK, Header Length 6, Data length Max, 245
0xB4 Request to TX, DTWake, Header Length 6, Data length Max, 245
0xB5 ACK to RX, DTWakeACK, Header Length 6, Data length Max, 245
0xB6 NACK to RX, DTWakeNACK, Header Length 6, Data length Max, 245
0xA0
DTSegmentWrite, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xA0
1 Flags
2 Header length
3 Data length
4 SegmentNum0
5 SegmentNum1
Data
Byte Purpose
6 DataArray Start
7 More data etc
0xA1
DTSegmentWriteACK, Header Length 6
Header
Byte Purpose
0 0xA1
1 Flags
2 Header length
3 Data length
4 SegmentNum0
5 SegmentNum1
0xA2
DTSegmentWriteACK, Header Length 6
Header
Byte Purpose
0 0xA2
1 Flags
2 Header length
3 Data length
4 Required SegmentNum0
5 Required SegmentNum1
0xA4
DTFileOpen, Header Length 12, Data length Max, 239
Header
Byte Purpose
0 0xA4
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
Data
Byte Purpose
12 FilenameArray Start
13 More FilenameArray etc
0xA5
DTFileOpenACK, Header Length 12
Header
Byte Purpose
0 0xA5
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xA6
DTFileOpenNACK, Header Length 12
Header
Byte Purpose
0 0xA6
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xA8
DTFileClose, Header Length 12, Data length Max, 239
Header
Byte Purpose
0 0xA8
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
Data
Byte Purpose
12 FilenameArray Start
13 More FilenameArray etc
0xA9
DTFileCloseACK, Header Length 12
Header
Byte Purpose
0 0xA9
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xAA
DTFileCloseNACK, Header Length 12
Header
Byte Purpose
0 0xAA
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xAC
DTDataSeek, Header Length 9, Data length Max, 242
Header
Byte Purpose
0 0xAC
1 Flags
2 Header length
3 Data length
4 DataSeek0
5 DataSeek1
6 DataSeek2
7 DataSeek3
8Unused
Data
Byte Purpose
9 FilenameArray Start
13 More FilenameArray etc
0xAD
DTDataSeekACK, Header Length 9, Data length Max, 242
Header
Byte Purpose
0 0xAD
1 Flags
2 Header length
3 Data length
4 DataSeek0
5 DataSeek1
6 DataSeek2
7 DataSeek3
8Unused
0xAE
DTDataSeekNACK, Header Length 9, Data length Max, 242
Header
Byte Purpose
0 0xAE
1 Flags
2 Header length
3 Data length
4 DataSeek0
5 DataSeek1
6 DataSeek2
7 DataSeek3
8Unused
0xB0
DTStart, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB0
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
Data
Byte Purpose
6 SegmentSize
7 LastSegmentSize
8 TXtimeoutmS0
9 TXtimeoutmS1
10 TXtimeoutmS2
11 TXtimeoutmS3
12 RXtimeoutmS0
13 RXtimeoutmS1
14 RXtimeoutmS2
15 RXtimeoutmS3
16 ACKtimeoutDTmS0
17 ACKtimeoutDTmS1
18 ACKtimeoutDTmS2
19 ACKtimeoutDTmS3
20 ACKdelaymS0
21 ACKdelaymS1
22 ACKdelaymS2
23 ACKdelaymS3
24 packetdelaymS0
25 packetdelaymS1
26 packetdelaymS2
27 packetdelaymS3
28 Frequency0
29 Frequency1
30 Frequency2
31 Frequency3
32 Offset0
33 Offset1
34 Offset2
35 Offset3
36 Spreading Factor
37 Bandwidth
38 Coding Rate
39 Optimisation
40 TXPower
41 Implicit/Explicit
42 TXattempts0
43 TXattempts1
44 HeaderSizeMax
45 DataSizeMax
0xB1
DTStartACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB1
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB2
DTStartNACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB2
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB4
DTWake, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB4
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB5
DTWakeACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB5
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB6
DTWakeNACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB6
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused

View File

@ -0,0 +1,340 @@
Data Transfer packet Definitions
0xA0 Request to TX, DTSegmentWrite, Header Length 6, Data length Max, 245
0xA1 ACK to RX, DTSegmentWriteACK, Header Length 6, Data length Max, 245
0xA2 NACK to RX, DTSegmentWriteNACK, Header Length 6, Data length Max, 245
0xA4 Request to TX, DTFileOpen, Header Length 12, Data length Max, 239
0xA5 ACK to RX, DTFileOpenACK, Header Length 12, Data length Max, 239
0xA6 NACK to RX, DTFileOpenNACK, Header Length 12, Data length Max, 239
0xA8 Request to TX, DTFileClose, Header Length 12, Data length Max, 239
0xA9 ACK to RX, DTFileCloseACK, Header Length 12, Data length Max, 239
0xAA NACK to RX, DTFileCloseNACK, Header Length 12, Data length Max, 239
0xAC Request to TX, DTFileSeek, Header Length 9, Data length Max, 242
0xAD ACK to RX, DTFileSeekACK, Header Length 9, Data length Max, 242
0xAE NACK to RX, DTFileSeekNACK, Header Length 9, Data length Max, 242
0xB0 Request to TX, DTStart, Header Length 6, Data length Max, 245
0xB1 ACK to RX, DTStartACK, Header Length 6, Data length Max, 245
0xB2 NACK to RX, DTStartNACK, Header Length 6, Data length Max, 245
0xB4 Request to TX, DTWake, Header Length 6, Data length Max, 245
0xB5 ACK to RX, DTWakeACK, Header Length 6, Data length Max, 245
0xB6 NACK to RX, DTWakeNACK, Header Length 6, Data length Max, 245
0xA0
DTSegmentWrite, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xA0
1 Flags
2 Header length
3 Data length
4 SegmentNum0
5 SegmentNum1
Data
Byte Purpose
6 DataArray Start
7 More data etc
0xA1
DTSegmentWriteACK, Header Length 6
Header
Byte Purpose
0 0xA1
1 Flags
2 Header length
3 Data length
4 SegmentNum0
5 SegmentNum1
0xA2
DTSegmentWriteACK, Header Length 6
Header
Byte Purpose
0 0xA2
1 Flags
2 Header length
3 Data length
4 Required SegmentNum0
5 Required SegmentNum1
0xA4
DTFileOpen, Header Length 12, Data length Max, 239
Header
Byte Purpose
0 0xA4
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
Data
Byte Purpose
12 FilenameArray Start
13 More FilenameArray etc
0xA5
DTFileOpenACK, Header Length 12
Header
Byte Purpose
0 0xA5
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xA6
DTFileOpenNACK, Header Length 12
Header
Byte Purpose
0 0xA6
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xA8
DTFileClose, Header Length 12, Data length Max, 239
Header
Byte Purpose
0 0xA8
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
Data
Byte Purpose
12 FilenameArray Start
13 More FilenameArray etc
0xA9
DTFileCloseACK, Header Length 12
Header
Byte Purpose
0 0xA9
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xAA
DTFileCloseNACK, Header Length 12
Header
Byte Purpose
0 0xAA
1 Flags
2 Header length
3 Data length
4 Filelength0
5 Filelength1
6 Filelength2
7 Filelength3
8 FileCRC0
9 FileCRC1
10 SegmentSize
11 Unused
0xAC
DTDataSeek, Header Length 9, Data length Max, 242
Header
Byte Purpose
0 0xAC
1 Flags
2 Header length
3 Data length
4 DataSeek0
5 DataSeek1
6 DataSeek2
7 DataSeek3
8Unused
Data
Byte Purpose
9 FilenameArray Start
13 More FilenameArray etc
0xAD
DTDataSeekACK, Header Length 9, Data length Max, 242
Header
Byte Purpose
0 0xAD
1 Flags
2 Header length
3 Data length
4 DataSeek0
5 DataSeek1
6 DataSeek2
7 DataSeek3
8Unused
0xAE
DTDataSeekNACK, Header Length 9, Data length Max, 242
Header
Byte Purpose
0 0xAE
1 Flags
2 Header length
3 Data length
4 DataSeek0
5 DataSeek1
6 DataSeek2
7 DataSeek3
8Unused
0xB0
DTStart, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB0
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
Data
Byte Purpose
6 SegmentSize
7 LastSegmentSize
8 TXtimeoutmS0
9 TXtimeoutmS1
10 TXtimeoutmS2
11 TXtimeoutmS3
12 RXtimeoutmS0
13 RXtimeoutmS1
14 RXtimeoutmS2
15 RXtimeoutmS3
16 ACKtimeoutDTmS0
17 ACKtimeoutDTmS1
18 ACKtimeoutDTmS2
19 ACKtimeoutDTmS3
20 ACKdelaymS0
21 ACKdelaymS1
22 ACKdelaymS2
23 ACKdelaymS3
24 packetdelaymS0
25 packetdelaymS1
26 packetdelaymS2
27 packetdelaymS3
28 Frequency0
29 Frequency1
30 Frequency2
31 Frequency3
32 Offset0
33 Offset1
34 Offset2
35 Offset3
36 Spreading Factor
37 Bandwidth
38 Coding Rate
39 Optimisation
40 TXPower
41 Implicit/Explicit
42 TXattempts0
43 TXattempts1
44 HeaderSizeMax
45 DataSizeMax
0xB1
DTStartACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB1
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB2
DTStartNACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB2
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB4
DTWake, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB4
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB5
DTWakeACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB5
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused
0xB6
DTWakeNACK, Header Length 6, Data length Max, 245
Header
Byte Purpose
0 0xB6
1 Flags
2 Header length
3 Data length
4 Unused
5 Unused

View File

@ -0,0 +1,79 @@
Several of the functions needed to make the data transfers work were already incorporated in the reliable packets functions, so I just had to add the library functions to transmit and receive the packets that consisted of a configurable header array and a data array.
The additional functions needed were;
uint8_t transmitDT(uint8_t *header, uint8_t headersize, uint8_t *dataarray, uint8_t size, uint16_t networkID, uint32_t txtimeout, int8_t txpower, uint8_t wait);
uint8_t receiveDT(uint8_t *header, uint8_t headersize, uint8_t *dataarray, uint8_t size, uint16_t networkID, uint32_t rxtimeout, uint8_t wait );
uint8_t sendACKDT(uint8_t *header, uint8_t headersize, int8_t txpower);
uint8_t waitACKDT(uint8_t *header, uint8_t headersize, uint32_t acktimeout);
The various sketch functions required such as sending an openfile command to the receiver, sending the the segments of the file and a close file command, would use the above core library functions with various different header formats. The detail of passing the file names across (for the receiver to save to SD with), calculating segment sizes and dealing with the segment sequencing would be dealt with at the Arduino sketch level where it would be easier to see exactly what is going on during a transfer. Perhaps at a later date it might be possible to move more of the required sketch code into a library function.
## Example Programs
A simple test program was needed so that you could check a particular set of LoRa modem parameters to see how reliable they were at a chosen distance for sending the large packets. This is the purpose of the first two example sketches, '**231\_Data\_Transfer\_Test\_Transmitter.ino**' and '**232\_Data\_Transfer\_Test\_Receiver.ino**'
The transmitter sends a test segment of size defined by DTSegmentSize in the Settings.h file where the LoRa modem settings can also be defined.
The test program does implement a check on the segment sequence. If the receiver has just had segment 10, then it next expects segment 11. If something goes wrong and say segment 12 appears next then the receiver recognises this and sends a NACK packet back to the transmitter to restart the sequence from number 11. You can test this recovery at any time by resetting the transmitter.
The program **221\_LoRa\_DTPacket\_Monitor.ino** or **222\_FLRC\_DTPacket\_Monitor.ino** can be used to monitor the progress of the test transmitter, be sure to use the same LoRa modem settings as the test transmitter and receiver.
## File Transfer
The purpose of the Data Transfer functions in the SX12XX Library is for applications such as moving files from one Arduino to another over a LoRa link. There are no guarantees in radio frequency reception of data packets, it's inevitable that some will be missed. Thus the data transfer functions need to deal with this as well as coping with packets from possible foreign sources or missed segments in the file. A single bit error in a graphic image for instance can render the image unreadable.
The examples **233\_SDfile\_Transfer\_Transmitter.ino** and **234\_SDfile\_Transfer\_Receiver.ino** transfer a choice of files; $50SATL.JPG 63091 bytes, $50SATS.JPG 6880 bytes and $50SATT.JPG 1068 bytes) from the SD card on the transmitter Arduino to the SD card on another receiver Arduino. Arduino DUEs were used for testing these examples. The JPG image files above are located in the examples\SX128x_examples\DataTransfer folder and will need to be copied to the SD card on the transmitter.
<br>
<p align="center">
<img width="250" src="/$50SATL.jpg">
</p>
<br>
The transmitter starts the transfer by requesting that the file name chosen is opened on the remote receiver, the transmitter will keep sending this open file request till it succeeds. Then the actual segment\data transfer is started and each segment will be transmitted until it is accepted. If there is a segment sequence error at the receiver then the transmitter is notified and the transmission of segments restarts at the correct location. The last segment sent can be a different size to those in the main body of the file transfer. The transmitter then sends a file close request and the receiver then returns the file length and CRC of the file now on it's SD card data back to the transmitter. This is a further integrity check that the data has been transferred correctly.
The transfer could be organised in such a way that the segment transmissions were blind, with no acknowledge, but that would then require the receiver to keep track of missed segments and later request re-transmission. There are some LoRa set-ups that use SSDV to transfer images from cameras, the image is processed and spilt into blocks and a part image can be displayed even if there are some blocks missing. However, the data transfer method described here has no processing that is dependant on an image or file type, it just treats the image or file as a string of bytes.
## Transfer a memory array
Example **235\_Array\_Transfer\_Transmitter.ino** is a version of **233\_SDfile\_Transfer\_Transmitter.ino** that demonstrates sending a memory array (DTsendarray) from a transmitter to a receiver that then saves the received array onto a file on an SD card. The DTsendarray is first populated with data from a file /$50SATS.JPG or /$50SATT.JPG by the transmitter. In this example the array is then sent as a sequence of segments, similar to the way a file would be read from SD and sent.
##Fine tuning for performance
The speed of the data transfers is mainly dependant on the LoRa settings used, higher\faster data rates come from using a lower spreading factor
and a higher bandwidth, although of course the higher the data rate the shorter the distance covered.
There are two program parameters in the example sketches that you may need to adjust. When the receiver has picked up a packet from the transmitter there is a programmable delay before the acknowledge is sent. This is the ACKdelaymS parameter. If the transmitter is particularly slow in changing from transmitting a packet and being ready to pick up the start of the acknowledge then it might miss it. Due to the delays in the receiver of writing a segment to SD an ACKdelaymS of 0 will likely work, but increase it if the transmitter is missing a lot of the acknowledge packets.
A second parameter to adjust is the ACKtimeoutDTmS, and this is the period the transmitter waits for a valid acknowledge before sending the packet again. This time-out needs to be long enough to receive an acknowledge, but not too long or every missed acknowledge could slow down the transfer as it waits for the time-out period before re-transmitting.
## Achieved data rates
With the segment length set at maximum for LoRa, 245 bytes, and LoRa settings of spreading factor 5, bandwidth 1600khz and coding rate 4:5 the 63019 byte $50SATL.JPG file took 4.54 seconds to transfer to the SD card on the receiver Arduino. That is an achieved data rate of;
(63091 * 8) / 4.54 = **111,173bps**.
The equivalent data sheet on air rate for the LoRa settings used is **203kbps**.
In FLRC mode, at its fastest rate, the same 63019 image file took 2.48 seconds to transfer an achieved data rate of **203,601bps**, the on air rate for this fastest FLRC mode is **1,300,000bps**
The Data Transfer examples will be found in the \examples\SX128x_examples\DataTransfer folder of the [**SX12XX-LoRa Library**](https://github.com/StuartsProjects/SX12XX-LoRa).
## Conclusions
Well, the data transfers work and the LoRa and FLRC are considerably faster than UHF LoRa devices.
## Duty Cycle
Whilst the described routines work well enough for SX127x in the UHF 433Mhz band in a lot of places in the World your limited to 10% duty cycle, so sending images continuously is not legal. However the library functions described here are for the 2.4Ghz SX128X library, and at 2.4Ghz there are few duty cycle restrictions.
<br>
**Stuart Robinson**
**November 2021**