diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..42ea2ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+CMakeLists.txt
+TODO.md
+cmake-build-debug
+waspmote-pro-ide-v04-macosx
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ab342d5
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+## 1.0.0 (2018-10-21)
+
+Initial release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bc16684
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2018 Nobody
+
+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.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..95bb19b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,229 @@
+# Archived code for 2016 Libelium Waspmote PRO v1.2 with Gas Sensor Board v2.0 and LoRaWAN
+
+This is just an archive of an Arduino sketch (and its required development tools), used to get more details from a
+Libelium Waspmote "Plug & Sense!" Smart Environment device as used by the City of Haarlem from May 2016 until
+October 2017.
+
+## Contents
+
+- [Code quality](#code-quality)
+- [Sensor quality](#sensor-quality)
+- [Hardware configuration](#hardware-configuration)
+- [Project contents](#project-contents)
+- [LoRaWAN](#lorawan)
+ - [Registering with The Things Network](#registering-with-the-things-network)
+ - [Uplink payload format](#uplink-payload-format)
+ - [TTN Payload function](#ttn-payload-function)
+ - [RN2483 known issues](#rn2483-known-issues)
+- [Programming using the Waspmote PRO IDE v04](#programming-using-the-waspmote-pro-ide-v04)
+ - [Installing the correct IDE](#installing-the-correct-ide)
+ - [Enabling debug logging](#enabling-debug-logging)
+ - [Using the LEDs](#using-the-leds)
+ - [Waspmote IDE known issues](#waspmote-ide-known-issues)
+
+
+## Code quality
+
+This is by no means intended to be production-ready software. Things that come to mind:
+
+- No power optimization has been done, at all. Like maybe the main board can sleep while the sensor board is awaiting
+ the initialization of a sensor? Maybe one can take measurements from one sensor while another is already initializing?
+- The used LoRaWAN packet format allows for ridiculously accurate temperature readings, and for negative values for
+ atmospheric pressure and NO2. All this will/should not happen, and hence is not quite efficient.
+- Only use the LED for the first few measurements after a restart.
+- Only take measurements when the battery level is good.
+- Allow for remote configuration, using downlinks. (Store remote settings in non-volatile memory to survive restarts.)
+- Allow for remotely restarting the device to join another LoRaWAN network.
+- Calculate the measurement interval based on the start of measurements, rather than time between measurements.
+- Confirm that the retry-until-success in the `setLoRaWAN` helper functions does not cause problems.
+
+Be sure to read the full documentation as provided by Libelium; this sketch was merely based on examples without diving
+into the technical documentation too much.
+
+
+## Sensor quality
+
+Two of these devices were used in the field from May 2016 until October 2017. Their hardware was already outdated when
+first installed, and never gave us any reliable results. One unit broke down due to external factors, and the other was
+taken into the office to see if its output could be improved by using different settings, but to no avail.
+
+The source code as used in the field was never released by an intermediate supplier. But peeking into its serial output
+revealed that for each sensor it was taking 10 samples, each 1 second apart, after which only the mean (average) value
+was transmitted. But the serial output also proved that the actual samples often showed a large variation. Rather than
+the mean, this sketch determines the median to discard the outliers, and also sends the minimum and maximum values for
+analysis. But as even temperature measurements would often show large variations within only 10 seconds, the device was
+declared crap and this sketch has never been used in the field.
+
+So, I do not endorse the early 2016 device, as its measurements were really bad. Later models may be okay, but I have
+not used those. The control hardware is quite nice, with great support for deep sleep and watchdog timers, and even
+offering a real-time clock. So if the sensors have meanwhile improved, and when not using the generated code, then this
+might be a nice unit.
+
+Beware that reported NO2 values will always need to be compensated for temperature and atmospheric pressure.
+This sketch simply transmits the reported values without any further postprocessing.
+
+
+## Hardware configuration
+
+Socket configuration for this sketch:
+
+- A: temperature, 9203, MCP9700A
+- B: atmospheric pressure, 9250, MPX4115A
+- D: NO2 9238-Pb, MiCS-2714; https://www.libelium.com/forum/viewtopic.php?f=28&t=22007&p=63548#p63568
+
+The LoRaWAN module identified itself as `RN2483 1.0.1 Dec 15 2015 09:38:09`.
+
+The NO2 sensor needs some parameters to be set, typically through some calibration. Without calibration,
+neither the documentation nor the support forum are helpful:
+
+- Gain: depends on the concentrations to be measured; changing the gain keeps the sensor from getting saturated.
+
+ > As a general rule, gain will be fixed at 1 in almost every application, only in very specific situations, such as
+ > operation in the limits of the sensor range, it will be necessary a different value.
+
+- Load resistance: depends on the actual calibration.
+
+ > Recommended values of load resistor: NO2 20 KOhm typical to 100K
+
+Also, the NO2 sensor needs to be pre-heated for at least 30 seconds.
+
+
+## Project contents
+
+ - [src/waspmote-otaa](./src/waspmote-otaa): the actual sketch and its dependencies.
+ - [src/generated](./src/generated): the sketch as generated by a Libelium tool, given the sensor configuration. As the
+ tool did not support LoRaWAN, and the code yields a very verbose text-based payload, this is only included as
+ documentation.
+ - [tools](./tools): the outdated Libelium Waspmote PRO IDE for Windows and OS X/macOS, and USB drivers.
+ - [docs](./docs): some outdated Libelium documentation, valid for this configuration.
+
+
+## LoRaWAN
+
+### Registering with The Things Network
+
+To register the device with The Things Network:
+
+- Use the Waspmote PRO IDE to upload the sketch, and peek into the serial output to see its hardware Device EUI.
+- Go to https://console.thethingsnetwork.org
+- Register an application. This will get you a public AppEUI.
+- Register a new OTAA device to the application, using the device's EUI. This will get you a device-specific AppKey.
+- Copy the application's public AppEUI and the device's secret AppKey into the sketch.
+- Upload the updated sketch.
+
+### Uplink payload format
+
+The values are sent in an 18 bytes MSB format. All values, except the battery level, are signed 16 bits integers where
+their original float value has been multiplied by 100 to retain 2 decimals.
+
+| bytes | data
+| ----- | ----------------------------------------------------------------------------
+| 00-05 | 3 x 16 bits minimum, median and maximum values for temperature, Celcius
+| 06-11 | 3 x 16 bits minimum, median and maximum values for atmospheric pressure, kPa
+| 12-17 | 3 x 16 bits minimum, median and maximum values for NO2, ppm
+| 18 | 8 bits unsigned battery level, percentage
+
+This is not at all optimal:
+
+- The decimals in the temperature readings are probably not very accurate and can be discarded, maybe allowing one
+ to limit temperature readings to use 3 x 8 bits.
+- Readings for pressure and NO2 can, if all is well, not be negative.
+- After determining that the readings are okay, one should stop sending the minimum and maximum values.
+
+### TTN Payload function
+
+To decode the values back into their original values:
+
+```javascript
+function Decoder(bytes, port) {
+ var i = 0;
+
+ function nextFloat() {
+ // Sign-extend to 32 bits to support negative values, by shifting 24 bits
+ // (too far) to the left, followed by a sign-propagating right shift:
+ return (bytes[i++]<<24>>16 | bytes[i++]) / 100;
+ }
+
+ function nextMinMedianMax() {
+ return {
+ min: nextFloat(),
+ median: nextFloat(),
+ max: nextFloat()
+ }
+ }
+
+ return {
+ temperature: nextMinMedianMax(),
+ pressure: nextMinMedianMax(),
+ no2: nextMinMedianMax(),
+ battery: bytes[i++]
+ }
+}
+```
+
+### RN2483 known issues
+
+- After a factory reset, make sure to set (dummy) values for DevAddr, AppSKey and NwkSKey, for otherwise calling
+ `LoRaWAN.saveConfig` (actually `mac save`) does not save the OTAA settings, and/or `LoRaWAN.joinABP` does not
+ recognize that they were saved.
+- Limited tests show that joining on low data rates might be troublesome, and that ADR might not be working. This has
+ not been investigated.
+
+
+## Programming using the Waspmote PRO IDE v04
+
+### Installing the correct IDE
+
+This device needs the old API, version 023, which is not included in the latest Waspmote PRO IDE. One could manually
+install it, but that's probably not worth the effort. The old IDE also does not support C++11 out of the box (while
+the latest IDE does), but even though that adds support for lambdas which would be a nice replacement of the function
+pointers used in `_execLoRaWAN` now, that's probably not worth the effort either.
+
+So, use the December 2013 Waspmote PRO IDE v04, as availble in the [tools](./tools) folder.
+
+Beware that Libelium warns one should never replace its proprietary bootloader, so beware when using different tooling:
+
+> The microcontroller Flash (128KB) contains both the uploaded program and the bootloader. The bootloader is a small
+> program which is executed at the beginning and proceeds to run the uploaded program. Libelium provides the Waspmote
+> IDE which won’t permit rewriting the bootloader.
+>
+> Do **NOT** use other IDEs, **only** the Waspmote IDE is recommended.
+>
+> Libelium does not recommend to implement Watchdog timers as some users have had some problems and the microcontroller
+> has needed to be re-flashed.
+>
+> If the bootloader is overwritten by using any of the previous practices, the warranty will be voided.
+
+### Enabling debug logging
+
+Even when enabling debugging, the Waspmote UART library hides most of the responses as soon as it finds a match. Like
+to see the actual RN2483 version information, enabling debugging does not help. Instead, one needs:
+
+```cpp
+uint8_t result = LoRaWAN.sendCommand("sys get ver\r\n", "\r\n", "invalid_param");
+```
+
+...which tells the `WaspUART` superclass to send `sys get ver` and then wait for either a newline (as returned after the
+RN2483 has printed its version details) or for the `invalid_param` error.
+
+To enable debugging see the header files in the IDE's `hardware/waspmote/cores/waspmote-api` folder, specifically the
+file `WaspUART.h` for debugging of its derived `WaspLoRaWAN`.
+
+### Using the LEDs
+
+The Waspmote being installed in a Plug & Sense! product, the blue LED in its power button is actually addressed using
+`Utils.blinkGreenLED`, not using `Utils.externalLEDBlink`.
+
+### Waspmote IDE known issues
+
+- Folder names should not include dashes.
+
+- On OS X and macOS, the old IDE needs the legacy Java SE 6 runtime; OS X and macOS will prompt one to download from
+ https://support.apple.com/kb/DL1572 However, though the names of the downloads are always `javaforosx.dmg`, an old
+ 2017 download from that very URL will not work for macOS 10.14 Mojava. After upgrading macOS make sure to download a
+ more recent release from the same URL.
+
+- Using the ancient Java, one will see errors like the following, but all works fine:
+
+ > Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Non-Java exception raised, not handled!
+ > (Original problem: Deprecated in 10_12... DO NOT EVER USE CGSEventRecord directly. Bad things, man.... bad things.)
diff --git a/docs/gases-sensor-board-2.0.pdf b/docs/gases-sensor-board-2.0.pdf
new file mode 100644
index 0000000..9f9c4da
Binary files /dev/null and b/docs/gases-sensor-board-2.0.pdf differ
diff --git a/docs/sensor-compatibility.pdf b/docs/sensor-compatibility.pdf
new file mode 100644
index 0000000..c18d07b
Binary files /dev/null and b/docs/sensor-compatibility.pdf differ
diff --git a/docs/waspmote-datasheet.pdf b/docs/waspmote-datasheet.pdf
new file mode 100644
index 0000000..472e0f3
Binary files /dev/null and b/docs/waspmote-datasheet.pdf differ
diff --git a/docs/waspmote-ide-user-guide.pdf b/docs/waspmote-ide-user-guide.pdf
new file mode 100644
index 0000000..e18643c
Binary files /dev/null and b/docs/waspmote-ide-user-guide.pdf differ
diff --git a/docs/waspmote-interruptions-programming-guide.pdf b/docs/waspmote-interruptions-programming-guide.pdf
new file mode 100644
index 0000000..414625c
Binary files /dev/null and b/docs/waspmote-interruptions-programming-guide.pdf differ
diff --git a/docs/waspmote-lorawan-networking-guide-4.3.pdf b/docs/waspmote-lorawan-networking-guide-4.3.pdf
new file mode 100644
index 0000000..eda6393
Binary files /dev/null and b/docs/waspmote-lorawan-networking-guide-4.3.pdf differ
diff --git a/docs/waspmote-lorawan-networking-guide-7.2.pdf b/docs/waspmote-lorawan-networking-guide-7.2.pdf
new file mode 100644
index 0000000..4377d9e
Binary files /dev/null and b/docs/waspmote-lorawan-networking-guide-7.2.pdf differ
diff --git a/docs/waspmote-plug-and-sense-overview.pdf b/docs/waspmote-plug-and-sense-overview.pdf
new file mode 100644
index 0000000..e8672bd
Binary files /dev/null and b/docs/waspmote-plug-and-sense-overview.pdf differ
diff --git a/docs/waspmote-plug-and-sense-sensors-guide.pdf b/docs/waspmote-plug-and-sense-sensors-guide.pdf
new file mode 100644
index 0000000..aaa7a5d
Binary files /dev/null and b/docs/waspmote-plug-and-sense-sensors-guide.pdf differ
diff --git a/docs/waspmote-plug-and-sense-technical-guide.pdf b/docs/waspmote-plug-and-sense-technical-guide.pdf
new file mode 100644
index 0000000..df3b753
Binary files /dev/null and b/docs/waspmote-plug-and-sense-technical-guide.pdf differ
diff --git a/docs/waspmote-programming-guide.pdf b/docs/waspmote-programming-guide.pdf
new file mode 100644
index 0000000..ee67d59
Binary files /dev/null and b/docs/waspmote-programming-guide.pdf differ
diff --git a/docs/waspmote-quickstart-guide.pdf b/docs/waspmote-quickstart-guide.pdf
new file mode 100644
index 0000000..d8c3bad
Binary files /dev/null and b/docs/waspmote-quickstart-guide.pdf differ
diff --git a/docs/waspmote-rtc-programming-guide.pdf b/docs/waspmote-rtc-programming-guide.pdf
new file mode 100644
index 0000000..2076cff
Binary files /dev/null and b/docs/waspmote-rtc-programming-guide.pdf differ
diff --git a/docs/waspmote-technical-guide.pdf b/docs/waspmote-technical-guide.pdf
new file mode 100644
index 0000000..f957179
Binary files /dev/null and b/docs/waspmote-technical-guide.pdf differ
diff --git a/docs/waspmote-utilities-programming-guide.pdf b/docs/waspmote-utilities-programming-guide.pdf
new file mode 100644
index 0000000..bcf3728
Binary files /dev/null and b/docs/waspmote-utilities-programming-guide.pdf differ
diff --git a/src/WaspmoteOTAA/RunningMedian.cpp b/src/WaspmoteOTAA/RunningMedian.cpp
new file mode 100644
index 0000000..595a26d
--- /dev/null
+++ b/src/WaspmoteOTAA/RunningMedian.cpp
@@ -0,0 +1,136 @@
+//
+// FILE: RunningMedian.cpp
+// AUTHOR: Rob dot Tillaart at gmail dot com
+// VERSION: 0.1.04.waspmote
+// PURPOSE: RunningMedian library for Arduino
+//
+// HISTORY:
+// 0.1.00 - 2011-02-16 initial version
+// 0.1.01 - 2011-02-22 added remarks from CodingBadly
+// 0.1.02 - 2012-03-15 added
+// 0.1.03 - 2013-09-30 added _sorted flag, minor refactor
+// 0.1.04 - 2013-10-17 added getAverage(uint8_t) - kudo's to Sembazuru
+// 2017-10-20 minor changes in .h file to support Libelium Waspmote
+//
+// Released to the public domain
+//
+
+#include "RunningMedian.h"
+
+RunningMedian::RunningMedian(uint8_t size)
+{
+ _size = constrain(size, MEDIAN_MIN_SIZE, MEDIAN_MAX_SIZE);
+ // array's could be allocated by malloc here,
+ // but using fixed size is easier.
+ clear();
+}
+
+RunningMedian::RunningMedian()
+{
+ _size = MEDIAN_DEF_SIZE;
+ clear();
+}
+// resets all counters
+void RunningMedian::clear()
+{
+ _cnt = 0;
+ _idx = 0;
+ _sorted = false;
+}
+
+// adds a new value to the data-set
+// or overwrites the oldest if full.
+void RunningMedian::add(float value)
+{
+ _ar[_idx++] = value;
+ if (_idx >= _size) _idx = 0; // wrap around
+ if (_cnt < _size) _cnt++;
+ _sorted = false;
+}
+
+float RunningMedian::getMedian()
+{
+ if (_cnt > 0)
+ {
+ if (_sorted == false) sort();
+ return _as[_cnt/2];
+ }
+ return NAN;
+}
+
+#ifdef RUNNING_MEDIAN_ALL
+float RunningMedian::getHighest()
+{
+ if (_cnt > 0)
+ {
+ if (_sorted == false) sort();
+ return _as[_cnt-1];
+ }
+ return NAN;
+}
+
+float RunningMedian::getLowest()
+{
+ if (_cnt > 0)
+ {
+ if (_sorted == false) sort();
+ return _as[0];
+ }
+ return NAN;
+}
+
+float RunningMedian::getAverage()
+{
+ if (_cnt > 0)
+ {
+ float sum = 0;
+ for (uint8_t i=0; i< _cnt; i++) sum += _ar[i];
+ return sum / _cnt;
+ }
+ return NAN;
+}
+
+float RunningMedian::getAverage(uint8_t nMedians)
+{
+ if ((_cnt > 0) && (nMedians > 0))
+ {
+ if (_cnt < nMedians) nMedians = _cnt; // when filling the array for first time
+ uint8_t start = ((_cnt - nMedians)/2);
+ uint8_t stop = start + nMedians;
+ sort();
+ float sum = 0;
+ for (uint8_t i = start; i < stop; i++) sum += _as[i];
+ return sum / nMedians;
+ }
+ return NAN;
+}
+
+uint8_t RunningMedian::getSize() { return _size; };
+
+uint8_t RunningMedian::getCount() { return _cnt; };
+#endif
+
+void RunningMedian::sort()
+{
+ // copy
+ for (uint8_t i=0; i< _cnt; i++) _as[i] = _ar[i];
+
+ // sort all
+ for (uint8_t i=0; i< _cnt-1; i++)
+ {
+ uint8_t m = i;
+ for (uint8_t j=i+1; j< _cnt; j++)
+ {
+ if (_as[j] < _as[m]) m = j;
+ }
+ if (m != i)
+ {
+ float t = _as[m]; // PATCH from 0.1.05 (was long)
+ _as[m] = _as[i];
+ _as[i] = t;
+ }
+ }
+ _sorted = true;
+}
+
+// END OF FILE
\ No newline at end of file
diff --git a/src/WaspmoteOTAA/RunningMedian.h b/src/WaspmoteOTAA/RunningMedian.h
new file mode 100644
index 0000000..b022de3
--- /dev/null
+++ b/src/WaspmoteOTAA/RunningMedian.h
@@ -0,0 +1,68 @@
+#ifndef RunningMedian_h
+#define RunningMedian_h
+//
+// FILE: RunningMedian.h
+// AUTHOR: Rob dot Tillaart at gmail dot com
+// PURPOSE: RunningMedian library for Arduino
+// VERSION: 0.1.04.waspmote
+// URL: http://arduino.cc/playground/Main/RunningMedian
+// HISTORY: See RunningMedian.cpp
+//
+// Released to the public domain
+//
+
+// Start of fixes for Waspmote
+// #if defined(ARDUINO) && ARDUINO >= 100
+// #include "Arduino.h"
+// #else
+// #include "WProgram.h"
+// #endif
+#include
+#include "wiring.h"
+typedef uint8_t boolean;
+// End of fixes for Waspmote
+
+#include
+
+#define RUNNING_MEDIAN_VERSION "0.1.04"
+
+// should at least be 5 to be practical
+#define MEDIAN_MIN_SIZE 1
+#define MEDIAN_MAX_SIZE 19
+#define MEDIAN_DEF_SIZE 5
+
+// conditional compile to minimize lib
+#define RUNNING_MEDIAN_ALL
+
+class RunningMedian
+{
+public:
+ RunningMedian(uint8_t);
+ RunningMedian();
+
+ void clear();
+ void add(float);
+ float getMedian();
+
+#ifdef RUNNING_MEDIAN_ALL
+ float getAverage();
+ float getAverage(uint8_t);
+ float getHighest();
+ float getLowest();
+
+ uint8_t getSize();
+ uint8_t getCount();
+#endif
+
+protected:
+ boolean _sorted;
+ uint8_t _size;
+ uint8_t _cnt;
+ uint8_t _idx;
+ float _ar[MEDIAN_MAX_SIZE];
+ float _as[MEDIAN_MAX_SIZE];
+ void sort();
+};
+
+#endif
+// END OF FILE
\ No newline at end of file
diff --git a/src/WaspmoteOTAA/WaspmoteOTAA.pde b/src/WaspmoteOTAA/WaspmoteOTAA.pde
new file mode 100644
index 0000000..8ebef10
--- /dev/null
+++ b/src/WaspmoteOTAA/WaspmoteOTAA.pde
@@ -0,0 +1,441 @@
+/**
+ * An example sketch for a 2016 Libelium Waspmote PRO v1.2, with Gas Sensor Board v2.0 and RN2483 LoRaWAN for EU868,
+ * using API v023.
+ *
+ * Hardware configuration of sockets:
+ * - A = temperature, 9203, MCP9700A
+ * - B = atmospheric pressure 9250, MPX4115A
+ * - D = NO2 9238-Pb, MiCS-2714 -- https://www.libelium.com/forum/viewtopic.php?f=28&t=22007&p=63548#p63568
+ *
+ * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
+ */
+
+#include
+#include
+// The old Arduino/Waspmote IDE does not support project sub-folders for third-party libraries.
+#include "RunningMedian.h"
+
+/**
+ * LoRaWAN OTAA 64-bit public Application EUI. MSB; a TTN-generated AppEUI always starts with 0x70.
+ */
+char APP_EUI[] = "7000000000000000";
+
+/**
+ * LoRaWAN OTAA 128-bit secret Application Key. MSB.
+ */
+char APP_KEY[] = "00000000000000000000000000000000";
+
+/**
+ * LoRaWAN initial data rate, used for OTAA Join. Low data rates seem troublesome for OTAA; needs investigation.
+ *
+ * 0: SF = 12, BW = 125 kHz, bitrate = 250 bps
+ * 1: SF = 11, BW = 125 kHz, bitrate = 440 bps
+ * 2: SF = 10, BW = 125 kHz, bitrate = 980 bps
+ * 3: SF = 9, BW = 125 kHz, bitrate = 1760 bps
+ * 4: SF = 8, BW = 125 kHz, bitrate = 3125 bps
+ * 5: SF = 7, BW = 125 kHz, bitrate = 5470 bps
+ */
+uint8_t INITIAL_DR = 3;
+
+/**
+ * LoRaWAN application port; any value from 1 to 223.
+ */
+uint8_t PORT = 1;
+
+/**
+ * Interval between the completion of one transmission, and the initialization of the next measurement cycle.
+ */
+char *sleepTime = "00:00:15:00";
+
+/**
+ * Number of seconds to wait after powering on the gases board.
+ */
+uint8_t boardInitSeconds = 2;
+
+/**
+ * Number of samples to take for temperature. As the median is taken, an odd number makes most sense.
+ */
+uint8_t temperatureSampleCount = 9;
+
+/**
+ * Number of samples to take for atmospheric pressure. As the median is taken, an odd number makes most sense.
+ */
+uint8_t pressureSampleCount = 9;
+
+/**
+ * Number of samples to take for NO2. As the median is taken, an odd number makes most sense.
+ */
+uint8_t no2SampleCount = 9;
+
+/**
+ * Number of seconds to pre-heat the NO2 sensor. This must be at least 30 seconds.
+ */
+uint8_t no2InitSeconds = 30;
+
+/**
+ * Gain for the NO2 sensor, 1 to 100. This depends on the concentrations to be measured; changing the gain keeps the
+ * sensor from getting saturated. As a general rule, gain will be fixed at 1 in almost every application; only in very
+ * specific situations, such as operation in the limits of the sensor range, a different value will be necessary.
+ */
+uint8_t no2Gain = 1;
+
+/**
+ * Load resistance for the NO2 sensor, 1 to 100 kOhm. This depends on calibration and is typically 20.
+ */
+float no2Resistance = 20;
+
+
+// ====================================================================================================================
+// End of user configuration
+// ====================================================================================================================
+
+
+/**
+ * Socket (UART) to which the LoRaWAN module is connected
+ */
+uint8_t socket = SOCKET0;
+
+/**
+ * Buffer to holdthe binary LoRaWAN uplink. MSB.
+ */
+byte data[50];
+
+void printLoRaWANResult(uint8_t result, char *charResult[] = NULL, uint32_t *intResult = NULL) {
+ switch (result) {
+ case LORAWAN_ANSWER_OK:
+ if (charResult != NULL) {
+ USB.println(*charResult);
+ } else if (intResult != NULL) {
+ USB.println(*intResult);
+ } else {
+ USB.println(F("OK"));
+ }
+ break;
+ case LORAWAN_ANSWER_ERROR:
+ USB.println(F("ANSWER_ERROR - Module communication error / erratic response to a function"));
+ break;
+ case LORAWAN_NO_ANSWER:
+ // includes duty cycle proplems after 3 x OTAA on SF12?
+ USB.println(F("NO_ANSWER - Module didn't respond"));
+ break;
+ case LORAWAN_INIT_ERROR:
+ USB.println(F("INIT_ERROR - Required keys to join to a network were not initialized (ABP)"));
+ break;
+ case LORAWAN_LENGTH_ERROR:
+ USB.println(F("LENGTH_ERROR - Error with data length / data to be sent length limit exceeded"));
+ break;
+ case LORAWAN_SENDING_ERROR:
+ USB.println(F("SENDING_ERROR - Sending error / server did not respond"));
+ break;
+ case LORAWAN_NOT_JOINED:
+ USB.println(F("NOT_JOINED - Module hasn't joined a network"));
+ break;
+ case LORAWAN_INPUT_ERROR:
+ USB.println(F("INPUT_ERROR - Invalid parameter"));
+ break;
+ case LORAWAN_VERSION_ERROR:
+ USB.println(F("VERSION_ERROR - Invalid version"));
+ break;
+ default:
+ USB.print(F("ERROR "));
+ USB.println(result, DEC);
+ }
+}
+
+/**
+ * Executes a function from the WaspLoRaWAN class, and repeats if unsuccessful.
+ */
+void _execLoRaWAN(const __FlashStringHelper *msg,
+ char *charResult[] = NULL, uint32_t *intResult = NULL,
+ uint8_t(WaspLoRaWAN::*f)() = NULL,
+ uint8_t(WaspLoRaWAN::*g)(char *) = NULL, char *c = 0,
+ uint8_t(WaspLoRaWAN::*h)(uint8_t) = NULL, uint8_t i = 0) {
+
+ // loop until result is LORAWAN_ANSWER_OK
+ while (1) {
+ Utils.blinkGreenLED(100, 1);
+
+ USB.print(msg);
+ uint8_t result;
+ if (f) {
+ result = (LoRaWAN.*f)();
+ } else if (g) {
+ result = (LoRaWAN.*g)(c);
+ } else {
+ result = (LoRaWAN.*h)(i);
+ }
+
+ USB.print(F(": "));
+ printLoRaWANResult(result, charResult, intResult);
+ if (result == LORAWAN_ANSWER_OK) {
+ break;
+ }
+ if (result != LORAWAN_ANSWER_ERROR) {
+ // When not resetting, then the module does not seem to recover from LORAWAN_NO_ANSWER. For OTAA during
+ // testing only LORAWAN_ANSWER_ERROR (recoverable twice) and LORAWAN_NO_ANSWER occurred.
+ LoRaWAN.reset();
+ }
+ }
+}
+
+/**
+ * Prints the given message and invokes the given function without any parameters until it returns success, and upon
+ * success prints the value of the given reference.
+ *
+ * @param msg the message to print
+ * @param f the function to invoke
+ * @param charResult the reference to the char result to print
+ */
+void getLoRaWAN(const __FlashStringHelper *msg, uint8_t(WaspLoRaWAN::*f)(), char *charResult) {
+ _execLoRaWAN(msg, &charResult, NULL, f);
+}
+
+/**
+ * Prints the given message and invokes the given function without any parameters until it returns success, and upon
+ * success prints the value of the given reference.
+ *
+ * @param msg the message to print
+ * @param f the function to invoke
+ * @param intResult the reference to the numeric result to print
+ */
+void getLoRaWAN(const __FlashStringHelper *msg, uint8_t(WaspLoRaWAN::*f)(), uint32_t *intResult) {
+ _execLoRaWAN(msg, NULL, intResult, f);
+}
+
+/**
+ * Prints the given message and invokes the given function without any parameters until it returns success.
+ *
+ * @param msg the message to print
+ * @param f the function to invoke
+ */
+void setLoRaWAN(const __FlashStringHelper *msg, uint8_t(WaspLoRaWAN::*f)()) {
+ _execLoRaWAN(msg, NULL, NULL, f);
+}
+
+/**
+ * Prints the given message and invokes the given function with the given char* parameter, until it returns success.
+ *
+ * @param msg the message to print
+ * @param g the function to invoke
+ * @param param the parameter to pass to the function
+ */
+void setLoRaWAN(const __FlashStringHelper *msg, uint8_t(WaspLoRaWAN::*g)(char *), char *param) {
+ _execLoRaWAN(msg, NULL, NULL, NULL, g, param);
+}
+
+/**
+ * Prints the given message and invokes the given function with the given int parameter, until it returns success.
+ *
+ * @param msg the message to print
+ * @param h the function to invoke
+ * @param param the parameter to pass to the function
+ */
+void setLoRaWAN(const __FlashStringHelper *msg, uint8_t(WaspLoRaWAN::*h)(uint8_t), uint8_t param) {
+ _execLoRaWAN(msg, NULL, NULL, NULL, NULL, NULL, h, param);
+}
+
+void printFloat(const __FlashStringHelper *msg, float f) {
+ // USB.printf does not Flash-strings, nor floats
+ USB.print(msg);
+ char s[10];
+ Utils.float2String(f, s, 2);
+ USB.print(s);
+}
+
+void printInt(const __FlashStringHelper *msg, int8_t i) {
+ USB.print(msg);
+ USB.print(i, DEC);
+}
+
+void printMemory() {
+ USB.printf(" - Free memory: %d bytes\n", freeMemory());
+}
+
+void setup() {
+ USB.ON();
+ USB.println(F("Configuring LoRaWAN module"));
+ setLoRaWAN(F(" - Switch on"), &WaspLoRaWAN::ON, socket);
+ setLoRaWAN(F(" - Factory reset"), &WaspLoRaWAN::factoryReset);
+ // Print the hardware EUI required to register the device at The Things Network
+ getLoRaWAN(F(" - Get hardware EUI"), &WaspLoRaWAN::getEUI, LoRaWAN._eui);
+
+ USB.print(F(" - Get version: "));
+ uint8_t result = LoRaWAN.sendCommand("sys get ver\r\n", "\r\n", "invalid_param");
+ if (result == 1) {
+ USB.print(LoRaWAN._buffer, LoRaWAN._length);
+ } else {
+ USB.println(F("FAILED"));
+ }
+
+ USB.print(F(" - Get Waspmote serial number: "));
+ USB.println(Utils.readSerialID());
+
+ // Some dummy values need to be set, for otherwise joinABP after a deep sleep yields error 3, Required keys to join
+ // to a network were not initialized.
+ setLoRaWAN(F(" - Set dummy DevAdr"), &WaspLoRaWAN::setDeviceAddr, "00000000");
+ setLoRaWAN(F(" - Set dummy AppSKey"), &WaspLoRaWAN::setAppSessionKey, "00000000000000000000000000000000");
+ setLoRaWAN(F(" - Set dummy NwkSKey"), &WaspLoRaWAN::setNwkSessionKey, "00000000000000000000000000000000");
+ // Not specifying a specific DevEUI will use the hardware EUI
+ setLoRaWAN(F(" - Set DevEUI to hardware EUI"), &WaspLoRaWAN::setDeviceEUI);
+ setLoRaWAN(F(" - Set AppEUI"), &WaspLoRaWAN::setAppEUI, APP_EUI);
+ setLoRaWAN(F(" - Set AppKey"), &WaspLoRaWAN::setAppKey, APP_KEY);
+ setLoRaWAN(F(" - Set data rate"), &WaspLoRaWAN::setDataRate, INITIAL_DR);
+ setLoRaWAN(F(" - Set ADR"), &WaspLoRaWAN::setADR, "on");
+ setLoRaWAN(F(" - Save configuration"), &WaspLoRaWAN::saveConfig);
+ setLoRaWAN(F(" - Join OTAA"), &WaspLoRaWAN::joinOTAA);
+ setLoRaWAN(F(" - Save configuration"), &WaspLoRaWAN::saveConfig);
+}
+
+void readSensor(uint8_t sensor, RunningMedian &samples) {
+ USB.print(F(" "));
+ samples.clear();
+ // Initiate a dummy reading for analog-to-digital converter channel selection
+ SensorGasv20.readValue(sensor);
+ for (uint8_t i = 0; i < samples.getSize(); i++) {
+ float f = SensorGasv20.readValue(sensor);
+ samples.add(f);
+ Utils.blinkGreenLED();
+ printFloat(F(" "), f);
+ delay(1000);
+ }
+ USB.println();
+ printInt(F(" count="), samples.getSize());
+ printFloat(F(" mean="), samples.getAverage());
+ printFloat(F(" median="), samples.getMedian());
+ printFloat(F(" min="), samples.getLowest());
+ printFloat(F(" max="), samples.getHighest());
+ USB.println();
+}
+
+uint8_t setSampleData(RunningMedian samples, uint8_t i) {
+ int16_t median = samples.getMedian() * 100;
+ int16_t min = samples.getLowest() * 100;
+ int16_t max = samples.getHighest() * 100;
+
+ data[i++] = min >> 8;
+ data[i++] = min;
+ data[i++] = median >> 8;
+ data[i++] = median;
+ data[i++] = max >> 8;
+ data[i++] = max;
+ return i;
+}
+
+/**
+ * Takes all measurements, and populates the LoRaWAN data.
+ *
+ * @return the length of the data
+ */
+uint8_t takeMeasurements() {
+ printMemory();
+
+ // Turn on the sensor board
+ SensorGasv20.ON();
+ USB.printf(" - Power on sensor board (waiting %u seconds for initialization)\n", boardInitSeconds);
+ USB.flush();
+ delay(1000L * boardInitSeconds);
+
+ // Temperature
+ USB.println(F(" - Temperature"));
+ RunningMedian temperatureSamples = RunningMedian(temperatureSampleCount);
+ readSensor(SENS_TEMPERATURE, temperatureSamples);
+
+ // Atmospheric pressure
+ USB.println(F(" - Atmospheric pressure"));
+ SensorGasv20.setSensorMode(SENS_ON, SENS_PRESSURE);
+ delay(30);
+
+ RunningMedian pressureSamples = RunningMedian(pressureSampleCount);
+ readSensor(SENS_PRESSURE, pressureSamples);
+
+ SensorGasv20.setSensorMode(SENS_OFF, SENS_PRESSURE);
+
+ // NO2
+ USB.printf(" - NO2 (waiting %u seconds for initialization)\n", no2InitSeconds);
+ SensorGasv20.configureSensor(SENS_SOCKET3B, no2Gain, no2Resistance);
+ SensorGasv20.setSensorMode(SENS_ON, SENS_SOCKET3B);
+ USB.flush();
+ delay(1000L * no2InitSeconds);
+
+ RunningMedian no2Samples = RunningMedian(no2SampleCount);
+ readSensor(SENS_SOCKET3B, no2Samples);
+
+ SensorGasv20.setSensorMode(SENS_OFF, SENS_SOCKET3B);
+
+ // Turn off sensor board
+ USB.println(F(" - Switch off sensor board"));
+ SensorGasv20.OFF();
+ delay(10);
+
+ // Battery
+ USB.println(F(" - Battery"));
+ USB.print(F(" "));
+ // First dummy reading for analog-to-digital converter channel selection
+ PWR.getBatteryLevel();
+ Utils.blinkGreenLED();
+ uint8_t batteryLevel = PWR.getBatteryLevel();
+ printInt(F(" "), batteryLevel);
+ printFloat(F("% ("), PWR.getBatteryVolts());
+ USB.println(" Volt)");
+
+ // Prepare a LoRaWAN message; MSB
+ uint8_t i = 0;
+ i = setSampleData(temperatureSamples, i);
+ i = setSampleData(pressureSamples, i);
+ i = setSampleData(no2Samples, i);
+ data[i++] = batteryLevel;
+ return i;
+}
+
+void loop() {
+ Utils.blinkGreenLED(500, 5);
+
+ USB.println(F("\nTaking measurements"));
+
+ uint8_t dataLen = takeMeasurements();
+ // In the Waspmote API v23, data must be passed as a HEX string.
+ char text[100];
+ Utils.hex2str((uint8_t *) data, text, dataLen);
+
+ USB.println(F("\nSending measurements"));
+ setLoRaWAN(F(" - Switch on LoRaWAN module"), &WaspLoRaWAN::ON, socket);
+ setLoRaWAN(F(" - Set ABP keys"), &WaspLoRaWAN::joinABP);
+ getLoRaWAN(F(" - Application EUI"), &WaspLoRaWAN::getAppEUI, LoRaWAN._appEUI);
+ getLoRaWAN(F(" - Device EUI"), &WaspLoRaWAN::getDeviceEUI, LoRaWAN._devEUI);
+ getLoRaWAN(F(" - Device Address"), &WaspLoRaWAN::getDeviceAddr, LoRaWAN._devAddr);
+ getLoRaWAN(F(" - Uplink counter"), &WaspLoRaWAN::getUpCounter, &LoRaWAN._upCounter);
+ getLoRaWAN(F(" - Downlink counter"), &WaspLoRaWAN::getDownCounter, &LoRaWAN._downCounter);
+
+ USB.print(F(" - Sending packet "));
+ USB.print(text);
+
+ // Only try sending once
+ uint8_t result = LoRaWAN.sendUnconfirmed(PORT, text);
+ USB.print(F(": "));
+ printLoRaWANResult(result);
+
+ if (result == LORAWAN_ANSWER_OK) {
+ if (LoRaWAN._dataReceived) {
+ USB.print(F(" - Received downlink on port "));
+ USB.println(LoRaWAN._port, DEC);
+ USB.print(F(" "));
+ USB.println(LoRaWAN._data);
+ }
+ }
+
+ getLoRaWAN(F(" - Uplink counter"), &WaspLoRaWAN::getUpCounter, &LoRaWAN._upCounter);
+ getLoRaWAN(F(" - Downlink counter"), &WaspLoRaWAN::getDownCounter, &LoRaWAN._downCounter);
+
+ // Do we really want to repeat this until we get success?
+ setLoRaWAN(F(" - Switch off LoRaWAN module"), &WaspLoRaWAN::OFF, socket);
+
+ printMemory();
+
+ Utils.blinkGreenLED(500, 3);
+
+ USB.print(F("\nEntering deep sleep for "));
+ USB.println(sleepTime);
+ // Deep sleep with all sensors off
+ PWR.deepSleep(sleepTime, RTC_OFFSET, RTC_ALM1_MODE1, ALL_OFF);
+ USB.println("\nWake up");
+}
diff --git a/src/generated/Waspmote_Plug_and_sense_test_code.txt b/src/generated/Waspmote_Plug_and_sense_test_code.txt
new file mode 100644
index 0000000..1482d38
--- /dev/null
+++ b/src/generated/Waspmote_Plug_and_sense_test_code.txt
@@ -0,0 +1,220 @@
+/*
+ * -------- Waspmote - Plug & Sense! - Code Generator ------------
+ *
+ * Code generated with Waspmote Plug & Sense! Code Generator.
+ * This code is intended to be used only with Waspmote Plug & Sense!
+ * series (encapsulated line) and is not valid for Waspmote. Use only
+ * with Waspmote Plug & Sense! IDE (do not confuse with Waspmote IDE).
+ *
+ * Copyright (C) 2012 Libelium Comunicaciones Distribuidas S.L.
+ * http://www.libelium.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Version: 0.1
+ * Generated: 04/10/2017
+ *
+ */
+
+// Step 1. Includes of the Sensor Board and Communications modules used
+
+#include
+
+
+
+#include
+
+// Step 2. Variables declaration
+
+char CONNECTOR_A[3] = "CA";
+char CONNECTOR_B[3] = "CB";
+char CONNECTOR_C[3] = "CC";
+char CONNECTOR_D[3] = "CD";
+char CONNECTOR_E[3] = "CE";
+char CONNECTOR_F[3] = "CF";
+
+long sequenceNumber = 0;
+
+char nodeID[10] = "hlm-24-139";
+
+char* sleepTime = "00:00:05:00";
+
+char data[100];
+
+float connectorAFloatValue;
+float connectorBFloatValue;
+float connectorCFloatValue;
+float connectorDFloatValue;
+float connectorEFloatValue;
+float connectorFFloatValue;
+
+int connectorAIntValue;
+int connectorBIntValue;
+int connectorCIntValue;
+int connectorDIntValue;
+int connectorEIntValue;
+int connectorFIntValue;
+
+char connectorAString[10];
+char connectorBString[10];
+char connectorCString[10];
+char connectorDString[10];
+char connectorEString[10];
+char connectorFString[10];
+
+int batteryLevel;
+char batteryLevelString[10];
+char BATTERY[4] = "BAT";
+
+char* macAddress="";
+
+packetXBee* packet;
+
+
+void setup()
+{
+
+// Step 3. Communication module initialization
+
+// Step 4. Communication module to ON
+
+ xbee802.ON();
+
+// Step 5. Initial message composition
+
+ // Memory allocation
+ packet=(packetXBee*) calloc(1,sizeof(packetXBee));
+ // Choose transmission mode: UNICAST or BROADCAST
+ packet->mode=UNICAST;
+ // Set destination XBee parameters to packet
+ xbee802.setDestinationParams( packet, macAddress, "Hello, this is Waspmote Plug & Sense!\r\n", MAC_TYPE);
+
+// Step 6. Initial message transmission
+
+ xbee802.sendXBee(packet);
+ // Free variables
+ free(packet);
+ packet=NULL;
+
+// Step 7. Communication module to OFF
+
+ xbee802.OFF();
+ delay(100);
+
+
+}
+
+void loop()
+{
+// Step 8. Turn on the Sensor Board
+
+ //Turn on the sensor board
+ SensorGasv20.ON();
+ //Turn on the RTC
+ RTC.ON();
+ //supply stabilization delay
+ delay(100);
+
+// Step 9. Turn on the sensors
+
+ //En el caso de la placa de eventos no aplica
+
+ delay(10000);
+
+ //Turning on NO2 Sensor
+ SensorGasv20.setSensorMode(SENS_ON, SENS_SOCKET3B);
+ delay(30000);
+
+// Step 10. Read the sensors
+
+
+
+ // First dummy reading for analog-to-digital converter channel selection
+ PWR.getBatteryLevel();
+ // Getting Battery Level
+ batteryLevel = PWR.getBatteryLevel();
+ // Conversion into a string
+ itoa(batteryLevel, batteryLevelString, 10);
+
+ //First dummy reading for analog-to-digital converter channel selection
+ SensorGasv20.readValue(SENS_TEMPERATURE);
+ //Sensor temperature reading
+ connectorAFloatValue = SensorGasv20.readValue(SENS_TEMPERATURE);
+ //Conversion into a string
+ Utils.float2String(connectorAFloatValue, connectorAString, 2);
+
+ //First dummy reading for analog-to-digital converter channel selection
+ SensorGasv20.readValue(SENS_HUMIDITY);
+ //Sensor temperature reading
+ connectorBFloatValue = SensorGasv20.readValue(SENS_HUMIDITY);
+ //Conversion into a string
+ Utils.float2String(connectorBFloatValue, connectorBString, 2);
+
+ // Configuring NO2 sensor
+ SensorGasv20.configureSensor(SENS_SOCKET3B, 1, 2);
+ delay(10);
+ //First dummy reading to set analog-to-digital channel
+ SensorGasv20.readValue(SENS_SOCKET3B);
+ connectorDFloatValue = SensorGasv20.readValue(SENS_SOCKET3B);
+ //Conversion into a string
+ Utils.float2String(connectorDFloatValue, connectorDString, 2);
+
+// Step 11. Turn off the sensors
+
+ //En el caso de la placa de eventos no aplica
+
+ SensorGasv20.setSensorMode(SENS_OFF, SENS_SOCKET3B);
+
+// Step 12. Message composition
+
+ //Data payload composition
+ sprintf(data,"I:%s#N:%li#%s:%s#%s:%s#%s:%s#%s:%s\r\n",
+ nodeID ,
+ sequenceNumber,
+ BATTERY, batteryLevelString,
+ CONNECTOR_A , connectorAString,
+ CONNECTOR_B , connectorBString,
+ CONNECTOR_D , connectorDString);
+
+ // Memory allocation
+ packet=(packetXBee*) calloc(1,sizeof(packetXBee));
+ // Choose transmission mode: UNICAST or BROADCAST
+ packet->mode=UNICAST;
+ // Set destination XBee parameters to packet
+ xbee802.setDestinationParams( packet, macAddress, data, MAC_TYPE);
+
+// Step 13. Communication module to ON
+
+ xbee802.ON();
+
+// Step 14. Message transmission
+
+ xbee802.sendXBee(packet);
+ // Free variables
+ free(packet);
+ packet=NULL;
+
+// Step 15. Communication module to OFF
+
+ xbee802.OFF();
+ delay(100);
+
+// Step 16. Entering Sleep Mode
+
+ PWR.deepSleep(sleepTime,RTC_OFFSET,RTC_ALM1_MODE1,ALL_OFF);
+ //Increase the sequence number after wake up
+ sequenceNumber++;
+
+
+}
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 0000000..0d58b95
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,4 @@
+# Tools
+
+These are the tools as downloaded from the public Libelium website, November 2017. Newer versions might be available,
+but might work with the older hardware.
diff --git a/tools/macos/FTDIUSBSerialDriver_v2_4_2.dmg b/tools/macos/FTDIUSBSerialDriver_v2_4_2.dmg
new file mode 100644
index 0000000..3ba6099
Binary files /dev/null and b/tools/macos/FTDIUSBSerialDriver_v2_4_2.dmg differ
diff --git a/tools/macos/javaforosx-OSX-2015-001.dmg b/tools/macos/javaforosx-OSX-2015-001.dmg
new file mode 100644
index 0000000..632cb31
Binary files /dev/null and b/tools/macos/javaforosx-OSX-2015-001.dmg differ
diff --git a/tools/macos/javaforosx-macOS-2017-001.dmg b/tools/macos/javaforosx-macOS-2017-001.dmg
new file mode 100644
index 0000000..939c491
Binary files /dev/null and b/tools/macos/javaforosx-macOS-2017-001.dmg differ
diff --git a/tools/macos/waspmote-pro-ide-v04-macosx.zip b/tools/macos/waspmote-pro-ide-v04-macosx.zip
new file mode 100644
index 0000000..6dd28d7
Binary files /dev/null and b/tools/macos/waspmote-pro-ide-v04-macosx.zip differ
diff --git a/tools/waspmote-ide-user-guide.pdf b/tools/waspmote-ide-user-guide.pdf
new file mode 100644
index 0000000..e18643c
Binary files /dev/null and b/tools/waspmote-ide-user-guide.pdf differ
diff --git a/tools/windows/waspmote-pro-ide-v04-windows.zip b/tools/windows/waspmote-pro-ide-v04-windows.zip
new file mode 100644
index 0000000..5a71a35
Binary files /dev/null and b/tools/windows/waspmote-pro-ide-v04-windows.zip differ