aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Trulson <jtrulson@ics.com>2015-10-30 17:58:36 -0600
committerMihai Tudor Panu <mihai.tudor.panu@intel.com>2015-11-24 14:58:40 -0800
commit2a1af1399a09e36f20dfe3f7775a462d897f9d8c (patch)
tree5aea67b1b701dc4484f4adc05eb6073e0d476a41
parent4de9734f0a4a85f2d612c5ccf7797ade946b0e1b (diff)
sx1276: Initial implementation
This driver was developed using an SX1276 based shield on the Galileo G2. It requires 3.3v of operation. It does not work with Edison, due to SPI issues. Signed-off-by: Jon Trulson <jtrulson@ics.com>
-rw-r--r--examples/c++/CMakeLists.txt5
-rw-r--r--examples/c++/sx1276-fsk.cxx121
-rw-r--r--examples/c++/sx1276-lora.cxx119
-rw-r--r--examples/javascript/sx1276-fsk.js112
-rw-r--r--examples/javascript/sx1276-lora.js110
-rw-r--r--examples/python/sx1276-fsk.py89
-rw-r--r--examples/python/sx1276-lora.py88
-rw-r--r--src/sx1276/CMakeLists.txt5
-rw-r--r--src/sx1276/LICENSE.txt25
-rw-r--r--src/sx1276/javaupm_sx1276.i36
-rw-r--r--src/sx1276/jsupm_sx1276.i9
-rw-r--r--src/sx1276/pyupm_sx1276.i15
-rw-r--r--src/sx1276/sx1276.cxx2160
-rw-r--r--src/sx1276/sx1276.h2035
14 files changed, 4929 insertions, 0 deletions
diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt
index 1d4a95e..ce4265f 100644
--- a/examples/c++/CMakeLists.txt
+++ b/examples/c++/CMakeLists.txt
@@ -155,6 +155,8 @@ add_executable (bma220-example bma220.cxx)
add_executable (dfrph-example dfrph.cxx)
add_executable (mcp9808-example mcp9808.cxx)
add_executable (groveultrasonic-example groveultrasonic.cxx)
+add_executable (sx1276-lora-example sx1276-lora.cxx)
+add_executable (sx1276-fsk-example sx1276-fsk.cxx)
include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l)
include_directories (${PROJECT_SOURCE_DIR}/src/grove)
@@ -274,6 +276,7 @@ include_directories (${PROJECT_SOURCE_DIR}/src/bma220)
include_directories (${PROJECT_SOURCE_DIR}/src/dfrph)
include_directories (${PROJECT_SOURCE_DIR}/src/mcp9808)
include_directories (${PROJECT_SOURCE_DIR}/src/groveultrasonic)
+include_directories (${PROJECT_SOURCE_DIR}/src/sx1276)
target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT})
@@ -430,3 +433,5 @@ target_link_libraries (bma220-example bma220 ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (dfrph-example dfrph ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (mcp9808-example mcp9808 ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries (groveultrasonic-example groveultrasonic ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (sx1276-lora-example sx1276 ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries (sx1276-fsk-example sx1276 ${CMAKE_THREAD_LIBS_INIT})
diff --git a/examples/c++/sx1276-fsk.cxx b/examples/c++/sx1276-fsk.cxx
new file mode 100644
index 0000000..f63d1bf
--- /dev/null
+++ b/examples/c++/sx1276-fsk.cxx
@@ -0,0 +1,121 @@
+/*
+ * Author: Jon Trulson <jtrulson@ics.com>
+ * Copyright (c) 2015 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <iostream>
+#include <signal.h>
+#include "sx1276.h"
+
+using namespace std;
+
+int shouldRun = true;
+
+void sig_handler(int signo)
+{
+ if (signo == SIGINT)
+ shouldRun = false;
+}
+
+
+int main(int argc, char **argv)
+{
+ signal(SIGINT, sig_handler);
+//! [Interesting]
+
+ cout << "Specify an argument to go into receive mode. Default is transmit"
+ << endl;
+
+ bool rx = false;
+ if (argc > 1)
+ rx = true;
+
+ // Instantiate an SX1276 using default parameters
+ upm::SX1276 *sensor = new upm::SX1276();
+
+ // 915Mhz
+ sensor->setChannel(915000000);
+
+ // FSK configuration (rx and tx must be configured the same):
+ // Tx output power = 14 dBm
+ // FSK freq deviation = 25000 Hz
+ // FSK bandwidth = 50000 bps
+ // FSK AFC bandwidth = 83333 Hz
+ // FSK datarate = 50000 bps
+ // FSK preamble len = 5
+ // FSK fixed length payload = false
+ // FSK CRC check = true
+ // FSK (rx) continuous Rx mode = false
+
+ sensor->setTxConfig(sensor->MODEM_FSK, 14, 25000, 0,
+ 50000, 0, 5, false, true, false, 0, false);
+
+ sensor->setRxConfig(sensor->MODEM_FSK, 50000, 50000,
+ 0, 83333, 5, 0, false, 0, true,
+ false, 0, false, true);
+
+ int count = 0;
+ int buflen = 64;
+ char buffer[buflen];
+
+ while (shouldRun)
+ {
+ if (!rx)
+ {
+ snprintf(buffer, buflen, "Ping %d", count++);
+ cout << "Sending..." << std::string(buffer) << endl;
+ sensor->sendStr(string(buffer), 3000);
+
+ sensor->setSleep();
+ sleep(1);
+ }
+ else
+ {
+ // receiving
+ cout << "Attempting to receive..." << endl;
+ int rv;
+ if (rv = sensor->setRx(3000))
+ {
+ cout << "setRx returned " << rv << endl;
+ }
+ else
+ {
+ cout << "Received Buffer: " << sensor->getRxBufferStr() << endl;
+ }
+
+ // go back to sleep when done
+ sensor->setSleep();
+ usleep(250000);
+ }
+
+
+ }
+//! [Interesting]
+
+ cout << "Exiting..." << endl;
+
+ delete sensor;
+
+ return 0;
+}
diff --git a/examples/c++/sx1276-lora.cxx b/examples/c++/sx1276-lora.cxx
new file mode 100644
index 0000000..7c45597
--- /dev/null
+++ b/examples/c++/sx1276-lora.cxx
@@ -0,0 +1,119 @@
+/*
+ * Author: Jon Trulson <jtrulson@ics.com>
+ * Copyright (c) 2015 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <iostream>
+#include <signal.h>
+#include "sx1276.h"
+
+using namespace std;
+
+int shouldRun = true;
+
+void sig_handler(int signo)
+{
+ if (signo == SIGINT)
+ shouldRun = false;
+}
+
+
+int main(int argc, char **argv)
+{
+ signal(SIGINT, sig_handler);
+//! [Interesting]
+ cout << "Specify an argument to go into receive mode. Default is transmit"
+ << endl;
+
+ bool rx = false;
+ if (argc > 1)
+ rx = true;
+
+ // Instantiate an SX1276 using default parameters
+ upm::SX1276 *sensor = new upm::SX1276();
+
+ // 915Mhz
+ sensor->setChannel(915000000);
+
+ // LORA configuration (rx and tx must be configured the same):
+ // Tx output power = 14 dBm
+ // LORA bandwidth = 125000 (can also be 250K and 500K)
+ // LORA spreading factor = 7
+ // LORA coding rate = 1 (4/5)
+ // LORA preamble len = 8
+ // LORA symbol timeout = 5
+ // LORA fixed payload = false
+ // LORA IQ inversion = false
+ // LORA (rx) continuous Rx mode = true
+
+ sensor->setTxConfig(sensor->MODEM_LORA, 14, 0, 125000,
+ 7, 1, 8, false, true, false, 0, false);
+
+ sensor->setRxConfig(sensor->MODEM_LORA, 125000, 7,
+ 1, 0, 8, 5, false, 0, true, false, 0, false, true);
+
+ int count = 0;
+ int buflen = 64;
+ char buffer[buflen];
+
+ while (shouldRun)
+ {
+ if (!rx)
+ {
+ snprintf(buffer, buflen, "Ping %d", count++);
+ cout << "Sending..." << std::string(buffer) << endl;
+ sensor->sendStr(string(buffer), 3000);
+ sensor->setSleep();
+ sleep(1);
+ }
+ else
+ {
+ // receiving
+ cout << "Attempting to receive..." << endl;
+ int rv;
+ if (rv = sensor->setRx(3000))
+ {
+ cout << "setRx returned " << rv << endl;
+ }
+ else
+ {
+ cout << "Received Buffer: " << sensor->getRxBufferStr() << endl;
+ }
+
+ // go back to sleep when done
+ sensor->setSleep();
+ usleep(5000);
+ }
+
+
+ }
+
+//! [Interesting]
+
+ cout << "Exiting..." << endl;
+
+ delete sensor;
+
+ return 0;
+}
diff --git a/examples/javascript/sx1276-fsk.js b/examples/javascript/sx1276-fsk.js
new file mode 100644
index 0000000..c3a4b3a
--- /dev/null
+++ b/examples/javascript/sx1276-fsk.js
@@ -0,0 +1,112 @@
+/*jslint node:true, vars:true, bitwise:true, unparam:true */
+/*jshint unused:true */
+
+/*
+ * Author: Jon Trulson <jtrulson@ics.com>
+ * Copyright (c) 2015 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+var sensorObj = require('jsupm_sx1276');
+
+var count = 0;
+var interval;
+
+/************** Functions **************/
+function transmit(sensor)
+{
+ var buffer = "Ping " + count;
+ count++;
+ console.log("Sending... " + buffer);
+
+ sensor.sendStr(buffer, 3000);
+ sensor.setSleep();
+}
+
+function receive(sensor)
+{
+ console.log("Attempting to receive... ");
+ var rv = 0;
+ rv = sensor.setRx(3000);
+
+ if (rv)
+ {
+ console.log("setRx returned " + rv);
+ }
+ else
+ {
+ console.log("Received Buffer: " + sensor.getRxBufferStr());
+ }
+
+ sensor.setSleep();
+}
+
+/************** Main code **************/
+// Instantiate an SX1276 using default parameters
+
+var sensor = new sensorObj.SX1276();
+
+console.log("Specify an argument to go into receive mode. Default is transmit");
+
+// 915Mhz
+sensor.setChannel(915000000)
+
+// FSK configuration (rx and tx must be configured the same):
+// Tx output power = 14 dBm
+// FSK freq deviation = 25000 Hz
+// FSK bandwidth = 50000 bps
+// FSK AFC bandwidth = 83333 Hz
+// FSK datarate = 50000 bps
+// FSK preamble len = 5
+// FSK fixed length payload = false
+// FSK CRC check = true
+// FSK (rx) continuous Rx mode = False
+
+sensor.setTxConfig(sensorObj.SX1276.MODEM_FSK, 14, 25000, 0,
+ 50000, 0, 5, false, true, false, 0, false);
+
+sensor.setRxConfig(sensorObj.SX1276.MODEM_FSK, 50000, 50000,
+ 0, 83333, 5, 0, false, 0, true,
+ false, 0, false, true);
+
+
+if (process.argv.length > 2)
+{
+ // receive mode
+ interval = setInterval(function() { receive(sensor); }, 250);
+}
+else
+{
+ // transmit mode
+ interval = setInterval(function() { transmit(sensor); }, 1000);
+}
+
+
+/************** Exit code **************/
+process.on('SIGINT', function()
+{
+ sensor = null;
+ sensorObj.cleanUp();
+ sensorObj = null;
+ console.log("Exiting...");
+ process.exit(0);
+});
diff --git a/examples/javascript/sx1276-lora.js b/examples/javascript/sx1276-lora.js
new file mode 100644
index 0000000..82581cc
--- /dev/null
+++ b/examples/javascript/sx1276-lora.js
@@ -0,0 +1,110 @@
+/*jslint node:true, vars:true, bitwise:true, unparam:true */
+/*jshint unused:true */
+
+/*
+ * Author: Jon Trulson <jtrulson@ics.com>
+ * Copyright (c) 2015 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+var sensorObj = require('jsupm_sx1276');
+
+var count = 0;
+var interval;
+
+/************** Functions **************/
+function transmit(sensor)
+{
+ var buffer = "Ping " + count;
+ count++;
+ console.log("Sending... " + buffer);
+
+ sensor.sendStr(buffer, 3000);
+ sensor.setSleep();
+}
+
+function receive(sensor)
+{
+ console.log("Attempting to receive... ");
+ var rv = 0;
+ rv = sensor.setRx(3000);
+
+ if (rv)
+ {
+ console.log("setRx returned " + rv);
+ }
+ else
+ {
+ console.log("Received Buffer: " + sensor.getRxBufferStr());
+ }
+
+ sensor.setSleep();
+}
+
+/************** Main code **************/
+// Instantiate an SX1276 using default parameters
+
+var sensor = new sensorObj.SX1276();
+
+console.log("Specify an argument to go into receive mode. Default is transmit");
+
+// 915Mhz
+sensor.setChannel(915000000)
+
+// LORA configuration (rx and tx must be configured the same):
+// Tx output power = 14 dBm
+// LORA bandwidth = 125000 (can also be 250K and 500K)
+// LORA spreading factor = 7
+// LORA coding rate = 1 (4/5)
+// LORA preamble len = 8
+// LORA symbol timeout = 5
+// LORA fixed payload = false
+// LORA IQ inversion = false
+// LORA (rx) continuous Rx mode = true
+
+sensor.setTxConfig(sensorObj.SX1276.MODEM_LORA, 14, 0, 125000,
+ 7, 1, 8, false, true, false, 0, false);
+
+sensor.setRxConfig(sensorObj.SX1276.MODEM_LORA, 125000, 7,
+ 1, 0, 8, 5, false, 0, true, false, 0, false, true);
+
+if (process.argv.length > 2)
+{
+ // receive mode
+ interval = setInterval(function() { receive(sensor); }, 250);
+}
+else
+{
+ // transmit mode
+ interval = setInterval(function() { transmit(sensor); }, 1000);
+}
+
+
+/************** Exit code **************/
+process.on('SIGINT', function()
+{
+ sensor = null;
+ sensorObj.cleanUp();
+ sensorObj = null;
+ console.log("Exiting...");
+ process.exit(0);
+});
diff --git a/examples/python/sx1276-fsk.py b/examples/python/sx1276-fsk.py
new file mode 100644
index 0000000..6899699
--- /dev/null
+++ b/examples/python/sx1276-fsk.py
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+# Author: Jon Trulson <jtrulson@ics.com>
+# Copyright (c) 2015 Intel Corporation.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import time, sys, signal, atexit
+import pyupm_sx1276 as sensorObj
+
+# Instantiate an SX1276 using default parameters
+sensor = sensorObj.SX1276()
+
+## Exit handlers ##
+# This stops python from printing a stacktrace when you hit control-C
+def SIGINTHandler(signum, frame):
+ raise SystemExit
+
+# This function lets you run code on exit
+def exitHandler():
+ print "Exiting"
+ sys.exit(0)
+
+# Register exit handlers
+atexit.register(exitHandler)
+signal.signal(signal.SIGINT, SIGINTHandler)
+
+print "Specify an argument to go into receive mode. Default is transmit"
+
+# 915Mhz
+sensor.setChannel(915000000)
+
+# FSK configuration (rx and tx must be configured the same):
+# Tx output power = 14 dBm
+# FSK freq deviation = 25000 Hz
+# FSK bandwidth = 50000 bps
+# FSK AFC bandwidth = 83333 Hz
+# FSK datarate = 50000 bps
+# FSK preamble len = 5
+# FSK fixed length payload = false
+# FSK CRC check = true
+# FSK (rx) continuous Rx mode = False
+
+sensor.setTxConfig(sensor.MODEM_FSK, 14, 25000, 0,
+ 50000, 0, 5, False, True, False, 0, False)
+
+sensor.setRxConfig(sensor.MODEM_FSK, 50000, 50000,
+ 0, 83333, 5, 0, False, 0, True,
+ False, 0, False, True)
+
+count = 0
+
+while True:
+ if (len(sys.argv) > 1):
+ # receive mode
+ print "Attempting to receive..."
+ rv = sensor.setRx(3000)
+ if (rv):
+ print "setRx returned ", rv
+ else:
+ print "Received Buffer: ", sensor.getRxBufferStr();
+ # go back to sleep when done
+
+ sensor.setSleep()
+ time.sleep(.25)
+ else:
+ # transmit mode
+ buffer = "Ping " + str(count)
+ count += 1
+ print "Sending..." + buffer
+ sensor.sendStr(buffer, 3000)
+ sensor.setSleep();
+ time.sleep(1);
diff --git a/examples/python/sx1276-lora.py b/examples/python/sx1276-lora.py
new file mode 100644
index 0000000..05d9700
--- /dev/null
+++ b/examples/python/sx1276-lora.py
@@ -0,0 +1,88 @@
+#!/usr/bin/python
+# Author: Jon Trulson <jtrulson@ics.com>
+# Copyright (c) 2015 Intel Corporation.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import time, sys, signal, atexit
+import pyupm_sx1276 as sensorObj
+
+# Instantiate an SX1276 using default parameters
+sensor = sensorObj.SX1276()
+
+## Exit handlers ##
+# This stops python from printing a stacktrace when you hit control-C
+def SIGINTHandler(signum, frame):
+ raise SystemExit
+
+# This function lets you run code on exit
+def exitHandler():
+ print "Exiting"
+ sys.exit(0)
+
+# Register exit handlers
+atexit.register(exitHandler)
+signal.signal(signal.SIGINT, SIGINTHandler)
+
+print "Specify an argument to go into receive mode. Default is transmit"
+
+# 915Mhz
+sensor.setChannel(915000000)
+
+# LORA configuration (rx and tx must be configured the same):
+# Tx output power = 14 dBm
+# LORA bandwidth = 125000 (can also be 250K and 500K)
+# LORA spreading factor = 7
+# LORA coding rate = 1 (4/5)
+# LORA preamble len = 8
+# LORA symbol timeout = 5
+# LORA fixed payload = false
+# LORA IQ inversion = false
+# LORA (rx) continuous Rx mode = true
+
+sensor.setTxConfig(sensor.MODEM_LORA, 14, 0, 125000,
+ 7, 1, 8, False, True, False, 0, False)
+
+sensor.setRxConfig(sensor.MODEM_LORA, 125000, 7,
+ 1, 0, 8, 5, False, 0, True, False, 0, False, True)
+
+count = 0
+
+while True:
+ if (len(sys.argv) > 1):
+ # receive mode
+ print "Attempting to receive..."
+ rv = sensor.setRx(3000)
+ if (rv):
+ print "setRx returned ", rv
+ else:
+ print "Received Buffer: ", sensor.getRxBufferStr();
+ # go back to sleep when done
+
+ sensor.setSleep()
+ time.sleep(.25)
+ else:
+ # transmit mode
+ buffer = "Ping " + str(count)
+ count += 1
+ print "Sending..." + buffer
+ sensor.sendStr(buffer, 3000)
+ sensor.setSleep();
+ time.sleep(1);
diff --git a/src/sx1276/CMakeLists.txt b/src/sx1276/CMakeLists.txt
new file mode 100644
index 0000000..21fb5ad
--- /dev/null
+++ b/src/sx1276/CMakeLists.txt
@@ -0,0 +1,5 @@
+set (libname "sx1276")
+set (libdescription "SZ1276 LoRa/FSK/OOK radio")
+set (module_src ${libname}.cxx)
+set (module_h ${libname}.h)
+upm_module_init()
diff --git a/src/sx1276/LICENSE.txt b/src/sx1276/LICENSE.txt
new file mode 100644
index 0000000..3078581
--- /dev/null
+++ b/src/sx1276/LICENSE.txt
@@ -0,0 +1,25 @@
+--- Revised BSD License ---
+Copyright (c) 2013, SEMTECH S.A.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Semtech corporation nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/src/sx1276/javaupm_sx1276.i b/src/sx1276/javaupm_sx1276.i
new file mode 100644
index 0000000..3c5e920
--- /dev/null
+++ b/src/sx1276/javaupm_sx1276.i
@@ -0,0 +1,36 @@
+%module(directors="1") javaupm_sx1276
+%include "../upm.i"
+%include "cpointer.i"
+%include "typemaps.i"
+%include "arrays_java.i";
+%include "../java_buffer.i"
+
+%feature("director") IsrCallback;
+
+%ignore generic_callback_isr;
+%include "../IsrCallback.h"
+
+%apply int {mraa::Edge};
+%apply float *INOUT { float *x, float *y, float *z };
+
+%typemap(jni) float* "jfloatArray"
+%typemap(jstype) float* "float[]"
+%typemap(jtype) float* "float[]"
+
+%typemap(javaout) float* {
+ return $jnicall;
+}
+
+%typemap(out) float *getAccelerometer {
+ $result = JCALL1(NewFloatArray, jenv, 3);
+ JCALL4(SetFloatArrayRegion, jenv, $result, 0, 3, $1);
+ delete [] $1;
+}
+
+%ignore getAccelerometer(float *, float *, float *);
+
+%{
+ #include "sx1276.h"
+%}
+
+%include "sx1276.h"
diff --git a/src/sx1276/jsupm_sx1276.i b/src/sx1276/jsupm_sx1276.i
new file mode 100644
index 0000000..2911804
--- /dev/null
+++ b/src/sx1276/jsupm_sx1276.i
@@ -0,0 +1,9 @@
+%module jsupm_sx1276
+%include "../upm.i"
+%include "cpointer.i"
+
+%include "sx1276.h"
+%{
+ #include "sx1276.h"
+%}
+
diff --git a/src/sx1276/pyupm_sx1276.i b/src/sx1276/pyupm_sx1276.i
new file mode 100644
index 0000000..bebbc00
--- /dev/null
+++ b/src/sx1276/pyupm_sx1276.i
@@ -0,0 +1,15 @@
+%module pyupm_sx1276
+%include "../upm.i"
+%include "cpointer.i"
+
+%include "stdint.i"
+
+%feature("autodoc", "3");
+
+%pointer_functions(float, floatp);
+
+%include "sx1276.h"
+%{
+ #include "sx1276.h"
+%}
+
diff --git a/src/sx1276/sx1276.cxx b/src/sx1276/sx1276.cxx
new file mode 100644
index 0000000..004d025
--- /dev/null
+++ b/src/sx1276/sx1276.cxx
@@ -0,0 +1,2160 @@
+/*
+ * Author: Jon Trulson <jtrulson@ics.com>
+ * Copyright (c) 2015 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <iostream>
+#include <stdexcept>
+#include <sstream>
+#include <string>
+#include <string.h>
+
+#include "sx1276.h"
+
+using namespace upm;
+using namespace std;
+
+//
+// FSK bandwidth to register definition structs
+//
+typedef struct
+{
+ uint32_t bandwidth;
+ uint8_t RegValue;
+} FskBandwidth_t;
+
+static const FskBandwidth_t FskBandwidths[] =
+{
+ { 2600 , 0x17 },
+ { 3100 , 0x0F },
+ { 3900 , 0x07 },
+ { 5200 , 0x16 },
+ { 6300 , 0x0E },
+ { 7800 , 0x06 },
+ { 10400 , 0x15 },
+ { 12500 , 0x0D },
+ { 15600 , 0x05 },
+ { 20800 , 0x14 },
+ { 25000 , 0x0C },
+ { 31300 , 0x04 },
+ { 41700 , 0x13 },
+ { 50000 , 0x0B },
+ { 62500 , 0x03 },
+ { 83333 , 0x12 },
+ { 100000, 0x0A },
+ { 125000, 0x02 },
+ { 166700, 0x11 },
+ { 200000, 0x09 },
+ { 250000, 0x01 },
+ { 300000, 0x00 }, // Invalid Badwidth
+};
+
+
+SX1276::SX1276(uint8_t chipRev, int bus, int cs, int resetPin, int dio0,
+ int dio1, int dio2, int dio3, int dio4, int dio5) :
+ m_spi(bus), m_gpioCS(cs), m_gpioReset(resetPin), m_gpioDIO0(dio0),
+ m_gpioDIO1(dio1), m_gpioDIO2(dio2), m_gpioDIO3(dio3), m_gpioDIO4(dio4),
+ m_gpioDIO5(dio5)
+{
+ m_spi.mode(mraa::SPI_MODE0);
+ m_spi.frequency(10000000); // 10Mhz, if supported
+
+ m_gpioCS.dir(mraa::DIR_OUT);
+ m_gpioCS.useMmap(true);
+ csOff();
+
+ m_gpioReset.dir(mraa::DIR_IN);
+
+ // 10ms for POR
+ usleep(10000);
+
+ // setup the interrupt handlers. All 6 of them.
+ m_gpioDIO0.dir(mraa::DIR_IN);
+ if (m_gpioDIO0.isr(mraa::EDGE_RISING, onDio0Irq, this))
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Gpio.isr(dio0) failed");
+
+ m_gpioDIO1.dir(mraa::DIR_IN);
+ if (m_gpioDIO1.isr(mraa::EDGE_RISING, onDio1Irq, this))
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Gpio.isr(dio1) failed");
+
+ m_gpioDIO2.dir(mraa::DIR_IN);
+ if (m_gpioDIO2.isr(mraa::EDGE_RISING, onDio2Irq, this))
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Gpio.isr(dio2) failed");
+
+ m_gpioDIO3.dir(mraa::DIR_IN);
+ if (m_gpioDIO3.isr(mraa::EDGE_RISING, onDio3Irq, this))
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Gpio.isr(dio3) failed");
+
+ m_gpioDIO4.dir(mraa::DIR_IN);
+ if (m_gpioDIO4.isr(mraa::EDGE_RISING, onDio4Irq, this))
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Gpio.isr(dio4) failed");
+
+ // this one isn't as vital, so no need to fail if this one can't be
+ // setup.
+ m_gpioDIO5.dir(mraa::DIR_IN);
+ if (m_gpioDIO5.isr(mraa::EDGE_RISING, onDio5Irq, this))
+ cerr << __FUNCTION__ << ": Gpio.isr(dio5) failed" << endl;
+
+ initClock();
+ m_radioEvent = REVENT_DONE;
+ m_settings.state = STATE_IDLE;
+ memset(m_rxBuffer, 0, FIFO_SIZE);
+ m_rxSNR = 0;
+ m_rxRSSI = 0;
+
+ // check the chip revision (to make sure we can read the regs properly)
+ uint8_t cRev = getChipVersion();
+ if (cRev != chipRev)
+ {
+ std::ostringstream str;
+ std::ostringstream str2;
+
+ str << hex << (int)cRev << dec;
+ str2 << hex << (int)chipRev << dec;
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Incorrect Chip Revision. Expected 0x" +
+ str2.str() + ", got 0x" + str.str());
+ }
+
+ pthread_mutexattr_t mutexAttrib;
+ pthread_mutexattr_init(&mutexAttrib);
+ // pthread_mutexattr_settype(&mutexAttrib, PTHREAD_MUTEX_RECURSIVE);
+
+ if (pthread_mutex_init(&m_intrLock, &mutexAttrib))
+ {
+ throw std::runtime_error(std::string(__FUNCTION__) +
+ ": pthread_mutex_init(intrLock) failed");
+ }
+
+ pthread_mutexattr_destroy(&mutexAttrib);
+
+ init();
+}
+
+SX1276::~SX1276()
+{
+ pthread_mutex_destroy(&m_intrLock);
+}
+
+uint8_t SX1276::readReg(uint8_t reg)
+{
+ uint8_t pkt[2] = {(reg & 0x7f), 0};
+
+ csOn();
+ if (m_spi.transfer(pkt, pkt, 2))
+ {
+ csOff();
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Spi.transfer() failed");
+ return 0;
+ }
+ csOff();
+
+ return pkt[1];
+}
+
+bool SX1276::writeReg(uint8_t reg, uint8_t val)
+{
+ uint8_t pkt[2] = {reg | m_writeMode, val};
+
+ csOn();
+ if (m_spi.transfer(pkt, NULL, 2))
+ {
+ csOff();
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Spi.transfer() failed");
+ return false;
+ }
+ csOff();
+
+ return true;
+}
+
+void SX1276::readFifo(uint8_t *buf, int len)
+{
+ // can't read more than 256 bytes
+ if (len > FIFO_SIZE)
+ {
+ throw std::length_error(string(__FUNCTION__) +
+ ": cannot read more than 256 bytes from FIFO");
+ return;
+ }
+
+ uint8_t pkt = 0;
+
+ csOn();
+ if (m_spi.transfer(&pkt, NULL, 1))
+ {
+ csOff();
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Spi.transfer(0) failed");
+ return;
+ }
+
+ if (m_spi.transfer(NULL, buf, len))
+ {
+ csOff();
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Spi.transfer(buf) failed");
+ return;
+ }
+ csOff();
+}
+
+void SX1276::writeFifo(uint8_t *buf, int len)
+{
+ // can't write more than 256 bytes
+ if (len > FIFO_SIZE)
+ {
+ throw std::length_error(string(__FUNCTION__) +
+ ": cannot write more than 256 bytes to FIFO");
+ return;
+ }
+
+ uint8_t pkt = (0 | m_writeMode);
+
+ csOn();
+ if (m_spi.transfer(&pkt, NULL, 1))
+ {
+ csOff();
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Spi.transfer(0) failed");
+ return;
+ }
+
+ if (m_spi.transfer(buf, NULL, len))
+ {
+ csOff();
+ throw std::runtime_error(string(__FUNCTION__) +
+ ": Spi.transfer(buf) failed");
+ return;
+ }
+ csOff();
+}
+
+
+uint8_t SX1276::getChipVersion()
+{
+ return readReg(COM_RegVersion);
+}
+
+
+void SX1276::reset()
+{
+ m_gpioReset.dir(mraa::DIR_OUT);
+ usleep(1000); // 1ms
+ m_gpioReset.dir(mraa::DIR_IN);
+ usleep(10000); // 10ms
+}
+
+
+void SX1276::init()
+{
+ typedef struct
+ {
+ RADIO_MODEM_T Modem;
+ uint8_t Addr;
+ uint8_t Value;
+ } radioRegisters_t;
+
+ // some initial setup
+ static const radioRegisters_t radioRegsInit[] = {
+ { MODEM_FSK , COM_RegLna , 0x23 },
+ { MODEM_FSK , FSK_RegRxConfig , 0x1E },
+ { MODEM_FSK , FSK_RegRssiConfg , 0xD2 },
+ { MODEM_FSK , FSK_RegPreambleDetect , 0xAA },
+ { MODEM_FSK , FSK_RegOsc , 0x07 },
+ { MODEM_FSK , FSK_RegSyncConfig , 0x12 },
+ { MODEM_FSK , FSK_RegSyncValue1 , 0xC1 },
+ { MODEM_FSK , FSK_RegSyncValue2 , 0x94 },
+ { MODEM_FSK , FSK_RegSyncValue3 , 0xC1 },
+ { MODEM_FSK , FSK_RegPacketConfig1 , 0xD8 },
+ { MODEM_FSK , FSK_RegFifoThresh , 0x8F },
+ { MODEM_FSK , FSK_RegImageCal , 0x02 },
+ { MODEM_FSK , COM_RegDioMapping1 , 0x00 },
+ { MODEM_FSK , COM_RegDioMapping2 , 0x30 },
+ { MODEM_LORA, LOR_RegMaxPayloadLength, 0x40 }
+ };
+
+ reset();
+
+ rxChainCalibration();
+
+ setOpMode(MODE_Sleep);
+
+ for (int i = 0; i < sizeof(radioRegsInit) / sizeof(radioRegisters_t); i++ )
+ {
+ setModem(radioRegsInit[i].Modem);
+ writeReg(radioRegsInit[i].Addr, radioRegsInit[i].Value);
+ }
+
+ setModem(MODEM_FSK);
+ m_settings.state = STATE_IDLE;
+}
+
+void SX1276::rxChainCalibration()
+{
+ uint8_t regPaConfigInitVal;
+ uint32_t initialFreq;
+ uint8_t reg;
+
+ // this function should only be called in init() (after reset()), as
+ // the device is configured for FSK mode, LF at that time.
+
+ // Save context
+ regPaConfigInitVal = readReg(COM_RegPaConfig);
+ initialFreq = (uint32_t) ( ((double)
+ (((uint32_t)readReg(COM_RegFrfMsb) << 16) |
+ ((uint32_t)readReg(COM_RegFrfMid) << 8) |
+ ((uint32_t)readReg(COM_RegFrfLsb)) ) )
+ * FXOSC_STEP);
+
+ // Cut the PA just in case, RFO output, power = -1 dBm
+ writeReg(COM_RegPaConfig, 0x00);
+
+ // Launch Rx chain calibration for LF band
+ reg = readReg(FSK_RegImageCal);
+ writeReg(FSK_RegImageCal, reg | IMAGECAL_ImageCalStart);
+
+ // spin until complete
+ while (readReg(FSK_RegImageCal) & IMAGECAL_ImageCalRunning)
+ usleep(1);
+
+ // cerr << __FUNCTION__ << ": Imagecal LF complete" << endl;
+
+ // Set a Frequency in HF band
+ setChannel(868000000);
+
+ // Launch Rx chain calibration for HF band
+ reg = readReg(FSK_RegImageCal);
+ writeReg(FSK_RegImageCal, reg | IMAGECAL_ImageCalStart);
+
+ // spin until complete
+ while (readReg(FSK_RegImageCal) & IMAGECAL_ImageCalRunning)
+ usleep(1);
+
+ // cerr << __FUNCTION__ << ": Imagecal HF complete" << endl;
+
+ // Restore context
+ writeReg(COM_RegPaConfig, regPaConfigInitVal);
+ setChannel(initialFreq);
+}
+
+void SX1276::setChannel(uint32_t freq)
+{
+ m_settings.channel = freq;
+
+ freq = ( uint32_t )( ( double )freq / FXOSC_STEP );
+
+ writeReg(COM_RegFrfMsb, ( uint8_t )( ( freq >> 16 ) & 0xff ) );
+ writeReg(COM_RegFrfMid, ( uint8_t )( ( freq >> 8 ) & 0xff ) );
+ writeReg(COM_RegFrfLsb, ( uint8_t )( freq & 0xff ) );
+}
+
+void SX1276::setOpMode(MODE_T opMode)
+{
+ static uint8_t opModePrev = MODE_Standby;
+
+ if(opMode != opModePrev)
+ {
+ opModePrev = opMode;
+
+ uint8_t reg = readReg(COM_RegOpMode) &
+ ~(_OPMODE_Mode_MASK << _OPMODE_Mode_SHIFT);
+
+ writeReg(COM_RegOpMode, (reg | (opMode << _OPMODE_Mode_SHIFT)) );
+ }
+}
+
+void SX1276::setModem(RADIO_MODEM_T modem)
+{
+ if (m_settings.modem == modem )
+ {
+ return;
+ }
+
+ m_settings.modem = modem;
+
+ uint8_t reg = 0;
+
+ switch (m_settings.modem)
+ {
+ default:
+ case MODEM_FSK:
+ setOpMode(MODE_Sleep);
+
+ // turn off lora
+ reg = (readReg(COM_RegOpMode) & ~OPMODE_LongRangeMode);
+ writeReg(COM_RegOpMode, reg);
+
+ writeReg(COM_RegDioMapping1, 0x00);
+ writeReg(COM_RegDioMapping2, 0x30); // DIO5=ModeReady
+
+ break;
+
+ case MODEM_LORA:
+ setOpMode(MODE_Sleep);
+ // turn lora on
+ reg = (readReg(COM_RegOpMode) | OPMODE_LongRangeMode);
+ writeReg(COM_RegOpMode, reg);
+
+ writeReg(COM_RegDioMapping1, 0x00);
+ writeReg(COM_RegDioMapping2, 0x00);
+
+ break;
+ }
+}
+
+bool SX1276::isChannelFree(RADIO_MODEM_T modem, uint32_t freq,
+ int16_t rssiThresh)
+{
+ int16_t rssi = 0;
+
+ setModem(modem);
+
+ setChannel(freq);
+
+ setOpMode(MODE_FSK_RxMode);
+
+ usleep(1000);
+
+ rssi = getRSSI(modem);
+
+ setSleep();
+
+ if (rssi > rssiThresh)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+int16_t SX1276::getRSSI(RADIO_MODEM_T modem)
+{
+ int16_t rssi = 0;
+
+ switch (modem)
+ {
+ case MODEM_FSK:
+ // devide by 2
+ rssi = -(readReg(FSK_RegRssiValue) >> 1);
+
+ break;
+
+ case MODEM_LORA:
+ {
+ uint8_t reg = readReg(LOR_RegRssiValue);
+ if (m_settings.channel > RF_MID_BAND_THRESH )
+ {
+ rssi = LOR_RSSI_OFFSET_HF + reg;
+ }
+ else
+ {
+ rssi = LOR_RSSI_OFFSET_LF + reg;
+ }
+ }
+ break;
+
+ default:
+ rssi = -1;
+
+ break;
+ }
+
+ return rssi;
+}
+
+void SX1276::setSleep()
+{
+ setOpMode(MODE_Sleep);
+ m_settings.state = STATE_IDLE;
+}
+
+void SX1276::setStandby()
+{
+ setOpMode(MODE_Standby);
+ m_settings.state = STATE_IDLE;
+}
+
+uint8_t SX1276::lookupFSKBandWidth(uint32_t bw)
+{
+ // This function looks up values in the fsk_bw_lookup_table and
+ // returns the approprite register value to use for either the
+ // FSK_RxBw or the FSK_RegAfcBw registers
+
+ // See Table 40 in the datasheet
+
+ for (int i=0; i<(sizeof(FskBandwidths)/sizeof(FskBandwidth_t)) - 1; i++)
+ {
+ if ( (bw >= FskBandwidths[i].bandwidth) &&
+ (bw < FskBandwidths[i + 1].bandwidth) )
+ {
+ return FskBandwidths[i].RegValue;
+ }
+ }
+
+ // shouldn't happen, but the universe is vast and indifferent...
+ throw std::range_error(std::string(__FUNCTION__) +
+ ": Unable to find bandwidth in lookup table. "
+ "Bandwidth must be between 2600 and 250000 for FSK");
+
+ return 0;
+}
+
+SX1276::RADIO_EVENT_T SX1276::sendStr(string buffer, int timeout)
+{
+ if (buffer.size() > (FIFO_SIZE - 1))
+ throw std::range_error(string(__FUNCTION__) +
+ ": buffer size must be less than 256");
+
+ // for LORA/FSK modem, there seems to be a 64 byte requirement,
+ // (LOR_RegRxNbBytes on the receiver) never seems to be anything
+ // other than 64. Same seems to go for the FSK modem. So, if the
+ // packet is less than 64, pad it out to 64 bytes. This requires
+ // investigation.
+ while (buffer.size() < 64)
+ buffer.push_back(0);
+
+ return send((uint8_t *)buffer.c_str(), buffer.size(), timeout);
+}
+
+SX1276::RADIO_EVENT_T SX1276::send(uint8_t *buffer, uint8_t size,
+ int txTimeout)
+{
+ switch (m_settings.modem)
+ {
+ case MODEM_FSK:
+ {
+ m_settings.fskPacketHandler.NbBytes = 0;
+ m_settings.fskPacketHandler.Size = size;
+
+ if (m_settings.fskSettings.FixLen == false)
+ {
+ writeFifo((uint8_t *)&size, 1);
+ }
+ else
+ {
+ writeReg(FSK_RegPayloadLength, size );
+ }
+
+ if ( (size > 0) && (size <= 64) )
+ {
+ m_settings.fskPacketHandler.ChunkSize = size;
+ }
+ else
+ {
+ m_settings.fskPacketHandler.ChunkSize = 32;
+ }
+
+ // Write payload buffer
+ writeFifo(buffer, m_settings.fskPacketHandler.ChunkSize);
+ m_settings.fskPacketHandler.NbBytes +=
+ m_settings.fskPacketHandler.ChunkSize;
+ }
+
+ break;
+
+ case MODEM_LORA:
+ {
+ if (m_settings.loraSettings.IqInverted == true)
+ {
+ uint8_t reg = readReg(LOR_RegInvertIQ);
+
+ reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx);
+ writeReg(LOR_RegInvertIQ, reg);
+
+ // warning, hardcoded undocumented magic number into
+ // undocumented register
+ writeReg(LOR_RegInvertIQ2, 0x19);
+ }
+ else
+ {
+ uint8_t reg = readReg(LOR_RegInvertIQ);
+ reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx);
+ reg |= INVERTIQ_InvertIQTxOff; // 'active' off.
+ writeReg(LOR_RegInvertIQ, reg);
+
+ // warning, hardcoded undocumented magic number into
+ // undocumented register
+ writeReg(LOR_RegInvertIQ2, 0x1d);
+ }
+
+ m_settings.loraPacketHandler.Size = size;
+ // cerr << "PAYLOAD SIZE " << (int)size << endl;
+
+ // Initializes the payload size
+ writeReg(LOR_RegPayloadLength, size);
+
+ // Full buffer used for Tx
+ writeReg(LOR_RegFifoTxBaseAddr, 0);
+ writeReg(LOR_RegFifoAddrPtr, 0 );
+
+ // FIFO operations can not take place in Sleep mode
+ if ((readReg(COM_RegOpMode) & _OPMODE_Mode_MASK) == MODE_Sleep)
+ {
+ setStandby();
+ usleep(1000); // 1ms
+ }
+
+ // Write payload buffer
+ writeFifo(buffer, size);
+ }
+
+ break;
+ }
+
+ return setTx(txTimeout);
+}
+
+
+void SX1276::setRxConfig(RADIO_MODEM_T modem, uint32_t bandwidth,
+ uint32_t datarate, uint8_t coderate,
+ uint32_t bandwidthAfc, uint16_t preambleLen,
+ uint16_t symbTimeout, bool fixLen,
+ uint8_t payloadLen,
+ bool crcOn, bool freqHopOn, uint8_t hopPeriod,
+ bool iqInverted, bool rxContinuous)
+{
+ setModem( modem );
+
+ uint8_t reg;
+
+ switch (modem)
+ {
+ case MODEM_FSK:
+ {
+ m_settings.fskSettings.Bandwidth = bandwidth;
+ m_settings.fskSettings.Datarate = datarate;
+ m_settings.fskSettings.BandwidthAfc = bandwidthAfc;
+ m_settings.fskSettings.FixLen = fixLen;
+ m_settings.fskSettings.PayloadLen = payloadLen;
+ m_settings.fskSettings.CrcOn = crcOn;
+ m_settings.fskSettings.IqInverted = iqInverted;
+ m_settings.fskSettings.RxContinuous = rxContinuous;
+ m_settings.fskSettings.PreambleLen = preambleLen;
+
+ datarate = (uint16_t)(FXOSC_FREQ / (double)datarate);
+ writeReg(FSK_RegBitrateMsb, (uint8_t)(datarate >> 8));
+ writeReg(FSK_RegBitrateLsb, (uint8_t)(datarate & 0xff));
+
+ writeReg(FSK_RegRxBw, lookupFSKBandWidth(bandwidth));
+ writeReg(FSK_RegAfcBw, lookupFSKBandWidth(bandwidthAfc));
+
+ writeReg(FSK_RegPreambleMsb,
+ (uint8_t)((preambleLen >> 8) & 0xff));
+ writeReg(FSK_RegPreambleLsb, (uint8_t)(preambleLen & 0xff));
+
+ if (fixLen)
+ {
+ writeReg(FSK_RegPayloadLength, payloadLen);
+ }
+
+ reg = readReg(FSK_RegPacketConfig1);
+ reg &= ~(PACKETCONFIG1_CrcOn | PACKETCONFIG1_PacketFormat);
+
+ if (!fixLen)
+ reg |= PACKETCONFIG1_PacketFormat; // variable len
+
+ if (crcOn)
+ reg |= PACKETCONFIG1_CrcOn;
+
+ writeReg(FSK_RegPacketConfig1, reg);
+ }
+
+ break;
+
+ case MODEM_LORA:
+ {
+ // convert the supplied (legal) LORA bandwidths into something
+ // the chip can handle.
+ switch (bandwidth)
+ {
+ case 125000:
+ bandwidth = BW_125;
+ break;
+
+ case 250000:
+ bandwidth = BW_250;
+ break;
+
+ case 500000:
+ bandwidth = BW_500;
+ break;
+
+ default:
+ throw std::runtime_error(std::string(__FUNCTION__) +
+ ": LORA bandwidth must be 125000, 250000, "
+ "or 500000");
+ }
+
+ m_settings.loraSettings.Bandwidth = bandwidth;
+ m_settings.loraSettings.Datarate = datarate;
+ m_settings.loraSettings.Coderate = coderate;
+ m_settings.loraSettings.FixLen = fixLen;
+ m_settings.loraSettings.PayloadLen = payloadLen;
+ m_settings.loraSettings.CrcOn = crcOn;
+ m_settings.loraSettings.FreqHopOn = freqHopOn;
+ m_settings.loraSettings.HopPeriod = hopPeriod;
+ m_settings.loraSettings.IqInverted = iqInverted;
+ m_settings.loraSettings.RxContinuous = rxContinuous;
+
+ // datarate is really LORA SPREADING_FACTOR_*
+ if (datarate > 12)
+ {
+ datarate = 12;
+ }
+ else if (datarate < 6)
+ {
+ datarate = 6;
+ }
+
+ if ( ((bandwidth == BW_125) && ((datarate == 11) ||
+ (datarate == 12))) ||
+ ((bandwidth == BW_250) && (datarate == 12)) )
+ {
+ m_settings.loraSettings.LowDatarateOptimize = true;
+ }
+ else
+ {
+ m_settings.loraSettings.LowDatarateOptimize = false;
+ }
+
+ reg = readReg(LOR_RegModemConfig1);
+ reg &= ~((_MODEMCONFIG1_CodingRate_MASK <<
+ _MODEMCONFIG1_CodingRate_SHIFT) |
+ (_MODEMCONFIG1_Bw_MASK << _MODEMCONFIG1_Bw_SHIFT) |
+ MODEMCONFIG1_ImplicitHeaderModeOn);
+
+ if (fixLen)
+ reg |= MODEMCONFIG1_ImplicitHeaderModeOn;
+
+ reg |= ((bandwidth & _MODEMCONFIG1_Bw_MASK) << _MODEMCONFIG1_Bw_SHIFT);
+ reg |= ((coderate & _MODEMCONFIG1_CodingRate_MASK) <<
+ _MODEMCONFIG1_CodingRate_SHIFT);
+
+ writeReg(LOR_RegModemConfig1, reg);
+
+ reg = readReg(LOR_RegModemConfig2);
+ reg &= ~((_MODEMCONFIG2_SpreadingFactor_MASK <<
+ _MODEMCONFIG2_SpreadingFactor_SHIFT) |
+ MODEMCONFIG2_RxPayloadCrcOn |
+ (_MODEMCONFIG2_SymbTimeoutMsb_MASK <<
+ _MODEMCONFIG2_SymbTimeoutMsb_SHIFT));
+
+ if (crcOn)
+ reg |= MODEMCONFIG2_RxPayloadCrcOn;
+
+ reg |= ((datarate & _MODEMCONFIG2_SpreadingFactor_MASK) <<
+ _MODEMCONFIG2_SpreadingFactor_SHIFT);
+
+ // mask symbTimeOut (MSB) for safety
+ reg |= ( ((symbTimeout >> 8) & _MODEMCONFIG2_SymbTimeoutMsb_MASK) <<
+ _MODEMCONFIG2_SymbTimeoutMsb_SHIFT);
+ writeReg(LOR_RegModemConfig2, reg);
+
+ reg = readReg(LOR_RegModemConfig3);
+
+ reg &= ~MODEMCONFIG3_LowDataRateOptimize;
+
+ if (m_settings.loraSettings.LowDatarateOptimize)
+ reg |= MODEMCONFIG3_LowDataRateOptimize;
+
+ writeReg(LOR_RegModemConfig3, reg);
+
+ writeReg(LOR_RegSymbTimeoutLsb, (uint8_t)(symbTimeout & 0xff));
+
+
+ writeReg(LOR_RegPreambleMsb, (uint8_t)((preambleLen >> 8) & 0xff));
+ writeReg(LOR_RegPreambleLsb, (uint8_t)(preambleLen & 0xff));
+
+ if (fixLen == 1)
+ writeReg(LOR_RegPayloadLength, payloadLen);
+
+ // The datasheet says this is only valid in FSK mode, but
+ // Semtech code indicates it is only available in LORA
+ // mode... So which is it?
+
+ // Lets assume for now that the code is correct, as there
+ // is a HopPeriod register for LoRa, and no such registers
+ // exist for FSK.
+ if (m_settings.loraSettings.FreqHopOn)
+ {
+ reg = readReg(LOR_RegPllHop);
+ reg &= ~PLLHOP_FastHopOn;
+ reg |= PLLHOP_FastHopOn;
+ writeReg(LOR_RegPllHop, reg);
+
+ writeReg(LOR_RegHopPeriod, m_settings.loraSettings.HopPeriod);
+ }
+ else
+ {
+ reg = readReg(LOR_RegPllHop);
+ reg &= ~PLLHOP_FastHopOn;
+ writeReg(LOR_RegPllHop, reg);
+ }
+
+ // errata checks - writing magic numbers into undocumented,
+ // reserved registers :) The Semtech code was broken in this
+ // logic.
+ if ( (bandwidth == BW_500) &&
+ (m_settings.channel > RF_MID_BAND_THRESH) )
+ {
+ // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz
+ // Bandwidth (HF)
+ writeReg(LOR_Reserved36, 0x02);
+ writeReg(LOR_Reserved3a, 0x64);
+ }
+ else if (bandwidth == BW_500 &&
+ (m_settings.channel >= 410000000))
+ {
+ // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz
+ // Bandwidth (LF above 410Mhz)
+ writeReg(LOR_Reserved36, 0x02);
+ writeReg(LOR_Reserved3a, 0x7f);
+ }
+ else
+ {
+ // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz
+ // Bandwidth (everything else)
+ writeReg(LOR_Reserved36, 0x03);
+ }
+
+ // datarate is really LORA spreading factor
+ if (datarate == 6)
+ {
+ // datarate == SPREADINGFACTOR_64
+ reg = readReg(LOR_RegDetectOptimize);
+ reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ reg |= (DETECTIONOPTIMIZE_SF6 <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ writeReg(LOR_RegDetectOptimize, reg);
+
+ // see page 27 in the datasheet
+ writeReg(LOR_RegDetectionThreshold, LOR_DetectionThreshold_SF6);
+ }
+ else
+ {
+ reg = readReg(LOR_RegDetectOptimize);
+ reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ reg |= (DETECTIONOPTIMIZE_SF7_SF12 <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ writeReg(LOR_RegDetectOptimize, reg);
+
+ // see page 27 in the datasheet
+ writeReg(LOR_RegDetectionThreshold,
+ LOR_DetectionThreshold_SF7_SF12);
+ }
+ }
+ break;
+ }
+}
+
+void SX1276::setTxConfig(RADIO_MODEM_T modem, int8_t power,
+ uint32_t fdev,
+ uint32_t bandwidth, uint32_t datarate,
+ uint8_t coderate, uint16_t preambleLen,
+ bool fixLen, bool crcOn, bool freqHopOn,
+ uint8_t hopPeriod, bool iqInverted)
+{
+ uint8_t paConfig = 0;
+ uint8_t paDac = 0;
+
+ setModem(modem);
+
+ paConfig = readReg(COM_RegPaConfig);
+ paDac = readReg(COM_RegPaDac);
+
+ uint8_t paSelect = 0x00; // default, +14dBm
+ if (m_settings.channel < RF_MID_BAND_THRESH)
+ paSelect = PACONFIG_PaSelect; // PA_BOOST, +20dBm
+
+ paConfig &= ~PACONFIG_PaSelect;
+ paConfig |= paSelect;
+ paConfig &= ~(_PACONFIG_MaxPower_MASK << _PACONFIG_MaxPower_SHIFT);
+ paConfig |= (7 << _PACONFIG_MaxPower_SHIFT); // PACONFIG_MaxPower = 7
+
+ if ((paConfig & PACONFIG_PaSelect))
+ {
+ if (power > 17)
+ {
+ paDac &= ~(_PADAC_PaDac_MASK << _PADAC_PaDac_SHIFT);
+ paDac |= (PADAC_BOOST << _PADAC_PaDac_SHIFT);
+ }
+ else
+ {
+ paDac &= ~(_PADAC_PaDac_MASK << _PADAC_PaDac_SHIFT);
+ paDac |= (PADAC_DEFAULT << _PADAC_PaDac_SHIFT);
+ }
+
+ if ((paDac & PADAC_BOOST) == PADAC_BOOST)
+ {
+ if (power < 5)
+ {
+ power = 5;
+ }
+ if (power > 20)
+ {
+ power = 20;
+ }
+ paConfig = ~(_PACONFIG_OutputPower_MASK & _PACONFIG_OutputPower_SHIFT);
+ paConfig |= ( ((uint8_t)(power - 5) &
+ _PACONFIG_OutputPower_MASK) <<
+ _PACONFIG_OutputPower_SHIFT );
+ }
+ else
+ {
+ if (power < 2)
+ {
+ power = 2;
+ }
+ if (power > 17)
+ {
+ power = 17;
+ }
+
+ paConfig = ~(_PACONFIG_OutputPower_MASK & _PACONFIG_OutputPower_SHIFT);
+ paConfig |= ( ((uint8_t)(power - 2) &
+ _PACONFIG_OutputPower_MASK) <<
+ _PACONFIG_OutputPower_SHIFT );
+ }
+ }
+ else
+ {
+ if (power < -1)
+ {
+ power = -1;
+ }
+ if (power > 14)
+ {
+ power = 14;
+ }
+
+ paConfig = ~(_PACONFIG_OutputPower_MASK & _PACONFIG_OutputPower_SHIFT);
+ paConfig |= ( ((uint8_t)(power + 1) &
+ _PACONFIG_OutputPower_MASK) <<
+ _PACONFIG_OutputPower_SHIFT );
+ }
+ writeReg(COM_RegPaConfig, paConfig);
+ writeReg(COM_RegPaDac, paDac);
+
+ uint8_t reg;
+
+ switch (modem)
+ {
+ case MODEM_FSK:
+ {
+ m_settings.fskSettings.Power = power;
+ m_settings.fskSettings.Fdev = fdev;
+ m_settings.fskSettings.Bandwidth = bandwidth;
+ m_settings.fskSettings.Datarate = datarate;
+ m_settings.fskSettings.PreambleLen = preambleLen;
+ m_settings.fskSettings.FixLen = fixLen;
+ m_settings.fskSettings.CrcOn = crcOn;
+ m_settings.fskSettings.IqInverted = iqInverted;
+
+ fdev = (uint16_t)((double)fdev / FXOSC_STEP);
+ writeReg(FSK_RegFdevMsb, (uint8_t)(fdev >> 8));
+ writeReg(FSK_RegFdevLsb, (uint8_t)(fdev & 0xFF));
+
+ datarate = (uint16_t)(FXOSC_FREQ / (double)datarate);
+ writeReg(FSK_RegBitrateMsb, (uint8_t)(datarate >> 8));
+ writeReg(FSK_RegBitrateLsb, (uint8_t)(datarate & 0xff));
+
+ writeReg(FSK_RegPreambleMsb, (uint8_t)(preambleLen >> 8));
+ writeReg(FSK_RegPreambleLsb, (uint8_t)(preambleLen & 0xff));
+
+ reg = readReg(FSK_RegPacketConfig1);
+ reg &= ~(PACKETCONFIG1_CrcOn | PACKETCONFIG1_PacketFormat);
+
+ if (!fixLen)
+ reg |= PACKETCONFIG1_PacketFormat; // variable len
+
+ if (crcOn)
+ reg |= PACKETCONFIG1_CrcOn;
+
+ writeReg(FSK_RegPacketConfig1, reg);
+ }
+ break;
+
+ case MODEM_LORA:
+ {
+ m_settings.loraSettings.Power = power;
+
+ // we convert bandwidth into appropriate BW_* constants for LORA
+ switch (bandwidth) {
+ case 125000:
+ bandwidth = BW_125;
+ break;
+
+ case 250000:
+ bandwidth = BW_250;
+ break;
+
+ case 500000:
+ bandwidth = BW_500;
+ break;
+
+ default:
+ throw std::runtime_error(std::string(__FUNCTION__) +
+ ": LORA bandwidth must be 125000, 250000, "
+ "or 500000");
+ }
+
+
+ m_settings.loraSettings.Bandwidth = bandwidth;
+ m_settings.loraSettings.Datarate = datarate;
+ m_settings.loraSettings.Coderate = coderate;
+ m_settings.loraSettings.PreambleLen = preambleLen;
+ m_settings.loraSettings.FixLen = fixLen;
+ m_settings.loraSettings.FreqHopOn = freqHopOn;
+ m_settings.loraSettings.HopPeriod = hopPeriod;
+ m_settings.loraSettings.CrcOn = crcOn;
+ m_settings.loraSettings.IqInverted = iqInverted;
+
+ // datarate is really SPREADINGFACTOR_* for LoRa
+ if (datarate > 12)
+ {
+ datarate = 12;
+ }
+ else if (datarate < 6)
+ {
+ datarate = 6;
+ }
+
+ if ( ((bandwidth == BW_125) && ((datarate == 11) ||
+ (datarate == 12))) ||
+ ((bandwidth == BW_250) && (datarate == 12)) )
+ {
+ m_settings.loraSettings.LowDatarateOptimize = true;
+ }
+ else
+ {
+ m_settings.loraSettings.LowDatarateOptimize = false;
+ }
+
+
+ // datasheet says this is only valid in FSK mode, but Semtech
+ // code indicates it is only available in LORA mode... So
+ // which is it?
+
+ // Lets assume for now that the code is correct, as there
+ // is a HopPeriod register for LoRa, and no such registers
+ // exist for FSK.
+ if (m_settings.loraSettings.FreqHopOn == true)
+ {
+ reg = readReg(LOR_RegPllHop);
+ reg &= ~PLLHOP_FastHopOn;
+ reg |= PLLHOP_FastHopOn;
+ writeReg(LOR_RegPllHop, reg);
+
+ writeReg(LOR_RegHopPeriod, m_settings.loraSettings.HopPeriod);
+ }
+ else
+ {
+ reg = readReg(LOR_RegPllHop);
+ reg &= ~PLLHOP_FastHopOn;
+ writeReg(LOR_RegPllHop, reg);
+ }
+
+ reg = readReg(LOR_RegModemConfig1);
+ reg &= ~((_MODEMCONFIG1_CodingRate_MASK <<
+ _MODEMCONFIG1_CodingRate_SHIFT) |
+ (_MODEMCONFIG1_Bw_MASK << _MODEMCONFIG1_Bw_SHIFT) |
+ MODEMCONFIG1_ImplicitHeaderModeOn);
+
+ if (fixLen)
+ reg |= MODEMCONFIG1_ImplicitHeaderModeOn;
+
+ reg |= ((bandwidth & _MODEMCONFIG1_Bw_MASK) << _MODEMCONFIG1_Bw_SHIFT);
+ reg |= ((coderate & _MODEMCONFIG1_CodingRate_MASK) <<
+ _MODEMCONFIG1_CodingRate_SHIFT);
+
+ writeReg(LOR_RegModemConfig1, reg);
+
+ reg = readReg(LOR_RegModemConfig2);
+ reg &= ~((_MODEMCONFIG2_SpreadingFactor_MASK <<
+ _MODEMCONFIG2_SpreadingFactor_SHIFT) |
+ MODEMCONFIG2_RxPayloadCrcOn);
+
+ if (crcOn)
+ reg |= MODEMCONFIG2_RxPayloadCrcOn;
+
+ reg |= ((datarate & _MODEMCONFIG2_SpreadingFactor_MASK) <<
+ _MODEMCONFIG2_SpreadingFactor_SHIFT);
+ writeReg(LOR_RegModemConfig2, reg);
+
+ reg = readReg(LOR_RegModemConfig3);
+
+ reg &= ~MODEMCONFIG3_LowDataRateOptimize;
+
+ if (m_settings.loraSettings.LowDatarateOptimize)
+ reg |= MODEMCONFIG3_LowDataRateOptimize;
+
+ writeReg(LOR_RegModemConfig3, reg);
+
+ writeReg(LOR_RegPreambleMsb, (uint8_t)((preambleLen >> 8) & 0xff));
+ writeReg(LOR_RegPreambleLsb, (uint8_t)(preambleLen & 0xff));
+
+ // datarate is SPREADINGFACTOR_*
+ if (datarate == 6)
+ {
+ reg = readReg(LOR_RegDetectOptimize);
+ reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ reg |= (DETECTIONOPTIMIZE_SF6 <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ writeReg(LOR_RegDetectOptimize, reg);
+
+ // see page 27 in the datasheet
+ writeReg(LOR_RegDetectionThreshold, LOR_DetectionThreshold_SF6);
+
+ }
+ else
+ {
+ reg = readReg(LOR_RegDetectOptimize);
+ reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ reg |= (DETECTIONOPTIMIZE_SF7_SF12 <<
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT);
+
+ writeReg(LOR_RegDetectOptimize, reg);
+
+ // see page 27 in the datasheet
+ writeReg(LOR_RegDetectionThreshold,
+ LOR_DetectionThreshold_SF7_SF12);
+ }
+ }
+
+ break;
+ }
+}
+
+SX1276::RADIO_EVENT_T SX1276::setTx(int timeout)
+{
+ uint8_t reg = 0;
+
+ switch (m_settings.modem)
+ {
+ case MODEM_FSK:
+ {
+ // DIO0=PacketSent
+ // DIO1=FifoLevel
+ // DIO2=FifoFull
+ // DIO3=FifoEmpty
+ // DIO4=LowBat
+ // DIO5=ModeReady
+
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK <<
+ DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DOIMAPPING1_Dio2Mapping_MASK <<
+ DOIMAPPING1_Dio2Mapping_SHIFT) );
+
+ writeReg(COM_RegDioMapping1, reg);
+
+
+ reg = readReg(COM_RegDioMapping2);
+ reg &= ~( (DOIMAPPING2_Dio4Mapping_MASK <<
+ DOIMAPPING2_Dio4Mapping_SHIFT) |
+ (DOIMAPPING2_Dio5Mapping_MASK <<
+ DOIMAPPING2_Dio5Mapping_SHIFT) );
+
+ writeReg(COM_RegDioMapping2, reg);
+
+ m_settings.fskPacketHandler.FifoThresh =
+ (readReg(FSK_RegFifoThresh) &
+ (_FIFOTHRESH_FifoThreshold_MASK << _FIFOTHRESH_FifoThreshold_SHIFT));
+ }
+
+ break;
+
+ case MODEM_LORA:
+ {
+ if (m_settings.loraSettings.FreqHopOn == true )
+ {
+ // mask out all except TxDone and FhssChangeChannel
+ writeReg(LOR_RegIrqFlagsMask,
+ LOR_IRQFLAG_RxTimeout |
+ LOR_IRQFLAG_RxDone |
+ LOR_IRQFLAG_PayloadCrcError |
+ LOR_IRQFLAG_ValidHeader |
+ // LOR_IRQFLAG_TxDone |
+ LOR_IRQFLAG_CadDone |
+ // LOR_IRQFLAG_FhssChangeChannel |
+ LOR_IRQFLAG_CadDetected);
+
+ // DIO0=TxDone, DIO2=FhssChangeChannel
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK <<
+ DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DOIMAPPING1_Dio2Mapping_MASK <<
+ DOIMAPPING1_Dio2Mapping_SHIFT) );
+ reg |= ( (DIOMAPPING_01 << DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DIOMAPPING_00 << DOIMAPPING1_Dio2Mapping_SHIFT) );
+ writeReg(COM_RegDioMapping1, reg);
+ }
+ else
+ {
+ // mask out all except TxDone
+ writeReg(LOR_RegIrqFlagsMask,
+ LOR_IRQFLAG_RxTimeout |
+ LOR_IRQFLAG_RxDone |
+ LOR_IRQFLAG_PayloadCrcError |
+ LOR_IRQFLAG_ValidHeader |
+ // LOR_IRQFLAG_TxDone |
+ LOR_IRQFLAG_CadDone |
+ LOR_IRQFLAG_FhssChangeChannel |
+ LOR_IRQFLAG_CadDetected);
+
+ // DIO0=TxDone
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK <<
+ DOIMAPPING1_Dio0Mapping_SHIFT) );
+ reg |= (DIOMAPPING_01 << DOIMAPPING1_Dio0Mapping_SHIFT);
+ writeReg(COM_RegDioMapping1, reg);
+ }
+ }
+ break;
+ }
+
+ m_settings.state = STATE_TX_RUNNING;
+ m_radioEvent = REVENT_EXEC;
+
+ setOpMode(MODE_TxMode);
+
+ initClock();
+ while ((getMillis() < timeout) && m_radioEvent == REVENT_EXEC)
+ usleep(100);
+
+ if (m_radioEvent == REVENT_EXEC)
+ {
+ // timeout
+ m_radioEvent = REVENT_TIMEOUT;
+ }
+
+ return m_radioEvent;
+}
+
+SX1276::RADIO_EVENT_T SX1276::setRx(uint32_t timeout)
+{
+ bool rxContinuous = false;
+ uint8_t reg = 0;
+
+ switch (m_settings.modem)
+ {
+ case MODEM_FSK:
+ {
+ rxContinuous = m_settings.fskSettings.RxContinuous;
+
+ // DIO0=PayloadReady
+ // DIO1=FifoLevel
+ // DIO2=SyncAddr
+ // DIO3=FifoEmpty
+ // DIO4=Preamble
+ // DIO5=ModeReady
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK <<
+ DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DOIMAPPING1_Dio2Mapping_MASK <<
+ DOIMAPPING1_Dio2Mapping_SHIFT) );
+ reg |= ( (DIOMAPPING_00 << DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DIOMAPPING_11 << DOIMAPPING1_Dio2Mapping_SHIFT) );
+ writeReg(COM_RegDioMapping1, reg);
+
+
+ reg = readReg(COM_RegDioMapping2);
+ reg &= ~( (DOIMAPPING2_Dio4Mapping_MASK <<
+ DOIMAPPING2_Dio4Mapping_SHIFT) |
+ (DOIMAPPING2_Dio5Mapping_MASK <<
+ DOIMAPPING2_Dio5Mapping_SHIFT) );
+ reg |= (DIOMAPPING_11 << DOIMAPPING2_Dio4Mapping_SHIFT) |
+ DOIMAPPING2_MapPreambleDetect;
+
+ writeReg(COM_RegDioMapping2, reg);
+
+ m_settings.fskPacketHandler.FifoThresh =
+ (readReg(FSK_RegFifoThresh) & _FIFOTHRESH_FifoThreshold_MASK);
+
+ m_settings.fskPacketHandler.PreambleDetected = false;
+ m_settings.fskPacketHandler.SyncWordDetected = false;
+ m_settings.fskPacketHandler.NbBytes = 0;
+ m_settings.fskPacketHandler.Size = 0;
+ }
+
+ break;
+
+ case MODEM_LORA:
+ {
+ // The datasheet does not mention anything other than an
+ // InvertIQ bit (0x40) in RegInvertIQ register (0x33). Here,
+ // we seem to have two bits in RegInvertIQ (existing one for
+ // RX), and a 'new' one for TXOff (0x01). In addition,
+ // INVERTIQ2 (0x3b) does not exist in the datasheet, it is
+ // marked as reserved. We will assume that the datasheet is
+ // out of date.
+
+ if (m_settings.loraSettings.IqInverted == true)
+ {
+ reg = readReg(LOR_RegInvertIQ);
+ reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx);
+ reg |= INVERTIQ_InvertIQRx;
+ writeReg(LOR_RegInvertIQ, reg);
+
+ // warning, hardcoded undocumented magic number into
+ // undocumented register
+ writeReg(LOR_RegInvertIQ2, 0x19);
+ }
+ else
+ {
+ reg = readReg(LOR_RegInvertIQ);
+ reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx);
+ reg |= INVERTIQ_InvertIQTxOff; // 'active' off.
+ writeReg(LOR_RegInvertIQ, reg);
+
+ // warning, hardcoded undocumented magic number into
+ // undocumented register
+ writeReg(LOR_RegInvertIQ2, 0x1d);
+ }
+
+ // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal
+ if (m_settings.loraSettings.Bandwidth < 9)
+ {
+ reg = readReg(LOR_RegDetectOptimize);
+ reg &= 0x7f; // clear undocumented bit 7
+ writeReg(LOR_RegDetectOptimize, reg);
+
+ // warning, writing magic numbers into undocumented
+ // registers
+ switch (m_settings.loraSettings.Bandwidth)
+ {
+ case 0: // 7.8 kHz
+ writeReg(LOR_Reserved2f, 0x48);
+ setChannel(m_settings.channel + 7.81e3);
+ break;
+ case 1: // 10.4 kHz
+ writeReg(LOR_Reserved2f, 0x44);
+ setChannel(m_settings.channel + 10.42e3);
+ break;
+ case 2: // 15.6 kHz
+ writeReg(LOR_Reserved2f, 0x44);
+ setChannel(m_settings.channel + 15.62e3);
+ break;
+ case 3: // 20.8 kHz
+ writeReg(LOR_Reserved2f, 0x44);
+ setChannel(m_settings.channel + 20.83e3);
+ break;
+ case 4: // 31.2 kHz
+ writeReg(LOR_Reserved2f, 0x44);
+ setChannel(m_settings.channel + 31.25e3);
+ break;
+ case 5: // 41.4 kHz
+ writeReg(LOR_Reserved2f, 0x44);
+ setChannel(m_settings.channel + 41.67e3);
+ break;
+ case 6: // 62.5 kHz
+ writeReg(LOR_Reserved2f, 0x40);
+ break;
+ case 7: // 125 kHz
+ writeReg(LOR_Reserved2f, 0x40);
+ break;
+ case 8: // 250 kHz
+ writeReg(LOR_Reserved2f, 0x40);
+ break;
+ }
+ }
+ else
+ {
+ reg = readReg(LOR_RegDetectOptimize);
+ reg |= 0x80; // set undocumented bit 7
+ writeReg(LOR_RegDetectOptimize, reg);
+ }
+
+ rxContinuous = m_settings.loraSettings.RxContinuous;
+
+ if (m_settings.loraSettings.FreqHopOn == true)
+ {
+ // mask out all except RxDone, RxTimeout, PayloadCrCError,
+ // and FhssChangeChannel
+ writeReg(LOR_RegIrqFlagsMask,
+ // LOR_IRQFLAG_RxTimeout |
+ // LOR_IRQFLAG_RxDone |
+ // LOR_IRQFLAG_PayloadCrcError |
+ LOR_IRQFLAG_ValidHeader |
+ LOR_IRQFLAG_TxDone |
+ LOR_IRQFLAG_CadDone |
+ // LOR_IRQFLAG_FhssChangeChannel |
+ LOR_IRQFLAG_CadDetected);
+
+ // DIO0=RxDone, DIO2=FhssChangeChannel
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK <<
+ DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DOIMAPPING1_Dio2Mapping_MASK <<
+ DOIMAPPING1_Dio2Mapping_SHIFT) );
+ reg |= ( (DIOMAPPING_00 << DOIMAPPING1_Dio0Mapping_SHIFT) |
+ (DIOMAPPING_00 << DOIMAPPING1_Dio2Mapping_SHIFT) );
+ writeReg(COM_RegDioMapping1, reg);
+ }
+ else
+ {
+ // mask out all except RxDone, RxTimeout, and PayloadCrCError
+ writeReg(LOR_RegIrqFlagsMask,
+ // LOR_IRQFLAG_RxTimeout |
+ // LOR_IRQFLAG_RxDone |
+ // LOR_IRQFLAG_PayloadCrcError |
+ LOR_IRQFLAG_ValidHeader |
+ LOR_IRQFLAG_TxDone |
+ LOR_IRQFLAG_CadDone |
+ LOR_IRQFLAG_FhssChangeChannel |
+ LOR_IRQFLAG_CadDetected);
+
+ // DIO0=RxDone
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~(DOIMAPPING1_Dio0Mapping_MASK <<
+ DOIMAPPING1_Dio0Mapping_SHIFT);
+ reg |= (DIOMAPPING_00 << DOIMAPPING1_Dio0Mapping_SHIFT);
+ writeReg(COM_RegDioMapping1, reg);
+ }
+
+ writeReg(LOR_RegFifoRxBaseAddr, 0);
+ writeReg(LOR_RegFifoAddrPtr, 0);
+ }
+
+ break;
+
+ }
+
+ memset(m_rxBuffer, 0, FIFO_SIZE);
+
+ m_settings.state = STATE_RX_RUNNING;
+ m_radioEvent = REVENT_EXEC;
+
+ if (m_settings.modem == MODEM_FSK)
+ {
+ setOpMode(MODE_FSK_RxMode);
+
+ if (rxContinuous == false)
+ {
+ // timer..?
+#if 0
+ TimerSetValue( &RxTimeoutSyncWord, ( 8.0 * ( m_settings.fsk.PreambleLen +
+ ( ( SX1276Read( REG_SYNCCONFIG ) &
+ ~RF_SYNCCONFIG_SYNCSIZE_MASK ) +
+ 1.0 ) + 10.0 ) /
+ ( double )m_settings.fsk.Datarate ) * 1e6 );
+ TimerStart( &RxTimeoutSyncWord );
+#endif
+ }
+ }
+ else
+ {
+ // LoRa
+
+ if (rxContinuous == true)
+ {
+ setOpMode(MODE_LOR_RxContinuous);
+ }
+ else
+ {
+ setOpMode(MODE_LOR_RxSingle);
+ }
+ }
+
+ initClock();
+ while ((getMillis() < timeout) && m_radioEvent == REVENT_EXEC)
+ usleep(100);
+
+ if (m_radioEvent == REVENT_EXEC)
+ {
+ // timeout
+ m_radioEvent = REVENT_TIMEOUT;
+ }
+
+ return m_radioEvent;
+}
+
+
+void SX1276::startCAD()
+{
+ switch (m_settings.modem)
+ {
+ case MODEM_LORA:
+ {
+ // mask out all except CadDone and CadDetected
+ writeReg(LOR_RegIrqFlagsMask,
+ LOR_IRQFLAG_RxTimeout |
+ LOR_IRQFLAG_RxDone |
+ LOR_IRQFLAG_PayloadCrcError |
+ LOR_IRQFLAG_ValidHeader |
+ LOR_IRQFLAG_TxDone |
+ // LOR_IRQFLAG_CadDone |
+ LOR_IRQFLAG_FhssChangeChannel //|
+ // LOR_IRQFLAG_CadDetected
+ );
+
+ // DIO3=CADDone
+
+ uint8_t reg;
+ reg = readReg(COM_RegDioMapping1);
+ reg &= ~(DOIMAPPING1_Dio3Mapping_MASK <<
+ DOIMAPPING1_Dio3Mapping_SHIFT);
+ reg |= (DIOMAPPING_00 << DOIMAPPING1_Dio3Mapping_SHIFT);
+ writeReg(COM_RegDioMapping1, reg);
+
+ m_settings.state = STATE_CAD;
+ setOpMode(MODE_LOR_CAD);
+ }
+
+ break;
+
+ case MODEM_FSK:
+ default:
+ break;
+ }
+}
+
+void SX1276::setMaxPayloadLength(RADIO_MODEM_T modem, uint8_t max)
+{
+ setModem(modem);
+
+ switch (modem)
+ {
+ case MODEM_FSK:
+ if (m_settings.fskSettings.FixLen == false)
+ {
+ writeReg(FSK_RegPayloadLength, max);
+ }
+
+ break;
+
+ case MODEM_LORA:
+ writeReg(LOR_RegMaxPayloadLength, max);
+
+ break;
+ }
+}
+
+
+void SX1276::onDio0Irq(void *ctx)
+{
+ upm::SX1276 *This = (upm::SX1276 *)ctx;
+
+ This->lockIntrs();
+
+ volatile uint8_t irqFlags = 0;
+
+ // cerr << __FUNCTION__ << ": Enter" << endl;
+
+ switch (This->m_settings.state)
+ {
+ case STATE_RX_RUNNING:
+
+ // RxDone interrupt
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+
+ if (This->m_settings.fskSettings.CrcOn == true )
+ {
+ irqFlags = This->readReg(FSK_RegIrqFlags2);
+
+ if (!(irqFlags & IRQFLAGS2_CrcOk))
+ {
+ // Clear Irqs
+ This->writeReg(FSK_RegIrqFlags1,
+ IRQFLAGS1_Rssi |
+ IRQFLAGS1_PreambleDetect |
+ IRQFLAGS1_SyncAddressMatch);
+ This->writeReg(FSK_RegIrqFlags2, IRQFLAGS2_FifoOverrun);
+
+ if (This->m_settings.fskSettings.RxContinuous == false )
+ {
+ This->m_settings.state = STATE_IDLE;
+ }
+ else
+ {
+ // Continuous mode restart Rx chain
+ This->writeReg(FSK_RegRxConfig,
+ This->readReg(FSK_RegRxConfig) |
+ RXCONFIG_RestartRxWithoutPllLock);
+ }
+
+ // RxError radio event
+ // cerr << __FUNCTION__ << ": RxError crc/sync timeout" << endl;
+ This->m_radioEvent = REVENT_ERROR;
+
+ This->m_settings.fskPacketHandler.PreambleDetected = false;
+ This->m_settings.fskPacketHandler.SyncWordDetected = false;
+ This->m_settings.fskPacketHandler.NbBytes = 0;
+ This->m_settings.fskPacketHandler.Size = 0;
+ break;
+ }
+ }
+
+ // Read received packet size
+ if ( (This->m_settings.fskPacketHandler.Size == 0) &&
+ (This->m_settings.fskPacketHandler.NbBytes == 0) )
+ {
+ if (This->m_settings.fskSettings.FixLen == false )
+ {
+ This->readFifo((uint8_t*)&(This->m_settings.fskPacketHandler.Size),
+ 1);
+ }
+ else
+ {
+ This->m_settings.fskPacketHandler.Size =
+ This->readReg(FSK_RegPayloadLength);
+ }
+
+ This->readFifo(This->m_rxBuffer +
+ This->m_settings.fskPacketHandler.NbBytes,
+ This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+
+ This->m_settings.fskPacketHandler.NbBytes +=
+ (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+ }
+ else
+ {
+ This->readFifo(This->m_rxBuffer +
+ This->m_settings.fskPacketHandler.NbBytes,
+ (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes));
+
+ This->m_settings.fskPacketHandler.NbBytes +=
+ (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+ }
+
+ if (This->m_settings.fskSettings.RxContinuous == false)
+ {
+ This->m_settings.state = STATE_IDLE;
+ }
+ else
+ {
+ // Continuous mode restart Rx chain
+ This->writeReg(FSK_RegRxConfig,
+ This->readReg(FSK_RegRxConfig) |
+ RXCONFIG_RestartRxWithoutPllLock);
+ }
+
+ // RxDone radio event
+ This->m_rxRSSI = This->m_settings.fskPacketHandler.RssiValue;
+ This->m_rxLen = This->m_settings.fskPacketHandler.Size;
+ This->m_radioEvent = REVENT_DONE;
+ // cerr << __FUNCTION__ << ": FSK RxDone" << endl;
+ // fprintf(stderr, "### %s: RX(%d): %s\n",
+ // __FUNCTION__,
+ // This->m_settings.fskPacketHandler.Size,
+ // This->m_rxBuffer);
+
+ This->m_settings.fskPacketHandler.PreambleDetected = false;
+ This->m_settings.fskPacketHandler.SyncWordDetected = false;
+ This->m_settings.fskPacketHandler.NbBytes = 0;
+ This->m_settings.fskPacketHandler.Size = 0;
+
+ break;
+
+ case MODEM_LORA:
+ {
+ int8_t snr = 0;
+
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_RxDone);
+
+ irqFlags = This->readReg(LOR_RegIrqFlags);
+
+ // cerr << "LORA PayloadCRC on = "
+ // << hex << (int)This->readReg(LOR_RegHopChannel) << dec << endl;
+ if (irqFlags & LOR_IRQFLAG_PayloadCrcError)
+ {
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_PayloadCrcError);
+ if (This->m_settings.loraSettings.RxContinuous == false)
+ {
+ This->m_settings.state = STATE_IDLE;
+ }
+ // RxError radio event
+ // cerr << __FUNCTION__ << ": RxError (payload crc error)" << endl;
+ This->m_radioEvent = REVENT_ERROR;
+
+ break;
+ }
+
+ This->m_settings.loraPacketHandler.SnrValue =
+ This->readReg(LOR_RegPktSnrValue);
+
+ if (This->m_settings.loraPacketHandler.SnrValue & 0x80)
+ {
+ // The SNR sign bit is 1
+ // Invert and divide by 4
+ snr = ( (~(This->m_settings.loraPacketHandler.SnrValue) + 1 ) &
+ 0xff) >> 2;
+ snr = -snr;
+ }
+ else
+ {
+ // Divide by 4
+ snr = (This->m_settings.loraPacketHandler.SnrValue & 0xff) >> 2;
+ }
+
+ int16_t rssi = This->readReg(LOR_RegPktRssiValue);
+
+ if (snr < 0)
+ {
+ if (This->m_settings.channel > RF_MID_BAND_THRESH)
+ {
+ This->m_settings.loraPacketHandler.RssiValue =
+ LOR_RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) + snr;
+ }
+ else
+ {
+ This->m_settings.loraPacketHandler.RssiValue =
+ LOR_RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) + snr;
+ }
+ }
+ else
+ {
+ if (This->m_settings.channel > RF_MID_BAND_THRESH)
+ {
+ This->m_settings.loraPacketHandler.RssiValue =
+ LOR_RSSI_OFFSET_HF + rssi + (rssi >> 4);
+ }
+ else
+ {
+ This->m_settings.loraPacketHandler.RssiValue =
+ LOR_RSSI_OFFSET_LF + rssi + (rssi >> 4);
+ }
+ }
+
+ This->m_settings.loraPacketHandler.Size =
+ This->readReg(LOR_RegRxNbBytes);
+
+ // cerr << "LORA HANDLER SIZE = "
+ // << (int)This->m_settings.loraPacketHandler.Size << endl;
+
+ // cerr << "LORA MAXPAYLOAD = "
+ // << (int)This->readReg(LOR_RegMaxPayloadLength) << endl;
+
+ This->readFifo(This->m_rxBuffer,
+ This->m_settings.loraPacketHandler.Size);
+
+ if (This->m_settings.loraSettings.RxContinuous == false)
+ {
+ This->m_settings.state = STATE_IDLE;
+ }
+
+ // RxDone radio event
+
+ // The returned size (from LOR_RegRxNbBytes) is always 64
+ // bytes regardless of the packet size I sent. Something
+ // is wrong here.
+ // cerr << __FUNCTION__ << ": RxDone (LORA)" << endl;
+ This->m_rxRSSI = (int)rssi;
+ This->m_rxSNR = (int)snr;
+ This->m_rxLen = This->m_settings.loraPacketHandler.Size;
+ This->m_radioEvent = REVENT_DONE;
+ // if (This->m_settings.state == STATE_RX_RUNNING)
+ // fprintf(stderr, "### %s: snr = %d rssi = %d RX(%d): %s\n",
+ // __FUNCTION__,
+ // (int)snr, (int)rssi,
+ // This->m_settings.loraPacketHandler.Size,
+ // This->m_rxBuffer);
+ // else
+ // fprintf(stderr, "### %s: snr = %d rssi = %d RX: INV BUFFER (crc)\n", __FUNCTION__,
+ // (int)snr, (int)rssi);
+
+ }
+
+ break;
+
+ default:
+ break;
+
+ }
+
+ break;
+
+ case STATE_TX_RUNNING:
+
+ // TxDone interrupt
+ switch (This->m_settings.modem)
+ {
+ case MODEM_LORA:
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_TxDone);
+ // fprintf(stderr, "%s: LORA IrqFlags = %02x\n", __FUNCTION__,
+ // This->readReg(LOR_RegIrqFlags));
+ // Intentional fall through
+
+ case MODEM_FSK:
+ default:
+ This->m_settings.state = STATE_IDLE;
+
+ // TxDone radio event
+ This->m_radioEvent = REVENT_DONE;
+ // cerr << __FUNCTION__ << ": TxDone" << endl;
+
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ This->unlockIntrs();
+}
+
+
+void SX1276::onDio1Irq(void *ctx)
+{
+ upm::SX1276 *This = (upm::SX1276 *)ctx;
+
+ This->lockIntrs();
+ // cerr << __FUNCTION__ << ": Enter" << endl;
+
+ switch (This->m_settings.state)
+ {
+ case STATE_RX_RUNNING:
+
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+ // FifoLevel interrupt
+ // Read received packet size
+ if ( (This->m_settings.fskPacketHandler.Size == 0 ) &&
+ (This->m_settings.fskPacketHandler.NbBytes == 0) )
+ {
+ if (This->m_settings.fskSettings.FixLen == false)
+ {
+ This->readFifo((uint8_t*)&(This->m_settings.fskPacketHandler.Size),
+ 1);
+ }
+ else
+ {
+ This->m_settings.fskPacketHandler.Size =
+ This->readReg(FSK_RegPayloadLength);
+ }
+ }
+
+ if ( (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes) >
+ This->m_settings.fskPacketHandler.FifoThresh)
+ {
+ This->readFifo((This->m_rxBuffer +
+ This->m_settings.fskPacketHandler.NbBytes),
+ This->m_settings.fskPacketHandler.FifoThresh);
+ This->m_settings.fskPacketHandler.NbBytes +=
+ This->m_settings.fskPacketHandler.FifoThresh;
+ }
+ else
+ {
+ This->readFifo((This->m_rxBuffer +
+ This->m_settings.fskPacketHandler.NbBytes),
+ This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+ This->m_settings.fskPacketHandler.NbBytes +=
+ (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+ }
+
+ break;
+
+ case MODEM_LORA:
+ // Sync time out
+ This->m_settings.state = STATE_IDLE;
+ // RxError (LORA timeout) radio events
+ // cerr << __FUNCTION__ << ": RxTimeout (LORA)" << endl;
+ This->m_radioEvent = REVENT_TIMEOUT;
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case STATE_TX_RUNNING:
+
+ switch (This->m_settings.modem )
+ {
+ case MODEM_FSK:
+ // FifoLevel interrupt
+ if ( (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes) >
+ This->m_settings.fskPacketHandler.ChunkSize)
+ {
+ This->writeFifo((This->m_rxBuffer +
+ This->m_settings.fskPacketHandler.NbBytes),
+ This->m_settings.fskPacketHandler.ChunkSize);
+ This->m_settings.fskPacketHandler.NbBytes +=
+ This->m_settings.fskPacketHandler.ChunkSize;
+ }
+ else
+ {
+ // Write the last chunk of data
+ This->writeFifo((This->m_rxBuffer +
+ This->m_settings.fskPacketHandler.NbBytes),
+ This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+ This->m_settings.fskPacketHandler.NbBytes +=
+ (This->m_settings.fskPacketHandler.Size -
+ This->m_settings.fskPacketHandler.NbBytes);
+ }
+ break;
+
+ case MODEM_LORA:
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ This->unlockIntrs();
+}
+
+void SX1276::onDio2Irq(void *ctx)
+{
+ upm::SX1276 *This = (upm::SX1276 *)ctx;
+
+ This->lockIntrs();
+ // cerr << __FUNCTION__ << ": Enter" << endl;
+
+ switch (This->m_settings.state)
+ {
+ case STATE_RX_RUNNING:
+
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+ if ( (This->m_settings.fskPacketHandler.PreambleDetected == true ) &&
+ (This->m_settings.fskPacketHandler.SyncWordDetected == false) )
+ {
+ This->m_settings.fskPacketHandler.SyncWordDetected = true;
+
+ This->m_settings.fskPacketHandler.RssiValue =
+ -(This->readReg(FSK_RegRssiValue) >> 1 );
+
+ This->m_settings.fskPacketHandler.AfcValue =
+ (int32_t)(double)( ((uint16_t)This->readReg(FSK_RegAfcMsb) << 8 ) |
+ (uint16_t)This->readReg(FSK_RegAfcLsb) ) *
+ FXOSC_STEP;
+ This->m_settings.fskPacketHandler.RxGain =
+ (This->readReg(COM_RegLna) >> 5) & 0x07;
+ }
+
+ break;
+
+ case MODEM_LORA:
+ if (This->m_settings.loraSettings.FreqHopOn == true)
+ {
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_FhssChangeChannel);
+
+ // Fhss radio event (unsupported currently)
+ // FhssChangeChannel( (readReg( LOR_RegHopChannel) &
+ // ~_HOPCHANNEL_FhssPresentChannel_MASK) );
+ //cerr << __FUNCTION__ << ": Fhss Change Channel (LORA, RX running)" << endl;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case STATE_TX_RUNNING:
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+ break;
+ case MODEM_LORA:
+ if (This->m_settings.loraSettings.FreqHopOn == true)
+ {
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_FhssChangeChannel);
+
+ // Fhss radio event (unsupported currently)
+ // FhssChangeChannel( (readReg( LOR_RegHopChannel) &
+ // ~_HOPCHANNEL_FhssPresentChannel_MASK) );
+ //cerr << __FUNCTION__ << ": Fhss Change Channel (LORA, TX running)" << endl;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ This->unlockIntrs();
+}
+
+void SX1276::onDio3Irq(void *ctx)
+{
+ upm::SX1276 *This = (upm::SX1276 *)ctx;
+
+ This->lockIntrs();
+ // cerr << __FUNCTION__ << ": Enter" << endl;
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+ break;
+
+ case MODEM_LORA:
+ if (This->readReg(LOR_RegIrqFlags) & LOR_IRQFLAG_CadDetected)
+ {
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags,
+ (LOR_IRQFLAG_CadDetected | LOR_IRQFLAG_CadDone));
+
+ // CADDetected radio event (true)
+ // cerr << __FUNCTION__ << ": CadDetected (LORA)" << endl;
+
+ }
+ else
+ {
+ // Clear Irq
+ This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_CadDone);
+ // CADDetected radio event (false)
+ //cerr << __FUNCTION__ << ": CadDone (LORA)" << endl;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ This->unlockIntrs();
+}
+
+void SX1276::onDio4Irq(void *ctx)
+{
+ upm::SX1276 *This = (upm::SX1276 *)ctx;
+
+ This->lockIntrs();
+ // cerr << __FUNCTION__ << ": Enter" << endl;
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+ {
+ if (This->m_settings.fskPacketHandler.PreambleDetected == false)
+ {
+ This->m_settings.fskPacketHandler.PreambleDetected = true;
+ }
+ }
+
+ break;
+
+ case MODEM_LORA:
+ break;
+
+ default:
+ break;
+ }
+
+ This->unlockIntrs();
+}
+
+void SX1276::onDio5Irq(void *ctx)
+{
+ upm::SX1276 *This = (upm::SX1276 *)ctx;
+
+ This->lockIntrs();
+ // cerr << __FUNCTION__ << ": Enter" << endl;
+ switch (This->m_settings.modem)
+ {
+ case MODEM_FSK:
+ break;
+ case MODEM_LORA:
+ // fprintf(stderr, "%s: LORA IrqFlags = %02x\n", __FUNCTION__,
+ // This->readReg(LOR_RegIrqFlags));
+ break;
+ default:
+ break;
+ }
+
+ This->unlockIntrs();
+}
+
+void SX1276::initClock()
+{
+ gettimeofday(&m_startTime, NULL);
+}
+
+uint32_t SX1276::getMillis()
+{
+ struct timeval elapsed, now;
+ uint32_t elapse;
+
+ // get current time
+ gettimeofday(&now, NULL);
+
+ // compute the delta since m_startTime
+ if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 )
+ {
+ elapsed.tv_usec += 1000000;
+ elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1;
+ }
+ else
+ {
+ elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec;
+ }
+
+ elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000));
+
+ // never return 0
+ if (elapse == 0)
+ elapse = 1;
+
+ return elapse;
+}
+
+
diff --git a/src/sx1276/sx1276.h b/src/sx1276/sx1276.h
new file mode 100644
index 0000000..bf4cd49
--- /dev/null
+++ b/src/sx1276/sx1276.h
@@ -0,0 +1,2035 @@
+/*
+ * Author: Jon Trulson <jtrulson@ics.com>
+ * Copyright (c) 2015 Intel Corporation.
+ *
+ * Thanks to Semtech for their example code at:
+ * https://github.com/Lora-net/LoRaMac-node
+ * released under a modified BSD license, for many clues as to how to
+ * initialize and operate this radio properly.
+ * See src/sx1276/LICENSE.txt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+
+#include <mraa/common.hpp>
+#include <mraa/spi.hpp>
+#include <mraa/gpio.hpp>
+
+namespace upm {
+
+ /**
+ * @brief SX1276 LoRa/FSK modem
+ * @defgroup sx1276 libupm-sx1276
+ * @ingroup spi gpio wifi
+ */
+
+ /**
+ * @library sx1276
+ * @sensor sx1276
+ * @comname SX1276 LoRa/FSK modem
+ * @altname SX1277 SX1278 SX1279
+ * @type wifi
+ * @man semtech
+ * @con spi gpio
+ * @web http://www.digikey.com/product-search/en?vendor=0&keywords=SX1276MB1LAS
+ *
+ * @brief API for the SX1276 LoRa/FSK modem
+ *
+ * The SX1276 is a FSK/OOK/LoRa modem capable of both Low Frequency
+ * and High Frequency communication.
+ *
+ * It requires a 3.3v power supply, do not use 5v.
+ *
+ * Frequency Hopping Spread Spectrum (FHSS) is not currently supported.
+ *
+ * While not all of the functionality of this device is supported
+ * initially, methods and register definitions are provided that
+ * should allow an end user to implement whatever features are
+ * required.
+ *
+ * FSK send/receive example
+ * @snippet sx1276-fsk.cxx Interesting
+ * LORA send/receive example
+ * @snippet sx1276-lora.cxx Interesting
+ */
+
+ class SX1276 {
+ public:
+
+ // The default chip revision
+ static const uint8_t chipRevision = 0x12;
+
+ // Our crystal oscillator frequency (32Mhz)
+ static const double FXOSC_FREQ = 32000000.0;
+
+ // Our freq stepping resolution (in Hz) if FXOSC_FREQ is 32Mhz
+ // (FXOSC_FREQ / 2^19) =
+ static const double FXOSC_STEP = 61.03515625;
+
+ // total FIFO size
+ static const int FIFO_SIZE = 256;
+
+ // differentiator between high and low bands
+ static const int RF_MID_BAND_THRESH = 525000000;
+
+ // LoRa RSSI offsets depending on LF or HF bands
+ static const int LOR_RSSI_OFFSET_HF = -157;
+ static const int LOR_RSSI_OFFSET_LF = -164;
+
+ /**
+ * What modem we are configured for
+ */
+ typedef enum {
+ MODEM_LORA = 0,
+ MODEM_FSK
+ } RADIO_MODEM_T;
+
+ /**
+ * Events that can occurr during a TX or RX operation.
+ *
+ * When sending or receiving a packet (calling setTx()/send() or
+ * setRx()), the state will be initialized to ESTATE_EXEC to
+ * indicate the operation is in progress. Once an event has
+ * occurred, this state will be updated accordingly.
+ *
+ * For receiving, if RX_DONE is set, then it is safe to retrieve
+ * the rx buffer (via getRxBuffer()/getRxBufferStr()) as well as
+ * query the RSSI and SNR. On RX_ERROR, these values will be the
+ * same as they were last set during the last RX_DONE event.
+ */
+ typedef enum {
+ REVENT_DONE = 0, // operation completed successfully
+ REVENT_EXEC, // runninsg something
+ REVENT_ERROR, // failed, crc error, sync timeout
+ REVENT_TIMEOUT // timed out
+ } RADIO_EVENT_T;
+
+ /**
+ * SX1276 registers
+ *
+ * NOTE: reserved registers must not be written into or read from.
+ * Reserved bitfields must always be 0.
+ *
+ * This device has a set of "common" registers, as well as
+ * registers that represent different things depending on whether
+ * the device in in LoRa mode or FSK/OOK mode. So here, we will
+ * prefix the register names with COM (common), LOR (LoRa mode),
+ * and FSK (FSK/OOK mode) accordingly.
+ */
+ typedef enum {
+ COM_RegFifo = 0x00, // FIFO r/w access
+ COM_RegOpMode = 0x01, // LoRa/FSK
+
+ FSK_RegBitrateMsb = 0x02,
+ LOR_Reserved02 = 0x02, // reserved
+
+ FSK_RegBitrateLsb = 0x03,
+ LOR_Reserved03 = 0x03, // reserved
+
+ FSK_RegFdevMsb = 0x04, // freq deviation
+ LOR_Reserved04 = 0x04, // reserved
+
+ FSK_RegFdevLsb = 0x05,
+ LOR_Reserved05 = 0x05, // reserved
+
+ COM_RegFrfMsb = 0x06, // carrier freq
+ COM_RegFrfMid = 0x07,
+ COM_RegFrfLsb = 0x08,
+ COM_RegPaConfig = 0x09,
+ COM_RegPaRamp = 0x0a,
+
+ COM_RegOcp = 0x0b, // overcurrent protection
+ COM_RegLna = 0x0c,
+
+ FSK_RegRxConfig = 0x0d,
+ LOR_RegFifoAddrPtr = 0x0d,
+
+ FSK_RegRssiConfg = 0x0e,
+ LOR_RegFifoTxBaseAddr = 0x0e,
+
+ FSK_RegRssiCollision = 0x0f,
+ LOR_RegFifoRxBaseAddr = 0x0f,
+
+ FSK_RegRssiThresh = 0x10,
+ LOR_RegFifoRxCurrentAddr = 0x10,
+
+ FSK_RegRssiValue = 0x11,
+ LOR_RegIrqFlagsMask = 0x11,
+
+ FSK_RegRxBw = 0x12,
+ LOR_RegIrqFlags = 0x12,
+
+ FSK_RegAfcBw = 0x13, // automatic freq cntrl
+ LOR_RegRxNbBytes = 0x13, // received pkt len
+
+ FSK_RegOokPeak = 0x14,
+ LOR_RegRxHeaderCntValueMsb = 0x14,
+
+ FSK_RegOokFix = 0x15,
+ LOR_RegRxHeaderCntValueLsb = 0x15,
+
+ FSK_RegOokAvg = 0x16,
+ LOR_RegRxPacketCntValueMsb = 0x16,
+
+ FSK_Reserved17 = 0x17, // reserved
+ LOR_RegRxPacketCntValueLsb = 0x17,
+
+ FSK_Reserved18 = 0x18, // reserved
+ LOR_RegModemStat = 0x18,
+
+ FSK_Reserved19 = 0x19, // reserved
+ LOR_RegPktSnrValue = 0x19,
+
+ FSK_RegAfcFei = 0x1a,
+ LOR_RegPktRssiValue = 0x1a,
+
+ FSK_RegAfcMsb = 0x1b,
+ LOR_RegRssiValue = 0x1b,
+
+ FSK_RegAfcLsb = 0x1c,
+ LOR_RegHopChannel = 0x1c, // fhss starting channel
+
+ FSK_RegFeiMsb = 0x1d,
+ LOR_RegModemConfig1 = 0x1d,
+
+ FSK_RegFeiLsb = 0x1e,
+ LOR_RegModemConfig2 = 0x1e,
+
+ FSK_RegPreambleDetect = 0x1f,
+ LOR_RegSymbTimeoutLsb = 0x1f,
+
+ FSK_RegRxTimeout1 = 0x20,
+ LOR_RegPreambleMsb = 0x20,
+
+ FSK_RegRxTimeout2 = 0x21,
+ LOR_RegPreambleLsb = 0x21,
+
+ FSK_RegRxTimeout3 = 0x22,
+ LOR_RegPayloadLength = 0x22,
+
+ FSK_RegRxDelay = 0x23,
+ LOR_RegMaxPayloadLength = 0x23,
+
+ FSK_RegOsc = 0x24,
+ LOR_RegHopPeriod = 0x24,
+
+ FSK_RegPreambleMsb = 0x25,
+ LOR_RegFifoRxByteAddr = 0x25,
+
+ FSK_RegPreambleLsb = 0x26,
+ LOR_RegModemConfig3 = 0x26,
+
+ FSK_RegSyncConfig = 0x27,
+ LOR_Reserved27 = 0x27, // reserved
+
+ FSK_RegSyncValue1 = 0x28,
+ LOR_RegFeiMsb = 0x28,
+
+ FSK_RegSyncValue2 = 0x29,
+ LOR_RegFeiMid = 0x29,
+
+ FSK_RegSyncValue3 = 0x2a,
+ LOR_RegFeiLsb = 0x2a,
+
+ FSK_RegSyncValue4 = 0x2b,
+ LOR_Reserved2b = 0x2b, // reserved
+
+ FSK_RegSyncValue5 = 0x2c,
+ LOR_RegRssiWideband = 0x2c,
+
+ FSK_RegSyncValue6 = 0x2d,
+ LOR_Reserved2d = 0x2d, // reserved
+
+ FSK_RegSyncValue7 = 0x2e,
+ LOR_Reserved2e = 0x2e, // reserved
+
+ FSK_RegSyncValue8 = 0x2f,
+ LOR_Reserved2f = 0x2f, // reserved
+
+ FSK_RegPacketConfig1 = 0x30,
+ LOR_Reserved30 = 0x30, // reserved
+
+ FSK_RegPacketConfig2 = 0x31,
+ LOR_RegDetectOptimize = 0x31,
+
+ FSK_RegPayloadLength = 0x32,
+ LOR_Reserved32 = 0x32, // reserved
+
+ FSK_RegNodeAddr = 0x33,
+ LOR_RegInvertIQ = 0x33,
+
+ FSK_RegBroadcastAddr = 0x34,
+ LOR_Reserved34 = 0x34, // reserved
+
+ FSK_RegFifoThresh = 0x35,
+ LOR_Reserved35 = 0x35, // reserved
+
+ FSK_RegSeqConfig1 = 0x36,
+ LOR_Reserved36 = 0x36, // reserved
+
+ FSK_RegSeqConfig2 = 0x37,
+ LOR_RegDetectionThreshold = 0x37,
+
+ FSK_RegTimerResol = 0x38,
+ LOR_Reserved38 = 0x38, // reserved
+
+ FSK_RegTimer1Coeff = 0x39,
+ LOR_RegSyncWord = 0x39,
+
+ FSK_RegTimer2Coeff = 0x3a,
+ LOR_Reserved3a = 0x3a, // reserved
+
+ FSK_RegImageCal = 0x3b,
+ LOR_Reserved3b = 0x3b, // reserved (in datasheet)?
+ LOR_RegInvertIQ2 = 0x3b, // does not exist in datasheet
+ // but used in Semtech code.
+ // UNDOCUMENTED
+
+ FSK_RegTemp = 0x3c,
+ LOR_Reserved3c = 0x3c, // reserved
+
+ FSK_RegLowBat = 0x3d,
+ LOR_Reserved3d = 0x3d, // reserved
+
+ FSK_RegIrqFlags1 = 0x3e,
+ LOR_Reserved3e = 0x3e, // reserved
+
+ FSK_RegIrqFlags2 = 0x3f,
+ LOR_Reserved3f = 0x3f, // reserved
+
+ COM_RegDioMapping1 = 0x40, // DIO0-DIO3
+ COM_RegDioMapping2 = 0x41, // DIO4-DIO5, clk out freq
+
+ COM_RegVersion = 0x42, // Semtech ID (silicon revision)
+
+ // 0x43 reserved
+
+ // The data sheet says this is FSK only, but the semtech code
+ // implies this is only valid for LoRa. So for now, assume the
+ // datasheet is wrong.
+ //
+ // FSK_RegPllHop = 0x44,
+ // LOR_Reserved44 = 0x44, // reserved
+
+ FSK_Reserved44 = 0x44,
+ LOR_RegPllHop = 0x44,
+
+ // 0x45-0x4a reserved
+
+ COM_RegTcxo = 0x4b,
+
+ // 0x4c reserved
+
+ COM_RegPaDac = 0x4d,
+
+ // 0x4e-0x5a reserved
+
+ COM_RegFormerTemp = 0x5b,
+
+ // 0x5c reserved
+
+ FSK_RegBitRateFrac = 0x5d,
+ LOR_Reserved5d = 0x5d, // reserved
+
+ // 0x5e-0x60 reserved
+
+ COM_RegAgcRef = 0x61,
+ COM_RegAgcThresh1 = 0x62,
+ COM_RegAgcThresh2 = 0x63,
+ COM_RegAgcThresh3 = 0x64,
+
+ // 0x65-0x6f reserved
+
+ COM_RegPll = 0x70
+
+ // 0x71-0xff reserved
+ } SX1276_REGS_T;
+
+ /**
+ * OpMode register (differing bitfields depending on mode)
+ */
+ typedef enum {
+ OPMODE_Mode0 = 0x01, // operating modes (sleep, etc)
+ OPMODE_Mode1 = 0x02,
+ OPMODE_Mode2 = 0x04,
+ _OPMODE_Mode_MASK = 7,
+ _OPMODE_Mode_SHIFT = 0,
+
+ OPMODE_LowFrequencyModeOn = 0x08,
+
+ // 0x10 reserved
+
+ OPMODE_FSK_ModulationType0 = 0x20,
+ OPMODE_FSK_ModulationType1 = 0x40,
+ _OPMODE_FSK_ModulationType_MASK = 3,
+ _OPMODE_FSK_ModulationType_SHIFT = 5,
+
+ OPMODE_LOR_Reserved0x20 = 0x20,
+
+ OPMODE_LOR_AccessSharedReg = 0x40, // tmp sw to FSK regs
+
+ OPMODE_LongRangeMode = 0x80 // LoRa mode enable(1), else FSK
+ } OPMODE_BITS_T;
+
+
+ /**
+ * Mode values
+ */
+ typedef enum {
+ MODE_Sleep = 0,
+ MODE_Standby = 1,
+ MODE_FSTX = 2, // freq synth
+ MODE_TxMode = 3,
+ MODE_FSRX = 4, // freq synth
+
+ MODE_FSK_RxMode = 5,
+ MODE_LOR_RxContinuous = 5, // continuous rx mode
+
+ MODE_FSK_Reserved6 = 6,
+ MODE_LOR_RxSingle = 6, // single packet rx mode
+
+ MODE_FSK_Reserved7 = 7,
+ MODE_LOR_CAD = 7 // channel activity detection
+ } MODE_T;
+
+ /**
+ * FSK_ModulationType values
+ */
+ typedef enum {
+ MODULATION_FSK = 0, // freq shift keying
+ MODULATION_OOK = 1, // on/off keying
+ // 2-3 reserved
+ } FSK_MODULATION_TYPE_T;
+
+ /**
+ * RegPaConfig register
+ */
+ typedef enum {
+ PACONFIG_OutputPower0 = 0x01,
+ PACONFIG_OutputPower1 = 0x02,
+ PACONFIG_OutputPower2 = 0x04,
+ PACONFIG_OutputPower3 = 0x08,
+ _PACONFIG_OutputPower_MASK = 15,
+ _PACONFIG_OutputPower_SHIFT = 0,
+
+ PACONFIG_MaxPower0 = 0x10,
+ PACONFIG_MaxPower1 = 0x20,
+ PACONFIG_MaxPower2 = 0x40,
+ _PACONFIG_MaxPower_MASK = 7,
+ _PACONFIG_MaxPower_SHIFT = 4,
+
+ PACONFIG_PaSelect = 0x80 // PA output pin,
+ // 0 = 14dBm, 1 = 20dBm
+ } PACONFIG_BITS_T;
+
+ /**
+ * RegPaRamp register
+ */
+ typedef enum {
+ PARAMP_PaRamp0 = 0x01, // rise/fall of ramp up/down
+ PARAMP_PaRamp1 = 0x02,
+ PARAMP_PaRamp2 = 0x04,
+ PARAMP_PaRamp3 = 0x08,
+ _PARAMP_PaRamp_MASK = 15,
+ _PARAMP_PaRamp_SHIFT = 0,
+
+ // 0x10 reserved
+
+ // LORA 0x20-0x40 reserved
+
+ PARAMP_FSK_ModulationShaping0 = 0x20,
+ PARAMP_FSK_ModulationShaping1 = 0x40,
+ _PARAMP_FSK_ModulationShaping_MASK = 3,
+ _PARAMP_FSK_ModulationShaping_SHIFT = 5
+
+ // 0x80 reserved
+ } PARAMP_BITS_T;
+
+ /**
+ * PARAMP_PaRamp values
+ */
+ typedef enum {
+ PARAMP_3_4MS = 0, // 3.4ms
+ PARAMP_2MS = 1,
+ PARAMP_1MS = 2,
+ PARAMP_500US = 3, // 500us
+ PARAMP_250US = 4,
+ PARAMP_125US = 5,
+ PARAMP_100US = 6,
+ PARAMP_62US = 7,
+ PARAMP_50US = 8,
+ PARAMP_40US = 9,
+ PARAMP_31US = 10,
+ PARAMP_25US = 11,
+ PARAMP_20US = 12,
+ PARAMP_15US = 13,
+ PARAMP_12US = 14,
+ PARAMP_10US = 15
+ } PARAMP_T;
+
+ /**
+ * PARAMP_ModulationShaping values. Note, these mean different
+ * things depending on whether you are using FSK or OOK. Hence
+ * the FSK/OOK dups. We will also name these as 'MODSHAPING_', rather
+ * than the lengthy 'MODULATIONSHAPING... '
+ */
+ typedef enum {
+ MODSHAPING_NOSHAPING = 0,
+
+ // FSK
+ MODSHAPING_FSK_GaussianFilterBT1 = 1, // BT = 1.0
+ MODSHAPING_FSK_GaussianFilterBT05 = 2, // BT = 0.5
+ MODSHAPING_FSK_GaussianFilterBT03 = 3, // BT = 0.3
+
+ // OOK
+ MODSHAPING_OOK_FCutoffBitRate = 1, // Fcutoff = BitRate
+ MODSHAPING_OOK_FCutoffBitRate2 = 2 // Fcutoff = 2*BitRate
+
+ // for OOK, 3 is reserved
+ } MODSHAPING_T;
+
+ /**
+ * RegOcp register (see datasheet for OcpTrim values)
+ */
+ typedef enum {
+ OCP_OcpTrim0 = 0x01,
+ OCP_OcpTrim1 = 0x02,
+ OCP_OcpTrim2 = 0x04,
+ OCP_OcpTrim3 = 0x08,
+ _OCP_OcpTrim_MASK = 15,
+ _OCP_OcpTrim_SHIFT = 0,
+
+ OCP_OcpOn = 0x10
+
+ // 0x20-0x80 reserved
+ } OCP_BITS_T;
+
+ /**
+ * Lna register
+ */
+ typedef enum {
+ LNA_LnaBoostHf0 = 0x01,
+ LNA_LnaBoostHf1 = 0x02,
+ _LNA_LnaBoostHf_MASK = 3,
+ _LNA_LnaBoostHf_SHIFT = 0,
+
+ // 0x04 reserved
+
+ LNA_LnaBoostLf0 = 0x08,
+ LNA_LnaBoostLf1 = 0x10,
+ _LNA_LnaBoostLf_MASK = 3,
+ _LNA_LnaBoostLf_SHIFT = 3,
+
+ LNA_LnaGain0 = 0x20,
+ LNA_LnaGain1 = 0x40,
+ LNA_LnaGain2 = 0x80,
+ _LNA_LnaGain_MASK = 7,
+ _LNA_LnaGain_SHIFT = 5
+ } LNA_BITS_T;
+
+ /**
+ * LnaBoostHf values
+ */
+ typedef enum {
+ LNABOOSTHF_Default = 0,
+ // 1-2 reserved
+ LNABOOSTHF_BoostOn = 3, // 150% LNA current
+ } LNABOOSTHF_T;
+
+ /**
+ * LnaBoostLf values
+ */
+ typedef enum {
+ LNABOOSTLF_Default = 0
+ // 1-3 reserved
+ } LNABOOSTLF_T;
+
+ /**
+ * LnaGain values
+ */
+ typedef enum {
+ // 0 reserved
+ LNAGAIN_G1 = 1, // max gain
+ LNAGAIN_G2 = 2,
+ LNAGAIN_G3 = 3,
+ LNAGAIN_G4 = 4,
+ LNAGAIN_G5 = 5,
+ LNAGAIN_G6 = 6 // minimum gain
+ // 7 reserved
+ } LNAGAIN_T;
+
+ /**
+ * FSK_RxConfig register. See Table 24 in the data sheet for
+ * the meanings of the RxTrigger values.
+ */
+ typedef enum {
+ RXCONFIG_RxTrigger0 = 0x01,
+ RXCONFIG_RxTrigger1 = 0x02,
+ RXCONFIG_RxTrigger2 = 0x04,
+ _RXCONFIG_RxTrigger_MASK = 7,
+ _RXCONFIG_RxTrigger_SHIFT = 0,
+
+ RXCONFIG_AgcAutoOn = 0x08,
+ RXCONFIG_AfcAutoOn = 0x10,
+ RXCONFIG_RestartRxWithPllLock = 0x20,
+ RXCONFIG_RestartRxWithoutPllLock = 0x40,
+ RXCONFIG_RestartRxOnCollision = 0x80
+ } RXCONFIG_BITS_T;
+
+ /**
+ * FSK_RssiConfig register
+ */
+ typedef enum {
+ RSSICONFIG_RssiSmoothing0 = 0x01, // RSSI sampling/averaging
+ RSSICONFIG_RssiSmoothing1 = 0x02,
+ RSSICONFIG_RssiSmoothing2 = 0x04,
+ _RSSICONFIG_RssiSmoothing_MASK = 7,
+ _RSSICONFIG_RssiSmoothing_SHIFT = 0,
+
+ RSSICONFIG_RssiOffset0 = 0x08, // 2's complement offset
+ RSSICONFIG_RssiOffset1 = 0x10,
+ RSSICONFIG_RssiOffset2 = 0x20,
+ RSSICONFIG_RssiOffset3 = 0x40,
+ RSSICONFIG_RssiOffset4 = 0x80,
+ _RSSICONFIG_RssiOffset_MASK = 31,
+ _RSSICONFIG_RssiOffset_SHIFT = 3
+ } RSSICONFIG_BITS_T;
+
+ /**
+ * RssiSmoothing values
+ */
+ typedef enum {
+ RSSISMOOTHING_2 = 0, // 2 samples used
+ RSSISMOOTHING_4 = 1,
+ RSSISMOOTHING_8 = 2,
+ RSSISMOOTHING_16 = 3,
+ RSSISMOOTHING_32 = 4,
+ RSSISMOOTHING_64 = 5,
+ RSSISMOOTHING_128 = 6,
+ RSSISMOOTHING_256 = 7
+ } RSSISMOOTHING_T;
+
+ /**
+ * LOR_RegIrqFlagsMask and LOR_RegIrqFlags registers
+ */
+ typedef enum {
+ LOR_IRQFLAG_CadDetected = 0x01,
+ LOR_IRQFLAG_FhssChangeChannel = 0x02,
+ LOR_IRQFLAG_CadDone = 0x04,
+ LOR_IRQFLAG_TxDone = 0x08,
+
+ LOR_IRQFLAG_ValidHeader = 0x10,
+ LOR_IRQFLAG_PayloadCrcError = 0x20,
+ LOR_IRQFLAG_RxDone = 0x40,
+ LOR_IRQFLAG_RxTimeout = 0x80
+ } LOR_IRQFLAG_BITS_T;
+
+ /**
+ * FSK_RxBw register and FSK_RegAfcBw registers
+ */
+ typedef enum {
+ RXBW_RxBwExp0 = 0x01,
+ RXBW_RxBwExp1 = 0x02,
+ RXBW_RxBwExp2 = 0x04,
+ _RXBW_RxBwExp_MASK = 7,
+ _RXBW_RxBwExp_SHIFT = 0,
+
+ RXBW_RxBwMant0 = 0x08,
+ RXBW_RxBwMant1 = 0x10,
+ _RXBW_RxBwMant_MASK = 3,
+ _RXBW_RxBwMant_SHIFT = 3,
+ // 0x20-0x80 reserved
+ } RXBW_BITS_T;
+
+ /**
+ * RXBW_RxBwMant values
+ */
+ typedef enum {
+ RXBWMANT_0 = 0,
+ RXBWMANT_1 = 1,
+ RXBWMANT_2 = 2
+ // 3 reserved
+ } RXBWMANT_T;
+
+ /**
+ * RXBW_RxBwExp values
+ */
+ typedef enum {
+ RXBWEXP_1 = 1,
+ RXBWEXP_2 = 2,
+ RXBWEXP_3 = 3,
+ RXBWEXP_4 = 4,
+ RXBWEXP_5 = 5,
+ RXBWEXP_6 = 6,
+ RXBWEXP_7 = 7
+ // other values reserved
+ } RXBWEXP_T;
+
+ /**
+ * FSK_OokPeak register
+ */
+ typedef enum {
+ OOKPEAK_OokPeakThreshStep0 = 0x01,
+ OOKPEAK_OokPeakThreshStep1 = 0x02,
+ OOKPEAK_OokPeakThreshStep2 = 0x04,
+ _OOKPEAK_OokPeakThreshStep_MASK = 7,
+ _OOKPEAK_OokPeakThreshStep_SHIFT = 0,
+
+ OOKPEAK_OokThreshType0 = 0x08,
+ OOKPEAK_OokThreshType1 = 0x10,
+ _OOKPEAK_OokThreshType_MASK = 3,
+ _OOKPEAK_OokThreshType_SHIFT = 3,
+
+ OOKPEAK_BitSyncOn = 0x20,
+
+ // 0x40-0x80 reserved
+ } OOKPEAK_BITS_T;
+
+ /**
+ * OokPeakThreshStep values
+ */
+ typedef enum {
+ OOKPEAKTHRESHSTEP_05dB = 0, // dec of RSSI threshold 0.5dB
+ OOKPEAKTHRESHSTEP_1dB = 1, // 1 dB
+ OOKPEAKTHRESHSTEP_15dB = 2, // 1.5 dB
+ OOKPEAKTHRESHSTEP_2dB = 3, // 2 dB
+ OOKPEAKTHRESHSTEP_3dB = 4,
+ OOKPEAKTHRESHSTEP_4dB = 5,
+ OOKPEAKTHRESHSTEP_5dB = 6,
+ OOKPEAKTHRESHSTEP_6dB = 7
+ } OOKPEAKTHRESHSTEP_T;
+
+ /**
+ * OokPeakThreshType values
+ */
+ typedef enum {
+ OOKTHRESHTYPE_FIXED = 0,
+ OOKTHRESHTYPE_PEAK = 1,
+ OOKTHRESHTYPE_AVERAGE = 2
+ // 3 reserved
+ } OOKTHRESHTYPE_T;
+
+ /**
+ * FSK_OokAvg register
+ */
+ typedef enum {
+ OOKAVG_OokAvgThreshFilt0 = 0x01,
+ OOKAVG_OokAvgThreshFilt1 = 0x02,
+ _OOKAVG_OokAvgThreshFilt_MASK = 3,
+ _OOKAVG_OokAvgThreshFilt_SHIFT = 0,
+
+ OOKAVG_OokAvgOffset0 = 0x04,
+ OOKAVG_OokAvgOffset1 = 0x08,
+ _OOKAVG_OokAvgOffset_MASK = 3,
+ _OOKAVG_OokAvgOffset_SHIFT = 2,
+
+ // 0x10 reserved
+
+ OOKAVG_OokPeakThreshDec0 = 0x20,
+ OOKAVG_OokPeakThreshDec1 = 0x40,
+ OOKAVG_OokPeakThreshDec2 = 0x80,
+ _OOKAVG_OokPeakThreshDec_MASK = 7,
+ _OOKAVG_OokPeakThreshDec_SHIFT = 5
+ } OOKAVG_BITS_T;
+
+ /**
+ * OokAvgThreshFilt values
+ */
+ typedef enum {
+ OOKAVGTHRESHFILT_32 = 0, // filter coedd in avg mode
+ OOKAVGTHRESHFILT_8 = 1,
+ OOKAVGTHRESHFILT_4 = 2,
+ OOKAVGTHRESHFILT_2 = 3
+ } OOKAVGTHRESHFILT_T;
+
+ /**
+ * OokAvgOffset values
+ */
+ typedef enum {
+ OOKAVGOFFSET_0 = 0, // 0.0dB
+ OOKAVGOFFSET_2 = 1,
+ OOKAVGOFFSET_4 = 2,
+ OOKAVGOFFSET_6 = 3
+ } OOKAVGOFFSET_T;
+
+ /**
+ * OokPeakThreshDec values
+ */
+ typedef enum {
+ OOKPEAKTHRESHDEC_1_1 = 0, // once per chip
+ OOKPEAKTHRESHDEC_1_2 = 1, // once every 2 chips...
+ OOKPEAKTHRESHDEC_1_4 = 2,
+ OOKPEAKTHRESHDEC_1_8 = 3,
+ OOKPEAKTHRESHDEC_2_1 = 4, // twice per chip
+ OOKPEAKTHRESHDEC_4_1 = 5, // 4 times every chip...
+ OOKPEAKTHRESHDEC_8_1 = 6,
+ OOKPEAKTHRESHDEC_16_1 = 7
+ } OOKPEAKTHRESHDEC_T;
+
+ /**
+ * LOR_ModemStat register
+ */
+ typedef enum {
+ MODEMSTAT_SignalDetected = 0x01,
+ MODEMSTAT_SignalSynchronized = 0x02,
+ MODEMSTAT_RxOngoing = 0x04,
+ MODEMSTAT_HeaderInfoValid = 0x08,
+ MODEMSTAT_ModemClear = 0x10,
+
+ MODEMSTAT_RxCodingRate0 = 0x20,
+ MODEMSTAT_RxCodingRate1 = 0x40,
+ MODEMSTAT_RxCodingRate2 = 0x80,
+ _MODEMSTAT_RxCodingRate_MASK = 7,
+ _MODEMSTAT_RxCodingRate_SHIFT = 5
+ } MODEMSTAT_BITS_T;
+
+ /**
+ * FSK_RegAfcFei register
+ */
+ typedef enum {
+ AFCFEI_AfcAutoClearOn = 0x01,
+ AFCFEI_AfcClear = 0x02,
+
+ // 0x04-0x08 reserved
+
+ AFCFEI_AgcStart = 0x10
+
+ // 0x20-0x80 reserved
+ } AFCFEI_BITS_T;
+
+ /**
+ * LOR_HopChannel register
+ */
+ typedef enum {
+ HOPCHANNEL_FhssPresentChannel0 = 0x01, // current freq hopping channel
+ HOPCHANNEL_FhssPresentChannel1 = 0x02,
+ HOPCHANNEL_FhssPresentChannel2 = 0x04,
+ HOPCHANNEL_FhssPresentChannel3 = 0x08,
+ HOPCHANNEL_FhssPresentChannel4 = 0x10,
+ HOPCHANNEL_FhssPresentChannel5 = 0x20,
+ _HOPCHANNEL_FhssPresentChannel_MASK = 63,
+ _HOPCHANNEL_FhssPresentChannel_SHIFT = 0,
+
+ HOPCHANNEL_CrcOnPayload = 0x40,
+ HOPCHANNEL_PllTimeout = 0x80
+ } HOPCHANNEL_BITS_T;
+
+ /**
+ * LOR_ModemConfig1 register
+ */
+ typedef enum {
+ MODEMCONFIG1_ImplicitHeaderModeOn = 0x01,
+
+ MODEMCONFIG1_CodingRate0 = 0x02,
+ MODEMCONFIG1_CodingRate1 = 0x04,
+ MODEMCONFIG1_CodingRate2 = 0x08,
+ _MODEMCONFIG1_CodingRate_MASK = 7,
+ _MODEMCONFIG1_CodingRate_SHIFT = 0,
+
+ MODEMCONFIG1_Bw0 = 0x10,
+ MODEMCONFIG1_Bw1 = 0x20,
+ MODEMCONFIG1_Bw2 = 0x40,
+ MODEMCONFIG1_Bw3 = 0x80,
+ _MODEMCONFIG1_Bw_MASK = 15,
+ _MODEMCONFIG1_Bw_SHIFT = 4
+ } MODEMCONFIG1_BITS_T;
+
+ /**
+ * CodingRate values
+ */
+ typedef enum {
+ CODINGRATE_4_5 = 1, // Error coding rate 4/5
+ CODINGRATE_4_6 = 2,
+ CODINGRATE_4_7 = 3,
+ CODINGRATE_4_8 = 4
+ } CODINGRATE_T;
+
+ /**
+ * Bw values
+ */
+ typedef enum {
+ BW_7_8 = 0, // 7.8Khz
+ BW_10_4 = 1,
+ BW_15_6 = 2,
+ BW_20_8 = 3,
+ BW_31_25 = 4,
+ BW_41_7 = 5,
+ BW_62_5 = 6,
+ BW_125 = 7,
+ BW_250 = 8,
+ BW_500 = 9
+
+ // BW250 and BW500 not supported in lower band (169Mhz)
+ } BW_T;
+
+ /**
+ * LOR_ModemConfig2 register
+ */
+ typedef enum {
+ MODEMCONFIG2_SymbTimeoutMsb0 = 0x01,
+ MODEMCONFIG2_SymbTimeoutMsb1 = 0x02,
+ _MODEMCONFIG2_SymbTimeoutMsb_MASK = 3,
+ _MODEMCONFIG2_SymbTimeoutMsb_SHIFT = 0,
+
+ MODEMCONFIG2_RxPayloadCrcOn = 0x04,
+
+ MODEMCONFIG2_TxContinuousMode = 0x08,
+
+ MODEMCONFIG2_SpreadingFactor0 = 0x10,
+ MODEMCONFIG2_SpreadingFactor1 = 0x20,
+ MODEMCONFIG2_SpreadingFactor2 = 0x40,
+ MODEMCONFIG2_SpreadingFactor3 = 0x80,
+ _MODEMCONFIG2_SpreadingFactor_MASK = 15,
+ _MODEMCONFIG2_SpreadingFactor_SHIFT = 4,
+ } MODEMCONFIG2_BITS_T;
+
+ /**
+ * SpreadingFactor values (expressed as a base-2 logarithm)
+ */
+ typedef enum {
+ SPREADINGFACTOR_64 = 6, // 64 chips/symbol
+ SPREADINGFACTOR_128 = 7,
+ SPREADINGFACTOR_256 = 8,
+ SPREADINGFACTOR_512 = 9,
+ SPREADINGFACTOR_1024 = 10,
+ SPREADINGFACTOR_2048 = 11,
+ SPREADINGFACTOR_4096 = 12
+
+ // other values reserved
+ } SPREADINGFACTOR_T;
+
+ /**
+ * FSK_PreableDetect register
+ */
+ typedef enum {
+ PREABLEDETECT_PreambleDetectorTol0 = 0x01,
+ PREABLEDETECT_PreambleDetectorTol1 = 0x02,
+ PREABLEDETECT_PreambleDetectorTol2 = 0x04,
+ PREABLEDETECT_PreambleDetectorTol3 = 0x08,
+ PREABLEDETECT_PreambleDetectorTol4 = 0x10,
+ _PREABLEDETECT_PreambleDetectorTol4_MASK = 31,
+ _PREABLEDETECT_PreambleDetectorTol4_SHIFT = 0,
+
+ PREABLEDETECT_PreambleDetectorSize0 = 0x20,
+ PREABLEDETECT_PreambleDetectorSize1 = 0x40,
+ _PREABLEDETECT_PreambleDetectorSize_MASK = 3,
+ _PREABLEDETECT_PreambleDetectorSize_SHIFT = 5,
+
+ PREABLEDETECT_PreambleDetectorOn = 0x80
+ } PREAMBLEDETECT_BITS_T;
+
+ /**
+ * PreambleDetectorSize values
+ */
+ typedef enum {
+ PREAMBLEDETECTORSIZE_1 = 0, // 1 byte
+ PREAMBLEDETECTORSIZE_2 = 1,
+ PREAMBLEDETECTORSIZE_3 = 2
+
+ // other values reserved
+ } PREAMBLEDETECTORSIZE_T;
+
+ /**
+ * FSK_Osc register
+ */
+ typedef enum {
+ OSC_ClkOut0 = 0x01, // clk output freq
+ OSC_ClkOut1 = 0x02,
+ OSC_ClkOut2 = 0x04,
+ _OSC_ClkOut_MASK = 7,
+ _OSC_ClkOut_SHIFT = 0,
+
+ OSC_RcCalStart = 0x08
+
+ // other bits reserved
+ } OSC_BITS_T;
+
+ /**
+ * ClkOut values
+ */
+ typedef enum {
+ CLKOUT_1 = 0, // FXOSC
+ CLKOUT_2 = 1, // FXOSC / 2 ...
+ CLKOUT_4 = 2,
+ CLKOUT_8 = 3,
+ CLKOUT_16 = 4,
+ CLKOUT_32 = 5,
+ CLKOUT_RC = 6, // RC, (automatically enabled)
+ CLKOUT_OFF = 7 // clkout off
+ } CLKOUT_T;
+
+ /**
+ * LOR_ModemConfig3 register
+ */
+ typedef enum {
+ // 0x01-0x02 reserved
+
+ MODEMCONFIG3_AgcAutoOn = 0x04,
+ MODEMCONFIG3_LowDataRateOptimize = 0x08 // req. for SF11 and SF12 and
+ // BW125
+
+ // 0x10-0x80 reserved
+ } MODEMCONFIG3_BITS_T;
+
+ /**
+ * FSK_SyncConfig register
+ */
+ typedef enum {
+ SYNCCONFIG_SyncSize0 = 0x01,
+ SYNCCONFIG_SyncSize1 = 0x02,
+ SYNCCONFIG_SyncSize2 = 0x04,
+ _SYNCCONFIG_SyncSize_MASK = 7,
+ _SYNCCONFIG_SyncSize_SHIFT = 0,
+
+ // 0x08 reserved
+
+ SYNCCONFIG_SyncOn = 0x10,
+ SYNCCONFIG_PreamblePolarity = 0x20,
+
+ SYNCCONFIG_AutoRestartMode0 = 0x40,
+ SYNCCONFIG_AutoRestartMode1 = 0x80,
+ _SYNCCONFIG_AutoRestartMode_MASK = 3,
+ _SYNCCONFIG_AutoRestartMode_SHIFT = 6,
+ } SYNCCONFIG_BITS_T;
+
+ /**
+ * AutoRestartMode values
+ */
+ typedef enum {
+ AUTORESTARTMODE_OFF = 0,
+ AUTORESTARTMODE_ON_NOPLL = 1, // don't wait for PLL resync
+ AUTORESTARTMODE_ON_PLL = 2 // wait for PLL resync
+ // other values reserved
+ } AUTORESTARTMODE_T;
+
+ /**
+ * LOR_FeiMsb register (4 bit MSB of Fei value)
+ */
+ typedef enum {
+ FEIMSB_FreqError0 = 0x01,
+ FEIMSB_FreqError1 = 0x02,
+ FEIMSB_FreqError2 = 0x04,
+ FEIMSB_FreqError3 = 0x08,
+ _FEIMSB_FreqError_MASK = 15,
+ _FEIMSB_FreqError_SHIFT = 0
+
+ // 0x10-0x80 reserved
+ } FEIMSB_BITS_T;
+
+ /**
+ * FSK_PacketConfig1 register
+ */
+ typedef enum {
+ PACKETCONFIG1_CrcWhiteningType = 0x01,
+
+ PACKETCONFIG1_AddressFiltering0 = 0x02,
+ PACKETCONFIG1_AddressFiltering1 = 0x04,
+ _PACKETCONFIG1_AddressFiltering_MASK = 3,
+ _PACKETCONFIG1_AddressFiltering_SHIFT = 1,
+
+ PACKETCONFIG1_CrcAutoClearOff = 0x08,
+ PACKETCONFIG1_CrcOn = 0x10,
+
+ PACKETCONFIG1_DcFree0 = 0x20,
+ PACKETCONFIG1_DcFree1 = 0x40,
+ _PACKETCONFIG1_DcFree_MASK = 3,
+ _PACKETCONFIG1_DcFree_SHIFT = 5,
+
+ PACKETCONFIG1_PacketFormat = 0x80 // fixed(0) or variable(1)
+ } PACKETCONFIG1_BITS_T;
+
+ /**
+ * AddressFiltering values
+ */
+ typedef enum {
+ ADDRESSFILTERING_NONE = 0,
+ ADDRESSFILTERING_NODE = 1, // must match node addr
+ ADDRESSFILTERING_NODE_BROADCAST = 2, // match node or broadcast
+ } ADDRESSFILTERING_T;
+
+ /**
+ * DcFree values (DC-free encoding/decoding schemes)
+ */
+ typedef enum {
+ DCFREE_NONE = 0,
+ DCFREE_MANCHESTER = 1,
+ DCFREE_WHITENING = 2
+ // other values reserved
+ } DCFREE_T;
+
+ /**
+ * FSK_PacketConfig2 register
+ */
+ typedef enum {
+ PACKETCONFIG2_PayloadLengthMsb0 = 0x01,
+ PACKETCONFIG2_PayloadLengthMsb1 = 0x02,
+ PACKETCONFIG2_PayloadLengthMsb2 = 0x04,
+ _PACKETCONFIG2_PayloadLengthMsb_MASK = 7,
+ _PACKETCONFIG2_PayloadLengthMsb_SHIFT = 0,
+
+ PACKETCONFIG2_BeaconOn = 0x08,
+
+ // 0x10 reserved (linked to io-homecontrol compat mode (?))
+
+ PACKETCONFIG2_IoHomeOn = 0x20,
+ PACKETCONFIG2_DataMode = 0x40, // continuous(0), packet(1)
+
+ // 0x80 reserved
+ } PACKETCONFIG2_BITS_T;
+
+ /**
+ * LOR_DetectOptimize register
+ */
+ typedef enum {
+ DETECTOPTIMIZE_DetectionOptimize0 = 0x01,
+ DETECTOPTIMIZE_DetectionOptimize1 = 0x02,
+ DETECTOPTIMIZE_DetectionOptimize2 = 0x04,
+ _DETECTOPTIMIZE_DetectionOptimize_MASK = 7,
+ _DETECTOPTIMIZE_DetectionOptimize_SHIFT = 0
+
+ // 0x08-0x80 reserved
+ } DETECTOPTIMIZE_BITS_T;
+
+ /**
+ * DetectionOptimize values
+ */
+ typedef enum {
+ DETECTIONOPTIMIZE_SF7_SF12 = 3,
+ DETECTIONOPTIMIZE_SF6 = 5
+
+ // other values reserved
+ } DETECTIONOPTIMIZE_T;
+
+ /**
+ * LOR_InvertIQ register
+ */
+ typedef enum {
+ INVERTIQ_InvertIQTxOff = 0x01, // invert LoRa I & Q signals
+ // UNDOCUMENTED
+
+ // 0x01-0x20 reserved
+
+ INVERTIQ_InvertIQRx = 0x40 // invert LoRa I & Q signals
+
+ // 0x80 reserved
+ } INVERTIQ_BITS_T;
+
+ /**
+ * FSK_FifoThresh register
+ */
+ typedef enum {
+ FIFOTHRESH_FifoThreshold0 = 0x01,
+ FIFOTHRESH_FifoThreshold1 = 0x02,
+ FIFOTHRESH_FifoThreshold2 = 0x04,
+ FIFOTHRESH_FifoThreshold3 = 0x08,
+ FIFOTHRESH_FifoThreshold4 = 0x10,
+ FIFOTHRESH_FifoThreshold5 = 0x20,
+ _FIFOTHRESH_FifoThreshold_MASK = 63,
+ _FIFOTHRESH_FifoThreshold_SHIFT = 0,
+
+ // 0x40 reserved
+
+ FIFOTHRESH_TxStartCondition = 0x80
+ } FIFOTHRESH_BITS_T;
+
+ /**
+ * FSK_SeqConfig1 register
+ */
+ typedef enum {
+ SEQCONFIG1_FromTransit = 0x01,
+ SEQCONFIG1_FromIdle = 0x02,
+ SEQCONFIG1_LowPowerSelection = 0x04,
+
+ SEQCONFIG1_FromStart0 = 0x08,
+ SEQCONFIG1_FromStart1 = 0x10,
+ _SEQCONFIG1_FromStart_MASK = 3,
+ _SEQCONFIG1_FromStart_SHIFT = 3,
+
+ SEQCONFIG1_IdleMode = 0x20,
+ SEQCONFIG1_SequencerStop = 0x40,
+ SEQCONFIG1_SequencerStart = 0x80
+ } SEQCONFIG1_BITS_T;
+
+ /**
+ * FromStart values
+ */
+ typedef enum {
+ FROMSTART_ToLowPowerSelection = 0,
+ FROMSTART_ToReceiveState = 1,
+ FROMSTART_ToTransmitState = 2,
+ FROMSTART_ToTransmitStateOnFifoLevel = 3
+ } FROMSTART_T;
+
+ /**
+ * FSK_SeqConfig2 register
+ */
+ typedef enum {
+ SEQCONFIG2_FromPacketReceived0 = 0x01,
+ SEQCONFIG2_FromPacketReceived1 = 0x02,
+ SEQCONFIG2_FromPacketReceived2 = 0x04,
+ _SEQCONFIG2_FromPacketReceived_MASK = 7,
+ _SEQCONFIG2_FromPacketReceived_SHIFT = 0,
+
+ SEQCONFIG2_FromRxTimeout0 = 0x08,
+ SEQCONFIG2_FromRxTimeout1 = 0x10,
+ _SEQCONFIG2_FromRxTimeout_MASK = 3,
+ _SEQCONFIG2_FromRxTimeout_SHIFT = 3,
+
+ SEQCONFIG2_FromReceive0 = 0x20,
+ SEQCONFIG2_FromReceive1 = 0x40,
+ SEQCONFIG2_FromReceive2 = 0x80,
+ _SEQCONFIG2_FromReceive_MASK = 3,
+ _SEQCONFIG2_FromReceive_SHIFT = 5
+ } SEQCONFIG2_BITS_T;
+
+ /**
+ * FromPacketReceived values
+ */
+ typedef enum {
+ FROMPACKETRECEIVED_ToSequencerOff = 0,
+ FROMPACKETRECEIVED_ToTransmitStateOnFifoEmpty = 1,
+ FROMPACKETRECEIVED_ToLowPowerSelection = 2,
+ FROMPACKETRECEIVED_ToReceiveViaFS = 3, // if freq was changed
+ FROMPACKETRECEIVED_ToReceive = 4 // if freq was not changed
+
+ // other values reserved
+ } FROMPACKETRECEIVED_T;
+
+ /**
+ * FromRxTimeout values
+ */
+ typedef enum {
+ FROMRXTIMEOUT_ToReceiveViaReceiveStart = 0,
+ FROMRXTIMEOUT_ToTransmitState = 1,
+ FROMRXTIMEOUT_ToLowPowerSelection = 2,
+ FROMRXTIMEOUT_ToSequencerOffState = 3
+ } FROMRXTIMEOUT_T;
+
+ /**
+ * FromReceive values
+ */
+ typedef enum {
+ FROMRECEIVE_ToPcketReceived = 1,
+ FROMRECEIVE_ToLowPowerSelection = 2,
+ FROMRECEIVE_ToPacketReceived = 3,
+ FROMRECEIVE_ToSequencerOffOnRSSI = 4, // RSSI interrupt
+ FROMRECEIVE_ToSequencerOffOnSync = 5, // SyncAddr interrupt
+ FROMRECEIVE_ToSequencerOffOnPreambleDetect = 6, // PreambleDetect intr
+ // other values reserved
+ } FROMRECEIVE_T;
+
+ /**
+ * FSK_TimerResol register
+ */
+ typedef enum {
+ TIMERRESOL_Timer2Resolution0 = 0x01,
+ TIMERRESOL_Timer2Resolution1 = 0x02,
+ _TIMERRESOL_Timer2Resolution_MASK = 3,
+ _TIMERRESOL_Timer2Resolution_SHIFT = 0,
+
+ TIMERRESOL_Timer1Resolution0 = 0x04,
+ TIMERRESOL_Timer1Resolution1 = 0x08,
+ _TIMERRESOL_Timer1Resolution_MASK = 3,
+ _TIMERRESOL_Timer1Resolution_SHIFT = 2
+
+ // 0x10-0x80 reserved
+ } TIMERRESOL_BITS_T;
+
+ /**
+ * Timer1/Timer2Resolution values
+ */
+ typedef enum {
+ TIMERRESOLUTION_DISABLED = 0,
+ TIMERRESOLUTION_64us = 1, // 64us
+ TIMERRESOLUTION_4_1ms = 2, // 4.1ms
+ TIMERRESOLUTION_262ms = 3 // 262ms
+ } TIMERRESOLUTION_T;
+
+ /**
+ * FSK_ImageCal register
+ */
+ typedef enum {
+ IMAGECAL_TempMonitorOff = 0x01,
+
+ IMAGECAL_TempThreshold0 = 0x02,
+ IMAGECAL_TempThreshold1 = 0x04,
+ _IMAGECAL_TempThreshold_MASK = 3,
+ _IMAGECAL_TempThreshold_SHIFT = 1,
+
+ IMAGECAL_TenpChange = 0x08,
+
+ // 0x10 reserved
+
+ IMAGECAL_ImageCalRunning = 0x20,
+ IMAGECAL_ImageCalStart = 0x40,
+ IMAGECAL_AutoImageCalOn = 0x80
+ } IMAGECAL_BITS_T;
+
+ /**
+ * TempThreshold values
+ */
+ typedef enum {
+ TEMPTHRESHOLD_5C = 0, // temp change to trigger new I/Q
+ TEMPTHRESHOLD_10C = 1, // calibration
+ TEMPTHRESHOLD_15C = 2,
+ TEMPTHRESHOLD_20C = 3
+ } TEMPTHRESHOLD_T;
+
+ /**
+ * FSK_LowBat register
+ */
+ typedef enum {
+ LOWBAT_LowBatTrim0 = 0x01,
+ LOWBAT_LowBatTrim1 = 0x02,
+ LOWBAT_LowBatTrim2 = 0x04,
+ _LOWBAT_LowBatTrim_MASK = 7,
+ _LOWBAT_LowBatTrim_SHIFT = 0,
+
+ LOWBAT_LowBatOn = 0x08
+
+ // 0x10-0z80 reserved
+ } LOWBAT_BITS_T;
+
+ /**
+ * LowBatTrim values
+ */
+ typedef enum {
+ LOWBATTRIM_1_695 = 0, // 1.695v
+ LOWBATTRIM_1_764 = 1,
+ LOWBATTRIM_1_835 = 2,
+ LOWBATTRIM_1_905 = 3,
+ LOWBATTRIM_1_976 = 4,
+ LOWBATTRIM_2_045 = 5,
+ LOWBATTRIM_2_116 = 6,
+ LOWBATTRIM_2_185 = 7
+ } LOWBATTRIM_T;
+
+ /**
+ * FSK_IrqFlags1 register
+ */
+ typedef enum {
+ IRQFLAGS1_SyncAddressMatch = 0x01,
+ IRQFLAGS1_PreambleDetect = 0x02,
+ IRQFLAGS1_Timeout = 0x04,
+ IRQFLAGS1_Rssi = 0x08,
+ IRQFLAGS1_PllLock = 0x10,
+ IRQFLAGS1_TxReady = 0x20,
+ IRQFLAGS1_RxReady = 0x40,
+ IRQFLAGS1_ModeReady = 0x80
+ } IRQFLAGS1_BITS_T;
+
+ /**
+ * FSK_IrqFlags2 register
+ */
+ typedef enum {
+ IRQFLAGS2_LowBat = 0x01,
+ IRQFLAGS2_CrcOk = 0x02,
+ IRQFLAGS2_PayloadReady = 0x04,
+ IRQFLAGS2_PacketSent = 0x08,
+ IRQFLAGS2_FifoOverrun = 0x10,
+ IRQFLAGS2_FifoLevel = 0x20,
+ IRQFLAGS2_FifoEmpty = 0x40,
+ IRQFLAGS2_FifoFull = 0x80
+ } IRQFLAGS2_BITS_T;
+
+ /**
+ * COM_DioMapping1 register. See Tables 18, 29, and 30 in the
+ * datasheet for the different mappings depending on mode.
+ */
+ typedef enum {
+ DOIMAPPING1_Dio3Mapping0 = 0x01,
+ DOIMAPPING1_Dio3Mapping1 = 0x02,
+ DOIMAPPING1_Dio3Mapping_MASK = 3,
+ DOIMAPPING1_Dio3Mapping_SHIFT = 0,
+
+ DOIMAPPING1_Dio2Mapping0 = 0x04,
+ DOIMAPPING1_Dio2Mapping1 = 0x08,
+ DOIMAPPING1_Dio2Mapping_MASK = 3,
+ DOIMAPPING1_Dio2Mapping_SHIFT = 2,
+
+ DOIMAPPING1_Dio1Mapping0 = 0x10,
+ DOIMAPPING1_Dio1Mapping1 = 0x20,
+ DOIMAPPING1_Dio1Mapping_MASK = 3,
+ DOIMAPPING1_Dio1Mapping_SHIFT = 4,
+
+ DOIMAPPING1_Dio0Mapping0 = 0x40,
+ DOIMAPPING1_Dio0Mapping1 = 0x80,
+ DOIMAPPING1_Dio0Mapping_MASK = 3,
+ DOIMAPPING1_Dio0Mapping_SHIFT = 6,
+ } DIOMAPPING1_BITS_T;
+
+
+ /**
+ * COM_DioMapping2 register. See Tables 18, 29, and 30 in the
+ * datasheet for the different mappings depending on mode.
+ */
+ typedef enum {
+ DOIMAPPING2_MapPreambleDetect = 0x01, // rssi intr(0), preambledet(1)
+
+ // 0x02-0x08 reserved
+
+ DOIMAPPING2_Dio5Mapping0 = 0x10,
+ DOIMAPPING2_Dio5Mapping1 = 0x20,
+ DOIMAPPING2_Dio5Mapping_MASK = 3,
+ DOIMAPPING2_Dio5Mapping_SHIFT = 4,
+
+ DOIMAPPING2_Dio4Mapping0 = 0x40,
+ DOIMAPPING2_Dio4Mapping1 = 0x80,
+ DOIMAPPING2_Dio4Mapping_MASK = 3,
+ DOIMAPPING2_Dio4Mapping_SHIFT = 6,
+ } DIOMAPPING2_BITS_T;
+
+ /**
+ * DioXMapping values
+ *
+ * These differ depending on LoRa, FSK packet, and FSK continous
+ * modes. See Tables 29, 30 (FSK), and 18 (LoRa) in the datasheet
+ * for details.
+ */
+ typedef enum {
+ DIOMAPPING_00 = 0,
+ DIOMAPPING_01 = 1,
+ DIOMAPPING_10 = 2,
+ DIOMAPPING_11 = 3
+ } DIOMAPPING_T;
+
+
+ /**
+ * LOR_PllHop (or FSK_PllHop depending on who you believe) register
+ */
+ typedef enum {
+ // 0x01-0x40 reserved
+
+ PLLHOP_FastHopOn = 0x80
+ } PLLHOP_BITS_T;
+
+ /**
+ * COM_Tcxo register
+ */
+ typedef enum {
+ // 0x01-0x08 reserved
+
+ TCXO_TcxoOn = 0x10
+
+ // 0x20-0x80 reserved
+ } TCXO_BITS_T;
+
+ /**
+ * COM_PaDac register
+ */
+ typedef enum {
+ PADAC_PaDac0 = 0x01,
+ PADAC_PaDac1 = 0x02,
+ PADAC_PaDac2 = 0x04,
+ _PADAC_PaDac_MASK = 7,
+ _PADAC_PaDac_SHIFT = 0
+
+ // 0x08-0x80 reserved
+ } PADAC_BITS_T;
+
+ /**
+ * PaDac values
+ */
+ typedef enum {
+ PADAC_DEFAULT = 4,
+ PADAC_BOOST = 7 // +20dBm on PA_BOOST when
+ // OuputPower = 1111
+ // other values reserved
+ } PADAC_T;
+
+ /**
+ * FSK_BitRateFrac register
+ */
+ typedef enum {
+ BITRATEFRAC_BitRateFrac0 = 0x01,
+ BITRATEFRAC_BitRateFrac1 = 0x02,
+ BITRATEFRAC_BitRateFrac2 = 0x04,
+ BITRATEFRAC_BitRateFrac3 = 0x08,
+ _BITRATEFRAC_BitRateFrac_MASK = 15,
+ _BITRATEFRAC_BitRateFrac_SHIFT = 0
+
+ // 0x10-0x80 reserved
+ } BITRATEFRAC_BITS_T;
+
+ /**
+ * COM_AgcRef register
+ *
+ * These registers have 2 sets of values depending on whether
+ * LowFrequencyModeOn is set or unset.
+ */
+ typedef enum {
+ AGCREF_AgcReferenceLevel0 = 0x01,
+ AGCREF_AgcReferenceLevel1 = 0x02,
+ AGCREF_AgcReferenceLevel2 = 0x04,
+ AGCREF_AgcReferenceLevel3 = 0x08,
+ AGCREF_AgcReferenceLevel4 = 0x10,
+ AGCREF_AgcReferenceLevel5 = 0x20,
+ _AGCREF_AgcReferenceLevel_MASK = 63,
+ _AGCREF_AgcReferenceLevel_SHIFT = 0
+
+ // 0x40-0x80 reserved
+ } ACFREF_BITS_T;
+
+ /**
+ * COM_AgcThresh1 register
+ *
+ * These registers have 2 sets of values depending on whether
+ * LowFrequencyModeOn is set or unset.
+ */
+ typedef enum {
+ AGCTHRESH1_AcgStep10 = 0x01,
+ AGCTHRESH1_AcgStep11 = 0x02,
+ AGCTHRESH1_AcgStep12 = 0x04,
+ AGCTHRESH1_AcgStep13 = 0x08,
+ _AGCTHRESH1_AcgStep1_MASK = 15,
+ _AGCTHRESH1_AcgStep1_SHIFT = 0,
+
+ // 0x10-0x80 reserved
+ } ACGTHRESH1_BITS_T;
+
+ /**
+ * COM_AgcThresh2 register
+ *
+ * These registers have 2 sets of values depending on whether
+ * LowFrequencyModeOn is set or unset.
+ */
+ typedef enum {
+ AGCTHRESH2_AcgStep30 = 0x01,
+ AGCTHRESH2_AcgStep31 = 0x02,
+ AGCTHRESH2_AcgStep32 = 0x04,
+ AGCTHRESH2_AcgStep33 = 0x08,
+ _AGCTHRESH2_AcgStep3_MASK = 15,
+ _AGCTHRESH2_AcgStep3_SHIFT = 0,
+
+ AGCTHRESH2_AcgStep20 = 0x10,
+ AGCTHRESH2_AcgStep21 = 0x20,
+ AGCTHRESH2_AcgStep22 = 0x40,
+ AGCTHRESH2_AcgStep23 = 0x80,
+ _AGCTHRESH2_AcgStep2_MASK = 15,
+ _AGCTHRESH2_AcgStep2_SHIFT = 4
+ } ACGTHRESH2_BITS_T;
+
+ /**
+ * LOR_RegDetectionThreshold values
+ */
+ typedef enum {
+ LOR_DetectionThreshold_SF7_SF12 = 0x0a,
+ LOR_DetectionThreshold_SF6 = 0x0c
+ } LOR_DETECTIONTHRESHOLD_T;
+
+ /**
+ * COM_AgcThresh3 register
+ *
+ * These registers have 2 sets of values depending on whether
+ * LowFrequencyModeOn is set or unset.
+ */
+ typedef enum {
+ AGCTHRESH3_AcgStep50 = 0x01,
+ AGCTHRESH3_AcgStep51 = 0x02,
+ AGCTHRESH3_AcgStep52 = 0x04,
+ AGCTHRESH3_AcgStep53 = 0x08,
+ _AGCTHRESH3_AcgStep5_MASK = 15,
+ _AGCTHRESH3_AcgStep5_SHIFT = 0,
+
+ AGCTHRESH3_AcgStep40 = 0x10,
+ AGCTHRESH3_AcgStep41 = 0x20,
+ AGCTHRESH3_AcgStep42 = 0x40,
+ AGCTHRESH3_AcgStep43 = 0x80,
+ _AGCTHRESH3_AcgStep4_MASK = 15,
+ _AGCTHRESH3_AcgStep4_SHIFT = 4
+ } ACGTHRESH3_BITS_T;
+
+
+ /**
+ * SX1276 constructor
+ *
+ * Since this is a shield, you will not have much choice as to
+ * what pins are used.
+ *
+ * @param chipRev chip revision, default is 0x12
+ * @param bus spi bus to use
+ * @param cs GPIO pin to use as SPI Chip Select
+ * @param reset GPIO pin to use as reset (A0=GPIO14)
+ * @param dio0 GPIO pin to use as reset DIO0 intr
+ * @param dio1 GPIO pin to use as reset DIO1 intr
+ * @param dio2 GPIO pin to use as reset DIO2 intr
+ * @param dio3 GPIO pin to use as reset DIO3 intr
+ * @param dio4 GPIO pin to use as reset DIO4 intr
+ * @param dio5 GPIO pin to use as reset DIO5 intr
+ */
+ SX1276(uint8_t chipRev=chipRevision, int bus=1, int cs=10, int resetPin=14,
+ int dio0=2, int dio1=3, int dio2=4, int dio3=5, int dio4=17,
+ int dio5=9);
+
+ /**
+ * SX1276 Destructor
+ */
+ ~SX1276();
+
+ /**
+ * read a register
+ *
+ * @param reg the register to read
+ * @return the value of the register
+ */
+ uint8_t readReg(uint8_t reg);
+
+ /**
+ * write to a register
+ *
+ * @param reg the register to write to
+ * @param val the value to write
+ * @return true if successful, false otherwise
+ */
+ bool writeReg(uint8_t reg, uint8_t val);
+
+ /**
+ * return the chip revision
+ *
+ * @return the chip revision (usually 0x12)
+ */
+ uint8_t getChipVersion();
+
+ /**
+ * reset the modem
+ */
+ void reset();
+
+ /**
+ * read the FIFO into a buffer
+ *
+ * @param buf The buffer to read data into
+ * @param len The length of the buffer
+ */
+ void readFifo(uint8_t *buf, int len);
+
+ /**
+ * write a buffer into the FIFO
+ *
+ * @param buf The buffer containing the data to write
+ * @param len The length of the buffer
+ */
+ void writeFifo(uint8_t *buf, int len);
+
+ /**
+ * Set the frequency to transmit and receive on
+ *
+ * @param freq The frequency to set
+ */
+ void setChannel(uint32_t freq);
+
+ /**
+ * Set the operating mode
+ *
+ * @param opMode One of the MODE_T values
+ */
+ void setOpMode(MODE_T opMode);
+
+ /**
+ * Set the modem to access. This can be either the LORA or
+ * KSK/OOK modem.
+ *
+ * @param modem One of the MODEM_T values
+ */
+ void setModem(RADIO_MODEM_T modem);
+
+ /**
+ * Place the SX1276 into sleep mode
+ */
+ void setSleep();
+
+ /**
+ * Place the SX1276 into standby mode
+ */
+ void setStandby();
+
+ /**
+ * Return the current Received Signal Strength Indicator for the
+ * given modem
+ *
+ * @param modem One of the MODEM_T values
+ */
+ int16_t getRSSI(RADIO_MODEM_T modem);
+
+ /**
+ * Check to see if a given channel is free by comparing the RSSI
+ * to the supplied threshold.
+ *
+ * @param modem One of the MODEM_T values
+ * @param freq The channel to check
+ * @param rssiThreshold The RSSI threshold, over which the channel
+ * os considerd in use.
+ */
+ bool isChannelFree(RADIO_MODEM_T modem, uint32_t freq, int16_t rssiThresh);
+
+ /**
+ * Send the supplied string. This writes the string into the FIFO
+ * and places the modem in transmit mode (via setTx()). This is a
+ * wrapper around send().
+ *
+ * @param buffer The buffer to send
+ * @param timeout The timeout in milliseconds
+ * @return one of the RADIO_EVENT_T values
+ */
+ RADIO_EVENT_T sendStr(std::string buffer, int timeout);
+
+ /**
+ * Send the supplied buffer. The writes the buffer into the FIFO
+ * and places the modem in transmit mode (via setTx()).
+ *
+ * @param buffer The buffer to send
+ * @param size The size of the buffer
+ * @param timeout The timeout in milliseconds
+ * @return one of the RADIO_EVENT_T values
+ */
+ RADIO_EVENT_T send(uint8_t *buffer, uint8_t size, int timeout);
+
+ /**
+ * Set the receive configuration for a modem. It is important
+ * that both the receive and transmit configurations match in order
+ * for communication to work between two radios.
+ *
+ * @param modem One of the MODEM_T values
+
+ * @param bandwidth The bandwidth to use. Valid values are
+ * FSK : >= 2600 and <= 250000 Hz
+ * LoRa: [125 kHz, 250 kHz, 500 kHz]
+ * @param datarate Sets the Datarate
+ * FSK : 600..300000 bits/s
+ * LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
+ * 10: 1024, 11: 2048, 12: 4096 chips]
+ * @param coderate Sets the coding rate (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
+ * @param bandwidthAfc Sets the AFC Bandwidth (FSK only)
+ * FSK : >= 2600 and <= 250000 Hz
+ * LoRa: N/A ( set to 0 )
+ * @param preambleLen Sets the Preamble length
+ * FSK : Number of bytes
+ * LoRa: Length in symbols (the hardware adds
+ * 4 more symbols)
+ * @param symbTimeout Sets the RxSingle timeout value (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: timeout in symbols
+ * @param fixLen Fixed length packets [false: variable, true: fixed]
+ * @param payloadLen Sets payload length when fixed lenght is used
+ * @param crcOn Enables/Disables the CRC [false: OFF, true: ON]
+ * @param FreqHopOn Enables disables the intra-packet frequency hopping
+ * FSK : N/A ( set to 0 )
+ * LoRa: [false: OFF, true: ON]
+ * @param HopPeriod Number of symbols bewteen each hop
+ * FSK : N/A ( set to 0 )
+ * LoRa: Number of symbols
+ * @param iqInverted Inverts IQ signals (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [false: not inverted, true: inverted]
+ * @param rxContinuous Sets the reception in continuous mode
+ * [false: single mode, true: continuous mode]
+ */
+ void setRxConfig(RADIO_MODEM_T modem, uint32_t bandwidth,
+ uint32_t datarate, uint8_t coderate,
+ uint32_t bandwidthAfc, uint16_t preambleLen,
+ uint16_t symbTimeout, bool fixLen,
+ uint8_t payloadLen,
+ bool crcOn, bool freqHopOn, uint8_t hopPeriod,
+ bool iqInverted, bool rxContinuous);
+
+ /**
+ * Set the transmit configuration for a modem. It is important
+ * that both the receive and transmit configurations match in order
+ * for communication to work between two radios.
+ *
+ * @param modem One of the MODEM_T values
+ * @param power Sets the output power [dBm]
+ * @param fdev Sets the frequency deviation (FSK only)
+ * FSK : [Hz]
+ * LoRa: 0
+ * @param bandwidth Sets the bandwidth (LoRa only)
+ * FSK : 0
+ * LoRa: [125 kHz, 250 kHz,
+ * or 500 kHz]
+ * @param datarate Sets the Datarate
+ * FSK : 600..300000 bits/s
+ * LoRa: [6: 64, 7: 128, 8: 256, 9: 512,
+ * 10: 1024, 11: 2048, 12: 4096 chips]
+ * @param coderate Sets the coding rate (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
+ * @param preambleLen Sets the preamble length
+ * FSK : Number of bytes
+ * LoRa: Length in symbols (the hardware adds
+ * 4 more symbols)
+ * @param fixLen Fixed length packets [false: variable, true: fixed]
+ * @param crcOn Enables disables the CRC [false: OFF, true: ON]
+ * @param FreqHopOn Enables disables the intra-packet frequency hopping
+ * FSK : N/A ( set to 0 )
+ * LoRa: [false: OFF, true: ON]
+ * @param HopPeriod Number of symbols bewteen each hop
+ * FSK : N/A ( set to 0 )
+ * LoRa: Number of symbols
+ * @param iqInverted Inverts IQ signals (LoRa only)
+ * FSK : N/A ( set to 0 )
+ * LoRa: [false: not inverted, true: inverted]
+ */
+ void setTxConfig(RADIO_MODEM_T modem, int8_t power, uint32_t fdev,
+ uint32_t bandwidth, uint32_t datarate,
+ uint8_t coderate, uint16_t preambleLen,
+ bool fixLen, bool crcOn, bool freqHopOn,
+ uint8_t hopPeriod, bool iqInverted);
+
+ /**
+ * Start a receive operation. The method will return when
+ * completed, either successfully, or in error (crc, or other
+ * issue). If completed successfully, the returned buffer can be
+ * read via getRxBuffer() or getRxBufferStr(). In addition,
+ * values for RSSI and SNR (Lora only) can be retrieved.
+ *
+ * @param timeout The timeout in milliseconds
+ * @return one of the RADIO_EVENT_T values
+ */
+ RADIO_EVENT_T setRx(uint32_t timeout);
+
+ /**
+ * Upon a successful receive, this method can be used to retrieve
+ * the received packet.
+ *
+ * @return The received buffer in a std::string
+ */
+ std::string getRxBufferStr()
+ {
+ std::string rBuffer((char *)m_rxBuffer, getRxLen());
+ return rBuffer;
+ };
+
+ /**
+ * Upon a successful receive, this method can be used to retrieve
+ * the received packet.
+ *
+ * @return a pointer to the received buffer. You can use
+ * getRxLen() to determine the number of valid bytes present.
+ */
+ uint8_t *getRxBuffer()
+ {
+ return (uint8_t*)m_rxBuffer;
+ };
+
+ /**
+ * Upon a successful receive, this method can be used to retrieve
+ * the received packet's Received Signal Strength Indicator (RSSI)
+ * value.
+ *
+ * @return RSSI value
+ */
+ int getRxRSSI()
+ {
+ return m_rxRSSI;
+ };
+
+ /**
+ * Upon a successful receive, this method can be used to retrieve
+ * the received packet's Signal to Noise (SNR) value.
+ *
+ * @return SNR value
+ */
+ int getRxSNR()
+ {
+ return m_rxSNR;
+ };
+
+ /**
+ * Upon a successful receive, this method can be used to retrieve
+ * the number of bytes received.
+ *
+ * @return the number of bytes received
+ */
+ int getRxLen()
+ {
+ return m_rxLen;
+ };
+
+
+ protected:
+ // I/O
+ mraa::Spi m_spi;
+ mraa::Gpio m_gpioCS;
+ mraa::Gpio m_gpioReset;
+
+ mraa::Gpio m_gpioDIO0;
+ mraa::Gpio m_gpioDIO1;
+ mraa::Gpio m_gpioDIO2;
+ mraa::Gpio m_gpioDIO3;
+ mraa::Gpio m_gpioDIO4;
+ mraa::Gpio m_gpioDIO5;
+
+ // calibration called during init()
+ void rxChainCalibration();
+
+ // interrupt handlers
+ static void onDio0Irq(void *ctx);
+ static void onDio1Irq(void *ctx);
+ static void onDio2Irq(void *ctx);
+ static void onDio3Irq(void *ctx);
+ static void onDio4Irq(void *ctx);
+ static void onDio5Irq(void *ctx);
+
+ /**
+ * What internal state are we in
+ */
+ typedef enum {
+ STATE_IDLE = 0,
+ STATE_RX_RUNNING,
+ STATE_TX_RUNNING,
+ STATE_CAD
+ } RADIO_STATES_T;
+
+ // needs to be OR'd onto registers for SPI write
+ static const uint8_t m_writeMode = 0x80;
+
+ // initialize the chip
+ void init();
+
+ // Start a transmit event (you should use send() or sendStr()
+ // rather than call this function directly.
+ RADIO_EVENT_T setTx(int timeout);
+
+ void startCAD(); // non-functional/non-tested
+
+ // not really used, maybe it should be
+ void setMaxPayloadLength(RADIO_MODEM_T modem, uint8_t max);
+
+ // Chip Select control (active LOW)
+ void csOn()
+ {
+ m_gpioCS.write(0);
+ };
+
+ void csOff()
+ {
+ m_gpioCS.write(1);
+ };
+
+ private:
+ // Thse structs will generate SWIG warnings, as we do not expose
+ // this data, they can be ignored.
+
+ // stored settings for the FSK modem
+ typedef struct
+ {
+ int8_t Power;
+ uint32_t Fdev;
+ uint32_t Bandwidth;
+ uint32_t BandwidthAfc;
+ uint32_t Datarate;
+ uint16_t PreambleLen;
+ bool FixLen;
+ uint8_t PayloadLen;
+ bool CrcOn;
+ bool IqInverted;
+ bool RxContinuous;
+ } radioFskSettings_t;
+
+ // stored settings for the LoRa modem
+ typedef struct
+ {
+ int8_t Power;
+ uint32_t Bandwidth;
+ uint32_t Datarate;
+ bool LowDatarateOptimize;
+ uint8_t Coderate;
+ uint16_t PreambleLen;
+ bool FixLen;
+ uint8_t PayloadLen;
+ bool CrcOn;
+ bool FreqHopOn;
+ uint8_t HopPeriod;
+ bool IqInverted;
+ bool RxContinuous;
+ } radioLoRaSettings_t;
+
+ // FSK packet handler state
+ typedef struct
+ {
+ uint8_t PreambleDetected;
+ uint8_t SyncWordDetected;
+ int8_t RssiValue;
+ int32_t AfcValue;
+ uint8_t RxGain;
+ uint16_t Size;
+ uint16_t NbBytes;
+ uint8_t FifoThresh;
+ uint8_t ChunkSize;
+ } radioFskPacketHandler_t;
+
+ // LoRa packet handler state
+ typedef struct
+ {
+ int8_t SnrValue;
+ int16_t RssiValue;
+ uint8_t Size;
+ } radioLoRaPacketHandler_t;
+
+ // our radio settings
+ struct {
+ RADIO_MODEM_T modem;
+ volatile RADIO_STATES_T state;
+ uint32_t channel;
+
+ radioFskSettings_t fskSettings;
+ volatile radioFskPacketHandler_t fskPacketHandler;
+
+ radioLoRaSettings_t loraSettings;
+ volatile radioLoRaPacketHandler_t loraPacketHandler;
+ } m_settings;
+
+ uint8_t lookupFSKBandWidth(uint32_t bw);
+
+ // received data (on successfull completion)
+ volatile int m_rxRSSI;
+ volatile int m_rxSNR;
+ volatile int m_rxLen;
+ uint8_t m_rxBuffer[FIFO_SIZE];
+
+ // for coordinating interrupt access
+ pthread_mutex_t m_intrLock;
+
+ void lockIntrs() { pthread_mutex_lock(&m_intrLock); };
+ void unlockIntrs() { pthread_mutex_unlock(&m_intrLock); };
+
+ // current radio event status
+ volatile RADIO_EVENT_T m_radioEvent;
+
+ // timer support
+ struct timeval m_startTime;
+ void initClock();
+ uint32_t getMillis();
+ };
+}
+
+