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

View File

@ -0,0 +1,158 @@
/*******************************************************************************************************
Based on the OV2640 Arducam programs.
ArduCAM demo (C)2017 Lee
Web: http://www.ArduCAM.com
*******************************************************************************************************/
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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 demonstration program that uses LoRa to transfer an image taken with an
OV2640 Arducam camera. The LoRa transfer is carried out using the data transfer functions of the
SX12XX-LoRa library. Program tested on Arduino DUE.
The Arducam software takes an image and saves it to a SD card. The filename is then passed across to
the LoRa library which transfers the file already on SD across to the remote receiver, which is running
program 239_StuartCAM_LoRa_Receiver or program 234_SDfile_Transfer_Receiver.
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 <ArduCAM.h> //get library here > https://github.com/ArduCAM/Arduino
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include "memorysaver.h" //part of Arducam library
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#include "Settings.h" //LoRa settings etc.
SX128XLT LoRa; //create an SX128XLT library instance called LoRa
#define ENABLEMONITOR //enable monitor prints
#define PRINTSEGMENTNUM //enable this define to print segment numbers
//#define DEBUG //enable this define to print debug info for segment transfers
//#define DEBUGSD //enable this defien to print SD file debug info
#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 //DTSDlibrary.h needs to use SD.h
#include "DTSDlibrary.h" //part of SX12XX library
#include "SDtransfer.h" //part of SX12XX library
char FileName[13]; //filename passed to both Arducam and LoRa routines
ArduCAM myCAM( OV2640, OV2640CS );
#include "OV2640.h" //include local Arducam code for OV2640 camera
void loop()
{
myCAMSaveToSDFile(FileName, sizeof(FileName)); //when finished FileName[] contains name of file saved on SD
Serial.print(FileName);
Serial.println(F(" Saved to SD card"));
SDsendFile(FileName, sizeof(FileName)); //transfer image via LoRa
Serial.println();
Serial.println();
delay(60000); //wait 60 seconds
}
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
digitalWrite(OV2640CS, HIGH);
pinMode(OV2640CS, OUTPUT); //set the camera CS as an output
digitalWrite(SDCS, HIGH);
pinMode(SDCS, OUTPUT); //set the SDCS as an output
Serial.begin(115200);
Serial.println(__FILE__);
Wire.begin();
SPI.begin();
//Initialize SD Card
while (!SD.begin(SDCS))
{
Serial.println(F("SD Card Error - program halted"));
while (1);
}
Serial.println(F("SD Card detected"));
//Initialize camera
setupOV2640(OV2640resolution); //resolution choice, see Settings.h file
//Initialise LoRa device
if (LoRa.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE))
{
Serial.println(F("LoRa device found"));
led_Flash(2, 125);
}
else
{
Serial.println(F("LoRa device error"));
while (1)
{
led_Flash(50, 50); //long fast speed flash indicates device error
}
}
Serial.println();
#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();
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"));
}
}

View File

@ -0,0 +1,458 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

View File

@ -0,0 +1,253 @@
/*******************************************************************************************************
Based on the OV2640 Arducam programs by;
ArduCAM demo (C)2017 Lee
Web: http://www.ArduCAM.com
*******************************************************************************************************/
#define OV2640_CHIPID_HIGH 0x0A
#define OV2640_CHIPID_LOW 0x0B
void setupOV2640(uint8_t resolution);
void myCAMSaveToSDFile2();
void myCAMSaveToSDFile(char *ACfilename, uint8_t ACfilenamesize);
void setupOV2640(uint8_t resolution)
{
uint8_t vid, pid;
uint8_t temp;
//Reset the CPLD
myCAM.write_reg(0x07, 0x80);
delay(100);
myCAM.write_reg(0x07, 0x00);
delay(100);
while (1) {
//Check if the ArduCAM SPI bus is OK
myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
temp = myCAM.read_reg(ARDUCHIP_TEST1);
if (temp != 0x55) {
Serial.println(F("OV2640 SPI interface Error!"));
delay(1000); continue;
} else {
Serial.println(F("OV2640 SPI interface OK")); break;
}
}
while (1) {
//Check if the camera module type is OV2640
myCAM.wrSensorReg8_8(0xff, 0x01);
myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
if ((vid != 0x26 ) && (( pid != 0x41 ) || ( pid != 0x42 ))) {
Serial.println(F("Can't find OV2640 module!"));
delay(1000); continue;
}
else {
Serial.println(F("OV2640 setup on I2C")); break;
}
}
myCAM.set_format(JPEG);
myCAM.InitCAM();
myCAM.OV2640_set_JPEG_size(resolution);
}
void myCAMSaveToSDFile2()
{
char str[8];
byte buf[256];
static int i = 0;
static int k = 0;
uint8_t temp = 0, temp_last = 0;
uint32_t length = 0;
bool is_header = false;
File outFile;
myCAM.flush_fifo(); //Flush the FIFO
myCAM.clear_fifo_flag(); //Clear the capture done flag
myCAM.start_capture(); //Start capture
Serial.println(F("start Capture"));
while (!myCAM.get_bit(ARDUCHIP_TRIG , CAP_DONE_MASK));
Serial.println(F("Capture Done"));
length = myCAM.read_fifo_length();
Serial.print(F("The fifo length is :"));
Serial.println(length, DEC);
if (length >= MAX_FIFO_SIZE) //384K
{
Serial.println(F("Over size."));
return ;
}
if (length == 0 ) //0 kb
{
Serial.println(F("Size is 0."));
return ;
}
k = k + 1; //Construct a file name
itoa(k, str, 10);
strcat(str, ".jpg");
outFile = SD.open(str, O_WRITE | O_CREAT | O_TRUNC); //Open the new file
if (!outFile) {
Serial.println(F("File open failed"));
return;
}
myCAM.CS_LOW();
myCAM.set_fifo_burst();
while ( length-- )
{
temp_last = temp;
temp = SPI.transfer(0x00);
//Read JPEG data from FIFO
if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
{
buf[i++] = temp; //save the last 0XD9
//Write the remain bytes in the buffer
myCAM.CS_HIGH();
outFile.write(buf, i);
//Close the file
outFile.close();
Serial.print(str);
Serial.println(F(" Image save OK"));
is_header = false;
i = 0;
}
if (is_header == true)
{
//Write image data to buffer if not full
if (i < 256)
buf[i++] = temp;
else
{
//Write 256 bytes image data to file
myCAM.CS_HIGH();
outFile.write(buf, 256);
i = 0;
buf[i++] = temp;
myCAM.CS_LOW();
myCAM.set_fifo_burst();
}
}
else if ((temp == 0xD8) & (temp_last == 0xFF))
{
is_header = true;
buf[i++] = temp_last;
buf[i++] = temp;
}
}
}
void myCAMSaveToSDFile(char *ACfilename, uint8_t ACfilenamesize)
{
byte buf[256];
char ACfilename2[ACfilenamesize+1];
static uint16_t i = 0;
static uint16_t k = 0;
uint8_t temp = 0, temp_last = 0;
uint32_t length = 0;
bool is_header = false;
File dataFile;
memset(ACfilename, 0, ACfilenamesize); //fill ACfilename array with nulls.
myCAM.flush_fifo(); //Flush the FIFO
myCAM.clear_fifo_flag(); //Clear the capture done flag
myCAM.start_capture(); //Start capture
Serial.println(F("Start capture "));
while (!myCAM.get_bit(ARDUCHIP_TRIG , CAP_DONE_MASK));
Serial.println(F("Done"));
length = myCAM.read_fifo_length();
Serial.print(F("FIFO length "));
Serial.println(length, DEC);
if (length >= MAX_FIFO_SIZE) //384K
{
Serial.println(F("FIFO over size"));
return ;
}
if (length == 0 ) //0 kb
{
Serial.println(F("FIFO size is 0"));
return ;
}
//Construct a file name
k = k + 1;
itoa(k, ACfilename2, 10);
strcat(ACfilename2, ".jpg");
ACfilename[0] = '/';
memcpy(ACfilename+1, ACfilename2, ACfilenamesize);
//Serial.print(F("Adjusted filename "));
//Serial.println(ACfilename);
dataFile = SD.open(ACfilename, O_WRITE | O_CREAT | O_TRUNC); //Open the new file
if (!dataFile)
{
Serial.print(ACfilename);
Serial.println(F(" File open failed"));
return;
}
myCAM.CS_LOW();
myCAM.set_fifo_burst();
while ( length-- )
{
temp_last = temp;
temp = SPI.transfer(0x00);
//Read JPEG data from FIFO
if ( (temp == 0xD9) && (temp_last == 0xFF) )
{
buf[i++] = temp; //save the last 0XD9
myCAM.CS_HIGH();
dataFile.write(buf, i); //Write the remain bytes in the buffer
dataFile.close(); //Close the file
Serial.print(ACfilename);
Serial.println();
is_header = false;
i = 0;
}
if (is_header == true)
{
//Write image data to buffer if not full
if (i < 256)
buf[i++] = temp;
else
{
//Write 256 bytes image data to file
myCAM.CS_HIGH();
dataFile.write(buf, 256);
i = 0;
buf[i++] = temp;
myCAM.CS_LOW();
myCAM.set_fifo_burst();
}
}
else if ((temp == 0xD8) & (temp_last == 0xFF))
{
is_header = true;
buf[i++] = temp_last;
buf[i++] = temp;
}
}
}

