Switch to platformio.
This commit is contained in:
parent
5ff63b5826
commit
6e668f7f1a
103 changed files with 3759 additions and 2426 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*/.vscode/ipch
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
|
@ -1,121 +0,0 @@
|
|||
// --------------------------------------
|
||||
// Jeenode_i2c_scanner
|
||||
//
|
||||
// Version 1
|
||||
// This program (or code that looks like it)
|
||||
// can be found in many places.
|
||||
// For example on the Arduino.cc forum.
|
||||
// The original author is not know.
|
||||
// Version 2, Juni 2012, Using Arduino 1.0.1
|
||||
// Adapted to be as simple as possible by Arduino.cc user Krodal
|
||||
// Version 3, Feb 26 2013
|
||||
// V3 by louarnold
|
||||
// Version 4, March 3, 2013, Using Arduino 1.0.3
|
||||
// by Arduino.cc user Krodal.
|
||||
// Changes by louarnold removed.
|
||||
// Scanning addresses changed from 0...127 to 1...119,
|
||||
// according to the i2c scanner by Nick Gammon
|
||||
// http://www.gammon.com.au/forum/?id=10896
|
||||
// Version 5, March 28, 2013
|
||||
// As version 4, but address scans now to 127.
|
||||
// A sensor seems to use address 120.
|
||||
// Version 6, March 21, 2014, Using Arduino 1.0.5 / current JeeLib from git
|
||||
// by Gareth Coleman, l0l.org.uk.
|
||||
// Reworked scanner so that it uses JeeLib instead of Wire and can also
|
||||
// scan I2C devices on 'ports' on JeeNodes. SDA goes to DIO, SCK to AIO.
|
||||
// Version 7, October 11, 2014, modified for JeeNode Micro, reporting over radio.
|
||||
// by flabbergast.
|
||||
//
|
||||
// This sketch tests the standard 7-bit addresses
|
||||
// Devices with higher bit address might not be seen properly.
|
||||
//
|
||||
|
||||
#define RF69_COMPAT 1
|
||||
#define RF_NODE_ID 22
|
||||
#define RF_GROUP 212
|
||||
#define RF_FREQ RF12_868MHZ
|
||||
|
||||
#include <JeeLib.h>
|
||||
|
||||
//##### radio packet structure #####
|
||||
typedef struct { // RFM12B RF payload datastructure
|
||||
byte address;
|
||||
byte port;
|
||||
} Payload;
|
||||
Payload payload;
|
||||
|
||||
byte presence, address, port;
|
||||
int nDevices;
|
||||
|
||||
// Which ports to scan?
|
||||
// On JNu, on the hardware port it "finds" devices at all addresses, I think because there's
|
||||
// a ISP programmer on the same line. So we only scan the "software" port.
|
||||
// Port 0 means (hardware) I2C interface, ports 1-4 use software
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
#define START_PORT 1
|
||||
#define END_PORT 1
|
||||
#else
|
||||
#define START_PORT 0
|
||||
#define END_PORT 4
|
||||
#endif
|
||||
|
||||
// this must be added since we're using the watchdog for low-power waiting
|
||||
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
|
||||
|
||||
void setup() {
|
||||
cli();
|
||||
CLKPR = bit(CLKPCE);
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
CLKPR = 0; // div 1, i.e. speed up to 8 MHz
|
||||
#else
|
||||
CLKPR = 1; // div 2, i.e. slow down to 8 MHz
|
||||
#endif
|
||||
sei();
|
||||
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
// power up the radio on JMv3
|
||||
bitSet(DDRB, 0);
|
||||
bitClear(PORTB, 0);
|
||||
#endif
|
||||
|
||||
rf12_initialize(RF_NODE_ID, RF_FREQ, RF_GROUP);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
nDevices = 0;
|
||||
|
||||
// send an echo that we're starting
|
||||
payload.port = 0xFF;
|
||||
payload.address = 0xFF;
|
||||
rf12_sendNow(0, &payload, sizeof payload);
|
||||
rf12_sendWait(2);
|
||||
|
||||
for(port = START_PORT; port <= END_PORT; port++ )
|
||||
{
|
||||
for(address = 1; address < 127; address++ )
|
||||
{
|
||||
// This i2c_scanner uses the return value of
|
||||
// the DeviceI2C.isPresent function to see if
|
||||
// a device exists. For overview of the PortI2C
|
||||
// and DeviceI2C functions see
|
||||
// http://jeelabs.org/2012/03/21/porti2c-the-big-picture/
|
||||
|
||||
presence = DeviceI2C(PortI2C(port),address).isPresent();
|
||||
|
||||
if (presence != 0)
|
||||
{
|
||||
payload.port = port;
|
||||
payload.address = address;
|
||||
nDevices++;
|
||||
rf12_sendNow(0, &payload, sizeof payload);
|
||||
rf12_sendWait(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
payload.port = 0xFF;
|
||||
payload.address = nDevices;
|
||||
rf12_sendNow(0, &payload, sizeof payload);
|
||||
rf12_sendWait(2);
|
||||
delay(20000); // wait 20 seconds for next scan
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/// @dir radioBlip
|
||||
/// Send out a radio packet every minute, consuming as little power as possible.
|
||||
// 2010-08-29 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
|
||||
|
||||
#define RF69_COMPAT 1
|
||||
#define RF_NODE_ID 22
|
||||
#define RF_GROUP 212
|
||||
#define RF_FREQ RF12_868MHZ
|
||||
|
||||
#include <JeeLib.h>
|
||||
|
||||
static long payload;
|
||||
|
||||
// this must be added since we're using the watchdog for low-power waiting
|
||||
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
|
||||
|
||||
void setup() {
|
||||
cli();
|
||||
CLKPR = bit(CLKPCE);
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
CLKPR = 0; // div 1, i.e. speed up to 8 MHz
|
||||
#else
|
||||
CLKPR = 1; // div 2, i.e. slow down to 8 MHz
|
||||
#endif
|
||||
sei();
|
||||
|
||||
#if defined(__AVR_ATtiny84__)
|
||||
// power up the radio on JMv3
|
||||
bitSet(DDRB, 0);
|
||||
bitClear(PORTB, 0);
|
||||
#endif
|
||||
|
||||
rf12_initialize(RF_NODE_ID, RF_FREQ, RF_GROUP);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
++payload;
|
||||
|
||||
rf12_sendNow(0, &payload, sizeof payload);
|
||||
// set the sync mode to 2 if the fuses are still the Arduino default
|
||||
// mode 3 (full powerdown) can only be used with 258 CK startup fuses
|
||||
rf12_sendWait(2);
|
||||
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
Sleepy::loseSomeTime(6000);
|
||||
rf12_sleep(RF12_WAKEUP);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
55
README.md
55
README.md
|
@ -1,57 +1,42 @@
|
|||
Arduino sketches
|
||||
================
|
||||
PlatformIO "jee" sketches
|
||||
=========================
|
||||
|
||||
Currently (2018-01-28T23:57:41) the sketches rely on the "newest" RFM69 driver from jcw, a.k.a. [native](https://github.com/jeelabs/embello/tree/master/lib/test-arduino). It should be compatible with Jeenode Zero.
|
||||
Note: used to be "Arduino" sketches; but on 2019-06-23T18:19:05 I've switched to [PlatformIO](https://platformio.org/). The Arduino code is in the `arduino` branch, and pretty much "frozen".
|
||||
|
||||
I really like this code - I can understand it without hunting through trillions of source files scattered over the directory tree, and hence it is easy to tweak. It produces small binaries. Just overall sweet!
|
||||
As of (2018-01-28T23:57:41) the sketches rely on the "newest" RFM69 driver from jcw, a.k.a. [native](https://github.com/jeelabs/embello/tree/master/lib/test-arduino). It should be compatible with Jeenode Zero (with Mecrisp Forth and jcw's driver).
|
||||
|
||||
Before this I used [jeelib] and then [RFM69] library. They're both good, just not my cup of tea (in order: too focused on backwards compatibility with RFM12b, trying (and succeeding) to be too general (making the code difficult to navigate through)).
|
||||
|
||||
RFu_RF69_thermistor.ino
|
||||
-----------------------
|
||||
rfu-htu21d, rfu-th02, rfu-thermistor
|
||||
------------------------------------
|
||||
|
||||
An Arduino sketch for running [Ciseco's RFµ-328](http://shop.ciseco.co.uk/rf-328-bare-arduino-atmega-328-compatible-micro-board-rfu-328/), with a HopeRF's [RFM69CW](http://www.hoperf.com/rf/fsk_module/RFM69CW.htm) radio soldered on, on a [XRF thermistor coin cell board](http://shop.ciseco.co.uk/temperature-xrf-development-sensor-thermistor/) from Ciseco.
|
||||
For [Ciseco's RFµ-328](http://shop.ciseco.co.uk/rf-328-bare-arduino-atmega-328-compatible-micro-board-rfu-328/), with a HopeRF's [RFM69CW](http://www.hoperf.com/rf/fsk_module/RFM69CW.htm) radio soldered on, on a [XRF thermistor coin cell board](http://shop.ciseco.co.uk/temperature-xrf-development-sensor-thermistor/) from Ciseco.
|
||||
|
||||
RFu_TH02.ino
|
||||
------------
|
||||
I have two that use [HTU21D](https://www.te.com/usa-en/product-CAT-HSC0004.html) (although one is called 'th02', the th02 sensor that I've used before went bad). Temperature and humidity.
|
||||
|
||||
An Arduino sketch for running [Ciseco's RFµ-328](http://shop.ciseco.co.uk/rf-328-bare-arduino-atmega-328-compatible-micro-board-rfu-328/), sending readings from HopeRF's [TH02](http://www.hoperf.com/sensor/app/TH02.htm) temperature & humidity sensor. All on the Ciseco's [RFµ developer board](http://shop.ciseco.co.uk/rfu-developer-kit/).
|
||||
(Sorry, the Ciseco links don't work anymore, the company went bust. One more
|
||||
reason to stick to open source...)
|
||||
|
||||
(Sorry, the Ciseco links don't work anymore, the company went bust. One more reason to stick to open source...)
|
||||
jnu-tmp102
|
||||
----------
|
||||
|
||||
JeeNodeMicro_TH02.ino
|
||||
---------------------
|
||||
For a [JeeNode Micro](http://jeelabs.net/projects/hardware/wiki/JeeNode_Micro) with a [TMP102](https://www.ti.com/lit/ds/symlink/tmp102.pdf) temperature sensor.
|
||||
|
||||
An Arduino sketch for a [JeeNode Micro](http://jeelabs.net/projects/hardware/wiki/JeeNode_Micro) with a [TH02](http://www.hoperf.com/sensor/app/TH02.htm) digital temperature and humidity sensor from HopeRF.
|
||||
|
||||
JeeNodeMicroTest sketches
|
||||
-------------------------
|
||||
blr_relay
|
||||
---------
|
||||
|
||||
Several programs to test things on a [JeeNode Micro](http://jeelabs.net/projects/hardware/wiki/JeeNode_Micro). They use radio for reporting things (e.g. I2C scanner) instead of software serial.
|
||||
For [Mini Relay Box](https://www.openhardware.io/view/300/Mini-Relay-Box). Has a relay and ACS712 current sensor. It is pretty neat - small and powered from the AC it switches.
|
||||
|
||||
The jeelib/ sketches
|
||||
--------------------
|
||||
blr_si7020
|
||||
----------
|
||||
|
||||
These are versions of the firmware that use [jeelib], or its version modified for RFu.
|
||||
For [Button size node](https://www.openhardware.io/view/299/Button-size-radionode-with-sensors-swarm-extension) board. It's got Si7020 temperature and humidity sensor, and BH1750 lux sensor. (Plus a few more things, like SPI flash and ATSHA204a, which I don't use.)
|
||||
|
||||
The rfm69lib/ sketches
|
||||
------------------------
|
||||
|
||||
These are versions of the firmware that use LowPowerLab's
|
||||
[RFM69] instead of
|
||||
[jeelib]. The _Gateway_ sketch is a
|
||||
replacement for RF12Demo, intended for running on the receiver side of
|
||||
things.
|
||||
|
||||
The JeeNode Micro RFM69 sketches use my modified version of the original
|
||||
[RFM69] library, so that it works on attiny84. This can be found on my
|
||||
github: [RFM69_f] / attiny branch.
|
||||
|
||||
## Sig
|
||||
|
||||
© 2014-8 flabbergast / BSD license
|
||||
© 2014-9 flabbergast / BSD license
|
||||
|
||||
[jeelib]: https://github.com/jcw/jeelib
|
||||
[RFM69]: https://github.com/lowpowerlab/RFM69
|
||||
[RFM69_f]: https://github.com/flabbergast/RFM69/tree/attiny
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
6
blr-relay/.gitignore
vendored
Normal file
6
blr-relay/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
blr-relay/.travis.yml
Normal file
67
blr-relay/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
blr-relay/.vscode/extensions.json
vendored
Normal file
7
blr-relay/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
blr-relay/include/README
Normal file
39
blr-relay/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
blr-relay/lib/README
Normal file
46
blr-relay/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
18
blr-relay/platformio.ini
Normal file
18
blr-relay/platformio.ini
Normal file
|
@ -0,0 +1,18 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:pro8MHzatmega328]
|
||||
platform = atmelavr
|
||||
board = pro8MHzatmega328
|
||||
framework = arduino
|
||||
|
||||
lib_deps =
|
||||
Adafruit NeoPixel
|
||||
ACS712 Current Sensor
|
|
@ -16,6 +16,7 @@
|
|||
// RGB pixel
|
||||
#define WS_PIN 6
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#ifdef __AVR__
|
||||
#include <avr/power.h>
|
11
blr-relay/test/README
Normal file
11
blr-relay/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
6
blr-si7020/.gitignore
vendored
Normal file
6
blr-si7020/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
blr-si7020/.travis.yml
Normal file
67
blr-si7020/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
blr-si7020/.vscode/extensions.json
vendored
Normal file
7
blr-si7020/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
blr-si7020/include/README
Normal file
39
blr-si7020/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
blr-si7020/lib/README
Normal file
46
blr-si7020/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
19
blr-si7020/platformio.ini
Normal file
19
blr-si7020/platformio.ini
Normal file
|
@ -0,0 +1,19 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:pro8MHzatmega328]
|
||||
platform = atmelavr
|
||||
board = pro8MHzatmega328
|
||||
framework = arduino
|
||||
|
||||
lib_deps =
|
||||
Low-Power
|
||||
BH1750
|
||||
Adafruit Si7021 Library
|
|
@ -46,6 +46,8 @@
|
|||
#define EIGHTSEC_SLEEPS 75 // how long to sleep between readings (times 8 sec)?
|
||||
//#define EIGHTSEC_SLEEPS 1
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
|
@ -198,7 +200,7 @@ void setup() {
|
|||
if (DEBUG) {
|
||||
Serial.println("Initialising BH1750...");
|
||||
}
|
||||
bh.begin(BH1750_ONE_TIME_LOW_RES_MODE);
|
||||
bh.begin(BH1750::ONE_TIME_LOW_RES_MODE);
|
||||
}
|
||||
|
||||
void loop() {
|
311
blr-si7020/src/rf69.h
Normal file
311
blr-si7020/src/rf69.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Native mode RF69 driver.
|
||||
|
||||
#ifndef chThdYield
|
||||
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
|
||||
#endif
|
||||
|
||||
template< typename SPI >
|
||||
class RF69 {
|
||||
public:
|
||||
void init (uint8_t id, uint8_t group, int freq);
|
||||
void encrypt (const char* key);
|
||||
void txPower (uint8_t level);
|
||||
void setHighPower (uint8_t onOff);
|
||||
|
||||
int receive (void* ptr, int len);
|
||||
void send (uint8_t header, const void* ptr, int len);
|
||||
void sleep ();
|
||||
|
||||
int16_t afc;
|
||||
uint8_t rssi;
|
||||
uint8_t lna;
|
||||
uint8_t myId;
|
||||
uint8_t parity;
|
||||
|
||||
uint8_t readReg (uint8_t addr) {
|
||||
return spi.rwReg(addr, 0);
|
||||
}
|
||||
void writeReg (uint8_t addr, uint8_t val) {
|
||||
spi.rwReg(addr | 0x80, val);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
REG_FIFO = 0x00,
|
||||
REG_OPMODE = 0x01,
|
||||
REG_FRFMSB = 0x07,
|
||||
REG_PALEVEL = 0x11,
|
||||
REG_OCP = 0x13,
|
||||
REG_LNAVALUE = 0x18,
|
||||
REG_AFCMSB = 0x1F,
|
||||
REG_AFCLSB = 0x20,
|
||||
REG_FEIMSB = 0x21,
|
||||
REG_FEILSB = 0x22,
|
||||
REG_RSSIVALUE = 0x24,
|
||||
REG_IRQFLAGS1 = 0x27,
|
||||
REG_IRQFLAGS2 = 0x28,
|
||||
REG_SYNCVALUE1 = 0x2F,
|
||||
REG_SYNCVALUE2 = 0x30,
|
||||
REG_SYNCVALUE3 = 0x31,
|
||||
REG_NODEADDR = 0x39,
|
||||
REG_BCASTADDR = 0x3A,
|
||||
REG_FIFOTHRESH = 0x3C,
|
||||
REG_PKTCONFIG2 = 0x3D,
|
||||
REG_AESKEYMSB = 0x3E,
|
||||
REG_TESTPA1 = 0x5A, // only present on RFM69HW/SX1231H
|
||||
REG_TESTPA2 = 0x5C, // only present on RFM69HW/SX1231H
|
||||
|
||||
MODE_SLEEP = 0<<2,
|
||||
MODE_STANDBY = 1<<2,
|
||||
MODE_TRANSMIT = 3<<2,
|
||||
MODE_RECEIVE = 4<<2,
|
||||
|
||||
START_TX = 0xC2,
|
||||
STOP_TX = 0x42,
|
||||
|
||||
RCCALSTART = 0x80,
|
||||
IRQ1_MODEREADY = 1<<7,
|
||||
IRQ1_RXREADY = 1<<6,
|
||||
IRQ1_SYNADDRMATCH = 1<<0,
|
||||
|
||||
IRQ2_FIFONOTEMPTY = 1<<6,
|
||||
IRQ2_PACKETSENT = 1<<3,
|
||||
IRQ2_PAYLOADREADY = 1<<2,
|
||||
};
|
||||
|
||||
void setMode (uint8_t newMode);
|
||||
void configure (const uint8_t* p);
|
||||
void setFrequency (uint32_t freq);
|
||||
void setHighPowerRegs (uint8_t onOff);
|
||||
|
||||
SPI spi;
|
||||
volatile uint8_t mode;
|
||||
uint8_t isRFM69HW;
|
||||
};
|
||||
|
||||
// driver implementation
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setMode (uint8_t newMode) {
|
||||
mode = newMode;
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
|
||||
if (isRFM69HW) {
|
||||
if ( newMode == MODE_TRANSMIT ) {
|
||||
setHighPowerRegs(1);
|
||||
}
|
||||
if ( newMode == MODE_RECEIVE ) {
|
||||
setHighPowerRegs(0);
|
||||
}
|
||||
}
|
||||
while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setFrequency (uint32_t hz) {
|
||||
// accept any frequency scale as input, including KHz and MHz
|
||||
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
|
||||
while (hz < 100000000)
|
||||
hz *= 10;
|
||||
|
||||
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
|
||||
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
|
||||
// due to this, the lower 6 bits of the calculated factor will always be 0
|
||||
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
|
||||
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
|
||||
uint32_t frf = (hz << 2) / (32000000L >> 11);
|
||||
writeReg(REG_FRFMSB, frf >> 10);
|
||||
writeReg(REG_FRFMSB+1, frf >> 2);
|
||||
writeReg(REG_FRFMSB+2, frf << 6);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::configure (const uint8_t* p) {
|
||||
while (true) {
|
||||
uint8_t cmd = p[0];
|
||||
if (cmd == 0)
|
||||
break;
|
||||
writeReg(cmd, p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t configRegs [] = {
|
||||
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
|
||||
0x02, 0x00, // DataModul = packet mode, fsk
|
||||
0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
|
||||
0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
|
||||
0x05, 0x02, // FdevMsb = 45 KHz
|
||||
0x06, 0xE1, // FdevLsb = 45 KHz
|
||||
0x0B, 0x20, // Low M
|
||||
0x19, 0x4A, // RxBw 100 KHz
|
||||
0x1A, 0x42, // AfcBw 125 KHz
|
||||
0x1E, 0x0C, // AfcAutoclearOn, AfcAutoOn
|
||||
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
|
||||
0x26, 0x07, // disable clkout
|
||||
0x29, 0xA0, // RssiThresh -80 dB
|
||||
// 0x2B, 0x40, // RSSI timeout after 128 bytes
|
||||
0x2D, 0x05, // PreambleSize = 5
|
||||
0x2E, 0x90, // SyncConfig = sync on, sync size = 3
|
||||
0x2F, 0xAA, // SyncValue1 = 0xAA
|
||||
0x30, 0x2D, // SyncValue2 = 0x2D ! this is used for group in old jee protocol, but jz4 uses 0x2d hardcoded
|
||||
0x31, 0x2A, // network group, default 42
|
||||
0x37, 0xD0, // PacketConfig1 = fixed, white, no filtering
|
||||
0x38, 0x42, // PayloadLength = 0, unlimited
|
||||
0x3C, 0x8F, // FifoTresh, not empty, level 15
|
||||
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
|
||||
0x6F, 0x20, // TestDagc ...
|
||||
0x71, 0x02, // RegTestAfc
|
||||
0
|
||||
};
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::init (uint8_t id, uint8_t group, int freq) {
|
||||
myId = id;
|
||||
|
||||
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
|
||||
parity = group ^ (group << 4);
|
||||
parity = (parity ^ (parity << 2)) & 0xC0;
|
||||
|
||||
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
|
||||
spi.master(3);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
while (readReg(REG_SYNCVALUE1) != 0xAA);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
while (readReg(REG_SYNCVALUE1) != 0x55);
|
||||
|
||||
configure(configRegs);
|
||||
configure(configRegs); // TODO why is this needed ???
|
||||
setFrequency(freq);
|
||||
|
||||
writeReg(REG_SYNCVALUE3, group);
|
||||
isRFM69HW = 0;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::encrypt (const char* key) {
|
||||
uint8_t cfg = readReg(REG_PKTCONFIG2) & ~0x01;
|
||||
if (key) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
writeReg(REG_AESKEYMSB + i, *key);
|
||||
if (*key != 0)
|
||||
++key;
|
||||
}
|
||||
cfg |= 0x01;
|
||||
}
|
||||
writeReg(REG_PKTCONFIG2, cfg);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::txPower (uint8_t level) {
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & ~0x1F) | level);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPower (uint8_t onOff) { // must call this with RFM69HW!
|
||||
isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, isRFM69HW ? 0x0F : 0x1A);
|
||||
if (isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x40 | 0x20); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x80 ); // enable P0 only
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPowerRegs (uint8_t onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::sleep () {
|
||||
setMode(MODE_SLEEP);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
int RF69<SPI>::receive (void* ptr, int len) {
|
||||
if (mode != MODE_RECEIVE)
|
||||
setMode(MODE_RECEIVE);
|
||||
else {
|
||||
static uint8_t lastFlag;
|
||||
if ((readReg(REG_IRQFLAGS1) & IRQ1_RXREADY) != lastFlag) {
|
||||
lastFlag ^= IRQ1_RXREADY;
|
||||
if (lastFlag) { // flag just went from 0 to 1
|
||||
rssi = readReg(REG_RSSIVALUE);
|
||||
lna = (readReg(REG_LNAVALUE) >> 3) & 0x7;
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_AFCMSB);
|
||||
afc = spi.transfer(0) << 8;
|
||||
afc |= spi.transfer(0);
|
||||
spi.disable();
|
||||
#else
|
||||
afc = readReg(REG_AFCMSB) << 8;
|
||||
afc |= readReg(REG_AFCLSB);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (readReg(REG_IRQFLAGS2) & IRQ2_PAYLOADREADY) {
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO);
|
||||
int count = spi.transfer(0);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = spi.transfer(0);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
spi.disable();
|
||||
#else
|
||||
int count = readReg(REG_FIFO);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = readReg(REG_FIFO);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// only accept packets intended for us, or broadcasts
|
||||
// ... or any packet if we're the special catch-all node
|
||||
uint8_t dest = *(uint8_t*) ptr;
|
||||
if ((dest & 0xC0) == parity) {
|
||||
uint8_t destId = dest & 0x3F;
|
||||
if (destId == myId || destId == 0 || myId == 63)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::send (uint8_t header, const void* ptr, int len) {
|
||||
setMode(MODE_SLEEP);
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO | 0x80);
|
||||
spi.transfer(len + 2);
|
||||
spi.transfer((header & 0x3F) | parity);
|
||||
spi.transfer((header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
spi.transfer(((const uint8_t*) ptr)[i]);
|
||||
spi.disable();
|
||||
#else
|
||||
writeReg(REG_FIFO, len + 2);
|
||||
writeReg(REG_FIFO, (header & 0x3F) | parity);
|
||||
writeReg(REG_FIFO, (header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
writeReg(REG_FIFO, ((const uint8_t*) ptr)[i]);
|
||||
#endif
|
||||
|
||||
setMode(MODE_TRANSMIT);
|
||||
while ((readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) == 0)
|
||||
chThdYield();
|
||||
|
||||
setMode(MODE_STANDBY);
|
||||
}
|
11
blr-si7020/test/README
Normal file
11
blr-si7020/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
|
@ -1 +0,0 @@
|
|||
../Gateway/rf69.h
|
6
gateway-wemos-d1/.gitignore
vendored
Normal file
6
gateway-wemos-d1/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
gateway-wemos-d1/.travis.yml
Normal file
67
gateway-wemos-d1/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
gateway-wemos-d1/.vscode/extensions.json
vendored
Normal file
7
gateway-wemos-d1/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
gateway-wemos-d1/include/README
Normal file
39
gateway-wemos-d1/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
gateway-wemos-d1/lib/README
Normal file
46
gateway-wemos-d1/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
14
gateway-wemos-d1/platformio.ini
Normal file
14
gateway-wemos-d1/platformio.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:d1_mini_pro]
|
||||
platform = espressif8266
|
||||
board = d1_mini_pro
|
||||
framework = arduino
|
|
@ -42,6 +42,8 @@ volatile uint8_t network_id = 109; //the same on all nodes that talk to each o
|
|||
|
||||
RF69<SpiDev16> rf;
|
||||
|
||||
void Blink(byte PIN, int DELAY_MS);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
delay(10);
|
311
gateway-wemos-d1/src/rf69.h
Normal file
311
gateway-wemos-d1/src/rf69.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Native mode RF69 driver.
|
||||
|
||||
#ifndef chThdYield
|
||||
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
|
||||
#endif
|
||||
|
||||
template< typename SPI >
|
||||
class RF69 {
|
||||
public:
|
||||
void init (uint8_t id, uint8_t group, int freq);
|
||||
void encrypt (const char* key);
|
||||
void txPower (uint8_t level);
|
||||
void setHighPower (uint8_t onOff);
|
||||
|
||||
int receive (void* ptr, int len);
|
||||
void send (uint8_t header, const void* ptr, int len);
|
||||
void sleep ();
|
||||
|
||||
int16_t afc;
|
||||
uint8_t rssi;
|
||||
uint8_t lna;
|
||||
uint8_t myId;
|
||||
uint8_t parity;
|
||||
|
||||
uint8_t readReg (uint8_t addr) {
|
||||
return spi.rwReg(addr, 0);
|
||||
}
|
||||
void writeReg (uint8_t addr, uint8_t val) {
|
||||
spi.rwReg(addr | 0x80, val);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
REG_FIFO = 0x00,
|
||||
REG_OPMODE = 0x01,
|
||||
REG_FRFMSB = 0x07,
|
||||
REG_PALEVEL = 0x11,
|
||||
REG_OCP = 0x13,
|
||||
REG_LNAVALUE = 0x18,
|
||||
REG_AFCMSB = 0x1F,
|
||||
REG_AFCLSB = 0x20,
|
||||
REG_FEIMSB = 0x21,
|
||||
REG_FEILSB = 0x22,
|
||||
REG_RSSIVALUE = 0x24,
|
||||
REG_IRQFLAGS1 = 0x27,
|
||||
REG_IRQFLAGS2 = 0x28,
|
||||
REG_SYNCVALUE1 = 0x2F,
|
||||
REG_SYNCVALUE2 = 0x30,
|
||||
REG_SYNCVALUE3 = 0x31,
|
||||
REG_NODEADDR = 0x39,
|
||||
REG_BCASTADDR = 0x3A,
|
||||
REG_FIFOTHRESH = 0x3C,
|
||||
REG_PKTCONFIG2 = 0x3D,
|
||||
REG_AESKEYMSB = 0x3E,
|
||||
REG_TESTPA1 = 0x5A, // only present on RFM69HW/SX1231H
|
||||
REG_TESTPA2 = 0x5C, // only present on RFM69HW/SX1231H
|
||||
|
||||
MODE_SLEEP = 0<<2,
|
||||
MODE_STANDBY = 1<<2,
|
||||
MODE_TRANSMIT = 3<<2,
|
||||
MODE_RECEIVE = 4<<2,
|
||||
|
||||
START_TX = 0xC2,
|
||||
STOP_TX = 0x42,
|
||||
|
||||
RCCALSTART = 0x80,
|
||||
IRQ1_MODEREADY = 1<<7,
|
||||
IRQ1_RXREADY = 1<<6,
|
||||
IRQ1_SYNADDRMATCH = 1<<0,
|
||||
|
||||
IRQ2_FIFONOTEMPTY = 1<<6,
|
||||
IRQ2_PACKETSENT = 1<<3,
|
||||
IRQ2_PAYLOADREADY = 1<<2,
|
||||
};
|
||||
|
||||
void setMode (uint8_t newMode);
|
||||
void configure (const uint8_t* p);
|
||||
void setFrequency (uint32_t freq);
|
||||
void setHighPowerRegs (uint8_t onOff);
|
||||
|
||||
SPI spi;
|
||||
volatile uint8_t mode;
|
||||
uint8_t isRFM69HW;
|
||||
};
|
||||
|
||||
// driver implementation
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setMode (uint8_t newMode) {
|
||||
mode = newMode;
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
|
||||
if (isRFM69HW) {
|
||||
if ( newMode == MODE_TRANSMIT ) {
|
||||
setHighPowerRegs(1);
|
||||
}
|
||||
if ( newMode == MODE_RECEIVE ) {
|
||||
setHighPowerRegs(0);
|
||||
}
|
||||
}
|
||||
while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setFrequency (uint32_t hz) {
|
||||
// accept any frequency scale as input, including KHz and MHz
|
||||
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
|
||||
while (hz < 100000000)
|
||||
hz *= 10;
|
||||
|
||||
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
|
||||
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
|
||||
// due to this, the lower 6 bits of the calculated factor will always be 0
|
||||
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
|
||||
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
|
||||
uint32_t frf = (hz << 2) / (32000000L >> 11);
|
||||
writeReg(REG_FRFMSB, frf >> 10);
|
||||
writeReg(REG_FRFMSB+1, frf >> 2);
|
||||
writeReg(REG_FRFMSB+2, frf << 6);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::configure (const uint8_t* p) {
|
||||
while (true) {
|
||||
uint8_t cmd = p[0];
|
||||
if (cmd == 0)
|
||||
break;
|
||||
writeReg(cmd, p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t configRegs [] = {
|
||||
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
|
||||
0x02, 0x00, // DataModul = packet mode, fsk
|
||||
0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
|
||||
0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
|
||||
0x05, 0x02, // FdevMsb = 45 KHz
|
||||
0x06, 0xE1, // FdevLsb = 45 KHz
|
||||
0x0B, 0x20, // Low M
|
||||
0x19, 0x4A, // RxBw 100 KHz
|
||||
0x1A, 0x42, // AfcBw 125 KHz
|
||||
0x1E, 0x0C, // AfcAutoclearOn, AfcAutoOn
|
||||
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
|
||||
0x26, 0x07, // disable clkout
|
||||
0x29, 0xA0, // RssiThresh -80 dB
|
||||
// 0x2B, 0x40, // RSSI timeout after 128 bytes
|
||||
0x2D, 0x05, // PreambleSize = 5
|
||||
0x2E, 0x90, // SyncConfig = sync on, sync size = 3
|
||||
0x2F, 0xAA, // SyncValue1 = 0xAA
|
||||
0x30, 0x2D, // SyncValue2 = 0x2D ! this is used for group in old jee protocol, but jz4 uses 0x2d hardcoded
|
||||
0x31, 0x2A, // network group, default 42
|
||||
0x37, 0xD0, // PacketConfig1 = fixed, white, no filtering
|
||||
0x38, 0x42, // PayloadLength = 0, unlimited
|
||||
0x3C, 0x8F, // FifoTresh, not empty, level 15
|
||||
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
|
||||
0x6F, 0x20, // TestDagc ...
|
||||
0x71, 0x02, // RegTestAfc
|
||||
0
|
||||
};
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::init (uint8_t id, uint8_t group, int freq) {
|
||||
myId = id;
|
||||
|
||||
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
|
||||
parity = group ^ (group << 4);
|
||||
parity = (parity ^ (parity << 2)) & 0xC0;
|
||||
|
||||
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
|
||||
spi.master(3);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
while (readReg(REG_SYNCVALUE1) != 0xAA);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
while (readReg(REG_SYNCVALUE1) != 0x55);
|
||||
|
||||
configure(configRegs);
|
||||
configure(configRegs); // TODO why is this needed ???
|
||||
setFrequency(freq);
|
||||
|
||||
writeReg(REG_SYNCVALUE3, group);
|
||||
isRFM69HW = 0;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::encrypt (const char* key) {
|
||||
uint8_t cfg = readReg(REG_PKTCONFIG2) & ~0x01;
|
||||
if (key) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
writeReg(REG_AESKEYMSB + i, *key);
|
||||
if (*key != 0)
|
||||
++key;
|
||||
}
|
||||
cfg |= 0x01;
|
||||
}
|
||||
writeReg(REG_PKTCONFIG2, cfg);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::txPower (uint8_t level) {
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & ~0x1F) | level);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPower (uint8_t onOff) { // must call this with RFM69HW!
|
||||
isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, isRFM69HW ? 0x0F : 0x1A);
|
||||
if (isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x40 | 0x20); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x80 ); // enable P0 only
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPowerRegs (uint8_t onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::sleep () {
|
||||
setMode(MODE_SLEEP);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
int RF69<SPI>::receive (void* ptr, int len) {
|
||||
if (mode != MODE_RECEIVE)
|
||||
setMode(MODE_RECEIVE);
|
||||
else {
|
||||
static uint8_t lastFlag;
|
||||
if ((readReg(REG_IRQFLAGS1) & IRQ1_RXREADY) != lastFlag) {
|
||||
lastFlag ^= IRQ1_RXREADY;
|
||||
if (lastFlag) { // flag just went from 0 to 1
|
||||
rssi = readReg(REG_RSSIVALUE);
|
||||
lna = (readReg(REG_LNAVALUE) >> 3) & 0x7;
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_AFCMSB);
|
||||
afc = spi.transfer(0) << 8;
|
||||
afc |= spi.transfer(0);
|
||||
spi.disable();
|
||||
#else
|
||||
afc = readReg(REG_AFCMSB) << 8;
|
||||
afc |= readReg(REG_AFCLSB);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (readReg(REG_IRQFLAGS2) & IRQ2_PAYLOADREADY) {
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO);
|
||||
int count = spi.transfer(0);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = spi.transfer(0);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
spi.disable();
|
||||
#else
|
||||
int count = readReg(REG_FIFO);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = readReg(REG_FIFO);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// only accept packets intended for us, or broadcasts
|
||||
// ... or any packet if we're the special catch-all node
|
||||
uint8_t dest = *(uint8_t*) ptr;
|
||||
if ((dest & 0xC0) == parity) {
|
||||
uint8_t destId = dest & 0x3F;
|
||||
if (destId == myId || destId == 0 || myId == 63)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::send (uint8_t header, const void* ptr, int len) {
|
||||
setMode(MODE_SLEEP);
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO | 0x80);
|
||||
spi.transfer(len + 2);
|
||||
spi.transfer((header & 0x3F) | parity);
|
||||
spi.transfer((header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
spi.transfer(((const uint8_t*) ptr)[i]);
|
||||
spi.disable();
|
||||
#else
|
||||
writeReg(REG_FIFO, len + 2);
|
||||
writeReg(REG_FIFO, (header & 0x3F) | parity);
|
||||
writeReg(REG_FIFO, (header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
writeReg(REG_FIFO, ((const uint8_t*) ptr)[i]);
|
||||
#endif
|
||||
|
||||
setMode(MODE_TRANSMIT);
|
||||
while ((readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) == 0)
|
||||
chThdYield();
|
||||
|
||||
setMode(MODE_STANDBY);
|
||||
}
|
11
gateway-wemos-d1/test/README
Normal file
11
gateway-wemos-d1/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
6
gateway/.gitignore
vendored
Normal file
6
gateway/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
gateway/.travis.yml
Normal file
67
gateway/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
gateway/.vscode/extensions.json
vendored
Normal file
7
gateway/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
6
gateway/.vscode/settings.json
vendored
Normal file
6
gateway/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"terminal.integrated.env.linux": {
|
||||
"PATH": "/home/js/.platformio/penv/bin:/home/js/.platformio/penv:/home/js/.cargo/bin:/usr/local/texbin:/home/js/bin/go/bin:/home/js/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl",
|
||||
"PLATFORMIO_CALLER": "vscode"
|
||||
}
|
||||
}
|
39
gateway/include/README
Normal file
39
gateway/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
gateway/lib/README
Normal file
46
gateway/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
14
gateway/platformio.ini
Normal file
14
gateway/platformio.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:uno]
|
||||
platform = atmelavr
|
||||
board = uno
|
||||
framework = arduino
|
311
gateway/src/rf69.h
Normal file
311
gateway/src/rf69.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Native mode RF69 driver.
|
||||
|
||||
#ifndef chThdYield
|
||||
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
|
||||
#endif
|
||||
|
||||
template< typename SPI >
|
||||
class RF69 {
|
||||
public:
|
||||
void init (uint8_t id, uint8_t group, int freq);
|
||||
void encrypt (const char* key);
|
||||
void txPower (uint8_t level);
|
||||
void setHighPower (uint8_t onOff);
|
||||
|
||||
int receive (void* ptr, int len);
|
||||
void send (uint8_t header, const void* ptr, int len);
|
||||
void sleep ();
|
||||
|
||||
int16_t afc;
|
||||
uint8_t rssi;
|
||||
uint8_t lna;
|
||||
uint8_t myId;
|
||||
uint8_t parity;
|
||||
|
||||
uint8_t readReg (uint8_t addr) {
|
||||
return spi.rwReg(addr, 0);
|
||||
}
|
||||
void writeReg (uint8_t addr, uint8_t val) {
|
||||
spi.rwReg(addr | 0x80, val);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
REG_FIFO = 0x00,
|
||||
REG_OPMODE = 0x01,
|
||||
REG_FRFMSB = 0x07,
|
||||
REG_PALEVEL = 0x11,
|
||||
REG_OCP = 0x13,
|
||||
REG_LNAVALUE = 0x18,
|
||||
REG_AFCMSB = 0x1F,
|
||||
REG_AFCLSB = 0x20,
|
||||
REG_FEIMSB = 0x21,
|
||||
REG_FEILSB = 0x22,
|
||||
REG_RSSIVALUE = 0x24,
|
||||
REG_IRQFLAGS1 = 0x27,
|
||||
REG_IRQFLAGS2 = 0x28,
|
||||
REG_SYNCVALUE1 = 0x2F,
|
||||
REG_SYNCVALUE2 = 0x30,
|
||||
REG_SYNCVALUE3 = 0x31,
|
||||
REG_NODEADDR = 0x39,
|
||||
REG_BCASTADDR = 0x3A,
|
||||
REG_FIFOTHRESH = 0x3C,
|
||||
REG_PKTCONFIG2 = 0x3D,
|
||||
REG_AESKEYMSB = 0x3E,
|
||||
REG_TESTPA1 = 0x5A, // only present on RFM69HW/SX1231H
|
||||
REG_TESTPA2 = 0x5C, // only present on RFM69HW/SX1231H
|
||||
|
||||
MODE_SLEEP = 0<<2,
|
||||
MODE_STANDBY = 1<<2,
|
||||
MODE_TRANSMIT = 3<<2,
|
||||
MODE_RECEIVE = 4<<2,
|
||||
|
||||
START_TX = 0xC2,
|
||||
STOP_TX = 0x42,
|
||||
|
||||
RCCALSTART = 0x80,
|
||||
IRQ1_MODEREADY = 1<<7,
|
||||
IRQ1_RXREADY = 1<<6,
|
||||
IRQ1_SYNADDRMATCH = 1<<0,
|
||||
|
||||
IRQ2_FIFONOTEMPTY = 1<<6,
|
||||
IRQ2_PACKETSENT = 1<<3,
|
||||
IRQ2_PAYLOADREADY = 1<<2,
|
||||
};
|
||||
|
||||
void setMode (uint8_t newMode);
|
||||
void configure (const uint8_t* p);
|
||||
void setFrequency (uint32_t freq);
|
||||
void setHighPowerRegs (uint8_t onOff);
|
||||
|
||||
SPI spi;
|
||||
volatile uint8_t mode;
|
||||
uint8_t isRFM69HW;
|
||||
};
|
||||
|
||||
// driver implementation
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setMode (uint8_t newMode) {
|
||||
mode = newMode;
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
|
||||
if (isRFM69HW) {
|
||||
if ( newMode == MODE_TRANSMIT ) {
|
||||
setHighPowerRegs(1);
|
||||
}
|
||||
if ( newMode == MODE_RECEIVE ) {
|
||||
setHighPowerRegs(0);
|
||||
}
|
||||
}
|
||||
while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setFrequency (uint32_t hz) {
|
||||
// accept any frequency scale as input, including KHz and MHz
|
||||
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
|
||||
while (hz < 100000000)
|
||||
hz *= 10;
|
||||
|
||||
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
|
||||
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
|
||||
// due to this, the lower 6 bits of the calculated factor will always be 0
|
||||
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
|
||||
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
|
||||
uint32_t frf = (hz << 2) / (32000000L >> 11);
|
||||
writeReg(REG_FRFMSB, frf >> 10);
|
||||
writeReg(REG_FRFMSB+1, frf >> 2);
|
||||
writeReg(REG_FRFMSB+2, frf << 6);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::configure (const uint8_t* p) {
|
||||
while (true) {
|
||||
uint8_t cmd = p[0];
|
||||
if (cmd == 0)
|
||||
break;
|
||||
writeReg(cmd, p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t configRegs [] = {
|
||||
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
|
||||
0x02, 0x00, // DataModul = packet mode, fsk
|
||||
0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
|
||||
0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
|
||||
0x05, 0x02, // FdevMsb = 45 KHz
|
||||
0x06, 0xE1, // FdevLsb = 45 KHz
|
||||
0x0B, 0x20, // Low M
|
||||
0x19, 0x4A, // RxBw 100 KHz
|
||||
0x1A, 0x42, // AfcBw 125 KHz
|
||||
0x1E, 0x0C, // AfcAutoclearOn, AfcAutoOn
|
||||
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
|
||||
0x26, 0x07, // disable clkout
|
||||
0x29, 0xA0, // RssiThresh -80 dB
|
||||
// 0x2B, 0x40, // RSSI timeout after 128 bytes
|
||||
0x2D, 0x05, // PreambleSize = 5
|
||||
0x2E, 0x90, // SyncConfig = sync on, sync size = 3
|
||||
0x2F, 0xAA, // SyncValue1 = 0xAA
|
||||
0x30, 0x2D, // SyncValue2 = 0x2D ! this is used for group in old jee protocol, but jz4 uses 0x2d hardcoded
|
||||
0x31, 0x2A, // network group, default 42
|
||||
0x37, 0xD0, // PacketConfig1 = fixed, white, no filtering
|
||||
0x38, 0x42, // PayloadLength = 0, unlimited
|
||||
0x3C, 0x8F, // FifoTresh, not empty, level 15
|
||||
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
|
||||
0x6F, 0x20, // TestDagc ...
|
||||
0x71, 0x02, // RegTestAfc
|
||||
0
|
||||
};
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::init (uint8_t id, uint8_t group, int freq) {
|
||||
myId = id;
|
||||
|
||||
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
|
||||
parity = group ^ (group << 4);
|
||||
parity = (parity ^ (parity << 2)) & 0xC0;
|
||||
|
||||
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
|
||||
spi.master(3);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
while (readReg(REG_SYNCVALUE1) != 0xAA);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
while (readReg(REG_SYNCVALUE1) != 0x55);
|
||||
|
||||
configure(configRegs);
|
||||
configure(configRegs); // TODO why is this needed ???
|
||||
setFrequency(freq);
|
||||
|
||||
writeReg(REG_SYNCVALUE3, group);
|
||||
isRFM69HW = 0;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::encrypt (const char* key) {
|
||||
uint8_t cfg = readReg(REG_PKTCONFIG2) & ~0x01;
|
||||
if (key) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
writeReg(REG_AESKEYMSB + i, *key);
|
||||
if (*key != 0)
|
||||
++key;
|
||||
}
|
||||
cfg |= 0x01;
|
||||
}
|
||||
writeReg(REG_PKTCONFIG2, cfg);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::txPower (uint8_t level) {
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & ~0x1F) | level);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPower (uint8_t onOff) { // must call this with RFM69HW!
|
||||
isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, isRFM69HW ? 0x0F : 0x1A);
|
||||
if (isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x40 | 0x20); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x80 ); // enable P0 only
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPowerRegs (uint8_t onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::sleep () {
|
||||
setMode(MODE_SLEEP);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
int RF69<SPI>::receive (void* ptr, int len) {
|
||||
if (mode != MODE_RECEIVE)
|
||||
setMode(MODE_RECEIVE);
|
||||
else {
|
||||
static uint8_t lastFlag;
|
||||
if ((readReg(REG_IRQFLAGS1) & IRQ1_RXREADY) != lastFlag) {
|
||||
lastFlag ^= IRQ1_RXREADY;
|
||||
if (lastFlag) { // flag just went from 0 to 1
|
||||
rssi = readReg(REG_RSSIVALUE);
|
||||
lna = (readReg(REG_LNAVALUE) >> 3) & 0x7;
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_AFCMSB);
|
||||
afc = spi.transfer(0) << 8;
|
||||
afc |= spi.transfer(0);
|
||||
spi.disable();
|
||||
#else
|
||||
afc = readReg(REG_AFCMSB) << 8;
|
||||
afc |= readReg(REG_AFCLSB);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (readReg(REG_IRQFLAGS2) & IRQ2_PAYLOADREADY) {
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO);
|
||||
int count = spi.transfer(0);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = spi.transfer(0);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
spi.disable();
|
||||
#else
|
||||
int count = readReg(REG_FIFO);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = readReg(REG_FIFO);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// only accept packets intended for us, or broadcasts
|
||||
// ... or any packet if we're the special catch-all node
|
||||
uint8_t dest = *(uint8_t*) ptr;
|
||||
if ((dest & 0xC0) == parity) {
|
||||
uint8_t destId = dest & 0x3F;
|
||||
if (destId == myId || destId == 0 || myId == 63)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::send (uint8_t header, const void* ptr, int len) {
|
||||
setMode(MODE_SLEEP);
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO | 0x80);
|
||||
spi.transfer(len + 2);
|
||||
spi.transfer((header & 0x3F) | parity);
|
||||
spi.transfer((header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
spi.transfer(((const uint8_t*) ptr)[i]);
|
||||
spi.disable();
|
||||
#else
|
||||
writeReg(REG_FIFO, len + 2);
|
||||
writeReg(REG_FIFO, (header & 0x3F) | parity);
|
||||
writeReg(REG_FIFO, (header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
writeReg(REG_FIFO, ((const uint8_t*) ptr)[i]);
|
||||
#endif
|
||||
|
||||
setMode(MODE_TRANSMIT);
|
||||
while ((readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) == 0)
|
||||
chThdYield();
|
||||
|
||||
setMode(MODE_STANDBY);
|
||||
}
|
11
gateway/test/README
Normal file
11
gateway/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
|
@ -1,170 +0,0 @@
|
|||
#if not defined(__AVR_ATtiny84__)
|
||||
#error "This one works only on attiny84. Sorry."
|
||||
#endif
|
||||
|
||||
/*
|
||||
* JeeNode Micro v3 + TH02 (software I2C)
|
||||
* (c) 2014 flabbergast
|
||||
*
|
||||
* Comments:
|
||||
* - Depends on TH02soft library: https://github.com/flabbergast/TH02soft
|
||||
* - I didn't go too overboard with the power savings, so e.g. battery voltage
|
||||
* is measured on every reading, and TH02 is given on lot of time to power up
|
||||
* and shut down. For more savings, one should also change the speed (prescale)
|
||||
* to something smaller when not using the radio (USI needs >= 4MHz). I don't
|
||||
* know how low can one go with I2C so that TH02 still communicates.
|
||||
* - should disable brown-out detection in fuses (BODLEVEL)
|
||||
*
|
||||
* Warning: You'll need to edit the TH02soft.cpp file and change the pins (SDA/SCL)!
|
||||
*
|
||||
* Notice: I needed to lower the output power of RFM69CW from 13dB (default, max)
|
||||
* to something smaller to run on CR2032 cell. Edit the actual value below.
|
||||
*
|
||||
* Warning: the resulting size of this sketch is about 6.4kB. So with Arduino 1.0.5,
|
||||
* you might get an error with something like "relocation truncated to fit:
|
||||
* R_AVR_13_PCREL against symbol `exit' defined". This is apparently due to
|
||||
* having old avr-gcc: https://code.google.com/p/arduino-tiny/issues/detail?id=58
|
||||
* [a bug which is triggered if the compiled code is >= 4kB.]
|
||||
* So either update the avr-gcc in Arduino, or fiddle with the code (use _nomath,
|
||||
* don't compute compensatedRH, don't include math.h, and such.) I managed to get
|
||||
* it under 4kB with tricks like that, see commit
|
||||
* ced130d978200841f12d2a394d2591cfc6580288 .
|
||||
*
|
||||
*/
|
||||
|
||||
#define RF69_COMPAT 1 // comment this out if you have RFM12B instead of RFM69CW
|
||||
#define RF_NODE_ID 17
|
||||
#define RF_GROUP 212
|
||||
#define RF_FREQ RF12_868MHZ
|
||||
#define RF_TX_POWER 18 // 0-31 (observe the max!!!), the Tx power is -18+value (max 13) dB
|
||||
|
||||
#define HALFMIN_SLEEPS 10 // how long to sleep between readings (times 30 sec)?
|
||||
|
||||
// TH02 power is on PA3 (arduino pin 7)
|
||||
#define TH02_POWER_DDR DDRA
|
||||
#define TH02_POWER_PORT PORTA
|
||||
#define TH02_POWER_BIT 3
|
||||
// SDA: PA0, SCL: PA1
|
||||
|
||||
#include <JeeLib.h>
|
||||
|
||||
#include <TH02soft.h>
|
||||
TH02 sensor;
|
||||
|
||||
//##### radio packet structure #####
|
||||
typedef struct { // RFM12B RF payload datastructure
|
||||
int16_t temp;
|
||||
int16_t rh_comp;
|
||||
uint16_t voltage;
|
||||
} Payload;
|
||||
Payload payload;
|
||||
|
||||
// this must be added since we're using the watchdog for low-power waiting
|
||||
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
|
||||
|
||||
void setup() {
|
||||
// disable stuff we don't need
|
||||
// Power tricks from: http://harizanov.com/2012/07/power-saving-techniques-for-the-attiny84-powered-tinysensor/
|
||||
// Timers
|
||||
bitSet(PRR, PRTIM1); // only keep timer 0 going
|
||||
// Disable ADC (should by off by default...)
|
||||
ADCSRA &= ~ bit(ADEN); // disable the ADC
|
||||
bitSet(PRR, PRADC); // power down the ADC
|
||||
|
||||
Sleepy::loseSomeTime(100);
|
||||
|
||||
// power up the radio on JMv3 (mosfet)
|
||||
bitSet(DDRB, 0);
|
||||
bitClear(PORTB, 0);
|
||||
|
||||
Sleepy::loseSomeTime(15);
|
||||
|
||||
rf12_initialize(RF_NODE_ID, RF_FREQ, RF_GROUP);
|
||||
|
||||
// need to decrease the TX output for attiny84
|
||||
if(RF69_COMPAT) {
|
||||
RF69::control(0x11 | 0x80, 128+ RF_TX_POWER); // 0x11 is 'RegPaLevel' register address
|
||||
}
|
||||
|
||||
// send an echo that we're starting
|
||||
// payload.temp = 1;
|
||||
// payload.rh_comp = 2;
|
||||
// payload.voltage = 3;
|
||||
// rf12_sendNow(0, &payload, sizeof payload);
|
||||
// rf12_sendWait(2);
|
||||
|
||||
// sleep
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
// disable USI (i.e. SPI)
|
||||
bitSet(PRR, PRUSI); // disable USI h/w
|
||||
|
||||
// initialise i2c
|
||||
sensor.init();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
/* Use internal bandgap 1.1V to read the battery voltage
|
||||
from http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
|
||||
*/
|
||||
bitClear(PRR, PRADC); // power up the ADC
|
||||
ADCSRA |= bit(ADEN); // enable the ADC
|
||||
// Read 1.1V reference against AVcc
|
||||
// set the reference to Vcc and the measurement to the internal 1.1V reference
|
||||
ADMUX = _BV(MUX5) | _BV(MUX0);
|
||||
delay(2); // Wait for Vref to settle
|
||||
ADCSRA |= _BV(ADSC); // Start conversion
|
||||
while (bit_is_set(ADCSRA,ADSC)); // measuring
|
||||
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
|
||||
uint8_t high = ADCH; // unlocks both
|
||||
ADCSRA &= ~ bit(ADEN); // disable the ADC
|
||||
bitSet(PRR, PRADC); // power down the ADC
|
||||
payload.voltage = 1125300L / ((high<<8) | low); // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
|
||||
|
||||
// turn on power to TH02
|
||||
bitSet(TH02_POWER_DDR,TH02_POWER_BIT);
|
||||
bitSet(TH02_POWER_PORT,TH02_POWER_BIT);
|
||||
delay(20); // wait for TH02 to power up (should be 10ms, max 15ms, but could
|
||||
// be more if not enough current (40mA))
|
||||
|
||||
// do a temp conversion
|
||||
sensor.startTempConv(true); // true: fastmode
|
||||
sensor.waitEndConversion();
|
||||
|
||||
// read the result
|
||||
payload.temp = sensor.getConversionValue_nomath();
|
||||
|
||||
// do a humidity conversion
|
||||
sensor.startRHConv(true);
|
||||
sensor.waitEndConversion();
|
||||
|
||||
// read the result (gets saved in sensor object)
|
||||
sensor.getConversionValue_nomath();
|
||||
|
||||
// power off TH02
|
||||
bitClear(TH02_POWER_PORT,TH02_POWER_BIT);
|
||||
delay(10); // wait for TH02 to power down (might be sinking 40mA current)
|
||||
bitClear(TH02_POWER_DDR,TH02_POWER_BIT); // configure as input
|
||||
|
||||
// compute the compensated humidity
|
||||
payload.rh_comp = sensor.getCompensatedRH(false);
|
||||
|
||||
// give the battery some time to recover
|
||||
Sleepy::loseSomeTime(1000);
|
||||
// power up the radio
|
||||
bitClear(PRR, PRUSI); // enable USI h/w
|
||||
// wakey wakey
|
||||
rf12_sleep(RF12_WAKEUP);
|
||||
// send the reading
|
||||
rf12_sendNow(0, &payload, sizeof payload);
|
||||
rf12_sendWait(2);
|
||||
// sleep
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
// disable USI
|
||||
bitSet(PRR, PRUSI); // disable USI h/w
|
||||
|
||||
// sleep for a time
|
||||
uint8_t i;
|
||||
for(i=0; i<HALFMIN_SLEEPS; i++)
|
||||
Sleepy::loseSomeTime(30000); // JeeLabs power save function: enter low power mode for x milliseconds (valid range 16-65000 ms)
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
JeeNode Micro + TH02
|
||||
====================
|
||||
|
||||
TH02 quick info
|
||||
---------------
|
||||
|
||||
- 2.1V - 3.6V
|
||||
- temp 0' - 70' C \pm 0.5'C
|
||||
- RH 0% - 80% \pm 3%
|
||||
- consumption:
|
||||
|
||||
- idle 150uA (a lot)
|
||||
- temp in progress 320uA (max 565uA) for 18ms - 35ms
|
||||
- RH in progress 240uA (max 560uA) for 18-35ms
|
||||
- powerup time 10ms (max 15ms), can take 40mA for 5ms.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# RFu_RF69_thermistor.ino
|
||||
|
||||
An Arduino sketch for running [Ciseco's RFµ-328](http://shop.ciseco.co.uk/rf-328-bare-arduino-atmega-328-compatible-micro-board-rfu-328/), with a HopeRF's [RFM69CW](http://www.hoperf.com/rf/fsk_module/RFM69CW.htm) radio soldered on, on a [XRF thermistor coin cell board](http://shop.ciseco.co.uk/temperature-xrf-development-sensor-thermistor/) from Ciseco.
|
||||
|
||||
Depends on
|
||||
[RFu_jeelib](https://github.com/flabbergast/RFu_jeelib/tree/rfu) (`rfu`
|
||||
branch) - it's just [Jeelib](https://github.com/jcw/jeelib) modified for
|
||||
different radio-to-atmega328p connections present on RFµ-328.
|
||||
|
||||
## Credits
|
||||
|
||||
* based partly on OpenEnergyMonitor's emonTH code:
|
||||
https://github.com/openenergymonitor/emonTH
|
||||
* measuring internal voltage code from:
|
||||
https://code.google.com/p/tinkerit/wiki/SecretVoltmeter
|
||||
* Thermistor code from lady Ada:
|
||||
https://learn.adafruit.com/thermistor/using-a-thermistor
|
||||
|
||||
## Sig
|
||||
|
||||
© 2014 flabbergast / BSD license
|
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
* RFu_RF69_thermistor.ino
|
||||
* (c) 2014 flabbergast
|
||||
* BSD license
|
||||
*
|
||||
* Sketch for running Ciseco's RFu-328, with a RFM69CW soldered, on a XRF thermistor
|
||||
* coin cell board from Ciseco.
|
||||
*
|
||||
* Links:
|
||||
* * RFu-328: http://shop.ciseco.co.uk/rf-328-bare-arduino-atmega-328-compatible-micro-board-rfu-328/
|
||||
* * Thermistor coin cell board: http://shop.ciseco.co.uk/temperature-xrf-development-sensor-thermistor/
|
||||
*
|
||||
* Credits:
|
||||
* * based partly on OpenEnergyMonitor's emonTH code: https://github.com/openenergymonitor/emonTH
|
||||
* * measuring internal voltage code from: https://code.google.com/p/tinkerit/wiki/SecretVoltmeter
|
||||
* * Thermistor code from lady Ada: https://learn.adafruit.com/thermistor/using-a-thermistor
|
||||
*
|
||||
* emon: Recommended node ID allocation
|
||||
* ------------------------------------------------------------------------------------------------------------
|
||||
* -ID- -Node Type-
|
||||
* 0 - Special allocation in JeeLib RFM12 driver - reserved for OOK use
|
||||
* 1-4 - Control nodes
|
||||
* 5-10 - Energy monitoring nodes
|
||||
* 11-14 --Un-assigned --
|
||||
* 15-16 - Base Station & logging nodes
|
||||
* 17-30 - Environmental sensing nodes (temperature humidity etc.)
|
||||
* 31 - Special allocation in JeeLib RFM12 driver - Node31 can communicate with nodes on any network group
|
||||
* -------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define DEBUG false // Set to true to enable serial (increases power usage)
|
||||
#define SERIAL_BAUD 115200
|
||||
#define FIRMWARE_VERSION "1.0"
|
||||
#define RF69_COMPAT 1 // define this to use the RF69 driver i.s.o. RF12
|
||||
|
||||
#define RF_TX_POWER 18 // 0-31 (observe the max!!!), the Tx power is -18+value (max 13) dB
|
||||
|
||||
const int time_between_readings = 5; // in minutes
|
||||
|
||||
#define RF_freq RF12_868MHZ // Frequency of RF12B module can be RF12_433MHZ, RF12_868MHZ or RF12_915MHZ. You should use the one matching the module you have.
|
||||
const int nodeID = 18; // RFM12B node ID - should be unique on network
|
||||
const int networkGroup = 212; // RFM12B wireless network group
|
||||
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <RFu_JeeLib.h>
|
||||
#include <math.h>
|
||||
|
||||
ISR(WDT_vect) { Sleepy::watchdogEvent(); } // Attached JeeLib sleep function to Atmega328 watchdog -enables MCU to be put into sleep mode inbetween readings to reduce power consumption
|
||||
|
||||
//##### radio packet structure #####
|
||||
typedef struct { // RFM12B RF payload datastructure
|
||||
int temp;
|
||||
int battery;
|
||||
} Payload;
|
||||
Payload tempsensor;
|
||||
|
||||
|
||||
//##### get VCC voltage (compare with internal bandgap 1.1V) #####
|
||||
long readVcc() {
|
||||
long result;
|
||||
// Read 1.1V reference against AVcc
|
||||
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
|
||||
delay(2); // Wait for Vref to settle
|
||||
ADCSRA |= _BV(ADSC); // Convert
|
||||
while (bit_is_set(ADCSRA,ADSC));
|
||||
result = ADCL;
|
||||
result |= ADCH<<8;
|
||||
result = 1126400L / result; // Back-calculate AVcc in mV
|
||||
return result;
|
||||
}
|
||||
|
||||
//##### Thermistor function: get temp from raw ADC #####
|
||||
// to which pin is the "power" end of the therm circuit connected (the other is GND)
|
||||
#define THERMISTOR_POWER 9
|
||||
// which analog pin to connect
|
||||
#define THERMISTORPIN A0
|
||||
// resistance at 25 degrees C
|
||||
#define THERMISTORNOMINAL 10000
|
||||
// temp. for nominal resistance (almost always 25 C)
|
||||
#define TEMPERATURENOMINAL 25
|
||||
// how many samples to take and average, more takes longer
|
||||
// but is more 'smooth'
|
||||
#define NUMSAMPLES 5
|
||||
// The beta coefficient of the thermistor (usually 3000-4000)
|
||||
#define BCOEFFICIENT 3977
|
||||
// the value of the 'other' resistor
|
||||
#define SERIESRESISTOR 9950
|
||||
|
||||
int samples[NUMSAMPLES];
|
||||
|
||||
// TODO:
|
||||
// - maybe should use a lookup table, this is too much computational power
|
||||
|
||||
int thermistor_routine() {
|
||||
uint8_t i;
|
||||
float average;
|
||||
|
||||
digitalWrite(THERMISTOR_POWER, HIGH); // power up the thermistor circuit
|
||||
delay(5); // wait for the voltages to settle (necessary? how long?)
|
||||
|
||||
// take N samples in a row, with a slight delay
|
||||
for (i=0; i< NUMSAMPLES; i++) {
|
||||
samples[i] = analogRead(THERMISTORPIN);
|
||||
delay(5);
|
||||
}
|
||||
// power down the thermistor circuit
|
||||
digitalWrite(THERMISTOR_POWER, LOW);
|
||||
|
||||
// average all the samples out
|
||||
average = 0;
|
||||
for (i=0; i< NUMSAMPLES; i++) {
|
||||
average += samples[i];
|
||||
}
|
||||
average /= NUMSAMPLES;
|
||||
|
||||
if (DEBUG) {
|
||||
Serial.print("Average analog reading ");
|
||||
Serial.println(average);
|
||||
}
|
||||
|
||||
// convert the value to resistance
|
||||
average = 1023 / average - 1;
|
||||
average = SERIESRESISTOR / average; // this would be '*' if thermistor is connected to VCC, not GND
|
||||
if (DEBUG) {
|
||||
Serial.print("Thermistor resistance ");
|
||||
Serial.println(average);
|
||||
}
|
||||
|
||||
float steinhart;
|
||||
steinhart = average / THERMISTORNOMINAL; // (R/Ro)
|
||||
steinhart = log(steinhart); // ln(R/Ro)
|
||||
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
|
||||
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
|
||||
steinhart = 1.0 / steinhart; // Invert
|
||||
steinhart -= 273.15; // convert to C
|
||||
|
||||
if (DEBUG) {
|
||||
Serial.print("Temperature ");
|
||||
Serial.print(steinhart);
|
||||
Serial.println(" *C");
|
||||
}
|
||||
|
||||
return((int)1000*steinhart); // return int, 1000 * temperature
|
||||
}
|
||||
|
||||
//################################################################################################################################
|
||||
//################################################################################################################################
|
||||
void setup() {
|
||||
//################################################################################################################################
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
Serial.println("");
|
||||
Serial.println("Initializing RFM...");
|
||||
delay(20);
|
||||
}
|
||||
|
||||
tempsensor.temp=0;
|
||||
|
||||
rf12_initialize(nodeID, RF_freq, networkGroup); // Initialize RFM12B
|
||||
|
||||
// Adjust TX power
|
||||
if(RF69_COMPAT) {
|
||||
RF69::control(0x11 | 0x80, 128+ RF_TX_POWER); // 0x11 is 'RegPaLevel' register address
|
||||
}
|
||||
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
|
||||
pinMode(THERMISTORPIN, INPUT);
|
||||
pinMode(THERMISTOR_POWER, OUTPUT);
|
||||
digitalWrite(THERMISTOR_POWER, LOW); // turn off the thermistor circuit
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("RFu+RFM69CW+thermistor sensor (by flabbergast); Firmware version ");
|
||||
Serial.println(FIRMWARE_VERSION);
|
||||
Serial.print("Node: ");
|
||||
Serial.print(nodeID);
|
||||
Serial.print(" Freq: ");
|
||||
if (RF_freq == RF12_433MHZ) Serial.print("433Mhz");
|
||||
if (RF_freq == RF12_868MHZ) Serial.print("868Mhz");
|
||||
if (RF_freq == RF12_915MHZ) Serial.print("915Mhz");
|
||||
Serial.print(" Network group: ");
|
||||
Serial.println(networkGroup);
|
||||
Serial.print(" Battery voltage (V) : ");
|
||||
Serial.println( readVcc() / 1000.0 );
|
||||
Serial.print(" Temperature ('C) : ");
|
||||
Serial.println( thermistor_routine() / 1000.0 );
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
||||
//################################################################################################################################
|
||||
// Power Save - turn off what we don't need - http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
|
||||
//################################################################################################################################
|
||||
ACSR |= (1 << ACD); // disable Analog comparator
|
||||
if (! DEBUG) power_usart0_disable(); //disable serial UART
|
||||
power_twi_disable();
|
||||
//power_timer0_disable(); //don't disable: sends the atmega to indefinite sleep immediately for some reason
|
||||
power_timer1_disable();
|
||||
power_spi_disable();
|
||||
|
||||
//################################################################################################################################
|
||||
|
||||
dodelay(1000); // wait a sec
|
||||
|
||||
} // end of setup
|
||||
|
||||
|
||||
//################################################################################################################################
|
||||
//################################################################################################################################
|
||||
void loop() {
|
||||
//################################################################################################################################
|
||||
|
||||
tempsensor.temp = thermistor_routine(); // read the temperature ('C * 1000)
|
||||
tempsensor.battery = readVcc(); // read battery voltage, convert ADC to volts x1000
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Battery voltage (V): ");
|
||||
Serial.println( tempsensor.battery/1000.0 );
|
||||
Serial.print("Temperature ('C): ");
|
||||
Serial.println( tempsensor.temp/1000.0 );
|
||||
|
||||
delay(100);
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Powering RF up ... ");
|
||||
delay(200);
|
||||
}
|
||||
power_spi_enable();
|
||||
rf12_sleep(RF12_WAKEUP);
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("sending a reading ... ");
|
||||
delay(50);
|
||||
}
|
||||
rf12_sendNow(0, &tempsensor, sizeof tempsensor);
|
||||
// set the sync mode to 2 if the fuses are still the Arduino default
|
||||
// mode 3 (full powerdown) can only be used with 258 CK startup fuses
|
||||
rf12_sendWait(2);
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("back to sleep.");
|
||||
}
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
power_spi_disable();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Going to sleep.");
|
||||
delay(200);
|
||||
}
|
||||
int i;
|
||||
for(i=0; i<time_between_readings; i++) {
|
||||
if (DEBUG) {
|
||||
dodelay(6000); // 6 sec
|
||||
} else {
|
||||
dodelay(60*1000); // minute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dodelay(unsigned int ms)
|
||||
{
|
||||
byte oldADCSRA=ADCSRA;
|
||||
byte oldADCSRB=ADCSRB;
|
||||
byte oldADMUX=ADMUX;
|
||||
|
||||
Sleepy::loseSomeTime(ms); // JeeLabs power save function: enter low power mode for x seconds (valid range 16-65000 ms)
|
||||
|
||||
ADCSRA=oldADCSRA; // restore ADC state
|
||||
ADCSRB=oldADCSRB;
|
||||
ADMUX=oldADMUX;
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* RFu-328 + TH02 (hardware I2C)
|
||||
* (c) 2014 flabbergast
|
||||
*
|
||||
* Comments:
|
||||
* - Depends on:
|
||||
* - TH02 library: https://github.com/hallard/TH02
|
||||
* - I2C library: http://www.dsscircuits.com/index.php/articles/66-arduino-i2c-master-library
|
||||
* - RFu_jeelib: https://github.com/flabbergast/RFu_jeelib (rfu branch)
|
||||
* - I didn't go too overboard with the power savings, so e.g. battery voltage
|
||||
* is measured on every reading, and TH02 is given on lot of time to power up
|
||||
* and shut down.
|
||||
* - I run it on "RFu developer board" with a 3.3V boost converter, from one
|
||||
* AA battery. So the thing runs on 3.3V, and the battery voltage is measured
|
||||
* ordinarily, via analogRead.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG false // Set to true to enable serial (increases power usage)
|
||||
#define SERIAL_BAUD 115200
|
||||
#define FIRMWARE_VERSION "1.0"
|
||||
|
||||
#define RF69_COMPAT 1 // comment this out if you have RFM12B instead of RFM69CW
|
||||
#define RF_NODE_ID 17
|
||||
#define RF_GROUP 212
|
||||
#define RF_FREQ RF12_868MHZ
|
||||
#define RF_TX_POWER 15 // 0-31 (observe the max!!!), the Tx power is -18+value (max 13) dB
|
||||
|
||||
#define HALFMIN_SLEEPS 10 // how long to sleep between readings (times 30 sec)?
|
||||
|
||||
// TH02 power is on pin 5
|
||||
#define TH02_POWER_PIN 5
|
||||
|
||||
// Battery '+' is connected to A2
|
||||
#define BATT_PIN A2
|
||||
|
||||
// Optional LED from 9 to GND, blips on sending a reading
|
||||
#define LED_PIN 9
|
||||
#define USE_LED true
|
||||
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#include <RFu_JeeLib.h>
|
||||
|
||||
#include <I2C.h>
|
||||
#include <TH02.h>
|
||||
TH02 sensor;
|
||||
|
||||
//##### radio packet structure #####
|
||||
typedef struct { // RFM12B RF payload datastructure
|
||||
int16_t temp;
|
||||
int16_t rh_comp;
|
||||
uint8_t voltage; // it's only one AA, so < 2.55V = (255/100)
|
||||
} Payload;
|
||||
Payload payload;
|
||||
|
||||
// this must be added since we're using the watchdog for low-power waiting
|
||||
ISR(WDT_vect) { Sleepy::watchdogEvent(); }
|
||||
|
||||
void ledOn() {
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
}
|
||||
void ledOff() {
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
pinMode(LED_PIN, INPUT);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
//################################################################################################################################
|
||||
// Power Save - turn off what we don't need - http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
|
||||
//################################################################################################################################
|
||||
ACSR |= (1 << ACD); // disable Analog comparator
|
||||
if (! DEBUG) power_usart0_disable(); //disable serial UART
|
||||
//power_twi_disable();
|
||||
//power_timer0_disable(); //don't disable: sends the atmega to indefinite sleep immediately for some reason
|
||||
power_timer1_disable();
|
||||
//power_spi_disable();
|
||||
//################################################################################################################################
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
Serial.println("");
|
||||
Serial.print("RFu + TH02 v");
|
||||
Serial.println(FIRMWARE_VERSION);
|
||||
Serial.println("Initializing RFM...");
|
||||
delay(20);
|
||||
}
|
||||
rf12_initialize(RF_NODE_ID, RF_FREQ, RF_GROUP);
|
||||
|
||||
// adjust the TX output to save power
|
||||
if(RF69_COMPAT) {
|
||||
RF69::control(0x11 | 0x80, 128+ RF_TX_POWER); // 0x11 is 'RegPaLevel' register address
|
||||
}
|
||||
|
||||
// sleep
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
// disable SPI
|
||||
power_spi_disable();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Initialising I2C...");
|
||||
}
|
||||
// initialise i2c
|
||||
I2c.begin();
|
||||
I2c.pullup(true); // Enable I2C Internal pullup
|
||||
I2c.setSpeed(1); // Set I2C to 400Khz.
|
||||
|
||||
// battery status pin is input (should be by default, but...)
|
||||
pinMode(BATT_PIN, INPUT);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// read battery status [boost output measures 3.27V, that's our reference]
|
||||
uint16_t v = (uint16_t)((analogRead(BATT_PIN) * 327.0)/1024.0);
|
||||
payload.voltage = (uint8_t) v;
|
||||
if (DEBUG) {
|
||||
Serial.print("Got battery voltage: ");
|
||||
Serial.println(payload.voltage);
|
||||
}
|
||||
|
||||
// turn on power to TH02
|
||||
pinMode(TH02_POWER_PIN, OUTPUT);
|
||||
digitalWrite(TH02_POWER_PIN, HIGH);
|
||||
delay(15); // wait for TH02 to power up (should be 10ms, max 15ms, but could
|
||||
// be more if not enough current (40mA))
|
||||
|
||||
// turn on I2C module
|
||||
power_twi_enable();
|
||||
// do a temp conversion
|
||||
sensor.startTempConv(true); // true: fastmode
|
||||
sensor.waitEndConversion();
|
||||
|
||||
// process the result
|
||||
sensor.getConversionValue();
|
||||
payload.temp = sensor.getLastRawTemp();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Got temp: ");
|
||||
Serial.println(payload.temp/100.0);
|
||||
}
|
||||
|
||||
// do a humidity conversion
|
||||
sensor.startRHConv(true);
|
||||
sensor.waitEndConversion();
|
||||
|
||||
// read the result (gets saved in sensor object)
|
||||
sensor.getConversionValue();
|
||||
|
||||
// power off TH02
|
||||
digitalWrite(TH02_POWER_PIN, LOW);
|
||||
delay(10); // wait for TH02 to power down (might be sinking 40mA current)
|
||||
pinMode(TH02_POWER_PIN, INPUT); // configure as input (should save a bit of power)
|
||||
|
||||
// compute the compensated humidity
|
||||
payload.rh_comp = sensor.getConpensatedRH(false);
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Got compensated RH: ");
|
||||
Serial.println(payload.rh_comp/100.0);
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Powering up radio and sending the reading...");
|
||||
delay(30);
|
||||
}
|
||||
if (USE_LED) {
|
||||
ledOn();
|
||||
// delay(5); // need a bit of time to see it go on
|
||||
}
|
||||
// power up the radio
|
||||
power_spi_enable();
|
||||
// wakey wakey
|
||||
rf12_sleep(RF12_WAKEUP);
|
||||
// send the reading
|
||||
rf12_sendNow(0, &payload, sizeof payload);
|
||||
rf12_sendWait(2);
|
||||
// sleep
|
||||
rf12_sleep(RF12_SLEEP);
|
||||
// disable SPI
|
||||
power_spi_enable();
|
||||
|
||||
if (USE_LED)
|
||||
ledOff();
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Going to sleep.");
|
||||
delay(100);
|
||||
}
|
||||
// sleep for a time
|
||||
uint8_t i;
|
||||
for(i=0; i<HALFMIN_SLEEPS; i++) {
|
||||
if(DEBUG) {
|
||||
Sleepy::loseSomeTime(6000); // JeeLabs power save function: enter low power mode for x milliseconds (valid range 16-65000 ms)
|
||||
} else {
|
||||
Sleepy::loseSomeTime(30000); // JeeLabs power save function: enter low power mode for x milliseconds (valid range 16-65000 ms)
|
||||
}
|
||||
}
|
||||
}
|
6
jnu-tmp102/.gitignore
vendored
Normal file
6
jnu-tmp102/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
jnu-tmp102/.travis.yml
Normal file
67
jnu-tmp102/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
jnu-tmp102/.vscode/extensions.json
vendored
Normal file
7
jnu-tmp102/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
jnu-tmp102/include/README
Normal file
39
jnu-tmp102/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
jnu-tmp102/lib/README
Normal file
46
jnu-tmp102/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
14
jnu-tmp102/platformio.ini
Normal file
14
jnu-tmp102/platformio.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:attiny84]
|
||||
platform = atmelavr
|
||||
board = attiny84
|
||||
framework = arduino
|
311
jnu-tmp102/src/rf69.h
Normal file
311
jnu-tmp102/src/rf69.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Native mode RF69 driver.
|
||||
|
||||
#ifndef chThdYield
|
||||
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
|
||||
#endif
|
||||
|
||||
template< typename SPI >
|
||||
class RF69 {
|
||||
public:
|
||||
void init (uint8_t id, uint8_t group, int freq);
|
||||
void encrypt (const char* key);
|
||||
void txPower (uint8_t level);
|
||||
void setHighPower (uint8_t onOff);
|
||||
|
||||
int receive (void* ptr, int len);
|
||||
void send (uint8_t header, const void* ptr, int len);
|
||||
void sleep ();
|
||||
|
||||
int16_t afc;
|
||||
uint8_t rssi;
|
||||
uint8_t lna;
|
||||
uint8_t myId;
|
||||
uint8_t parity;
|
||||
|
||||
uint8_t readReg (uint8_t addr) {
|
||||
return spi.rwReg(addr, 0);
|
||||
}
|
||||
void writeReg (uint8_t addr, uint8_t val) {
|
||||
spi.rwReg(addr | 0x80, val);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
REG_FIFO = 0x00,
|
||||
REG_OPMODE = 0x01,
|
||||
REG_FRFMSB = 0x07,
|
||||
REG_PALEVEL = 0x11,
|
||||
REG_OCP = 0x13,
|
||||
REG_LNAVALUE = 0x18,
|
||||
REG_AFCMSB = 0x1F,
|
||||
REG_AFCLSB = 0x20,
|
||||
REG_FEIMSB = 0x21,
|
||||
REG_FEILSB = 0x22,
|
||||
REG_RSSIVALUE = 0x24,
|
||||
REG_IRQFLAGS1 = 0x27,
|
||||
REG_IRQFLAGS2 = 0x28,
|
||||
REG_SYNCVALUE1 = 0x2F,
|
||||
REG_SYNCVALUE2 = 0x30,
|
||||
REG_SYNCVALUE3 = 0x31,
|
||||
REG_NODEADDR = 0x39,
|
||||
REG_BCASTADDR = 0x3A,
|
||||
REG_FIFOTHRESH = 0x3C,
|
||||
REG_PKTCONFIG2 = 0x3D,
|
||||
REG_AESKEYMSB = 0x3E,
|
||||
REG_TESTPA1 = 0x5A, // only present on RFM69HW/SX1231H
|
||||
REG_TESTPA2 = 0x5C, // only present on RFM69HW/SX1231H
|
||||
|
||||
MODE_SLEEP = 0<<2,
|
||||
MODE_STANDBY = 1<<2,
|
||||
MODE_TRANSMIT = 3<<2,
|
||||
MODE_RECEIVE = 4<<2,
|
||||
|
||||
START_TX = 0xC2,
|
||||
STOP_TX = 0x42,
|
||||
|
||||
RCCALSTART = 0x80,
|
||||
IRQ1_MODEREADY = 1<<7,
|
||||
IRQ1_RXREADY = 1<<6,
|
||||
IRQ1_SYNADDRMATCH = 1<<0,
|
||||
|
||||
IRQ2_FIFONOTEMPTY = 1<<6,
|
||||
IRQ2_PACKETSENT = 1<<3,
|
||||
IRQ2_PAYLOADREADY = 1<<2,
|
||||
};
|
||||
|
||||
void setMode (uint8_t newMode);
|
||||
void configure (const uint8_t* p);
|
||||
void setFrequency (uint32_t freq);
|
||||
void setHighPowerRegs (uint8_t onOff);
|
||||
|
||||
SPI spi;
|
||||
volatile uint8_t mode;
|
||||
uint8_t isRFM69HW;
|
||||
};
|
||||
|
||||
// driver implementation
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setMode (uint8_t newMode) {
|
||||
mode = newMode;
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
|
||||
if (isRFM69HW) {
|
||||
if ( newMode == MODE_TRANSMIT ) {
|
||||
setHighPowerRegs(1);
|
||||
}
|
||||
if ( newMode == MODE_RECEIVE ) {
|
||||
setHighPowerRegs(0);
|
||||
}
|
||||
}
|
||||
while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setFrequency (uint32_t hz) {
|
||||
// accept any frequency scale as input, including KHz and MHz
|
||||
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
|
||||
while (hz < 100000000)
|
||||
hz *= 10;
|
||||
|
||||
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
|
||||
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
|
||||
// due to this, the lower 6 bits of the calculated factor will always be 0
|
||||
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
|
||||
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
|
||||
uint32_t frf = (hz << 2) / (32000000L >> 11);
|
||||
writeReg(REG_FRFMSB, frf >> 10);
|
||||
writeReg(REG_FRFMSB+1, frf >> 2);
|
||||
writeReg(REG_FRFMSB+2, frf << 6);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::configure (const uint8_t* p) {
|
||||
while (true) {
|
||||
uint8_t cmd = p[0];
|
||||
if (cmd == 0)
|
||||
break;
|
||||
writeReg(cmd, p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t configRegs [] = {
|
||||
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
|
||||
0x02, 0x00, // DataModul = packet mode, fsk
|
||||
0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
|
||||
0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
|
||||
0x05, 0x02, // FdevMsb = 45 KHz
|
||||
0x06, 0xE1, // FdevLsb = 45 KHz
|
||||
0x0B, 0x20, // Low M
|
||||
0x19, 0x4A, // RxBw 100 KHz
|
||||
0x1A, 0x42, // AfcBw 125 KHz
|
||||
0x1E, 0x0C, // AfcAutoclearOn, AfcAutoOn
|
||||
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
|
||||
0x26, 0x07, // disable clkout
|
||||
0x29, 0xA0, // RssiThresh -80 dB
|
||||
// 0x2B, 0x40, // RSSI timeout after 128 bytes
|
||||
0x2D, 0x05, // PreambleSize = 5
|
||||
0x2E, 0x90, // SyncConfig = sync on, sync size = 3
|
||||
0x2F, 0xAA, // SyncValue1 = 0xAA
|
||||
0x30, 0x2D, // SyncValue2 = 0x2D ! this is used for group in old jee protocol, but jz4 uses 0x2d hardcoded
|
||||
0x31, 0x2A, // network group, default 42
|
||||
0x37, 0xD0, // PacketConfig1 = fixed, white, no filtering
|
||||
0x38, 0x42, // PayloadLength = 0, unlimited
|
||||
0x3C, 0x8F, // FifoTresh, not empty, level 15
|
||||
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
|
||||
0x6F, 0x20, // TestDagc ...
|
||||
0x71, 0x02, // RegTestAfc
|
||||
0
|
||||
};
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::init (uint8_t id, uint8_t group, int freq) {
|
||||
myId = id;
|
||||
|
||||
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
|
||||
parity = group ^ (group << 4);
|
||||
parity = (parity ^ (parity << 2)) & 0xC0;
|
||||
|
||||
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
|
||||
spi.master(3);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
while (readReg(REG_SYNCVALUE1) != 0xAA);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
while (readReg(REG_SYNCVALUE1) != 0x55);
|
||||
|
||||
configure(configRegs);
|
||||
configure(configRegs); // TODO why is this needed ???
|
||||
setFrequency(freq);
|
||||
|
||||
writeReg(REG_SYNCVALUE3, group);
|
||||
isRFM69HW = 0;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::encrypt (const char* key) {
|
||||
uint8_t cfg = readReg(REG_PKTCONFIG2) & ~0x01;
|
||||
if (key) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
writeReg(REG_AESKEYMSB + i, *key);
|
||||
if (*key != 0)
|
||||
++key;
|
||||
}
|
||||
cfg |= 0x01;
|
||||
}
|
||||
writeReg(REG_PKTCONFIG2, cfg);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::txPower (uint8_t level) {
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & ~0x1F) | level);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPower (uint8_t onOff) { // must call this with RFM69HW!
|
||||
isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, isRFM69HW ? 0x0F : 0x1A);
|
||||
if (isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x40 | 0x20); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x80 ); // enable P0 only
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPowerRegs (uint8_t onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::sleep () {
|
||||
setMode(MODE_SLEEP);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
int RF69<SPI>::receive (void* ptr, int len) {
|
||||
if (mode != MODE_RECEIVE)
|
||||
setMode(MODE_RECEIVE);
|
||||
else {
|
||||
static uint8_t lastFlag;
|
||||
if ((readReg(REG_IRQFLAGS1) & IRQ1_RXREADY) != lastFlag) {
|
||||
lastFlag ^= IRQ1_RXREADY;
|
||||
if (lastFlag) { // flag just went from 0 to 1
|
||||
rssi = readReg(REG_RSSIVALUE);
|
||||
lna = (readReg(REG_LNAVALUE) >> 3) & 0x7;
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_AFCMSB);
|
||||
afc = spi.transfer(0) << 8;
|
||||
afc |= spi.transfer(0);
|
||||
spi.disable();
|
||||
#else
|
||||
afc = readReg(REG_AFCMSB) << 8;
|
||||
afc |= readReg(REG_AFCLSB);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (readReg(REG_IRQFLAGS2) & IRQ2_PAYLOADREADY) {
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO);
|
||||
int count = spi.transfer(0);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = spi.transfer(0);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
spi.disable();
|
||||
#else
|
||||
int count = readReg(REG_FIFO);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = readReg(REG_FIFO);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// only accept packets intended for us, or broadcasts
|
||||
// ... or any packet if we're the special catch-all node
|
||||
uint8_t dest = *(uint8_t*) ptr;
|
||||
if ((dest & 0xC0) == parity) {
|
||||
uint8_t destId = dest & 0x3F;
|
||||
if (destId == myId || destId == 0 || myId == 63)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::send (uint8_t header, const void* ptr, int len) {
|
||||
setMode(MODE_SLEEP);
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO | 0x80);
|
||||
spi.transfer(len + 2);
|
||||
spi.transfer((header & 0x3F) | parity);
|
||||
spi.transfer((header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
spi.transfer(((const uint8_t*) ptr)[i]);
|
||||
spi.disable();
|
||||
#else
|
||||
writeReg(REG_FIFO, len + 2);
|
||||
writeReg(REG_FIFO, (header & 0x3F) | parity);
|
||||
writeReg(REG_FIFO, (header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
writeReg(REG_FIFO, ((const uint8_t*) ptr)[i]);
|
||||
#endif
|
||||
|
||||
setMode(MODE_TRANSMIT);
|
||||
while ((readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) == 0)
|
||||
chThdYield();
|
||||
|
||||
setMode(MODE_STANDBY);
|
||||
}
|
11
jnu-tmp102/test/README
Normal file
11
jnu-tmp102/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
|
@ -1,243 +0,0 @@
|
|||
// Gateway sketch
|
||||
// (c) 2014 flabbergast
|
||||
// Based on:
|
||||
// Sample RFM69 receiver/gateway sketch, with ACK and optional encryption
|
||||
// Passes through any wireless received messages to the serial port & responds to ACKs
|
||||
// Library and code by Felix Rusu - felix@lowpowerlab.com
|
||||
// Get the RFM69 and SPIFlash library at: https://github.com/LowPowerLab/
|
||||
|
||||
#include <RFM69.h>
|
||||
#include <SPI.h>
|
||||
|
||||
/**************************************
|
||||
***** Parameters - read through! *****
|
||||
**************************************/
|
||||
|
||||
volatile uint8_t node_id = 5; //unique for each node on same network
|
||||
volatile uint8_t network_id = 107; //the same on all nodes that talk to each other
|
||||
#define FREQUENCY RF69_868MHZ
|
||||
#define ENCRYPTKEY "beleampanchineto" //exactly the same 16 characters/bytes on all nodes!
|
||||
//#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
|
||||
#define ACK_TIME 30 // max # of ms to wait for an ack
|
||||
#define SERIAL_BAUD 115200
|
||||
|
||||
#define LED 9 // LEDs on Digital 9 (PB1)
|
||||
#define LED_on LOW
|
||||
|
||||
bool promiscuousMode = true; //set to 'true' to sniff all packets on the same network
|
||||
|
||||
// default params are OK for JeeNodes
|
||||
RFM69 radio;
|
||||
// RFu+RFM69CW is wired a differently
|
||||
//RFM69 radio(4, 3, false, 1); // CS, interrupt_pin, is_HW, interrupt_num
|
||||
|
||||
/************************
|
||||
***** Main program *****
|
||||
************************/
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
delay(10);
|
||||
Serial.print(F("RFM69 gateway starting..."));
|
||||
radio.initialize(FREQUENCY,node_id,network_id);
|
||||
#ifdef IS_RFM69HW
|
||||
radio.setHighPower(); //only for RFM69HW!
|
||||
#endif
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
radio.promiscuous(promiscuousMode);
|
||||
Serial.println(F("done."));
|
||||
}
|
||||
|
||||
byte ackCount=0;
|
||||
byte temperature;
|
||||
byte fTemp;
|
||||
|
||||
#define MAX_LEN 60
|
||||
char buffer[MAX_LEN];
|
||||
byte buf_pos=0;
|
||||
byte buf_end=0;
|
||||
byte message[MAX_LEN/2];
|
||||
byte msg_pos;
|
||||
char input;
|
||||
|
||||
|
||||
void loop() {
|
||||
//process any serial input
|
||||
if (Serial.available() > 0)
|
||||
{
|
||||
input = (char)Serial.read();
|
||||
switch (input) {
|
||||
case 'r': //d=dump all register values
|
||||
radio.readAllRegs();
|
||||
break;
|
||||
case 'E': //E=enable encryption
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
Serial.println(F("Encryption enabled"));
|
||||
break;
|
||||
case 'e': //e=disable encryption
|
||||
radio.encrypt(null);
|
||||
Serial.println(F("Encryption disabled"));
|
||||
break;
|
||||
case 'p':
|
||||
promiscuousMode = !promiscuousMode;
|
||||
radio.promiscuous(promiscuousMode);
|
||||
Serial.print(F("Promiscuous mode "));Serial.println(promiscuousMode ? "on" : "off");
|
||||
break;
|
||||
case 't':
|
||||
temperature = radio.readTemperature(-1); // -1 = user cal factor, adjust for correct ambient
|
||||
fTemp = 1.8 * temperature + 32; // 9/5=1.8
|
||||
Serial.print(F("Radio Temp is "));
|
||||
Serial.print(temperature);
|
||||
Serial.print(F("C, "));
|
||||
Serial.print(fTemp); //converting to F loses some resolution, obvious when C is on edge between 2 values (ie 26C=78F, 27C=80F)
|
||||
Serial.println('F');
|
||||
break;
|
||||
case 'i':
|
||||
Serial.print(F("NodeID:"));
|
||||
Serial.print(node_id);
|
||||
Serial.print(F(" NetworkID:"));
|
||||
Serial.print(network_id);
|
||||
//Serial.print(F(" Encryption:"));
|
||||
Serial.print(F(" Promiscuous:"));
|
||||
Serial.print( (promiscuousMode ? "yes" : "no") );
|
||||
Serial.print(F(" Frequency:"));
|
||||
Serial.println(radio.getFrequency());
|
||||
Serial.print(F("Compiled: "));
|
||||
Serial.print(F(__DATE__));
|
||||
Serial.print(F(", "));
|
||||
Serial.print(F(__TIME__));
|
||||
Serial.print(F(", avr-gcc "));
|
||||
Serial.println(F(__VERSION__));
|
||||
break;
|
||||
case 'h':
|
||||
Serial.println(F("Help: dump_[r]egisters [p]romiscuous_toggle [t]emp [e]ncr_off [E]ncr_on [i]nfo [h]elp"));
|
||||
Serial.println(F(" NNN,...,NNN,RRRs : send bytes NNN,...,NNN to RRR"));
|
||||
Serial.println(F(" NNN,...,NNN,RRRa : send bytes NNN,...,NNN to RRR, requesting ACK"));
|
||||
Serial.println(F(" NNNn : change node ID to NNN; NNNg : change network ID to NNN"));
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case ',':
|
||||
if(buf_pos < MAX_LEN) {
|
||||
Serial.print(input);
|
||||
buffer[buf_pos++]=input;
|
||||
}
|
||||
break;
|
||||
case 's': // send buffer
|
||||
case 'a': // send buffer with ack
|
||||
case 'n': // node id
|
||||
case 'g': // group
|
||||
// process serial buffer
|
||||
msg_pos = 0;
|
||||
buf_end = buf_pos;
|
||||
buf_pos = 0;
|
||||
for(byte i=0; i<MAX_LEN/2; i++)
|
||||
message[i]=0;
|
||||
Serial.println(input);
|
||||
while(buf_pos<buf_end) {
|
||||
if( buffer[buf_pos]>='0' && buffer[buf_pos]<='9' ) {
|
||||
message[msg_pos] = message[msg_pos]*10 + (buffer[buf_pos]-'0');
|
||||
} else if( buffer[buf_pos] == ',' ) {
|
||||
msg_pos++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
buf_pos++;
|
||||
}
|
||||
if((input=='s' || input=='a') && buf_pos==buf_end) { // got a message to send
|
||||
Serial.print(F("Sending "));
|
||||
for(byte i=0; i<msg_pos; i++) {
|
||||
Serial.print(message[i],DEC);
|
||||
Serial.print(' ');
|
||||
}
|
||||
Serial.print(F("to "));
|
||||
Serial.print(message[msg_pos], DEC);
|
||||
if(input=='a')
|
||||
Serial.println(F(" with ACK request"));
|
||||
else
|
||||
Serial.println("");
|
||||
radio.send(message[msg_pos], (const void*)message, msg_pos, (input=='a'));
|
||||
// {
|
||||
// Serial.println(F(" ... OK!"));
|
||||
// } else {
|
||||
// Serial.println(F(" ... not_OK!"));
|
||||
// }
|
||||
}
|
||||
if(msg_pos==0 && buf_pos==buf_end) { // processed the whole buffer and it's just one byte
|
||||
if(input=='n') { // change node id
|
||||
radio.setAddress(message[0]);
|
||||
node_id = message[0];
|
||||
Serial.print(F("New node ID: "));
|
||||
Serial.println(node_id, DEC);
|
||||
} else if (input=='g') { // change group id
|
||||
radio.setNetwork(message[0]);
|
||||
network_id = message[0];
|
||||
Serial.print(F("New network ID: "));
|
||||
Serial.println(network_id, DEC);
|
||||
}
|
||||
}
|
||||
buf_pos = 0;
|
||||
break;
|
||||
default:
|
||||
Serial.println("");
|
||||
buf_pos = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (radio.receiveDone())
|
||||
{
|
||||
Serial.print("OK "); Serial.print(radio.SENDERID, DEC); Serial.print(" ");
|
||||
for (byte i = 0; i < radio.DATALEN; i++) {
|
||||
Serial.print((word)radio.DATA[i], DEC);
|
||||
Serial.print(' ');
|
||||
}
|
||||
Serial.print(" (RX_RSSI:");Serial.print(radio.RSSI);Serial.print("dB) ");
|
||||
if (promiscuousMode) {
|
||||
Serial.print("(TO:");Serial.print(radio.TARGETID, DEC);Serial.print(") ");
|
||||
}
|
||||
// print also a timestamp
|
||||
Serial.print("(timestamp:");
|
||||
Serial.print(millis());
|
||||
Serial.print(") ");
|
||||
|
||||
if (radio.ACKRequested())
|
||||
{
|
||||
byte theNodeID = radio.SENDERID;
|
||||
radio.sendACK();
|
||||
Serial.print(" (ACK_sent)");
|
||||
|
||||
// // When a node requests an ACK, respond to the ACK
|
||||
// // and also send a packet requesting an ACK (every 3rd one only)
|
||||
// // This way both TX/RX NODE functions are tested on 1 end at the GATEWAY
|
||||
// if (ackCount++%3==0)
|
||||
// {
|
||||
// Serial.print(" Pinging node ");
|
||||
// Serial.print(theNodeID);
|
||||
// Serial.print(" - ACK...");
|
||||
// delay(3); //need this when sending right after reception .. ?
|
||||
// if (radio.sendWithRetry(theNodeID, "ACK TEST", 8, 0)) // 0 = only 1 attempt, no retries
|
||||
// Serial.print("ok!");
|
||||
// else Serial.print("nothing");
|
||||
// }
|
||||
}
|
||||
Serial.println();
|
||||
Blink(LED,3);
|
||||
}
|
||||
}
|
||||
|
||||
void Blink(byte PIN, int DELAY_MS)
|
||||
{
|
||||
pinMode(PIN, OUTPUT);
|
||||
digitalWrite(PIN, LED_on);
|
||||
delay(DELAY_MS);
|
||||
digitalWrite(PIN, !LED_on);
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
#if not defined(__AVR_ATtiny84__)
|
||||
#error "This one works only on attiny84. Sorry."
|
||||
#endif
|
||||
|
||||
// Code for Jeenode Micro with TMP102 sensor
|
||||
|
||||
// Uses LowPowerLab's RFM69 library
|
||||
// or the modified version by flabbergast which runs also on
|
||||
// attiny84 (eg. JeeNode Micro).
|
||||
//
|
||||
// Based on radioBlip from jeelib: https://github.com/jcw/jeelib
|
||||
// (2010-08-29 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php)
|
||||
|
||||
|
||||
// Compilation notes:
|
||||
// compiling with the arduino-tiny core worked https://code.google.com/archive/p/arduino-tiny/
|
||||
// compiling the same thing with the high-low-tech did NOT http://highlowtech.org/?p=1695
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// Defines / tweakables¡
|
||||
#define NODE_ID 19 //unique for each node on same network
|
||||
#define GATEWAY_ID 1 // where to send the readings?
|
||||
#define NETWORK_ID 107 //the same on all nodes that talk to each other
|
||||
#define FREQUENCY RF69_868MHZ
|
||||
#define ENCRYPTKEY "beleampanchineto" //exactly the same 16 characters/bytes on all nodes!
|
||||
#define RF_TX_POWER 17 // 0-31, the Tx power is -18+value (max 13) dB
|
||||
|
||||
#define EIGHTSEC_SLEEPS 32 // this*8seconds = how long do we sleep between readings
|
||||
// it's only approximate (test in real conditions)
|
||||
// for instance my JNu sleeps longer
|
||||
|
||||
#define TMP102ADDRESS 0x48
|
||||
|
||||
//---------------------------------------
|
||||
// Includes and global variables
|
||||
#include <RFM69_f.h>
|
||||
#include "SoftI2Cmaster.h"
|
||||
|
||||
#include <avr/sleep.h> //Needed for sleep_mode
|
||||
#include <avr/wdt.h> //Needed to enable/disable watch dog timer
|
||||
|
||||
RFM69 radio;
|
||||
|
||||
//---------------------------------------
|
||||
// radio packet structure
|
||||
typedef struct {
|
||||
int16_t temp; // to get the celsius, divide by 16
|
||||
uint16_t voltage;
|
||||
} Payload;
|
||||
Payload payload;
|
||||
|
||||
//---------------------------------------
|
||||
// Function declarations
|
||||
void setup_sleep(void);
|
||||
int16_t getTemperature(void);
|
||||
bool tmp102_start_oneoff(void);
|
||||
bool tmp102_select_tempreg(void);
|
||||
uint16_t tmp102_read_reg(void);
|
||||
bool tmp102_setup(void);
|
||||
|
||||
//---------------------------------------
|
||||
// Initialisation
|
||||
void setup() {
|
||||
|
||||
// this initialises I2C
|
||||
// and sends TMP102 into shutdown/sleep state
|
||||
tmp102_setup();
|
||||
|
||||
// Power tricks from: http://harizanov.com/2012/07/power-saving-techniques-for-the-attiny84-powered-tinysensor/
|
||||
// turn off the modules we don't need
|
||||
bitSet(PRR, PRTIM1); // only keep timer0 going (disable timer1)
|
||||
ADCSRA &= ~ bit(ADEN); // disable the ADC
|
||||
bitSet(PRR, PRADC); // power down the ADC
|
||||
MCUCR |= _BV(BODS) | _BV(BODSE); //turn off the brown-out detector (should be off by fuses, but...)
|
||||
|
||||
// setup watchdog and sleep mode
|
||||
setup_sleep();
|
||||
|
||||
// power up the radio on JMv3 (mosfet)
|
||||
bitSet(DDRB, 0);
|
||||
bitClear(PORTB, 0);
|
||||
|
||||
radio.initialize(FREQUENCY,NODE_ID,NETWORK_ID);
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
// adjust the TX output to limit used power
|
||||
radio.setPowerLevel(RF_TX_POWER);
|
||||
// sleep
|
||||
radio.sleep();
|
||||
bitSet(PRR, PRUSI); // disable USI h/w
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
// Main loop
|
||||
void loop() {
|
||||
|
||||
// Use internal bandgap 1.1V to read the battery voltage
|
||||
// from http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
|
||||
bitClear(PRR, PRADC); // power up the ADC
|
||||
ADCSRA |= bit(ADEN); // enable the ADC
|
||||
// Read 1.1V reference against AVcc
|
||||
// set the reference to Vcc and the measurement to the internal 1.1V reference
|
||||
ADMUX = _BV(MUX5) | _BV(MUX0);
|
||||
delay(2); // Wait for Vref to settle
|
||||
ADCSRA |= _BV(ADSC); // Start conversion
|
||||
while (bit_is_set(ADCSRA,ADSC)) ; // measuring
|
||||
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
|
||||
uint8_t high = ADCH; // unlocks both
|
||||
ADCSRA &= ~ bit(ADEN); // disable the ADC
|
||||
bitSet(PRR, PRADC); // power down the ADC
|
||||
payload.voltage = 1125300L / ((high<<8) | low); // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
|
||||
|
||||
// get the temp reading
|
||||
payload.temp = getTemperature();
|
||||
|
||||
// send the payload
|
||||
bitClear(PRR, PRUSI); // enable USI h/w
|
||||
radio.send(GATEWAY_ID, (const void*)(&payload), sizeof(payload), false); // last param = requestACK
|
||||
// ... aaand back to sleep
|
||||
radio.sleep();
|
||||
bitSet(PRR, PRUSI); // disable USI h/w
|
||||
|
||||
uint8_t i;
|
||||
for(i=0; i<EIGHTSEC_SLEEPS; i++)
|
||||
sleep_cpu(); // this is wrapper around sleep_mode();
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
/* TMP102 code */
|
||||
|
||||
// something bigger than any 12bit value (it's signed int!)
|
||||
#define TMP102_READ_UNSUCCESSFUL (1<<14)
|
||||
|
||||
// to get celsius from the return value, do (return_value/16)
|
||||
// [altenatively (return_value>>4) but this ends up wrong on negative
|
||||
// temperatures, and "rounds" the result]
|
||||
// the fn returns TMP102_READ_UNSUCCESSFUL if there was a problem communicating
|
||||
// with TMP102
|
||||
int16_t getTemperature(){
|
||||
if(!tmp102_start_oneoff())
|
||||
return TMP102_READ_UNSUCCESSFUL;
|
||||
|
||||
// when woken up from a shutdown, it should take 26ms to do a conversion
|
||||
delay(29);
|
||||
|
||||
if(!tmp102_select_tempreg())
|
||||
return TMP102_READ_UNSUCCESSFUL;
|
||||
|
||||
return(tmp102_read_reg());
|
||||
}
|
||||
|
||||
// see the comments below for some description of the config bytes
|
||||
|
||||
bool tmp102_start_oneoff() {
|
||||
if(i2c_start( (TMP102ADDRESS<<1) | I2C_WRITE )) // announce writing
|
||||
if(i2c_write( 0x01 )) // want to write to config
|
||||
if(i2c_write( B11100001 )) // write two config bytes
|
||||
if(i2c_write( B10100000 )) {
|
||||
i2c_stop(); // finish transaction
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tmp102_select_tempreg() {
|
||||
if(i2c_start( (TMP102ADDRESS<<1) | I2C_WRITE )) // announce writing
|
||||
if(i2c_write( 0x00 )) { // want to select the temp register
|
||||
i2c_stop(); // finish transaction
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t tmp102_read_reg() {
|
||||
uint16_t result;
|
||||
// Read 2 bytes adc data MSB and LSB from TH02
|
||||
if(i2c_start( (TMP102ADDRESS<<1) | I2C_READ )) { // announce reading
|
||||
result = i2c_read(false) << 8; // read first byte
|
||||
result |= i2c_read(true); // read last byte
|
||||
i2c_stop(); // finish transaction
|
||||
return (result>>4); // it's actually left-justified 12 bits
|
||||
}
|
||||
return TMP102_READ_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
bool tmp102_setup() {
|
||||
// initialise i2c
|
||||
i2c_init();
|
||||
|
||||
// actually send the sensor into shutdown, we'll request one conversion
|
||||
// during one temperature request call
|
||||
if(i2c_start( (TMP102ADDRESS<<1) | I2C_WRITE )) // announce writing
|
||||
if(i2c_write( 0x01 )) // want to write to config
|
||||
if(i2c_write( B01100001 )) // write two config bytes
|
||||
if(i2c_write( B10100000 )) {
|
||||
i2c_stop(); // finish transaction
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
// the sensor starts conversion after reset/powerup, takes 26ms
|
||||
// two configuration bytes
|
||||
byte MSB = (0<<7) | // OS: "one-shot/conversion ready":
|
||||
// writing 1 in shutdown mode starts a conversion,
|
||||
// it will read 0 during conversion
|
||||
// after conversion, it reads 1
|
||||
(1<<6) | // R1: converter resolution; read-only
|
||||
(1<<5) | // R0:
|
||||
(0<<4) | // F1: "fault queue": how many faults to trigger alarm change
|
||||
(0<<3) | // F0: ...
|
||||
(0<<2) | // POL: inverts AL if set
|
||||
(0<<1) | // TM: "thermostat": 0=comparator, 1=interrupt
|
||||
(1<<0); // SD: shutdown mode (default=0=continuous conversion) NOT-DEFAULT
|
||||
byte LSB = (1<<7) | // CR1: conversion rate: 0=1/4Hz, 1=1Hz
|
||||
(0<<6) | // CR0: ... 2=4Hz (default), 3=8Hz
|
||||
(1<<5) | // AL: read-only: "alarm/comparator": 1 if tmp>=Thigh for F[1:0] cycles
|
||||
(0<<4); // EM: extended mode (13 bit)
|
||||
*/
|
||||
|
||||
//---------------------------------------
|
||||
/* Sleep code */
|
||||
|
||||
void setup_sleep() {
|
||||
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //Power down everything, wake up from WDT
|
||||
sleep_enable(); // avr docs recommend doing for every sleep: sleep_enable(); sei(); sleep_cpu(); sleep_disable();
|
||||
// now setup watchdog timer:
|
||||
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
|
||||
// 6=1sec, 7=2sec, B100000=4sec, B100001=8sec
|
||||
// want 8 secs
|
||||
#define SLEEP_BYTE B100001
|
||||
//This order of commands is important and cannot be combined
|
||||
MCUSR &= ~(1<<WDRF); //Clear the watch dog reset
|
||||
WDTCSR |= (1<<WDCE) | (1<<WDE); //Set WD_change enable, set WD enable
|
||||
WDTCSR = SLEEP_BYTE; //Set new watchdog timeout value
|
||||
WDTCSR |= _BV(WDIE); //Set the interrupt enable, this will keep unit from resetting after each int
|
||||
}
|
||||
|
||||
// watchdog interrupt
|
||||
ISR(WDT_vect) {
|
||||
// don't need to do anything, it's here just so that we wake up
|
||||
}
|
|
@ -1,541 +0,0 @@
|
|||
#define SCL_PIN 1
|
||||
#define SCL_PORT PORTA
|
||||
#define SDA_PIN 0
|
||||
#define SDA_PORT PORTA
|
||||
#define I2C_TIMEOUT 1000
|
||||
|
||||
// **********************************************************************************
|
||||
// ***************** including SoftI2CMaster.h here *********************************
|
||||
// **********************************************************************************
|
||||
// **** original at: https://github.com/felias-fogg/SoftI2CMaster *******************
|
||||
// **********************************************************************************
|
||||
|
||||
/* Arduino SoftI2C library.
|
||||
*
|
||||
* Copyright (C) 2013, Bernhard Nebel and Peter Fleury
|
||||
*
|
||||
* This is a very fast and very light-weight software I2C-master library
|
||||
* written in assembler. It is based on Peter Fleury's I2C software
|
||||
* library: http://homepage.hispeed.ch/peterfleury/avr-software.html
|
||||
*
|
||||
* This Library 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 Library 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 the Arduino I2cMaster Library. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* In order to use the library, you need to define SDA_PIN, SCL_PIN,
|
||||
* SDA_PORT and SCL_PORT before including this file. Have a look at
|
||||
* http://www.arduino.cc/en/Reference/PortManipulation for finding out
|
||||
* which values to use. For example, if you use digital pin 3 (corresponding
|
||||
* to PD3) for SDA and digital pin 13 (corresponding to PB5)
|
||||
* for SCL on a standard Arduino,
|
||||
* you have to use the following definitions:
|
||||
* #define SDA_PIN 3
|
||||
* #define SDA_PORT PORTD
|
||||
* #define SCL_PIN 5
|
||||
* #define SCL_PORT PORTB
|
||||
*
|
||||
* You can also define the following constants (see also below):
|
||||
* - I2C_CPUFREQ, when changing CPU clock frequency dynamically
|
||||
* - I2C_FASTMODE = 1 meaning that the I2C bus allows speeds up to 400 kHz
|
||||
* - I2C_SLOWMODE = 1 meaning that the I2C bus will allow only up to 25 kHz
|
||||
* - I2C_NOINTERRUPT = 1 in order to prohibit interrupts while
|
||||
* communicating (see below). This can be useful if you use the library
|
||||
* for communicationg with SMbus devices, which have timeouts.
|
||||
* Note, however, that interrupts are disabled from issuing a start condition
|
||||
* until issuing a stop condition. So use this option with care!
|
||||
* - I2C_TIMEOUT = 0..10000 mssec in order to return from the I2C functions
|
||||
* in case of a I2C bus lockup (i.e., SCL constantly low). 0 means no timeout
|
||||
*/
|
||||
|
||||
/* Changelog:
|
||||
* Version 1.3:
|
||||
* - added "__attribute__ ((used))" for all functions declared with "__attribute__ ((noinline))"
|
||||
* Now the module is also usable in Arduino 1.6.11+
|
||||
* Version 1.2:
|
||||
* - added pragma to avoid "unused parameter warnings" (suggestion by Walter)
|
||||
* - replaced wrong license file
|
||||
* Version 1.1:
|
||||
* - removed I2C_CLOCK_STRETCHING
|
||||
* - added I2C_TIMEOUT time in msec (0..10000) until timeout or 0 if no timeout
|
||||
* - changed i2c_init to return true iff both SDA and SCL are high
|
||||
* - changed interrupt disabling so that the previous IRQ state is restored
|
||||
* Version 1.0: basic functionality
|
||||
*/
|
||||
#include <avr/io.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifndef _SOFTI2C_H
|
||||
#define _SOFTI2C_H 1
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
|
||||
|
||||
// Init function. Needs to be called once in the beginning.
|
||||
// Returns false if SDA or SCL are low, which probably means
|
||||
// a I2C bus lockup or that the lines are not pulled up.
|
||||
bool __attribute__ ((noinline)) i2c_init(void) __attribute__ ((used));
|
||||
|
||||
// Start transfer function: <addr> is the 8-bit I2C address (including the R/W
|
||||
// bit).
|
||||
// Return: true if the slave replies with an "acknowledge", false otherwise
|
||||
bool __attribute__ ((noinline)) i2c_start(uint8_t addr) __attribute__ ((used));
|
||||
|
||||
// Similar to start function, but wait for an ACK! Be careful, this can
|
||||
// result in an infinite loop!
|
||||
void __attribute__ ((noinline)) i2c_start_wait(uint8_t addr) __attribute__ ((used));
|
||||
|
||||
// Repeated start function: After having claimed the bus with a start condition,
|
||||
// you can address another or the same chip again without an intervening
|
||||
// stop condition.
|
||||
// Return: true if the slave replies with an "acknowledge", false otherwise
|
||||
bool __attribute__ ((noinline)) i2c_rep_start(uint8_t addr) __attribute__ ((used));
|
||||
|
||||
// Issue a stop condition, freeing the bus.
|
||||
void __attribute__ ((noinline)) i2c_stop(void) asm("ass_i2c_stop") __attribute__ ((used));
|
||||
|
||||
// Write one byte to the slave chip that had been addressed
|
||||
// by the previous start call. <value> is the byte to be sent.
|
||||
// Return: true if the slave replies with an "acknowledge", false otherwise
|
||||
bool __attribute__ ((noinline)) i2c_write(uint8_t value) asm("ass_i2c_write") __attribute__ ((used));
|
||||
|
||||
|
||||
// Read one byte. If <last> is true, we send a NAK after having received
|
||||
// the byte in order to terminate the read sequence.
|
||||
uint8_t __attribute__ ((noinline)) i2c_read(bool last) __attribute__ ((used));
|
||||
|
||||
// You can set I2C_CPUFREQ independently of F_CPU if you
|
||||
// change the CPU frequency on the fly. If you do not define it,
|
||||
// it will use the value of F_CPU
|
||||
#ifndef I2C_CPUFREQ
|
||||
#define I2C_CPUFREQ F_CPU
|
||||
#endif
|
||||
|
||||
// If I2C_FASTMODE is set to 1, then the highest possible frequency below 400kHz
|
||||
// is selected. Be aware that not all slave chips may be able to deal with that!
|
||||
#ifndef I2C_FASTMODE
|
||||
#define I2C_FASTMODE 0
|
||||
#endif
|
||||
|
||||
// If I2C_FASTMODE is not defined or defined to be 0, then you can set
|
||||
// I2C_SLOWMODE to 1. In this case, the I2C frequency will not be higher
|
||||
// than 25KHz. This could be useful for problematic buses.
|
||||
#ifndef I2C_SLOWMODE
|
||||
#define I2C_SLOWMODE 0
|
||||
#endif
|
||||
|
||||
// if I2C_NOINTERRUPT is 1, then the I2C routines are not interruptable.
|
||||
// This is most probably only necessary if you are using a 1MHz system clock,
|
||||
// you are communicating with a SMBus device, and you want to avoid timeouts.
|
||||
// Be aware that the interrupt bit is enabled after each call. So the
|
||||
// I2C functions should not be called in interrupt routines or critical regions.
|
||||
#ifndef I2C_NOINTERRUPT
|
||||
#define I2C_NOINTERRUPT 0
|
||||
#endif
|
||||
|
||||
// I2C_TIMEOUT can be set to a value between 1 and 10000.
|
||||
// If it is defined and nonzero, it leads to a timeout if the
|
||||
// SCL is low longer than I2C_TIMEOUT milliseconds, i.e., max timeout is 10 sec
|
||||
#ifndef I2C_TIMEOUT
|
||||
#define I2C_TIMEOUT 0
|
||||
#else
|
||||
#if I2C_TIMEOUT > 10000
|
||||
#error I2C_TIMEOUT is too large
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define I2C_TIMEOUT_DELAY_LOOPS (I2C_CPUFREQ/1000UL)*I2C_TIMEOUT/4000UL
|
||||
#if I2C_TIMEOUT_DELAY_LOOPS < 1
|
||||
#define I2C_MAX_STRETCH 1
|
||||
#else
|
||||
#if I2C_TIMEOUT_DELAY_LOOPS > 60000UL
|
||||
#define I2C_MAX_STRETCH 60000UL
|
||||
#else
|
||||
#define I2C_MAX_STRETCH I2C_TIMEOUT_DELAY_LOOPS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if I2C_FASTMODE
|
||||
#define I2C_DELAY_COUNTER (((I2C_CPUFREQ/350000L)/2-18)/3)
|
||||
#else
|
||||
#if I2C_SLOWMODE
|
||||
#define I2C_DELAY_COUNTER (((I2C_CPUFREQ/23500L)/2-18)/3)
|
||||
#else
|
||||
#define I2C_DELAY_COUNTER (((I2C_CPUFREQ/90000L)/2-18)/3)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Table of I2C bus speed in kbit/sec:
|
||||
// CPU clock: 1MHz 2MHz 4MHz 8MHz 16MHz 20MHz
|
||||
// Fast I2C mode 40 80 150 300 400 400
|
||||
// Standard I2C mode 40 80 100 100 100 100
|
||||
// Slow I2C mode 25 25 25 25 25 25
|
||||
|
||||
// constants for reading & writing
|
||||
#define I2C_READ 1
|
||||
#define I2C_WRITE 0
|
||||
|
||||
// map the IO register back into the IO address space
|
||||
#define SDA_DDR (_SFR_IO_ADDR(SDA_PORT) - 1)
|
||||
#define SCL_DDR (_SFR_IO_ADDR(SCL_PORT) - 1)
|
||||
#define SDA_OUT _SFR_IO_ADDR(SDA_PORT)
|
||||
#define SCL_OUT _SFR_IO_ADDR(SCL_PORT)
|
||||
#define SDA_IN (_SFR_IO_ADDR(SDA_PORT) - 2)
|
||||
#define SCL_IN (_SFR_IO_ADDR(SCL_PORT) - 2)
|
||||
|
||||
#ifndef __tmp_reg__
|
||||
#define __tmp_reg__ 0
|
||||
#endif
|
||||
|
||||
|
||||
// Internal delay functions.
|
||||
void __attribute__ ((noinline)) i2c_delay_half(void) asm("ass_i2c_delay_half") __attribute__ ((used));
|
||||
void __attribute__ ((noinline)) i2c_wait_scl_high(void) asm("ass_i2c_wait_scl_high") __attribute__ ((used));
|
||||
|
||||
void i2c_delay_half(void)
|
||||
{ // function call 3 cycles => 3C
|
||||
#if I2C_DELAY_COUNTER < 1
|
||||
__asm__ __volatile__ (" ret");
|
||||
// 7 cycles for call and return
|
||||
#else
|
||||
__asm__ __volatile__
|
||||
(
|
||||
" ldi r25, %[DELAY] ;load delay constant ;; 4C \n\t"
|
||||
"_Lidelay: \n\t"
|
||||
" dec r25 ;decrement counter ;; 4C+xC \n\t"
|
||||
" brne _Lidelay ;;5C+(x-1)2C+xC\n\t"
|
||||
" ret ;; 9C+(x-1)2C+xC = 7C+xC"
|
||||
: : [DELAY] "M" I2C_DELAY_COUNTER : "r25");
|
||||
// 7 cycles + 3 times x cycles
|
||||
#endif
|
||||
}
|
||||
|
||||
void i2c_wait_scl_high(void)
|
||||
{
|
||||
#if I2C_TIMEOUT <= 0
|
||||
__asm__ __volatile__
|
||||
("_Li2c_wait_stretch: \n\t"
|
||||
" sbis %[SCLIN],%[SCLPIN] ;wait for SCL high \n\t"
|
||||
" rjmp _Li2c_wait_stretch \n\t"
|
||||
" cln ;signal: no timeout \n\t"
|
||||
" ret "
|
||||
: : [SCLIN] "I" (SCL_IN), [SCLPIN] "I" (SCL_PIN));
|
||||
#else
|
||||
__asm__ __volatile__
|
||||
( " ldi r27, %[HISTRETCH] ;load delay counter \n\t"
|
||||
" ldi r26, %[LOSTRETCH] \n\t"
|
||||
"_Lwait_stretch: \n\t"
|
||||
" clr __tmp_reg__ ;do next loop 255 times \n\t"
|
||||
"_Lwait_stretch_inner_loop: \n\t"
|
||||
" rcall _Lcheck_scl_level ;call check function ;; 12C \n\t"
|
||||
" brpl _Lstretch_done ;done if N=0 ;; +1 = 13C\n\t"
|
||||
" dec __tmp_reg__ ;dec inner loop counter;; +1 = 14C\n\t"
|
||||
" brne _Lwait_stretch_inner_loop ;; +2 = 16C\n\t"
|
||||
" sbiw r26,1 ;dec outer loop counter \n\t"
|
||||
" brne _Lwait_stretch ;continue with outer loop \n\t"
|
||||
" sen ;timeout -> set N-bit=1 \n\t"
|
||||
" rjmp _Lwait_return ;and return with N=1\n\t"
|
||||
"_Lstretch_done: ;SCL=1 sensed \n\t"
|
||||
" cln ;OK -> clear N-bit \n\t"
|
||||
" rjmp _Lwait_return ; and return with N=0 \n\t"
|
||||
|
||||
"_Lcheck_scl_level: ;; call = 3C\n\t"
|
||||
" cln ;; +1C = 4C \n\t"
|
||||
" sbic %[SCLIN],%[SCLPIN] ;skip if SCL still low ;; +2C = 6C \n\t"
|
||||
" rjmp _Lscl_high ;; +0C = 6C \n\t"
|
||||
" sen ;; +1 = 7C\n\t "
|
||||
"_Lscl_high: "
|
||||
" nop ;; +1C = 8C \n\t"
|
||||
" ret ;return N-Bit=1 if low ;; +4 = 12C\n\t"
|
||||
|
||||
"_Lwait_return:"
|
||||
: : [SCLIN] "I" (SCL_IN), [SCLPIN] "I" (SCL_PIN),
|
||||
[HISTRETCH] "M" (I2C_MAX_STRETCH>>8),
|
||||
[LOSTRETCH] "M" (I2C_MAX_STRETCH&0xFF)
|
||||
: "r26", "r27");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool i2c_init(void)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(" cbi %[SDADDR],%[SDAPIN] ;release SDA \n\t"
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL \n\t"
|
||||
" cbi %[SDAOUT],%[SDAPIN] ;clear SDA output value \n\t"
|
||||
" cbi %[SCLOUT],%[SCLPIN] ;clear SCL output value \n\t"
|
||||
" clr r24 ;set return value to false \n\t"
|
||||
" clr r25 ;set return value to false \n\t"
|
||||
" sbis %[SDAIN],%[SDAPIN] ;check for SDA high\n\t"
|
||||
" ret ;if low return with false \n\t"
|
||||
" sbis %[SCLIN],%[SCLPIN] ;check for SCL high \n\t"
|
||||
" ret ;if low return with false \n\t"
|
||||
" ldi r24,1 ;set return value to true \n\t"
|
||||
" ret "
|
||||
: :
|
||||
[SCLDDR] "I" (SCL_DDR), [SCLPIN] "I" (SCL_PIN),
|
||||
[SCLIN] "I" (SCL_IN), [SCLOUT] "I" (SCL_OUT),
|
||||
[SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN),
|
||||
[SDAIN] "I" (SDA_IN), [SDAOUT] "I" (SDA_OUT));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool i2c_start(uint8_t addr)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(
|
||||
#if I2C_NOINTERRUPT
|
||||
" cli ;clear IRQ bit \n\t"
|
||||
#endif
|
||||
" sbis %[SCLIN],%[SCLPIN] ;check for clock stretching slave\n\t"
|
||||
" rcall ass_i2c_wait_scl_high ;wait until SCL=H\n\t"
|
||||
" sbi %[SDADDR],%[SDAPIN] ;force SDA low \n\t"
|
||||
" rcall ass_i2c_delay_half ;wait T/2 \n\t"
|
||||
" rcall ass_i2c_write ;now write address \n\t"
|
||||
" ret"
|
||||
: : [SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN),
|
||||
[SCLIN] "I" (SCL_IN),[SCLPIN] "I" (SCL_PIN));
|
||||
return true; // we never return here!
|
||||
}
|
||||
|
||||
bool i2c_rep_start(uint8_t addr)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
|
||||
(
|
||||
#if I2C_NOINTERRUPT
|
||||
" cli \n\t"
|
||||
#endif
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low \n\t"
|
||||
" rcall ass_i2c_delay_half ;delay T/2 \n\t"
|
||||
" cbi %[SDADDR],%[SDAPIN] ;release SDA \n\t"
|
||||
" rcall ass_i2c_delay_half ;delay T/2 \n\t"
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL \n\t"
|
||||
" rcall ass_i2c_delay_half ;delay T/2 \n\t"
|
||||
" sbis %[SCLIN],%[SCLPIN] ;check for clock stretching slave\n\t"
|
||||
" rcall ass_i2c_wait_scl_high ;wait until SCL=H\n\t"
|
||||
" sbi %[SDADDR],%[SDAPIN] ;force SDA low \n\t"
|
||||
" rcall ass_i2c_delay_half ;delay T/2 \n\t"
|
||||
" rcall ass_i2c_write \n\t"
|
||||
" ret"
|
||||
: : [SCLDDR] "I" (SCL_DDR), [SCLPIN] "I" (SCL_PIN),[SCLIN] "I" (SCL_IN),
|
||||
[SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN));
|
||||
return true; // just to fool the compiler
|
||||
}
|
||||
|
||||
void i2c_start_wait(uint8_t addr)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(
|
||||
" push r24 ;save original parameter \n\t"
|
||||
"_Li2c_start_wait1: \n\t"
|
||||
" pop r24 ;restore original parameter\n\t"
|
||||
" push r24 ;and save again \n\t"
|
||||
#if I2C_NOINTERRUPT
|
||||
" cli ;disable interrupts \n\t"
|
||||
#endif
|
||||
" sbis %[SCLIN],%[SCLPIN] ;check for clock stretching slave\n\t"
|
||||
" rcall ass_i2c_wait_scl_high ;wait until SCL=H\n\t"
|
||||
" sbi %[SDADDR],%[SDAPIN] ;force SDA low \n\t"
|
||||
" rcall ass_i2c_delay_half ;delay T/2 \n\t"
|
||||
" rcall ass_i2c_write ;write address \n\t"
|
||||
" tst r24 ;if device not busy -> done \n\t"
|
||||
" brne _Li2c_start_wait_done \n\t"
|
||||
" rcall ass_i2c_stop ;terminate write & enable IRQ \n\t"
|
||||
" rjmp _Li2c_start_wait1 ;device busy, poll ack again \n\t"
|
||||
"_Li2c_start_wait_done: \n\t"
|
||||
" pop __tmp_reg__ ;pop off orig argument \n\t"
|
||||
" ret "
|
||||
: : [SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN),
|
||||
[SCLIN] "I" (SCL_IN),[SCLPIN] "I" (SCL_PIN));
|
||||
}
|
||||
|
||||
void i2c_stop(void)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low \n\t"
|
||||
" sbi %[SDADDR],%[SDAPIN] ;force SDA low \n\t"
|
||||
" rcall ass_i2c_delay_half ;T/2 delay \n\t"
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL \n\t"
|
||||
" rcall ass_i2c_delay_half ;T/2 delay \n\t"
|
||||
" sbis %[SCLIN],%[SCLPIN] ;check for clock stretching slave\n\t"
|
||||
" rcall ass_i2c_wait_scl_high ;wait until SCL=H\n\t"
|
||||
" cbi %[SDADDR],%[SDAPIN] ;release SDA \n\t"
|
||||
" rcall ass_i2c_delay_half \n\t"
|
||||
#if I2C_NOINTERRUPT
|
||||
" sei ;enable interrupts again!\n\t"
|
||||
#endif
|
||||
: : [SCLDDR] "I" (SCL_DDR), [SCLPIN] "I" (SCL_PIN), [SCLIN] "I" (SCL_IN),
|
||||
[SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN));
|
||||
}
|
||||
|
||||
bool i2c_write(uint8_t value)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(
|
||||
" sec ;set carry flag \n\t"
|
||||
" rol r24 ;shift in carry and shift out MSB \n\t"
|
||||
" rjmp _Li2c_write_first \n\t"
|
||||
"_Li2c_write_bit:\n\t"
|
||||
" lsl r24 ;left shift into carry ;; 1C\n\t"
|
||||
"_Li2c_write_first:\n\t"
|
||||
" breq _Li2c_get_ack ;jump if TXreg is empty;; +1 = 2C \n\t"
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low ;; +2 = 4C \n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
" brcc _Li2c_write_low ;;+1/+2=5/6C\n\t"
|
||||
" nop ;; +1 = 7C \n\t"
|
||||
" cbi %[SDADDR],%[SDAPIN] ;release SDA ;; +2 = 9C \n\t"
|
||||
" rjmp _Li2c_write_high ;; +2 = 11C \n\t"
|
||||
"_Li2c_write_low: \n\t"
|
||||
" sbi %[SDADDR],%[SDAPIN] ;force SDA low ;; +2 = 9C \n\t"
|
||||
" rjmp _Li2c_write_high ;;+2 = 11C \n\t"
|
||||
"_Li2c_write_high: \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;;+X = 11C+X\n\t"
|
||||
#endif
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL ;;+2 = 13C+X\n\t"
|
||||
" cln ;clear N-bit ;;+1 = 14C+X\n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
" sbis %[SCLIN],%[SCLPIN] ;check for SCL high ;;+2 = 16C+X\n\t"
|
||||
" rcall ass_i2c_wait_scl_high \n\t"
|
||||
" brpl _Ldelay_scl_high ;;+2 = 18C+X\n\t"
|
||||
"_Li2c_write_return_false: \n\t"
|
||||
" clr r24 ; return false because of timeout \n\t"
|
||||
" rjmp _Li2c_write_return \n\t"
|
||||
"_Ldelay_scl_high: \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;;+X= 18C+2X\n\t"
|
||||
#endif
|
||||
" rjmp _Li2c_write_bit \n\t"
|
||||
" ;; +2 = 20C +2X for one bit-loop \n\t"
|
||||
"_Li2c_get_ack: \n\t"
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low ;; +2 = 5C \n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
" cbi %[SDADDR],%[SDAPIN] ;release SDA ;;+2 = 7C \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;; +X = 7C+X \n\t"
|
||||
#endif
|
||||
" clr r25 ;; 17C+2X \n\t"
|
||||
" clr r24 ;return 0 ;; 14C + X \n\t"
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL ;; +2 = 9C+X\n\t"
|
||||
"_Li2c_ack_wait: \n\t"
|
||||
" cln ; clear N-bit ;; 10C + X\n\t"
|
||||
" nop \n\t"
|
||||
" sbis %[SCLIN],%[SCLPIN] ;wait SCL high ;; 12C + X \n\t"
|
||||
" rcall ass_i2c_wait_scl_high \n\t"
|
||||
" brmi _Li2c_write_return_false ;; 13C + X \n\t "
|
||||
" sbis %[SDAIN],%[SDAPIN] ;if SDA hi -> return 0 ;; 15C + X \n\t"
|
||||
" ldi r24,1 ;return true ;; 16C + X \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;; 16C + 2X \n\t"
|
||||
#endif
|
||||
"_Li2c_write_return: \n\t"
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low so SCL=H is short\n\t"
|
||||
" ret \n\t"
|
||||
" ;; + 4 = 17C + 2X for acknowldge bit"
|
||||
::
|
||||
[SCLDDR] "I" (SCL_DDR), [SCLPIN] "I" (SCL_PIN), [SCLIN] "I" (SCL_IN),
|
||||
[SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN), [SDAIN] "I" (SDA_IN));
|
||||
return true; // fooling the compiler
|
||||
}
|
||||
|
||||
uint8_t i2c_read(bool last)
|
||||
{
|
||||
__asm__ __volatile__
|
||||
(
|
||||
" ldi r23,0x01 \n\t"
|
||||
"_Li2c_read_bit: \n\t"
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low ;; 2C \n\t"
|
||||
" cbi %[SDADDR],%[SDAPIN] ;release SDA(prev. ACK);; 4C \n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
" nop \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;; 4C+X \n\t"
|
||||
#endif
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL ;; 6C + X \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;; 6C + 2X \n\t"
|
||||
#endif
|
||||
" cln ; clear N-bit ;; 7C + 2X \n\t"
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
" sbis %[SCLIN], %[SCLPIN] ;check for SCL high ;; 9C +2X \n\t"
|
||||
" rcall ass_i2c_wait_scl_high \n\t"
|
||||
" brmi _Li2c_read_return ;return if timeout ;; 10C + 2X\n\t"
|
||||
" clc ;clear carry flag ;; 11C + 2X\n\t"
|
||||
" sbic %[SDAIN],%[SDAPIN] ;if SDA is high ;; 11C + 2X\n\t"
|
||||
" sec ;set carry flag ;; 12C + 2X\n\t"
|
||||
" rol r23 ;store bit ;; 13C + 2X\n\t"
|
||||
" brcc _Li2c_read_bit ;while receiv reg not full \n\t"
|
||||
" ;; 15C + 2X for one bit loop \n\t"
|
||||
|
||||
"_Li2c_put_ack: \n\t"
|
||||
" sbi %[SCLDDR],%[SCLPIN] ;force SCL low ;; 2C \n\t"
|
||||
" cpi r24,0 ;; 3C \n\t"
|
||||
" breq _Li2c_put_ack_low ;if (ack=0) ;; 5C \n\t"
|
||||
" cbi %[SDADDR],%[SDAPIN] ;release SDA \n\t"
|
||||
" rjmp _Li2c_put_ack_high \n\t"
|
||||
"_Li2c_put_ack_low: ;else \n\t"
|
||||
" sbi %[SDADDR],%[SDAPIN] ;force SDA low ;; 7C \n\t"
|
||||
"_Li2c_put_ack_high: \n\t"
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;; 7C + X \n\t"
|
||||
#endif
|
||||
" cbi %[SCLDDR],%[SCLPIN] ;release SCL ;; 9C +X \n\t"
|
||||
" cln ;clear N ;; +1 = 10C\n\t"
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
" sbis %[SCLIN],%[SCLPIN] ;wait SCL high ;; 12C + X\n\t"
|
||||
" rcall ass_i2c_wait_scl_high \n\t"
|
||||
#if I2C_DELAY_COUNTER >= 1
|
||||
" rcall ass_i2c_delay_half ;delay T/2 ;; 11C + 2X\n\t"
|
||||
#endif
|
||||
"_Li2c_read_return: \n\t"
|
||||
" nop \n\t "
|
||||
" nop \n\t "
|
||||
"sbi %[SCLDDR],%[SCLPIN] ;force SCL low so SCL=H is short\n\t"
|
||||
" mov r24,r23 ;; 12C + 2X \n\t"
|
||||
" clr r25 ;; 13 C + 2X\n\t"
|
||||
" ret ;; 17C + X"
|
||||
::
|
||||
[SCLDDR] "I" (SCL_DDR), [SCLPIN] "I" (SCL_PIN), [SCLIN] "I" (SCL_IN),
|
||||
[SDADDR] "I" (SDA_DDR), [SDAPIN] "I" (SDA_PIN), [SDAIN] "I" (SDA_IN)
|
||||
);
|
||||
return ' '; // fool the compiler!
|
||||
}
|
||||
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// **********************************************************************************
|
||||
// ******************** end of SoftI2CMaster.h here *********************************
|
||||
// **********************************************************************************
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
* RFu-328 + TH02 (hardware I2C)
|
||||
* (c) 2014 flabbergast
|
||||
*
|
||||
* Comments:
|
||||
* - Depends on:
|
||||
* - TH02 library: https://github.com/hallard/TH02
|
||||
* - I2C library: http://www.dsscircuits.com/index.php/articles/66-arduino-i2c-master-library
|
||||
* - RFM69 library: https://github.com/LowPowerLab/RFM69
|
||||
* - LowPower library: https://github.com/rocketscream/Low-Power
|
||||
* - I didn't go too overboard with the power savings, so e.g. battery voltage
|
||||
* is measured on every reading, and TH02 is given on lot of time to power up
|
||||
* and shut down.
|
||||
* - I run it on "RFu developer board" with a 3.3V boost converter, from one
|
||||
* AA battery. So the thing runs on 3.3V, and the battery voltage is measured
|
||||
* ordinarily, via analogRead.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG false // Set to true to enable serial (increases power usage)
|
||||
#define FIRMWARE_VERSION "2.0"
|
||||
|
||||
#define NODE_ID 17 //unique for each node on same network
|
||||
#define GATEWAY_ID 1 // where to send the readings?
|
||||
#define NETWORK_ID 107 //the same on all nodes that talk to each other
|
||||
#define FREQUENCY RF69_868MHZ
|
||||
#define ENCRYPTKEY "beleampanchineto" //exactly the same 16 characters/bytes on all nodes!
|
||||
//#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
|
||||
#define RF_TX_POWER 17 // 0-31, the Tx power is -18+value (max 13) dB
|
||||
|
||||
#define SERIAL_BAUD 115200
|
||||
|
||||
#define EIGHTSEC_SLEEPS 36 // how long to sleep between readings (times 8 sec)?
|
||||
|
||||
// TH02 power is on pin 5
|
||||
#define TH02_POWER_PIN 5
|
||||
|
||||
// Battery '+' is connected to A2
|
||||
#define BATT_PIN A2
|
||||
|
||||
// Optional LED from 9 to GND, blips on sending a reading
|
||||
#define LED_PIN 9
|
||||
#define USE_LED true
|
||||
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#include <RFM69.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#include <LowPower.h>
|
||||
#include <I2C.h>
|
||||
#include <TH02.h>
|
||||
TH02 sensor;
|
||||
|
||||
//##### radio packet structure #####
|
||||
typedef struct { // RFM12B RF payload datastructure
|
||||
int16_t temp;
|
||||
int16_t rh_comp;
|
||||
uint8_t voltage; // it's only one AA, so < 2.55V = (255/100)
|
||||
} Payload;
|
||||
Payload payload;
|
||||
|
||||
void ledOn() {
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
}
|
||||
void ledOff() {
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
pinMode(LED_PIN, INPUT);
|
||||
}
|
||||
|
||||
//RFM69 radio;
|
||||
RFM69 radio(4, 3, false, 1); // CS, interrupt_pin, is_HW, interrupt_num
|
||||
|
||||
void setup() {
|
||||
//################################################################################################################################
|
||||
// Power Save - turn off what we don't need - http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
|
||||
//################################################################################################################################
|
||||
ACSR |= (1 << ACD); // disable Analog comparator
|
||||
if (! DEBUG) power_usart0_disable(); //disable serial UART
|
||||
//power_twi_disable();
|
||||
//power_timer0_disable(); //don't disable: sends the atmega to indefinite sleep immediately for some reason
|
||||
power_timer1_disable();
|
||||
//power_spi_disable();
|
||||
//################################################################################################################################
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
Serial.println("");
|
||||
Serial.print("RFu + TH02 v");
|
||||
Serial.println(FIRMWARE_VERSION);
|
||||
Serial.println("Initializing RFM...");
|
||||
delay(20);
|
||||
}
|
||||
radio.initialize(FREQUENCY,NODE_ID,NETWORK_ID);
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
|
||||
// adjust the TX output to save power
|
||||
radio.setPowerLevel(RF_TX_POWER);
|
||||
|
||||
// sleep
|
||||
radio.sleep();
|
||||
// disable SPI
|
||||
power_spi_disable();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Initialising I2C...");
|
||||
}
|
||||
// initialise i2c
|
||||
I2c.begin();
|
||||
I2c.pullup(true); // Enable I2C Internal pullup
|
||||
I2c.setSpeed(1); // Set I2C to 400Khz.
|
||||
|
||||
// battery status pin is input (should be by default, but...)
|
||||
pinMode(BATT_PIN, INPUT);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// read battery status [boost output measures 3.27V, that's our reference]
|
||||
uint16_t v = (uint16_t)((analogRead(BATT_PIN) * 327.0)/1024.0);
|
||||
payload.voltage = (uint8_t) v;
|
||||
if (DEBUG) {
|
||||
Serial.print("Got battery voltage: ");
|
||||
Serial.println(payload.voltage);
|
||||
}
|
||||
|
||||
// turn on power to TH02
|
||||
pinMode(TH02_POWER_PIN, OUTPUT);
|
||||
digitalWrite(TH02_POWER_PIN, HIGH);
|
||||
delay(15); // wait for TH02 to power up (should be 10ms, max 15ms, but could
|
||||
// be more if not enough current (40mA))
|
||||
|
||||
// turn on I2C module
|
||||
power_twi_enable();
|
||||
// do a temp conversion
|
||||
sensor.startTempConv(true); // true: fastmode
|
||||
sensor.waitEndConversion();
|
||||
|
||||
// process the result
|
||||
sensor.getConversionValue();
|
||||
payload.temp = sensor.getLastRawTemp();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Got temp: ");
|
||||
Serial.println(payload.temp/100.0);
|
||||
}
|
||||
|
||||
// do a humidity conversion
|
||||
sensor.startRHConv(true);
|
||||
sensor.waitEndConversion();
|
||||
|
||||
// read the result (gets saved in sensor object)
|
||||
sensor.getConversionValue();
|
||||
|
||||
// power off TH02
|
||||
digitalWrite(TH02_POWER_PIN, LOW);
|
||||
delay(10); // wait for TH02 to power down (might be sinking 40mA current)
|
||||
pinMode(TH02_POWER_PIN, INPUT); // configure as input (should save a bit of power)
|
||||
|
||||
// compute the compensated humidity
|
||||
payload.rh_comp = sensor.getConpensatedRH(false);
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Got compensated RH: ");
|
||||
Serial.println(payload.rh_comp/100.0);
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Powering up radio and sending the reading...");
|
||||
delay(30);
|
||||
}
|
||||
if (USE_LED) {
|
||||
ledOn();
|
||||
// delay(5); // need a bit of time to see it go on
|
||||
}
|
||||
// power up the SPI bus
|
||||
power_spi_enable();
|
||||
|
||||
// send the reading
|
||||
radio.send(GATEWAY_ID, (const void*)(&payload), sizeof(payload), false); // last param = requestACK
|
||||
// sleep
|
||||
radio.sleep();
|
||||
// disable SPI
|
||||
power_spi_disable();
|
||||
|
||||
if (USE_LED)
|
||||
ledOff();
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Going to sleep.");
|
||||
delay(100);
|
||||
}
|
||||
// sleep for a time
|
||||
uint8_t i;
|
||||
for(i=0; i<EIGHTSEC_SLEEPS; i++) {
|
||||
if(DEBUG) {
|
||||
LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF);
|
||||
} else {
|
||||
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
/*
|
||||
* RFu_RF69_thermistor.ino
|
||||
* (c) 2014 flabbergast
|
||||
* BSD license
|
||||
*
|
||||
* Sketch for running Ciseco's RFu-328, with a RFM69CW soldered, on a XRF thermistor
|
||||
* coin cell board from Ciseco.
|
||||
*
|
||||
* Depends on:
|
||||
* - RFM69 library: https://github.com/LowPowerLab/RFM69
|
||||
* - LowPower library: https://github.com/rocketscream/Low-Power
|
||||
*
|
||||
* Links:
|
||||
* * RFu-328: http://shop.ciseco.co.uk/rf-328-bare-arduino-atmega-328-compatible-micro-board-rfu-328/
|
||||
* * Thermistor coin cell board: http://shop.ciseco.co.uk/temperature-xrf-development-sensor-thermistor/
|
||||
*
|
||||
* Credits:
|
||||
* * based partly on OpenEnergyMonitor's emonTH code: https://github.com/openenergymonitor/emonTH
|
||||
* * measuring internal voltage code from: https://code.google.com/p/tinkerit/wiki/SecretVoltmeter
|
||||
* * Thermistor code from lady Ada: https://learn.adafruit.com/thermistor/using-a-thermistor
|
||||
*
|
||||
* emon: Recommended node ID allocation
|
||||
* ------------------------------------------------------------------------------------------------------------
|
||||
* -ID- -Node Type-
|
||||
* 0 - Special allocation in JeeLib RFM12 driver - reserved for OOK use
|
||||
* 1-4 - Control nodes
|
||||
* 5-10 - Energy monitoring nodes
|
||||
* 11-14 --Un-assigned --
|
||||
* 15-16 - Base Station & logging nodes
|
||||
* 17-30 - Environmental sensing nodes (temperature humidity etc.)
|
||||
* 31 - Special allocation in JeeLib RFM12 driver - Node31 can communicate with nodes on any network group
|
||||
* -------------------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define DEBUG false // Set to true to enable serial (increases power usage)
|
||||
#define FIRMWARE_VERSION "2.0"
|
||||
|
||||
#define NODE_ID 18 //unique for each node on same network
|
||||
#define GATEWAY_ID 1 // where to send the readings?
|
||||
#define NETWORK_ID 107 //the same on all nodes that talk to each other
|
||||
#define FREQUENCY RF69_868MHZ
|
||||
#define ENCRYPTKEY "beleampanchineto" //exactly the same 16 characters/bytes on all nodes!
|
||||
//#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
|
||||
#define RF_TX_POWER 17 // 0-31, the Tx power is -18+value (max 13) dB
|
||||
|
||||
#define EIGHTSEC_SLEEPS 36 // how long to sleep between readings (times 8 sec)?
|
||||
#define SERIAL_BAUD 115200
|
||||
|
||||
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <RFM69.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#include <LowPower.h>
|
||||
|
||||
//RFM69 radio;
|
||||
RFM69 radio(4, 3, false, 1); // CS, interrupt_pin, is_HW, interrupt_num
|
||||
|
||||
//##### radio packet structure #####
|
||||
typedef struct { // RFM12B RF payload datastructure
|
||||
int temp;
|
||||
int battery;
|
||||
} Payload;
|
||||
Payload tempsensor;
|
||||
|
||||
|
||||
//##### get VCC voltage (compare with internal bandgap 1.1V) #####
|
||||
long readVcc() {
|
||||
long result;
|
||||
// Read 1.1V reference against AVcc
|
||||
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
|
||||
delay(2); // Wait for Vref to settle
|
||||
ADCSRA |= _BV(ADSC); // Convert
|
||||
while (bit_is_set(ADCSRA,ADSC));
|
||||
result = ADCL;
|
||||
result |= ADCH<<8;
|
||||
result = 1126400L / result; // Back-calculate AVcc in mV
|
||||
return result;
|
||||
}
|
||||
|
||||
//##### Thermistor function: get temp from raw ADC #####
|
||||
// to which pin is the "power" end of the therm circuit connected (the other is GND)
|
||||
#define THERMISTOR_POWER 9
|
||||
// which analog pin to connect
|
||||
#define THERMISTORPIN A0
|
||||
// resistance at 25 degrees C
|
||||
#define THERMISTORNOMINAL 10000
|
||||
// temp. for nominal resistance (almost always 25 C)
|
||||
#define TEMPERATURENOMINAL 25
|
||||
// how many samples to take and average, more takes longer
|
||||
// but is more 'smooth'
|
||||
#define NUMSAMPLES 5
|
||||
// The beta coefficient of the thermistor (usually 3000-4000)
|
||||
#define BCOEFFICIENT 3977
|
||||
// the value of the 'other' resistor
|
||||
#define SERIESRESISTOR 9950
|
||||
|
||||
int samples[NUMSAMPLES];
|
||||
|
||||
// TODO:
|
||||
// - maybe should use a lookup table, this is too much computational power
|
||||
|
||||
int thermistor_routine() {
|
||||
uint8_t i;
|
||||
float average;
|
||||
|
||||
digitalWrite(THERMISTOR_POWER, HIGH); // power up the thermistor circuit
|
||||
delay(5); // wait for the voltages to settle (necessary? how long?)
|
||||
|
||||
// take N samples in a row, with a slight delay
|
||||
for (i=0; i< NUMSAMPLES; i++) {
|
||||
samples[i] = analogRead(THERMISTORPIN);
|
||||
delay(5);
|
||||
}
|
||||
// power down the thermistor circuit
|
||||
digitalWrite(THERMISTOR_POWER, LOW);
|
||||
|
||||
// average all the samples out
|
||||
average = 0;
|
||||
for (i=0; i< NUMSAMPLES; i++) {
|
||||
average += samples[i];
|
||||
}
|
||||
average /= NUMSAMPLES;
|
||||
|
||||
if (DEBUG) {
|
||||
Serial.print("Average analog reading ");
|
||||
Serial.println(average);
|
||||
}
|
||||
|
||||
// convert the value to resistance
|
||||
average = 1023 / average - 1;
|
||||
average = SERIESRESISTOR / average; // this would be '*' if thermistor is connected to VCC, not GND
|
||||
if (DEBUG) {
|
||||
Serial.print("Thermistor resistance ");
|
||||
Serial.println(average);
|
||||
}
|
||||
|
||||
float steinhart;
|
||||
steinhart = average / THERMISTORNOMINAL; // (R/Ro)
|
||||
steinhart = log(steinhart); // ln(R/Ro)
|
||||
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
|
||||
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
|
||||
steinhart = 1.0 / steinhart; // Invert
|
||||
steinhart -= 273.15; // convert to C
|
||||
|
||||
if (DEBUG) {
|
||||
Serial.print("Temperature ");
|
||||
Serial.print(steinhart);
|
||||
Serial.println(" *C");
|
||||
}
|
||||
|
||||
return((int)1000*steinhart); // return int, 1000 * temperature
|
||||
}
|
||||
|
||||
//################################################################################################################################
|
||||
//################################################################################################################################
|
||||
void setup() {
|
||||
//################################################################################################################################
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
Serial.println("");
|
||||
Serial.println("Initializing RFM...");
|
||||
delay(20);
|
||||
}
|
||||
|
||||
tempsensor.temp=0;
|
||||
|
||||
radio.initialize(FREQUENCY,NODE_ID,NETWORK_ID);
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
|
||||
// Adjust TX power
|
||||
radio.setPowerLevel(RF_TX_POWER);
|
||||
|
||||
// sleep
|
||||
radio.sleep();
|
||||
|
||||
pinMode(THERMISTORPIN, INPUT);
|
||||
pinMode(THERMISTOR_POWER, OUTPUT);
|
||||
digitalWrite(THERMISTOR_POWER, LOW); // turn off the thermistor circuit
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("RFu+RFM69CW+thermistor sensor (by flabbergast); Firmware version ");
|
||||
Serial.println(FIRMWARE_VERSION);
|
||||
Serial.print("Node: ");
|
||||
Serial.print(NODE_ID);
|
||||
Serial.print(" Network group: ");
|
||||
Serial.println(NETWORK_ID);
|
||||
Serial.print(" Battery voltage (V) : ");
|
||||
Serial.println( readVcc() / 1000.0 );
|
||||
Serial.print(" Temperature ('C) : ");
|
||||
Serial.println( thermistor_routine() / 1000.0 );
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
||||
//################################################################################################################################
|
||||
// Power Save - turn off what we don't need - http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html
|
||||
//################################################################################################################################
|
||||
ACSR |= (1 << ACD); // disable Analog comparator
|
||||
if (! DEBUG) power_usart0_disable(); //disable serial UART
|
||||
power_twi_disable();
|
||||
//power_timer0_disable(); //don't disable: sends the atmega to indefinite sleep immediately for some reason
|
||||
power_timer1_disable();
|
||||
power_spi_disable();
|
||||
|
||||
//################################################################################################################################
|
||||
|
||||
dodelay(SLEEP_1S, ADC_OFF, BOD_OFF); // wait a sec
|
||||
|
||||
} // end of setup
|
||||
|
||||
|
||||
//################################################################################################################################
|
||||
//################################################################################################################################
|
||||
void loop() {
|
||||
//################################################################################################################################
|
||||
|
||||
tempsensor.temp = thermistor_routine(); // read the temperature ('C * 1000)
|
||||
tempsensor.battery = readVcc(); // read battery voltage, convert ADC to volts x1000
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Battery voltage (V): ");
|
||||
Serial.println( tempsensor.battery/1000.0 );
|
||||
Serial.print("Temperature ('C): ");
|
||||
Serial.println( tempsensor.temp/1000.0 );
|
||||
|
||||
delay(100);
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("Powering RF up ... ");
|
||||
delay(200);
|
||||
}
|
||||
power_spi_enable();
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.print("sending a reading ... ");
|
||||
delay(50);
|
||||
}
|
||||
radio.send(GATEWAY_ID, (const void*)(&tempsensor), sizeof(tempsensor), false); // last param = ACKrequested
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("back to sleep.");
|
||||
}
|
||||
radio.sleep();
|
||||
power_spi_disable();
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
Serial.println("Going to sleep.");
|
||||
delay(200);
|
||||
}
|
||||
int i;
|
||||
for(i=0; i<EIGHTSEC_SLEEPS; i++) {
|
||||
if (DEBUG) {
|
||||
dodelay(SLEEP_500MS, ADC_OFF, BOD_OFF); // 1/2 sec
|
||||
} else {
|
||||
dodelay(SLEEP_8S, ADC_OFF, BOD_OFF); // 8 secs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dodelay(period_t period, adc_t adc, bod_t bod)
|
||||
{
|
||||
byte oldADCSRA=ADCSRA;
|
||||
byte oldADCSRB=ADCSRB;
|
||||
byte oldADMUX=ADMUX;
|
||||
|
||||
LowPower.powerDown(period, adc, bod);
|
||||
|
||||
ADCSRA=oldADCSRA; // restore ADC state
|
||||
ADCSRB=oldADCSRB;
|
||||
ADMUX=oldADMUX;
|
||||
}
|
||||
|
6
rfu-htu21d/.gitignore
vendored
Normal file
6
rfu-htu21d/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
rfu-htu21d/.travis.yml
Normal file
67
rfu-htu21d/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
rfu-htu21d/.vscode/extensions.json
vendored
Normal file
7
rfu-htu21d/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
rfu-htu21d/include/README
Normal file
39
rfu-htu21d/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
rfu-htu21d/lib/README
Normal file
46
rfu-htu21d/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
18
rfu-htu21d/platformio.ini
Normal file
18
rfu-htu21d/platformio.ini
Normal file
|
@ -0,0 +1,18 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:uno]
|
||||
platform = atmelavr
|
||||
board = uno
|
||||
framework = arduino
|
||||
|
||||
lib_deps =
|
||||
Adafruit HTU21DF Library
|
||||
Low-Power
|
|
@ -51,7 +51,7 @@
|
|||
#define NETWORK_ID 109 //the same on all nodes that talk to each other
|
||||
#define FREQUENCY 8683 //autoscaled to 1-1000
|
||||
#define ENCRYPTKEY "beleampanchineto" //exactly the same 16 characters/bytes on all nodes!
|
||||
#define RF_TX_POWER 21 // 0-31, the Tx power is -18+value (max 13) dB
|
||||
#define RF_TX_POWER 26 // 0-31, the Tx power is -18+value (max 13) dB
|
||||
|
||||
#define SERIAL_BAUD 115200
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
|||
#define LED_PIN 10 // need to wire this somewhere else I suppose
|
||||
#define USE_LED false // for some reason when this is used, the radio hangs - perhaps it's connected to something
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
|
@ -72,7 +73,7 @@
|
|||
RF69<SpiDev4> rf;
|
||||
|
||||
#include <LowPower.h>
|
||||
#include <I2C.h>
|
||||
// #include <I2C.h>
|
||||
|
||||
#include <Wire.h>
|
||||
#include "Adafruit_HTU21DF.h"
|
311
rfu-htu21d/src/rf69.h
Normal file
311
rfu-htu21d/src/rf69.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Native mode RF69 driver.
|
||||
|
||||
#ifndef chThdYield
|
||||
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
|
||||
#endif
|
||||
|
||||
template< typename SPI >
|
||||
class RF69 {
|
||||
public:
|
||||
void init (uint8_t id, uint8_t group, int freq);
|
||||
void encrypt (const char* key);
|
||||
void txPower (uint8_t level);
|
||||
void setHighPower (uint8_t onOff);
|
||||
|
||||
int receive (void* ptr, int len);
|
||||
void send (uint8_t header, const void* ptr, int len);
|
||||
void sleep ();
|
||||
|
||||
int16_t afc;
|
||||
uint8_t rssi;
|
||||
uint8_t lna;
|
||||
uint8_t myId;
|
||||
uint8_t parity;
|
||||
|
||||
uint8_t readReg (uint8_t addr) {
|
||||
return spi.rwReg(addr, 0);
|
||||
}
|
||||
void writeReg (uint8_t addr, uint8_t val) {
|
||||
spi.rwReg(addr | 0x80, val);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
REG_FIFO = 0x00,
|
||||
REG_OPMODE = 0x01,
|
||||
REG_FRFMSB = 0x07,
|
||||
REG_PALEVEL = 0x11,
|
||||
REG_OCP = 0x13,
|
||||
REG_LNAVALUE = 0x18,
|
||||
REG_AFCMSB = 0x1F,
|
||||
REG_AFCLSB = 0x20,
|
||||
REG_FEIMSB = 0x21,
|
||||
REG_FEILSB = 0x22,
|
||||
REG_RSSIVALUE = 0x24,
|
||||
REG_IRQFLAGS1 = 0x27,
|
||||
REG_IRQFLAGS2 = 0x28,
|
||||
REG_SYNCVALUE1 = 0x2F,
|
||||
REG_SYNCVALUE2 = 0x30,
|
||||
REG_SYNCVALUE3 = 0x31,
|
||||
REG_NODEADDR = 0x39,
|
||||
REG_BCASTADDR = 0x3A,
|
||||
REG_FIFOTHRESH = 0x3C,
|
||||
REG_PKTCONFIG2 = 0x3D,
|
||||
REG_AESKEYMSB = 0x3E,
|
||||
REG_TESTPA1 = 0x5A, // only present on RFM69HW/SX1231H
|
||||
REG_TESTPA2 = 0x5C, // only present on RFM69HW/SX1231H
|
||||
|
||||
MODE_SLEEP = 0<<2,
|
||||
MODE_STANDBY = 1<<2,
|
||||
MODE_TRANSMIT = 3<<2,
|
||||
MODE_RECEIVE = 4<<2,
|
||||
|
||||
START_TX = 0xC2,
|
||||
STOP_TX = 0x42,
|
||||
|
||||
RCCALSTART = 0x80,
|
||||
IRQ1_MODEREADY = 1<<7,
|
||||
IRQ1_RXREADY = 1<<6,
|
||||
IRQ1_SYNADDRMATCH = 1<<0,
|
||||
|
||||
IRQ2_FIFONOTEMPTY = 1<<6,
|
||||
IRQ2_PACKETSENT = 1<<3,
|
||||
IRQ2_PAYLOADREADY = 1<<2,
|
||||
};
|
||||
|
||||
void setMode (uint8_t newMode);
|
||||
void configure (const uint8_t* p);
|
||||
void setFrequency (uint32_t freq);
|
||||
void setHighPowerRegs (uint8_t onOff);
|
||||
|
||||
SPI spi;
|
||||
volatile uint8_t mode;
|
||||
uint8_t isRFM69HW;
|
||||
};
|
||||
|
||||
// driver implementation
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setMode (uint8_t newMode) {
|
||||
mode = newMode;
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
|
||||
if (isRFM69HW) {
|
||||
if ( newMode == MODE_TRANSMIT ) {
|
||||
setHighPowerRegs(1);
|
||||
}
|
||||
if ( newMode == MODE_RECEIVE ) {
|
||||
setHighPowerRegs(0);
|
||||
}
|
||||
}
|
||||
while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setFrequency (uint32_t hz) {
|
||||
// accept any frequency scale as input, including KHz and MHz
|
||||
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
|
||||
while (hz < 100000000)
|
||||
hz *= 10;
|
||||
|
||||
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
|
||||
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
|
||||
// due to this, the lower 6 bits of the calculated factor will always be 0
|
||||
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
|
||||
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
|
||||
uint32_t frf = (hz << 2) / (32000000L >> 11);
|
||||
writeReg(REG_FRFMSB, frf >> 10);
|
||||
writeReg(REG_FRFMSB+1, frf >> 2);
|
||||
writeReg(REG_FRFMSB+2, frf << 6);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::configure (const uint8_t* p) {
|
||||
while (true) {
|
||||
uint8_t cmd = p[0];
|
||||
if (cmd == 0)
|
||||
break;
|
||||
writeReg(cmd, p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t configRegs [] = {
|
||||
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
|
||||
0x02, 0x00, // DataModul = packet mode, fsk
|
||||
0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
|
||||
0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
|
||||
0x05, 0x02, // FdevMsb = 45 KHz
|
||||
0x06, 0xE1, // FdevLsb = 45 KHz
|
||||
0x0B, 0x20, // Low M
|
||||
0x19, 0x4A, // RxBw 100 KHz
|
||||
0x1A, 0x42, // AfcBw 125 KHz
|
||||
0x1E, 0x0C, // AfcAutoclearOn, AfcAutoOn
|
||||
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
|
||||
0x26, 0x07, // disable clkout
|
||||
0x29, 0xA0, // RssiThresh -80 dB
|
||||
// 0x2B, 0x40, // RSSI timeout after 128 bytes
|
||||
0x2D, 0x05, // PreambleSize = 5
|
||||
0x2E, 0x90, // SyncConfig = sync on, sync size = 3
|
||||
0x2F, 0xAA, // SyncValue1 = 0xAA
|
||||
0x30, 0x2D, // SyncValue2 = 0x2D ! this is used for group in old jee protocol, but jz4 uses 0x2d hardcoded
|
||||
0x31, 0x2A, // network group, default 42
|
||||
0x37, 0xD0, // PacketConfig1 = fixed, white, no filtering
|
||||
0x38, 0x42, // PayloadLength = 0, unlimited
|
||||
0x3C, 0x8F, // FifoTresh, not empty, level 15
|
||||
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
|
||||
0x6F, 0x20, // TestDagc ...
|
||||
0x71, 0x02, // RegTestAfc
|
||||
0
|
||||
};
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::init (uint8_t id, uint8_t group, int freq) {
|
||||
myId = id;
|
||||
|
||||
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
|
||||
parity = group ^ (group << 4);
|
||||
parity = (parity ^ (parity << 2)) & 0xC0;
|
||||
|
||||
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
|
||||
spi.master(3);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
while (readReg(REG_SYNCVALUE1) != 0xAA);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
while (readReg(REG_SYNCVALUE1) != 0x55);
|
||||
|
||||
configure(configRegs);
|
||||
configure(configRegs); // TODO why is this needed ???
|
||||
setFrequency(freq);
|
||||
|
||||
writeReg(REG_SYNCVALUE3, group);
|
||||
isRFM69HW = 0;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::encrypt (const char* key) {
|
||||
uint8_t cfg = readReg(REG_PKTCONFIG2) & ~0x01;
|
||||
if (key) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
writeReg(REG_AESKEYMSB + i, *key);
|
||||
if (*key != 0)
|
||||
++key;
|
||||
}
|
||||
cfg |= 0x01;
|
||||
}
|
||||
writeReg(REG_PKTCONFIG2, cfg);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::txPower (uint8_t level) {
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & ~0x1F) | level);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPower (uint8_t onOff) { // must call this with RFM69HW!
|
||||
isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, isRFM69HW ? 0x0F : 0x1A);
|
||||
if (isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x40 | 0x20); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x80 ); // enable P0 only
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPowerRegs (uint8_t onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::sleep () {
|
||||
setMode(MODE_SLEEP);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
int RF69<SPI>::receive (void* ptr, int len) {
|
||||
if (mode != MODE_RECEIVE)
|
||||
setMode(MODE_RECEIVE);
|
||||
else {
|
||||
static uint8_t lastFlag;
|
||||
if ((readReg(REG_IRQFLAGS1) & IRQ1_RXREADY) != lastFlag) {
|
||||
lastFlag ^= IRQ1_RXREADY;
|
||||
if (lastFlag) { // flag just went from 0 to 1
|
||||
rssi = readReg(REG_RSSIVALUE);
|
||||
lna = (readReg(REG_LNAVALUE) >> 3) & 0x7;
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_AFCMSB);
|
||||
afc = spi.transfer(0) << 8;
|
||||
afc |= spi.transfer(0);
|
||||
spi.disable();
|
||||
#else
|
||||
afc = readReg(REG_AFCMSB) << 8;
|
||||
afc |= readReg(REG_AFCLSB);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (readReg(REG_IRQFLAGS2) & IRQ2_PAYLOADREADY) {
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO);
|
||||
int count = spi.transfer(0);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = spi.transfer(0);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
spi.disable();
|
||||
#else
|
||||
int count = readReg(REG_FIFO);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = readReg(REG_FIFO);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// only accept packets intended for us, or broadcasts
|
||||
// ... or any packet if we're the special catch-all node
|
||||
uint8_t dest = *(uint8_t*) ptr;
|
||||
if ((dest & 0xC0) == parity) {
|
||||
uint8_t destId = dest & 0x3F;
|
||||
if (destId == myId || destId == 0 || myId == 63)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::send (uint8_t header, const void* ptr, int len) {
|
||||
setMode(MODE_SLEEP);
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO | 0x80);
|
||||
spi.transfer(len + 2);
|
||||
spi.transfer((header & 0x3F) | parity);
|
||||
spi.transfer((header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
spi.transfer(((const uint8_t*) ptr)[i]);
|
||||
spi.disable();
|
||||
#else
|
||||
writeReg(REG_FIFO, len + 2);
|
||||
writeReg(REG_FIFO, (header & 0x3F) | parity);
|
||||
writeReg(REG_FIFO, (header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
writeReg(REG_FIFO, ((const uint8_t*) ptr)[i]);
|
||||
#endif
|
||||
|
||||
setMode(MODE_TRANSMIT);
|
||||
while ((readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) == 0)
|
||||
chThdYield();
|
||||
|
||||
setMode(MODE_STANDBY);
|
||||
}
|
11
rfu-htu21d/test/README
Normal file
11
rfu-htu21d/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
6
rfu-th02/.gitignore
vendored
Normal file
6
rfu-th02/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
rfu-th02/.travis.yml
Normal file
67
rfu-th02/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
rfu-th02/.vscode/extensions.json
vendored
Normal file
7
rfu-th02/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
rfu-th02/include/README
Normal file
39
rfu-th02/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
rfu-th02/lib/README
Normal file
46
rfu-th02/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
18
rfu-th02/platformio.ini
Normal file
18
rfu-th02/platformio.ini
Normal file
|
@ -0,0 +1,18 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:uno]
|
||||
platform = atmelavr
|
||||
board = uno
|
||||
framework = arduino
|
||||
|
||||
lib_deps =
|
||||
Adafruit HTU21DF Library
|
||||
Low-Power
|
|
@ -70,6 +70,7 @@
|
|||
#define LED_PIN 9
|
||||
#define USE_LED true
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
|
311
rfu-th02/src/rf69.h
Normal file
311
rfu-th02/src/rf69.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
// Native mode RF69 driver.
|
||||
|
||||
#ifndef chThdYield
|
||||
#define chThdYield() // FIXME should be renamed, ChibiOS leftover
|
||||
#endif
|
||||
|
||||
template< typename SPI >
|
||||
class RF69 {
|
||||
public:
|
||||
void init (uint8_t id, uint8_t group, int freq);
|
||||
void encrypt (const char* key);
|
||||
void txPower (uint8_t level);
|
||||
void setHighPower (uint8_t onOff);
|
||||
|
||||
int receive (void* ptr, int len);
|
||||
void send (uint8_t header, const void* ptr, int len);
|
||||
void sleep ();
|
||||
|
||||
int16_t afc;
|
||||
uint8_t rssi;
|
||||
uint8_t lna;
|
||||
uint8_t myId;
|
||||
uint8_t parity;
|
||||
|
||||
uint8_t readReg (uint8_t addr) {
|
||||
return spi.rwReg(addr, 0);
|
||||
}
|
||||
void writeReg (uint8_t addr, uint8_t val) {
|
||||
spi.rwReg(addr | 0x80, val);
|
||||
}
|
||||
|
||||
protected:
|
||||
enum {
|
||||
REG_FIFO = 0x00,
|
||||
REG_OPMODE = 0x01,
|
||||
REG_FRFMSB = 0x07,
|
||||
REG_PALEVEL = 0x11,
|
||||
REG_OCP = 0x13,
|
||||
REG_LNAVALUE = 0x18,
|
||||
REG_AFCMSB = 0x1F,
|
||||
REG_AFCLSB = 0x20,
|
||||
REG_FEIMSB = 0x21,
|
||||
REG_FEILSB = 0x22,
|
||||
REG_RSSIVALUE = 0x24,
|
||||
REG_IRQFLAGS1 = 0x27,
|
||||
REG_IRQFLAGS2 = 0x28,
|
||||
REG_SYNCVALUE1 = 0x2F,
|
||||
REG_SYNCVALUE2 = 0x30,
|
||||
REG_SYNCVALUE3 = 0x31,
|
||||
REG_NODEADDR = 0x39,
|
||||
REG_BCASTADDR = 0x3A,
|
||||
REG_FIFOTHRESH = 0x3C,
|
||||
REG_PKTCONFIG2 = 0x3D,
|
||||
REG_AESKEYMSB = 0x3E,
|
||||
REG_TESTPA1 = 0x5A, // only present on RFM69HW/SX1231H
|
||||
REG_TESTPA2 = 0x5C, // only present on RFM69HW/SX1231H
|
||||
|
||||
MODE_SLEEP = 0<<2,
|
||||
MODE_STANDBY = 1<<2,
|
||||
MODE_TRANSMIT = 3<<2,
|
||||
MODE_RECEIVE = 4<<2,
|
||||
|
||||
START_TX = 0xC2,
|
||||
STOP_TX = 0x42,
|
||||
|
||||
RCCALSTART = 0x80,
|
||||
IRQ1_MODEREADY = 1<<7,
|
||||
IRQ1_RXREADY = 1<<6,
|
||||
IRQ1_SYNADDRMATCH = 1<<0,
|
||||
|
||||
IRQ2_FIFONOTEMPTY = 1<<6,
|
||||
IRQ2_PACKETSENT = 1<<3,
|
||||
IRQ2_PAYLOADREADY = 1<<2,
|
||||
};
|
||||
|
||||
void setMode (uint8_t newMode);
|
||||
void configure (const uint8_t* p);
|
||||
void setFrequency (uint32_t freq);
|
||||
void setHighPowerRegs (uint8_t onOff);
|
||||
|
||||
SPI spi;
|
||||
volatile uint8_t mode;
|
||||
uint8_t isRFM69HW;
|
||||
};
|
||||
|
||||
// driver implementation
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setMode (uint8_t newMode) {
|
||||
mode = newMode;
|
||||
writeReg(REG_OPMODE, (readReg(REG_OPMODE) & 0xE3) | newMode);
|
||||
if (isRFM69HW) {
|
||||
if ( newMode == MODE_TRANSMIT ) {
|
||||
setHighPowerRegs(1);
|
||||
}
|
||||
if ( newMode == MODE_RECEIVE ) {
|
||||
setHighPowerRegs(0);
|
||||
}
|
||||
}
|
||||
while ((readReg(REG_IRQFLAGS1) & IRQ1_MODEREADY) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setFrequency (uint32_t hz) {
|
||||
// accept any frequency scale as input, including KHz and MHz
|
||||
// multiply by 10 until freq >= 100 MHz (don't specify 0 as input!)
|
||||
while (hz < 100000000)
|
||||
hz *= 10;
|
||||
|
||||
// Frequency steps are in units of (32,000,000 >> 19) = 61.03515625 Hz
|
||||
// use multiples of 64 to avoid multi-precision arithmetic, i.e. 3906.25 Hz
|
||||
// due to this, the lower 6 bits of the calculated factor will always be 0
|
||||
// this is still 4 ppm, i.e. well below the radio's 32 MHz crystal accuracy
|
||||
// 868.0 MHz = 0xD90000, 868.3 MHz = 0xD91300, 915.0 MHz = 0xE4C000
|
||||
uint32_t frf = (hz << 2) / (32000000L >> 11);
|
||||
writeReg(REG_FRFMSB, frf >> 10);
|
||||
writeReg(REG_FRFMSB+1, frf >> 2);
|
||||
writeReg(REG_FRFMSB+2, frf << 6);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::configure (const uint8_t* p) {
|
||||
while (true) {
|
||||
uint8_t cmd = p[0];
|
||||
if (cmd == 0)
|
||||
break;
|
||||
writeReg(cmd, p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t configRegs [] = {
|
||||
// POR value is better for first rf_sleep 0x01, 0x00, // OpMode = sleep
|
||||
0x02, 0x00, // DataModul = packet mode, fsk
|
||||
0x03, 0x02, // BitRateMsb, data rate = 49,261 khz
|
||||
0x04, 0x8A, // BitRateLsb, divider = 32 MHz / 650
|
||||
0x05, 0x02, // FdevMsb = 45 KHz
|
||||
0x06, 0xE1, // FdevLsb = 45 KHz
|
||||
0x0B, 0x20, // Low M
|
||||
0x19, 0x4A, // RxBw 100 KHz
|
||||
0x1A, 0x42, // AfcBw 125 KHz
|
||||
0x1E, 0x0C, // AfcAutoclearOn, AfcAutoOn
|
||||
//0x25, 0x40, //0x80, // DioMapping1 = SyncAddress (Rx)
|
||||
0x26, 0x07, // disable clkout
|
||||
0x29, 0xA0, // RssiThresh -80 dB
|
||||
// 0x2B, 0x40, // RSSI timeout after 128 bytes
|
||||
0x2D, 0x05, // PreambleSize = 5
|
||||
0x2E, 0x90, // SyncConfig = sync on, sync size = 3
|
||||
0x2F, 0xAA, // SyncValue1 = 0xAA
|
||||
0x30, 0x2D, // SyncValue2 = 0x2D ! this is used for group in old jee protocol, but jz4 uses 0x2d hardcoded
|
||||
0x31, 0x2A, // network group, default 42
|
||||
0x37, 0xD0, // PacketConfig1 = fixed, white, no filtering
|
||||
0x38, 0x42, // PayloadLength = 0, unlimited
|
||||
0x3C, 0x8F, // FifoTresh, not empty, level 15
|
||||
0x3D, 0x12, // 0x10, // PacketConfig2, interpkt = 1, autorxrestart off
|
||||
0x6F, 0x20, // TestDagc ...
|
||||
0x71, 0x02, // RegTestAfc
|
||||
0
|
||||
};
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::init (uint8_t id, uint8_t group, int freq) {
|
||||
myId = id;
|
||||
|
||||
// b7 = group b7^b5^b3^b1, b6 = group b6^b4^b2^b0
|
||||
parity = group ^ (group << 4);
|
||||
parity = (parity ^ (parity << 2)) & 0xC0;
|
||||
|
||||
// 10 MHz, i.e. 30 MHz / 3 (or 4 MHz if clock is still at 12 MHz)
|
||||
spi.master(3);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0xAA);
|
||||
while (readReg(REG_SYNCVALUE1) != 0xAA);
|
||||
do
|
||||
writeReg(REG_SYNCVALUE1, 0x55);
|
||||
while (readReg(REG_SYNCVALUE1) != 0x55);
|
||||
|
||||
configure(configRegs);
|
||||
configure(configRegs); // TODO why is this needed ???
|
||||
setFrequency(freq);
|
||||
|
||||
writeReg(REG_SYNCVALUE3, group);
|
||||
isRFM69HW = 0;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::encrypt (const char* key) {
|
||||
uint8_t cfg = readReg(REG_PKTCONFIG2) & ~0x01;
|
||||
if (key) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
writeReg(REG_AESKEYMSB + i, *key);
|
||||
if (*key != 0)
|
||||
++key;
|
||||
}
|
||||
cfg |= 0x01;
|
||||
}
|
||||
writeReg(REG_PKTCONFIG2, cfg);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::txPower (uint8_t level) {
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & ~0x1F) | level);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPower (uint8_t onOff) { // must call this with RFM69HW!
|
||||
isRFM69HW = onOff;
|
||||
writeReg(REG_OCP, isRFM69HW ? 0x0F : 0x1A);
|
||||
if (isRFM69HW) // turning ON
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x40 | 0x20); // enable P1 & P2 amplifier stages
|
||||
else
|
||||
writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0x1F) | 0x80 ); // enable P0 only
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::setHighPowerRegs (uint8_t onOff) {
|
||||
writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::sleep () {
|
||||
setMode(MODE_SLEEP);
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
int RF69<SPI>::receive (void* ptr, int len) {
|
||||
if (mode != MODE_RECEIVE)
|
||||
setMode(MODE_RECEIVE);
|
||||
else {
|
||||
static uint8_t lastFlag;
|
||||
if ((readReg(REG_IRQFLAGS1) & IRQ1_RXREADY) != lastFlag) {
|
||||
lastFlag ^= IRQ1_RXREADY;
|
||||
if (lastFlag) { // flag just went from 0 to 1
|
||||
rssi = readReg(REG_RSSIVALUE);
|
||||
lna = (readReg(REG_LNAVALUE) >> 3) & 0x7;
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_AFCMSB);
|
||||
afc = spi.transfer(0) << 8;
|
||||
afc |= spi.transfer(0);
|
||||
spi.disable();
|
||||
#else
|
||||
afc = readReg(REG_AFCMSB) << 8;
|
||||
afc |= readReg(REG_AFCLSB);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (readReg(REG_IRQFLAGS2) & IRQ2_PAYLOADREADY) {
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO);
|
||||
int count = spi.transfer(0);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = spi.transfer(0);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
spi.disable();
|
||||
#else
|
||||
int count = readReg(REG_FIFO);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint8_t v = readReg(REG_FIFO);
|
||||
if (i < len)
|
||||
((uint8_t*) ptr)[i] = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// only accept packets intended for us, or broadcasts
|
||||
// ... or any packet if we're the special catch-all node
|
||||
uint8_t dest = *(uint8_t*) ptr;
|
||||
if ((dest & 0xC0) == parity) {
|
||||
uint8_t destId = dest & 0x3F;
|
||||
if (destId == myId || destId == 0 || myId == 63)
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
template< typename SPI >
|
||||
void RF69<SPI>::send (uint8_t header, const void* ptr, int len) {
|
||||
setMode(MODE_SLEEP);
|
||||
|
||||
#if RF69_SPI_BULK
|
||||
spi.enable();
|
||||
spi.transfer(REG_FIFO | 0x80);
|
||||
spi.transfer(len + 2);
|
||||
spi.transfer((header & 0x3F) | parity);
|
||||
spi.transfer((header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
spi.transfer(((const uint8_t*) ptr)[i]);
|
||||
spi.disable();
|
||||
#else
|
||||
writeReg(REG_FIFO, len + 2);
|
||||
writeReg(REG_FIFO, (header & 0x3F) | parity);
|
||||
writeReg(REG_FIFO, (header & 0xC0) | myId);
|
||||
for (int i = 0; i < len; ++i)
|
||||
writeReg(REG_FIFO, ((const uint8_t*) ptr)[i]);
|
||||
#endif
|
||||
|
||||
setMode(MODE_TRANSMIT);
|
||||
while ((readReg(REG_IRQFLAGS2) & IRQ2_PACKETSENT) == 0)
|
||||
chThdYield();
|
||||
|
||||
setMode(MODE_STANDBY);
|
||||
}
|
11
rfu-th02/test/README
Normal file
11
rfu-th02/test/README
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
This directory is intended for PIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
6
rfu-thermistor/.gitignore
vendored
Normal file
6
rfu-thermistor/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.pio
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
67
rfu-thermistor/.travis.yml
Normal file
67
rfu-thermistor/.travis.yml
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Continuous Integration (CI) is the practice, in software
|
||||
# engineering, of merging all developer working copies with a shared mainline
|
||||
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||
#
|
||||
# Documentation:
|
||||
#
|
||||
# * Travis CI Embedded Builds with PlatformIO
|
||||
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||
#
|
||||
# * PlatformIO integration with Travis CI
|
||||
# < https://docs.platformio.org/page/ci/travis.html >
|
||||
#
|
||||
# * User Guide for `platformio ci` command
|
||||
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||
#
|
||||
#
|
||||
# Please choose one of the following templates (proposed below) and uncomment
|
||||
# it (remove "# " before each line) or use own configuration according to the
|
||||
# Travis CI documentation (see above).
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio run
|
||||
|
||||
|
||||
#
|
||||
# Template #2: The project is intended to be used as a library with examples.
|
||||
#
|
||||
|
||||
# language: python
|
||||
# python:
|
||||
# - "2.7"
|
||||
#
|
||||
# sudo: false
|
||||
# cache:
|
||||
# directories:
|
||||
# - "~/.platformio"
|
||||
#
|
||||
# env:
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||
#
|
||||
# install:
|
||||
# - pip install -U platformio
|
||||
# - platformio update
|
||||
#
|
||||
# script:
|
||||
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
7
rfu-thermistor/.vscode/extensions.json
vendored
Normal file
7
rfu-thermistor/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
]
|
||||
}
|
39
rfu-thermistor/include/README
Normal file
39
rfu-thermistor/include/README
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
rfu-thermistor/lib/README
Normal file
46
rfu-thermistor/lib/README
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
17
rfu-thermistor/platformio.ini
Normal file
17
rfu-thermistor/platformio.ini
Normal file
|
@ -0,0 +1,17 @@
|
|||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:uno]
|
||||
platform = atmelavr
|
||||
board = uno
|
||||
framework = arduino
|
||||
|
||||
lib_deps =
|
||||
Low-Power
|
|
@ -45,7 +45,7 @@
|
|||
#define EIGHTSEC_SLEEPS 36 // how long to sleep between readings (times 8 sec)?
|
||||
#define SERIAL_BAUD 115200
|
||||
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <avr/power.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <math.h>
|
||||
|
@ -66,6 +66,10 @@ typedef struct { // RFM12B RF payload datastructure
|
|||
} Payload;
|
||||
Payload tempsensor;
|
||||
|
||||
void dodelay(period_t period, adc_t adc, bod_t bod);
|
||||
long readVcc(void);
|
||||
int thermistor_routine(void);
|
||||
|
||||
|
||||
//##### get VCC voltage (compare with internal bandgap 1.1V) #####
|
||||
long readVcc() {
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue