diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 1d4a95e3..ce4265fe 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 00000000..f63d1bfd --- /dev/null +++ b/examples/c++/sx1276-fsk.cxx @@ -0,0 +1,121 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include +#include +#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 00000000..7c455977 --- /dev/null +++ b/examples/c++/sx1276-lora.cxx @@ -0,0 +1,119 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include +#include +#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 00000000..c3a4b3a0 --- /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 + * 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 00000000..82581cc2 --- /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 + * 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 00000000..68996997 --- /dev/null +++ b/examples/python/sx1276-fsk.py @@ -0,0 +1,89 @@ +#!/usr/bin/python +# Author: Jon Trulson +# 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 00000000..05d97009 --- /dev/null +++ b/examples/python/sx1276-lora.py @@ -0,0 +1,88 @@ +#!/usr/bin/python +# Author: Jon Trulson +# 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 00000000..21fb5add --- /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 00000000..30785815 --- /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 00000000..3c5e9207 --- /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 00000000..2911804b --- /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 00000000..bebbc009 --- /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 00000000..004d025f --- /dev/null +++ b/src/sx1276/sx1276.cxx @@ -0,0 +1,2160 @@ +/* + * Author: Jon Trulson + * 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 +#include +#include +#include +#include +#include + +#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 00000000..bf4cd497 --- /dev/null +++ b/src/sx1276/sx1276.h @@ -0,0 +1,2035 @@ +/* + * Author: Jon Trulson + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +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(); + }; +} + +