View File

@ -0,0 +1,72 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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 on LoRa device
#define NRESET 9 //reset on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done
#define LED1 8 //On board LED, high for on
#define SDCS 30 //select pin for SD card
#define OV2640CS 22 //select for SPI on camera
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
#define Monitorport Serial //Port where serial prints go
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 = 750; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 750; //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 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 DTSendAttempts = 10; //number of attempts sending a packet before a restart
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 uint8_t Maxfilenamesize = 32; //size of DTfilename buffer
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 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
/******************************************************************************
Setup the resolution OV2640 required, the choices are;
OV2640_160x120,OV2640_176x144,OV2640_320x240,OV2640_352x288,OV2640_640x480,
OV2640_800x600, OV2640_1024x768,OV2640_1280x1024,OV2640_1600x1200
*******************************************************************************/
const uint8_t OV2640resolution = OV2640_640x480;

View File

@ -0,0 +1,142 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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 to receive images sent via LoRa using program
238_StuartCAM_LoRa_Remote_Camera which uses an OV2640 Arducam camera to take pictures.
The received images\files are saved onto an SD card. Arduino DUEs were used for this test.
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.
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,60 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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 on LoRa device
#define NRESET 9 //reset on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done
#define LED1 8 //On board LED, high for on
#define SDCS 30 //select pin for SD card
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
#define Monitorport Serial //Port where serial prints go
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 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
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,447 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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.
*******************************************************************************************************/
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-cam-take-photo-save-microsd-card
Github repository at https://github.com/RuiSantosdotme/ESP32-CAM-Arduino-IDE
IMPORTANT!!!
- Select Board "AI Thinker ESP32-CAM"
- GPIO 0 must be connected to GND to upload a sketch
- After connecting GPIO 0 to GND, press the ESP32-CAM on-board RESET button to put your board in flashing mode
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
/*******************************************************************************************************
Program Operation - This is a program using the ESP32CAM to take pictures and transmit those pictures via
LoRa radio to another remote Arduino.
This program is for an ESP32CAM board that has an SPI LoRa module set up on the following pins; NSS 12,
NRESET 14, SCK 4, MISO 13, MOSI 2, RFBUSY 15, 3.3V VCC and GND. All other pins on the SX128X are not
connected.
Note that the white LED on pin 4 or the transistor controlling it need to be removed so that the LoRa
device can properly use pin 4.
The program wakes up, takes a picture and starts the transfer of the picture (from its memory array in
PSRAM) with LoRa, more details of the file transfer process will be found here;
https://stuartsprojects.github.io/2021/09/20/Large-Data-Transfers-with-LoRa-Part3.html
Note that if the camera fails then the program will attempt to send, and wait for the acknowledge, for a
DTinfo packet reporting the fail.
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 <Arduino.h>
#include "FS.h" //SD Card ESP32
#include "SD_MMC.h" //SD Card ESP32
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h" //disable brownout problems
#include "driver/rtc_io.h"
#include <SPI.h>
#include <SX128XLT.h> //get library here > https://github.com/StuartsProjects/SX12XX-LoRa
#include <ProgramLT_Definitions.h>
#include "Settings.h" //LoRa and program settings
SX128XLT LoRa; //create a library class instance called LoRa, needed for ARtransferIRQ.h
#define ENABLEMONITOR //enable define to see progress messages in ARtransferIRQ.h
#define PRINTSEGMENTNUM //enable to print segment numbers as transfer progresses
#define ENABLEARRAYCRC //enable this define to use and show CRCs
//#define DISABLEPAYLOADCRC //enable this define if you want to not use packet payload CRC checking
//#define DEBUG //enable this define to show data transfer debug info
RTC_DATA_ATTR int16_t bootCount = 0; //variables to save in RTC ram
RTC_DATA_ATTR uint16_t sleepcount = 0;
RTC_DATA_ATTR uint16_t pictureNumber = 0; //number of picture taken, set to 0 on reset
#include "esp_camera.h"
camera_config_t config; //stores the camera configuration parameters
#include <ARtransferIRQ.h> //library of array transfer functions
bool SDOK;
void loop()
{
SDOK = false;
ARDTflags = 0;
if (initMicroSDCard()) //need to setup SD card before camera
{
Serial.println(F("SD Card OK"));
SDOK = true;
}
else
{
Serial.println(F("****************************"));
Serial.println(F("ERROR - SD Card Mount Failed"));
Serial.println(F("****************************"));
bitSet(ARDTflags, ARNoFileSave);
}
if (!configInitCamera())
{
bitSet(ARDTflags, ARNoCamera); //set flag bit for no camera working
Serial.println(F("Camera config failed"));
Serial.println(F("Sending DTInfo packet"));
setupLoRaDevice();
ARsendDTInfo();
startSleep();
}
else
{
if (takePhotoSend(PicturesToTake, PictureDelaymS))
{
//picture taken OK
startSleep();
}
else
{
//picture take failed
Serial.println("********************************");
Serial.println("ERROR - Take picture send failed");
Serial.println("********************************");
Serial.println();
Serial.println("Sending DTInfo packet");
Serial.flush();
bitSet(ARDTflags, ARNoCamera); //set flag bit for no camera working
setupLoRaDevice();
ARsendDTInfo();
startSleep();
}
}
startSleep();
}
void startSleep()
{
LoRa.setSleep(CONFIGURATION_RETENTION);
rtc_gpio_hold_en(GPIO_NUM_4);
rtc_gpio_hold_en(GPIO_NUM_12); //hold LoRa device off in sleep
esp_sleep_enable_timer_wakeup(SleepTimesecs * uS_TO_S_FACTOR);
Serial.print(F("Start Sleep "));
Serial.print(SleepTimesecs);
Serial.println(F("s"));
Serial.flush();
sleepcount++;
esp_deep_sleep_start();
Serial.println("This should never be printed !!!");
}
bool initMicroSDCard()
{
if (!SD_MMC.begin("/sdcard", true)) //use this line for 1 bit mode, pin 2 only, 4,12,13 not used
{
return false;
}
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE)
{
Serial.println(F("Unknown SD card type"));
return false;
}
return true;
}
void redFlash(uint16_t flashes, uint16_t ondelaymS, uint16_t offdelaymS)
{
uint16_t index;
pinMode(REDLED, OUTPUT); //setup pin as output
for (index = 1; index <= flashes; index++)
{
digitalWrite(REDLED, LOW);
delay(ondelaymS);
digitalWrite(REDLED, HIGH);
delay(offdelaymS);
}
pinMode(REDLED, INPUT); //setup pin as input
}
bool setupLoRaDevice()
{
SPI.begin(SCK, MISO, MOSI, NSS);
if (LoRa.begin(NSS, NRESET, RFBUSY, LORA_DEVICE))
{
Serial.println(F("LoRa device found"));
}
else
{
Serial.println(F("LoRa Device error"));
return false;
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Monitorport.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Monitorport.println(F("Using FLRC packets"));
#endif
Serial.println();
return true;
}
void setup()
{
redFlash(4, 125, 125);
//WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
rtc_gpio_hold_dis(GPIO_NUM_4);
rtc_gpio_hold_dis(GPIO_NUM_12); //LoRa NSS back to normal control after sleep
pinMode(2, INPUT_PULLUP);
digitalWrite(NSS, HIGH);
pinMode(NSS, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println(F(__FILE__));
if (bootCount == 0) //run this only the first time after programming or power up
{
bootCount = bootCount + 1;
}
Serial.println(F("Awake !"));
Serial.print(F("Bootcount "));
Serial.println(bootCount);
Serial.print(F("Sleepcount "));
Serial.println(sleepcount);
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Serial.println(F("Payload CRC disabled"));
}
else
{
Serial.println(F("Payload CRC enabled"));
}
}
//***********************************************************************************************
// Start camera Code
//***********************************************************************************************
bool configInitCamera()
{
Serial.println(F("Initialising the camera module "));
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG; //YUV422,GRAYSCALE,RGB565,JPEG
//Select lower framesize if the camera doesn't support PSRAM
if (psramFound())
{
Serial.println(F("PSRAM found"));
config.frame_size = FRAMESIZE_SVGA; //FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA, XUGA == 100K+, SVGA = 25K+
config.jpeg_quality = 10; //10-63 lower number means higher quality
config.fb_count = 2;
}
else
{
Serial.println(F("No PSRAM"));
config.frame_size = FRAMESIZE_XGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
esp_err_t err = esp_camera_init(&config); //Initialize the Camera
if (err != ESP_OK)
{
Serial.printf("Camera init failed with error 0x%x", err);
Serial.println();
return false;
}
sensor_t * s = esp_camera_sensor_get();
s->set_brightness(s, 0); // -2 to 2
s->set_contrast(s, 0); // -2 to 2
s->set_saturation(s, 0); // -2 to 2
s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
s->set_whitebal(s, 1); // 0 = disable , 1 = enable
s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
s->set_wb_mode(s, 1); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
s->set_aec2(s, 1); // 0 = disable , 1 = enable
s->set_ae_level(s, 0); // -2 to 2
s->set_aec_value(s, 450); // 0 to 1200
s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable
s->set_agc_gain(s, 0); // 0 to 30
s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6
s->set_bpc(s, 1); // 0 = disable , 1 = enable
s->set_wpc(s, 0); // 0 = disable , 1 = enable
s->set_raw_gma(s, 1); // 0 = disable , 1 = enable
s->set_lenc(s, 0); // 0 = disable , 1 = enable
s->set_hmirror(s, 0); // 0 = disable , 1 = enable
s->set_vflip(s, 0); // 0 = disable , 1 = enable
s->set_dcw(s, 1); // 0 = disable , 1 = enable
s->set_colorbar(s, 0); // 0 = disable , 1 = enable
return true;
}
uint16_t takePhotoSend(uint8_t num, uint32_t gapmS)
{
uint8_t index = 1;
char filenamearray[32];
bool sentOK = false;
String path;
camera_fb_t * fb = esp_camera_fb_get();
for (index = 1; index <= num; index++) //take a number of pictures, send last
{
pictureNumber++;
path = "/pic" + String(pictureNumber) + ".jpg";
Serial.print("Next picture file name ");
Serial.println(path.c_str());
if (!fb)
{
Serial.println(F("*****************************"));
Serial.println(F("ERROR - Camera capture failed"));
Serial.println(F("*****************************"));
delay(1000);
pictureNumber--; //restore picture number
bitSet(ARDTflags, ARNoFileSave);
}
Serial.println(F("Camera capture success"));
#ifdef DEBUG
Serial.print(F("First 8 bytes "));
printarrayHEX(fb->buf, 0, 8);
Serial.println();
Serial.print(F("Last 8 bytes "));
printarrayHEX(fb->buf, (fb->len - 8), 8);
Serial.println();
#endif
if (SDOK)
{
Serial.println(F("Save picture to SD card"));
fs::FS &fs = SD_MMC; //save picture to microSD card
File file = fs.open(path.c_str(), FILE_WRITE);
if (!file)
{
Serial.println(F("*********************************************"));
Serial.println(F("ERROR Failed to open SD file in writing mode"));
Serial.println(F("*********************************************"));
bitSet(ARDTflags, ARNoFileSave);
}
else
{
file.write(fb->buf, fb->len); // payload (image), payload length
Serial.printf("Saved file to path: %s\r\n", path.c_str());
}
file.close();
}
else
{
Serial.println(F("***************"));
Serial.println(F("No SD available"));
Serial.println(F("***************"));
}
}
SD_MMC.end();
if (setupLoRaDevice())
{
Serial.print(F("Send with LoRa "));
Serial.println(path.c_str());
uint8_t tempsize = path.length();
path.toCharArray(filenamearray, tempsize + 1); //copy file name to the local filenamearray
filenamearray[tempsize + 1] = 0; //ensure there is a null at end of filename in filenamearray
sentOK = ARsendArray(fb->buf, fb->len, filenamearray, tempsize + 1); //pass array pointer and length across to LoRa send function
}
else
{
Serial.println(F("LoRa device not available"));
}
esp_camera_fb_return(fb); //return the frame buffer back to the driver for reuse
delay(gapmS);
if (sentOK)
{
Serial.print(filenamearray);
Serial.println(F(" Sent OK"));
return pictureNumber;
}
else
{
Serial.print(filenamearray);
Serial.println(F(" Send picture failed"));
}
return 0;
}
void printarrayHEX(uint8_t *buff, uint32_t startaddr, uint32_t len)
{
uint32_t index;
uint8_t buffdata;
for (index = startaddr; index < (startaddr + len); index++)
{
buffdata = buff[index];
if (buffdata < 16)
{
Serial.print(F("0"));
}
Serial.print(buffdata, HEX);
Serial.print(F(" "));
}
}

View File

@ -0,0 +1,92 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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.
*******************************************************************************************************/
//updated pinouts for 'ESP32CAM_Long_Range_Wireless_Adapter PCB dated 271121
//Note transistor driving the White LED on pin 4, or the LED needs to be removed
#define NSS 12 //select on LoRa device
#define NRESET 14 //reset pin on LoRa device
#define RFBUSY 15 //busy pin on LoRa device
#define SCK 4 //SCK on SPI3
#define MISO 13 //MISO on SPI3
#define MOSI 2 //MOSI on SPI3
#define REDLED 33 //pin number for ESP32CAM on board red LED, set logic level low for on
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
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 ACKdelaystartendmS = 25; //ms delay before ack sent at array start wrie and end write
const uint32_t ACKsegtimeoutmS = 75; //mS to wait for receiving an ACK before re-trying transmit segment
const uint32_t ACKopentimeoutmS = 500; //mS to wait for receiving an ACK before re-trying transmit file open
const uint32_t ACKclosetimeoutmS = 500; //mS to wait for receiving an ACK before re-trying transmit file close
const uint32_t DuplicatedelaymS = 25; //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 DTSegmentSize = 245; //number of bytes in each segment or payload
const uint8_t ARDTfilenamesize = 32; //size of filename buffer
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 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 uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
const uint16_t SleepTimesecs = 15; //sleep time in seconds after each TX loop
const uint32_t uS_TO_S_FACTOR = 1000000; //Conversion factor for micro seconds to seconds
const uint8_t PicturesToTake = 1; //number of pictures to take at each wakeup, only last is sent via LoRa
const uint32_t PictureDelaymS = 1000; //delay in mS between pictures
#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
// Pin definition for CAMERA_MODEL_AI_THINKER
// Change pin definition if you're using another ESP32 with camera module
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22

View File

@ -0,0 +1,222 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 20/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 receiver program for an ESP32CAM board that has an SPI LoRa module set up
on the following pins; NSS 12, NRESET 14, SCK 4, MISO 13, MOSI 2, RFBUSY 15, 3.3V VCC and GND. All other
pins on the SX128X are not connected. The received pictures are saved to the ESP32CAMs SD card.
Note that the white LED on pin 4 or the transistor controlling it need to be removed so that the LoRa
device can properly use pin 4.
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 "FS.h" //SD Card ESP32
#include "SD_MMC.h" //SD Card ESP32
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h" //disable brownout problems
#include "driver/rtc_io.h"
#include <SX128XLT.h> //SX12XX-LoRa library
#include <ProgramLT_Definitions.h> //part of SX12XX-LoRa library
#include "Settings.h" //LoRa settings etc.
#define ENABLEMONITOR //enable this define to monitor data transfer information, needed for ARtransferIRQ.h
#define ENABLEARRAYCRC //enable this define to check and print CRC of sent array
#define PRINTSEGMENTNUM //enable this define to print segment numbers during data transfer
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
//#define DEBUG //enable more detail of transfer progress
SX128XLT LoRa; //create an SX128XLT library instance called LoRa
#include <ARtransferIRQ.h>
uint8_t *PSRAMptr; //create a global pointer to the array to send, so all functions have access
bool SDOK;
bool savedtoSDOK;
void loop()
{
uint32_t arraylength;
SDOK = false;
Serial.println(F("LoRa file transfer receiver ready"));
setupLoRaDevice();
//if there is a successful array transfer the returned length > 0
arraylength = ARreceiveArray(PSRAMptr, sizeof(ARDTarraysize), ReceiveTimeoutmS);
SPI.end();
digitalWrite(NSS, HIGH);
digitalWrite(NRESET, HIGH);
if (arraylength)
{
Serial.print(F("Returned picture length "));
Serial.println(arraylength);
if (initMicroSDCard())
{
SDOK = true;
Serial.println("SD Card OK");
Serial.print(ARDTfilenamebuff);
Serial.println(F(" Save picture to SD card"));
fs::FS &fs = SD_MMC; //save picture to microSD card
File file = fs.open(ARDTfilenamebuff, FILE_WRITE);
if (!file)
{
Serial.println("*********************************************");
Serial.println("ERROR Failed to open SD file in writing mode");
Serial.println("*********************************************");
savedtoSDOK = false;
}
else
{
file.write(PSRAMptr, arraylength); // pointer to array and length
Serial.print(ARDTfilenamebuff);
Serial.println(" Saved to SD");
savedtoSDOK = true;
}
file.close();
SD_MMC.end();
}
else
{
Serial.println("No SD available");
}
}
else
{
Serial.println(F("Error receiving picture"));
if (ARDTArrayTimeout)
{
Serial.println(F("Timeout receiving picture"));
}
}
Serial.println();
}
bool setupLoRaDevice()
{
SPI.begin(SCK, MISO, MOSI, NSS);
if (LoRa.begin(NSS, NRESET, RFBUSY, LORA_DEVICE))
{
Serial.println(F("LoRa device found"));
}
else
{
Serial.println(F("LoRa Device error"));
return false;
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Monitorport.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Monitorport.println(F("Using FLRC packets"));
#endif
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Serial.println(F("Payload CRC disabled"));
}
else
{
Serial.println(F("Payload CRC enabled"));
}
return true;
}
bool initMicroSDCard()
{
if (!SD_MMC.begin("/sdcard", true)) //use this line for 1 bit mode, pin 2 only, 4,12,13 not used
{
Serial.println("*****************************");
Serial.println("ERROR - SD Card Mount Failed");
Serial.println("*****************************");
return false;
}
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE)
{
Serial.println("No SD Card found");
return false;
}
return true;
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(REDLED, HIGH);
delay(delaymS);
digitalWrite(REDLED, LOW);
delay(delaymS);
}
}
void setup()
{
uint32_t available_PSRAM_size;
uint32_t new_available_PSRAM_size;
//WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
pinMode(REDLED, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
ARsetDTLED(REDLED); //setup LED pin for data transfer indicator
digitalWrite(NSS, HIGH);
pinMode(NSS, OUTPUT); //disable LoRa device for now
Serial.begin(115200); //format is Serial.begin(baud-rate, protocol, RX pin, TX pin);
Serial.println();
Serial.println(__FILE__);
if (psramInit())
{
Serial.println("PSRAM is correctly initialised");
available_PSRAM_size = ESP.getFreePsram();
Serial.println((String)"PSRAM Size available: " + available_PSRAM_size);
}
else
{
Serial.println("PSRAM not available");
while (1);
}
Serial.println("Allocate array in PSRAM");
uint8_t *byte_array = (uint8_t *) ps_malloc(ARDTarraysize * sizeof(uint8_t));
PSRAMptr = byte_array; //save the pointe to byte_array to global pointer
new_available_PSRAM_size = ESP.getFreePsram();
Serial.println((String)"PSRAM Size available: " + new_available_PSRAM_size);
Serial.print("PSRAM array bytes allocated: ");
Serial.println(available_PSRAM_size - new_available_PSRAM_size);
Serial.println();
}

View File

@ -0,0 +1,63 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 20/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 12 //select on LoRa device
#define NRESET 14 //reset pin on LoRa device
#define RFBUSY 15 //busy pin on LoRa device
#define SCK 4 //SCK on SPI3
#define MISO 13 //MISO on SPI3
#define MOSI 2 //MOSI on SPI3
#define REDLED 33 //pin number for ESP32CAM on board red LED, set logic level low for on
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
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 = 3000000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after general packet actioned and ack sent
const uint32_t ACKdelaystartendmS = 25; //ms delay before ack sent at array start wrie and end write
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 = 25; //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 uint32_t ReceiveTimeoutmS = 60000; //mS waiting for array transfer before timeout
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 6 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t ARDTfilenamesize = 32; //size of DTfilename buffer used by array transfer functions
const uint32_t ARDTarraysize = 0x20000; //maximum file\array size to receive
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
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
#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,253 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 20/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 receiver program for an ESP32CAM board that has an SPI LoRa module set up
on the following pins; NSS 12, NRESET 14, SCK 4, MISO 13, MOSI 2, RFBUSY 15, 3.3V VCC and GND. All other
pins on the SX128X are not connected. The received pictures are saved to the ESP32CAMs SD card and also
transferred to a connected PC using the YModem protocol over the normal program upload port. Progress
or debug messages can be seen by connection an additional serial adapter to pin 33 on the ESP32CAM.
For details of the PC upload process see here;
https://stuartsprojects.github.io/2022/02/05/Long-Range-Wireless-Adapter-for-ESP32CAM.html
Note that the white LED on pin 4 or the transistor controlling it need to be removed so that the LoRa
device can properly use pin 4.
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 "FS.h" //SD Card ESP32
#include "SD_MMC.h" //SD Card ESP32
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h" //disable brownout problems
#include "driver/rtc_io.h"
#include <SX128XLT.h> //SX12XX-LoRa library
#include <ProgramLT_Definitions.h> //part of SX12XX-LoRa library
#include "Settings.h" //LoRa settings etc.
#define ENABLEMONITOR //enable this define to monitor data transfer information, needed for ARtransferIRQ.h
#define ENABLEARRAYCRC //enable this define to check and print CRC of sent array
#define PRINTSEGMENTNUM //enable this define to print segment numbers during data transfer
//#define DISABLEPAYLOADCRC //enable this define if you want to disable payload CRC checking
//#define DEBUG //enable more detail of transfer progress
SX128XLT LoRa; //create an SX128XLT library instance called LoRa
#include <ARtransferIRQ.h>
uint8_t *PSRAMptr; //create a global pointer to the array to send, so all functions have access
bool SDOK;
bool savedtoSDOK;
#include "YModemArray.h"
void loop()
{
uint32_t arraylength, bytestransfered;
SDOK = false;
Monitorport.println(F("LoRa file transfer receiver ready"));
setupLoRaDevice();
//if there is a successful array transfer the returned length > 0
//arraylength = LocalARreceiveArray(PSRAMptr, sizeof(ARDTarraysize), ReceiveTimeoutmS);
arraylength = ARreceiveArray(PSRAMptr, sizeof(ARDTarraysize), ReceiveTimeoutmS);
SPI.end();
digitalWrite(NSS, HIGH);
digitalWrite(NRESET, HIGH);
if (arraylength)
{
Monitorport.print(F("Returned picture length "));
Monitorport.println(arraylength);
if (initMicroSDCard())
{
SDOK = true;
Monitorport.println("SD Card OK");
Monitorport.print(ARDTfilenamebuff);
Monitorport.println(F(" Save picture to SD card"));
fs::FS &fs = SD_MMC; //save picture to microSD card
File file = fs.open(ARDTfilenamebuff, FILE_WRITE);
if (!file)
{
Monitorport.println("*********************************************");
Monitorport.println("ERROR Failed to open SD file in writing mode");
Monitorport.println("*********************************************");
savedtoSDOK = false;
}
else
{
file.write(PSRAMptr, arraylength); // pointer to array and length
Monitorport.print(ARDTfilenamebuff);
Monitorport.println(" Saved to SD");
savedtoSDOK = true;
}
file.close();
SD_MMC.end();
}
else
{
Monitorport.println("No SD available");
}
}
else
{
Monitorport.println(F("Error receiving picture"));
if (ARDTArrayTimeout)
{
Monitorport.println(F("Timeout receiving picture"));
}
}
Monitorport.println();
if (arraylength)
{
Monitorport.println(F("File received - start YModem transfer to PC"));
//bytestransfered = yModemSend(ARDTfilenamebuff, 1, 1);
bytestransfered = yModemSend(ARDTfilenamebuff, PSRAMptr, arraylength, 1, 1);
if (bytestransfered > 0)
{
Monitorport.print(F("YModem transfer completed "));
Monitorport.print(bytestransfered);
Monitorport.println(F(" bytes sent"));
}
else
{
Monitorport.println(F("YModem transfer FAILED"));
}
Monitorport.println();
}
}
bool setupLoRaDevice()
{
SPI.begin(SCK, MISO, MOSI, NSS);
if (LoRa.begin(NSS, NRESET, RFBUSY, LORA_DEVICE))
{
Monitorport.println(F("LoRa device found"));
}
else
{
Monitorport.println(F("LoRa Device error"));
return false;
}
#ifdef USELORA
LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
Monitorport.println(F("Using LoRa packets"));
#endif
#ifdef USEFLRC
LoRa.setupFLRC(Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
Monitorport.println(F("Using FLRC packets"));
#endif
#ifdef DISABLEPAYLOADCRC
LoRa.setReliableConfig(NoReliableCRC);
#endif
if (LoRa.getReliableConfig(NoReliableCRC))
{
Monitorport.println(F("Payload CRC disabled"));
}
else
{
Monitorport.println(F("Payload CRC enabled"));
}
return true;
}
bool initMicroSDCard()
{
if (!SD_MMC.begin("/sdcard", true)) //use this line for 1 bit mode, pin 2 only, 4,12,13 not used
{
Monitorport.println("*****************************");
Monitorport.println("ERROR - SD Card Mount Failed");
Monitorport.println("*****************************");
return false;
}
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE)
{
Monitorport.println("No SD Card found");
return false;
}
return true;
}
void led_Flash(uint16_t flashes, uint16_t delaymS)
{
uint16_t index;
for (index = 1; index <= flashes; index++)
{
digitalWrite(REDLED, HIGH);
delay(delaymS);
digitalWrite(REDLED, LOW);
delay(delaymS);
}
}
void setup()
{
uint32_t available_PSRAM_size;
uint32_t new_available_PSRAM_size;
//WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
pinMode(REDLED, OUTPUT); //setup pin as output for indicator LED
led_Flash(2, 125); //two quick LED flashes to indicate program start
ARsetDTLED(REDLED); //setup LED pin for data transfer indicator
digitalWrite(NSS, HIGH);
pinMode(NSS, OUTPUT); //disable LoRa device for now
YModemSerial.begin(115200);
Monitorport.begin(115200, SERIAL_8N1, RXD2, TXD2); //monitor port, format is Monitorport.begin(baud-rate, protocol, RX pin, TX pin);
Monitorport.println();
Monitorport.println(__FILE__);
if (psramInit())
{
Monitorport.println("PSRAM is correctly initialised");
available_PSRAM_size = ESP.getFreePsram();
Monitorport.println((String)"PSRAM Size available: " + available_PSRAM_size);
}
else
{
Monitorport.println("PSRAM not available");
while (1);
}
Monitorport.println("Allocate array in PSRAM");
uint8_t *byte_array = (uint8_t *) ps_malloc(ARDTarraysize * sizeof(uint8_t));
PSRAMptr = byte_array; //save the pointe to byte_array to global pointer
new_available_PSRAM_size = ESP.getFreePsram();
Monitorport.println((String)"PSRAM Size available: " + new_available_PSRAM_size);
Monitorport.print("PSRAM array bytes allocated: ");
Monitorport.println(available_PSRAM_size - new_available_PSRAM_size);
Monitorport.println();
}

View File

@ -0,0 +1,69 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 20/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 12 //select on LoRa device
#define NRESET 14 //reset pin on LoRa device
#define RFBUSY 15 //busy pin on LoRa device
#define SCK 4 //SCK on SPI3
#define MISO 13 //MISO on SPI3
#define MOSI 2 //MOSI on SPI3
#define REDLED 33 //pin number for ESP32CAM on board red LED, set logic level low for on
#define RXD2 40 //RX pin for monitor port, not used
#define TXD2 33 //TX pin for monitor port
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
#define YModemSerial Serial
#define Monitorport Serial2
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 = 3000000; //mS to wait for receiving a packet
const uint32_t ACKdelaymS = 0; //ms delay after general packet actioned and ack sent
const uint32_t ACKdelaystartendmS = 25; //ms delay before ack sent at array start wrie and end write
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 = 25; //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 uint32_t ReceiveTimeoutmS = 60000; //mS waiting for array transfer before timeout
const uint8_t HeaderSizeMax = 12; //max size of header in bytes, minimum size is 6 bytes
const uint8_t DataSizeMax = 245; //max size of data array in bytes
const uint8_t ARDTfilenamesize = 32; //size of DTfilename buffer used by array transfer functions
const uint32_t ARDTarraysize = 0x20000; //maximum file\array size to receive
const uint16_t NetworkID = 0x3210; //a unique identifier to go out with packet
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
#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,280 @@
// 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
//Revised code 02/01/22, changed for ESP32;
//sprintf(spfBuff, "%ld", numBytesStillToSend); to sprintf(spfBuff, "%lu", numBytesStillToSend);
//uint32_t numBytesStillToSend = 0; to long unsigned int numBytesStillToSend = 0;
//numBytesThisPacket = min( numBytesStillToSend, sizeof(yPacket.payload)); to numBytesThisPacket = min( (uint32_t) numBytesStillToSend, sizeof(yPacket.payload));
//Revised code 05/01/22, changed;
//Organised YModem transfer to operate from previously filled memor array rather then and SD file
//Revised code 28/01/22 to stop transfer hanging if PC receiving YModem stops responding
#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 uint32_t yModemSend(const char *filename, uint8_t *arraydata, uint32_t arraysize, int waitForReceiver, int batchMode )
{
//*filename is pointer to filename
//*arraydata is pointer to the transferred array
//arraysize is the lengrth of data to transfer
//uint32_t numBytesStillToSend = 0;
long unsigned int 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 index;
uint32_t arraybytescopied = 0;
uint32_t startmS;
uint8_t ackerrors = 0;
if (arraysize > ARDTarraysize)
{
Monitorport.println("Array too big");
return 0;
}
// get the size of the file and convert to an ASCII representation for header packet
numBytesStillToSend = arraysize;
sprintf(spfBuff, "%lu", numBytesStillToSend);
// wait here for the receiving device to respond
if (waitForReceiver)
{
Monitorport.println("Waiting for receiver ping ...");
while ( YModemSerial.available() ) YModemSerial.read();
startmS = millis();
do
{
if (YModemSerial.available()) answer = YModemSerial.read();
}
while ((answer != 'C') && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
}
if (answer != 'C')
{
Monitorport.println("Timeout starting YModem transfer");
return 0;
}
else
{
Monitorport.println("done");
}
Monitorport.print("Ymodem Sending ");
Monitorport.println(filename);
Monitorport.print(spfBuff);
Monitorport.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 ); //spfBuff is file size ?
// 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( (uint32_t) numBytesStillToSend, sizeof(yPacket.payload));
//now fill yPacket.payload with numBytesThisPacket from arraydata
for (index = 0; index < numBytesThisPacket; index++)
{
yPacket.payload[index] = arraydata[arraybytescopied];
arraybytescopied++;
}
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
YModemSerial.write( (uint8_t*) &yPacket, sizeof(yPacket));
// wait for the receiver to send back a response to the packet
startmS = millis();
while ((!YModemSerial.available()) && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if (!YModemSerial.available())
{
Monitorport.println("Timeout waiting YModem response");
return 0;
}
answer = YModemSerial.read();
switch (answer)
{
case X_NAK:
//something went wrong - send the same packet again?
Monitorport.print("N");
ackerrors++;
break;
case X_ACK:
//got ACK to move to the next block of data
Monitorport.print(".");
doNextBlock = 1;
break;
default:
//unknown response
Monitorport.print("?");
ackerrors++;
break;
}
if (ackerrors >= ackerrorlimit)
{
Monitorport.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 ((!YModemSerial.available()) && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if (!YModemSerial.available())
{
Monitorport.println("Timeout waiting YModem response");
return 0;
}
answer = YModemSerial.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
YModemSerial.write(X_EOT);
// need to send EOT again for YMODEM
startmS = millis();
while ((!YModemSerial.available()) && ((uint32_t) (millis() - startmS) < transfertimeoutmS));
if (!YModemSerial.available())
{
Monitorport.println("Timeout waiting YModem response");
return 0;
}
answer = YModemSerial.read();
YModemSerial.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)));
YModemSerial.write( (uint8_t*)&yPacket, sizeof(yPacket));
}
Monitorport.println("done");
return arraybytescopied;
}

