Switch to platformio.

This commit is contained in:
flabbergast 2019-06-23 18:39:32 +01:00
parent 5ff63b5826
commit 6e668f7f1a
103 changed files with 3759 additions and 2426 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*/.vscode/ipch

View file

@ -1 +0,0 @@
../Gateway/rf69.h

View file

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

View file

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

View file

@ -1 +0,0 @@
../Gateway/rf69.h

View file

@ -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

View file

@ -1 +0,0 @@
../Gateway/rf69.h

View file

@ -1 +0,0 @@
../Gateway/rf69.h

View file

@ -1 +0,0 @@
../Gateway/rf69.h

6
blr-relay/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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
View 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
View 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

View file

@ -1 +0,0 @@
../Gateway/rf69.h

View file

@ -1 +0,0 @@
../Gateway/rf69.h

6
gateway-wemos-d1/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.pio
.pioenvs
.piolibdeps
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json

View 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

View 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"
]
}

View 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

View 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

View 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

View file

@ -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
View 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);
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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)
}

View file

@ -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.

View file

@ -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

View file

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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View file

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

View file

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

View file

@ -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 *********************************
// **********************************************************************************

View file

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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View file

@ -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
View 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
View 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
View file

@ -0,0 +1,6 @@
.pio
.pioenvs
.piolibdeps
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json

View 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

View 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"
]
}

View 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
View 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

View 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

View file

@ -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