View File

@ -0,0 +1,200 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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 for receiving files transmitted by LoRa and saving them to SD card
and then optionally transfering them to a PC using a second serial port and the Ymodem protocol. Arduino
DUEs were used for testing the program, these have multiple hardware Serial ports.
Progress messages on the transfer are sent to the IDE Serial monitor and printed on a connected ILI9341
TFT display.
For details of the PC upload process see here;
https://stuartsprojects.github.io/2022/01/01/StuartCAM-ESP32CAM-Getting-the-Pictures-Onto-a-PC.html
Serial monitor baud rate is set at 115200.
*******************************************************************************************************/
#include <SPI.h>
#include <SX128XLT.h>
#include <ProgramLT_Definitions.h>
#define USELORA //enable this define to use LoRa packets
//#define USEFLRC //enable this define to use FLRC packets
#include "Settings.h" //LoRa settings etc.
SX128XLT LoRa; //create an SX128XLT library instance called LoRa, required by SDtransfer
//#define SDLIB //define SDLIB for SD.h or SDFATLIB for SDfat.h
#define SDFATLIB
#include "DTSDlibrary.h"
#include "Adafruit_GFX.h" //get library here > https://github.com/adafruit/Adafruit-GFX-Library
#include "Adafruit_ILI9341.h" //get library here > https://github.com/adafruit/Adafruit_ILI9341
Adafruit_ILI9341 disp = Adafruit_ILI9341(DISPCS, DISPDC, DISPRESET); //for dispaly defaults, textsize and rotation, see Settings.h
#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
#define ENABLEPCTRANSFER //enable this define for YModem transfer to PC
#include "SDtransferDisplay.h" //library of data transfer with display functions
#include "YModem.h" //YModem for the the save SD File
uint32_t bytestransfered; //bytes transfered via YModem
void loop()
{
SDreceiveaPacketDT();
#ifdef ENABLEPCTRANSFER
if (SDDTFileSaved)
{
Monitorport.println(F("File saved to SD - start YModem transfer to PC"));
setCursor(0, 4);
disp.print(F("Run YModem"));
digitalWrite(LED1, HIGH);
setCursor(0, 6);
disp.print(F(" ")); //clear segment number
bytestransfered = yModemSend(SDDTfilenamebuff, 1, 1);
if (bytestransfered > 0)
{
Monitorport.print(F("YModem transfer completed "));
Monitorport.print(bytestransfered);
Monitorport.println(F(" bytes sent"));
}
else
{
Monitorport.println(F("YModem transfer FAILED"));
}
Monitorport.println();
setCursor(0, 4);
disp.print(F(" "));
digitalWrite(LED1, LOW);
SDDTFileSaved = false;
//Monitorport.println(F("YModem transfer finished"));
//Monitorport.println();
Monitorport.println(F("Wait for file"));
}
#endif
}
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
YModemSerial.begin(115200);
#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
}
disp.begin();
disp.fillScreen(ILI9341_BLACK);
disp.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
disp.setRotation(rotation);
disp.setTextSize(textscale);
setCursor(0, 0);
disp.print(F("Waiting File"));
SDDTSegmentNext = 0;
SDDTFileOpened = false;
SDDTFileSaved = false; //file has not been saved to SD yet
#ifdef ENABLEMONITOR
Monitorport.println(F("SDfile transfer receiver ready"));
Monitorport.println();
#endif
}

View File

@ -0,0 +1,461 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/22
This code 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.
*******************************************************************************************************/
#ifdef SDFATLIB
#include <SdFat.h>
SdFat SD;
File dataFile; //name the file instance needed for SD library routines
#endif
#ifdef SDLIB
#include <SD.h>
File dataFile; //name the file instance needed for SD library routines
#endif
#ifndef Monitorport
#define Monitorport Serial //output to Serial if no other port defined
#endif
bool DTSD_dumpFileASCII(char *buff);
bool DTSD_dumpFileHEX(char *buff);
bool DTSD_dumpSegmentHEX(uint8_t segmentsize);
bool DTSD_initSD(uint8_t CSpin);
uint32_t DTSD_getFileSize(char *buff);
void DTSD_printDirectory();
uint32_t DTSD_openFileRead(char *buff);
uint16_t DTSD_getNumberSegments(uint32_t filesize, uint8_t segmentsize);
uint8_t DTSD_getLastSegmentSize(uint32_t filesize, uint8_t segmentsize);
bool DTSD_openNewFileWrite(char *buff);
bool DTSD_openFileWrite(char *buff, uint32_t position);
uint8_t DTSD_readFileSegment(uint8_t *buff, uint8_t segmentsize);
uint8_t DTSD_writeSegmentFile(uint8_t *buff, uint8_t segmentsize);
void DTSD_seekFileLocation(uint32_t position);
uint16_t DTSD_createFile(char *buff);
uint16_t DTSD_fileCRCCCITT();
void DTSD_fileFlush();
void DTSD_closeFile();
void printDirectorySD(File dir, int numTabs);
bool DTSD_dumpFileASCII(char *buff)
{
File dataFile = SD.open(buff); //open the test file note that only one file can be open at a time,
if (dataFile) //if the file is available, read from it
{
while (dataFile.available())
{
Monitorport.write(dataFile.read());
}
dataFile.close();
return true;
}
else
{
return false;
}
}
bool DTSD_dumpFileHEX(char *buff)
{
//Note, this function will return true if the SD card is removed.
uint16_t Loopv1, Loopv2;
uint8_t fileData;
uint32_t filesize;
if (!SD.exists(buff))
{
return false;
}
dataFile = SD.open(buff);
filesize = dataFile.size();
filesize--; //file data locations are from 0 to (filesize -1);
Monitorport.print(F("Lcn 0 1 2 3 4 5 6 7 8 9 A B C D E F"));
Monitorport.println();
if (dataFile) //if the file is available, read from it
{
while (dataFile.available())
{
for (Loopv1 = 0; Loopv1 <= filesize;)
{
Monitorport.print(F("0x"));
if (Loopv1 < 0x10)
{
Monitorport.print(F("0"));
}
Monitorport.print((Loopv1), HEX);
Monitorport.print(F(" "));
for (Loopv2 = 0; Loopv2 <= 15; Loopv2++)
{
fileData = dataFile.read();
if (fileData < 0x10)
{
Monitorport.print(F("0"));
}
Monitorport.print(fileData, HEX);
Monitorport.print(F(" "));
Loopv1++;
}
Monitorport.println();
}
}
dataFile.close();
return true;
}
else
{
Monitorport.println(F("File not available"));
return false;
}
}
bool DTSD_initSD(uint8_t CSpin)
{
if (SD.begin(CSpin))
{
return true;
}
else
{
return false;
}
}
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;
}
#ifdef SDFATLIB
void DTSD_printDirectory()
{
dataFile = SD.open("/");
Monitorport.println(F("Card directory"));
SD.ls("/", LS_R);
}
#endif
#ifdef SDLIB
void DTSD_printDirectory()
{
dataFile = SD.open("/");
printDirectorySD(dataFile, 0);
Monitorport.println();
}
void printDirectorySD(File dir, int numTabs)
{
while (true)
{
File entry = dir.openNextFile();
if (! entry)
{
//no more files
break;
}
for (uint8_t i = 0; i < numTabs; i++)
{
Monitorport.print('\t');
}
Monitorport.print(entry.name());
if (entry.isDirectory())
{
Monitorport.println("/");
printDirectorySD(entry, numTabs + 1);
}
else
{
// files have sizes, directories do not
Monitorport.print("\t\t");
Monitorport.println(entry.size(), DEC);
}
entry.close();
}
}
#endif
bool DTSD_dumpSegmentHEX(uint8_t segmentsize)
{
uint16_t Loopv1, Loopv2;
uint8_t fileData;
Monitorport.print(F("Print segment of "));
Monitorport.print(segmentsize);
Monitorport.println(F(" bytes"));
Monitorport.print(F("Lcn 0 1 2 3 4 5 6 7 8 9 A B C D E F"));
Monitorport.println();
if (dataFile) //if the file is available, read from it
{
for (Loopv1 = 0; Loopv1 < segmentsize;)
{
Monitorport.print(F("0x"));
if (Loopv1 < 0x10)
{
Monitorport.print(F("0"));
}
Monitorport.print((Loopv1), HEX);
Monitorport.print(F(" "));
for (Loopv2 = 0; Loopv2 <= 15; Loopv2++)
{
//stop printing if all of segment has been printed
if (Loopv1 < segmentsize)
{
fileData = dataFile.read();
if (fileData < 0x10)
{
Monitorport.print(F("0"));
}
Monitorport.print(fileData, HEX);
Monitorport.print(F(" "));
Loopv1++;
}
}
Monitorport.println();
}
return true;
}
else
{
return false;
}
}
uint32_t DTSD_openFileRead2(char *buff)
{
uint32_t filesize;
dataFile = SD.open(buff);
filesize = dataFile.size();
dataFile.seek(0);
return filesize;
}
uint32_t DTSD_openFileRead(char *buff)
{
uint32_t filesize;
if (SD.exists(buff))
{
//Monitorport.println(F("File exists"));
dataFile = SD.open(buff);
filesize = dataFile.size();
dataFile.seek(0);
return filesize;
}
else
{
//Monitorport.println(F("File does not exist"));
return 0;
}
}
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;
}
bool DTSD_openNewFileWrite(char *buff)
{
if (SD.exists(buff))
{
//Monitorport.print(buff);
//Monitorport.println(F(" File exists - deleting"));
SD.remove(buff);
}
if (dataFile = SD.open(buff, FILE_WRITE))
{
//Monitorport.print(buff);
//Monitorport.println(F(" SD File opened"));
return true;
}
else
{
//Monitorport.print(buff);
//Monitorport.println(F(" ERROR opening file"));
return false;
}
}
bool DTSD_openFileWrite(char *buff, uint32_t position)
{
dataFile = SD.open(buff, FILE_WRITE); //seems to operate as append
dataFile.seek(position); //seek to first position in file
if (dataFile)
{
return true;
}
else
{
return false;
}
}
uint8_t DTSD_readFileSegment(uint8_t *buff, uint8_t segmentsize)
{
uint8_t index = 0;
uint8_t fileData;
while (index < segmentsize)
{
fileData = (uint8_t) dataFile.read();
buff[index] = fileData;
index++;
};
if (index == segmentsize)
{
return segmentsize; //if all written return segment size
}
else
{
return index - 1; //if not all written return number bytes read
}
}
uint8_t DTSD_writeSegmentFile(uint8_t *buff, uint8_t segmentsize)
{
uint8_t index, byteswritten = 0;
for (index = 0; index < segmentsize; index++)
{
dataFile.write(buff[index]);
byteswritten++;
}
return byteswritten;
}
void DTSD_seekFileLocation(uint32_t position)
{
dataFile.seek(position); //seek to position in file
return;
}
uint16_t DTSD_createFile(char *buff)
{
//creats a new filename use this definition as the base;
//char filename[] = "/SD0000.txt"; //filename used as base for creating logfile, 0000 replaced with numbers
//the 0000 in the filename is replaced with the next number avaialable
uint16_t index;
for (index = 1; index <= 9999; index++) {
buff[3] = index / 1000 + '0';
buff[4] = ((index % 1000) / 100) + '0';
buff[5] = ((index % 100) / 10) + '0';
buff[6] = index % 10 + '0' ;
if (! SD.exists(buff))
{
// only open a new file if it doesn't exist
dataFile = SD.open(buff, FILE_WRITE);
break;
}
}
if (!dataFile)
{
return 0;
}
return index; //return number of logfile created
}
uint16_t DTSD_fileCRCCCITT(uint32_t fsize)
{
uint32_t index;
uint16_t CRCcalc;
uint8_t j, filedata;
CRCcalc = 0xFFFF; //start value for CRC16
for (index = 0; index < fsize; index++)
{
filedata = dataFile.read();
CRCcalc ^= (((uint16_t) filedata ) << 8);
for (j = 0; j < 8; j++)
{
if (CRCcalc & 0x8000)
CRCcalc = (CRCcalc << 1) ^ 0x1021;
else
CRCcalc <<= 1;
}
}
return CRCcalc;
}
void DTSD_fileFlush()
{
dataFile.flush();
}
void DTSD_closeFile()
{
dataFile.close(); //close local file
}

View File

@ -0,0 +1,71 @@
/*******************************************************************************************************
Programs for Arduino - Copyright of the author Stuart Robinson - 19/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 on LoRa device
#define NRESET 9 //reset on LoRa device
#define RFBUSY 7 //RFBUSY pin on LoRa device
#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done
#define LED1 8 //On board LED, high for on
#define SDCS 30 //select pin for SD card
#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using
#define DISPCS 23 //CS for ILI9341
#define DISPDC 24 //DC for ILI9341
#define DISPRESET 25 //RESET for ILI9341
#define TOUCHCS 29 //ILI9341 may have touch ICs, so we need to disable it, set to -1 if not fitted
#define YModemSerial Serial2 //define Serial port to use for Ymodem transfer here
#define Monitorport Serial //define Hardware Serial monitor port to use for monitoring
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 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
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
//******* ILI9341 Display settings here ***************
const uint8_t textscale = 3;
const byte rotation = 1;

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;
}