Merge branch 'master' into development
Conflicts: libraries/LiquidCrystal/LCD.cpp libraries/LiquidCrystal/LCD.h libraries/MySensors/MySensor.cpp libraries/MySensors/examples/MysensorMicro/MysensorMicro.ino
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
|
||||
#include "Encoder.h"
|
||||
|
||||
// Yes, all the code is in the header file, to provide the user
|
||||
// configure options with #define (before they include it), and
|
||||
// to facilitate some crafty optimizations!
|
||||
|
||||
Encoder_internal_state_t * Encoder::interruptArgs[];
|
||||
|
||||
|
||||
@@ -0,0 +1,942 @@
|
||||
/* Encoder Library, for measuring quadrature encoded signals
|
||||
* http://www.pjrc.com/teensy/td_libs_Encoder.html
|
||||
* Copyright (c) 2011,2013 PJRC.COM, LLC - Paul Stoffregen <paul@pjrc.com>
|
||||
*
|
||||
* Version 1.2 - fix -2 bug in C-only code
|
||||
* Version 1.1 - expand to support boards with up to 60 interrupts
|
||||
* Version 1.0 - initial release
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef Encoder_h_
|
||||
#define Encoder_h_
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#elif defined(WIRING)
|
||||
#include "Wiring.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#include "pins_arduino.h"
|
||||
#endif
|
||||
|
||||
#include "utility/direct_pin_read.h"
|
||||
|
||||
#if defined(ENCODER_USE_INTERRUPTS) || !defined(ENCODER_DO_NOT_USE_INTERRUPTS)
|
||||
#define ENCODER_USE_INTERRUPTS
|
||||
#define ENCODER_ARGLIST_SIZE CORE_NUM_INTERRUPT
|
||||
#include "utility/interrupt_pins.h"
|
||||
#ifdef ENCODER_OPTIMIZE_INTERRUPTS
|
||||
#include "utility/interrupt_config.h"
|
||||
#endif
|
||||
#else
|
||||
#define ENCODER_ARGLIST_SIZE 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// All the data needed by interrupts is consolidated into this ugly struct
|
||||
// to facilitate assembly language optimizing of the speed critical update.
|
||||
// The assembly code uses auto-incrementing addressing modes, so the struct
|
||||
// must remain in exactly this order.
|
||||
typedef struct {
|
||||
volatile IO_REG_TYPE * pin1_register;
|
||||
volatile IO_REG_TYPE * pin2_register;
|
||||
IO_REG_TYPE pin1_bitmask;
|
||||
IO_REG_TYPE pin2_bitmask;
|
||||
uint8_t state;
|
||||
int32_t position;
|
||||
} Encoder_internal_state_t;
|
||||
|
||||
class Encoder
|
||||
{
|
||||
public:
|
||||
Encoder(uint8_t pin1, uint8_t pin2) {
|
||||
#ifdef INPUT_PULLUP
|
||||
pinMode(pin1, INPUT_PULLUP);
|
||||
pinMode(pin2, INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(pin1, INPUT);
|
||||
digitalWrite(pin1, HIGH);
|
||||
pinMode(pin2, INPUT);
|
||||
digitalWrite(pin2, HIGH);
|
||||
#endif
|
||||
encoder.pin1_register = PIN_TO_BASEREG(pin1);
|
||||
encoder.pin1_bitmask = PIN_TO_BITMASK(pin1);
|
||||
encoder.pin2_register = PIN_TO_BASEREG(pin2);
|
||||
encoder.pin2_bitmask = PIN_TO_BITMASK(pin2);
|
||||
encoder.position = 0;
|
||||
// allow time for a passive R-C filter to charge
|
||||
// through the pullup resistors, before reading
|
||||
// the initial state
|
||||
delayMicroseconds(2000);
|
||||
uint8_t s = 0;
|
||||
if (DIRECT_PIN_READ(encoder.pin1_register, encoder.pin1_bitmask)) s |= 1;
|
||||
if (DIRECT_PIN_READ(encoder.pin2_register, encoder.pin2_bitmask)) s |= 2;
|
||||
encoder.state = s;
|
||||
#ifdef ENCODER_USE_INTERRUPTS
|
||||
interrupts_in_use = attach_interrupt(pin1, &encoder);
|
||||
interrupts_in_use += attach_interrupt(pin2, &encoder);
|
||||
#endif
|
||||
//update_finishup(); // to force linker to include the code (does not work)
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENCODER_USE_INTERRUPTS
|
||||
inline int32_t read() {
|
||||
if (interrupts_in_use < 2) {
|
||||
noInterrupts();
|
||||
update(&encoder);
|
||||
} else {
|
||||
noInterrupts();
|
||||
}
|
||||
int32_t ret = encoder.position;
|
||||
interrupts();
|
||||
return ret;
|
||||
}
|
||||
inline void write(int32_t p) {
|
||||
noInterrupts();
|
||||
encoder.position = p;
|
||||
interrupts();
|
||||
}
|
||||
#else
|
||||
inline int32_t read() {
|
||||
update(&encoder);
|
||||
return encoder.position;
|
||||
}
|
||||
inline void write(int32_t p) {
|
||||
encoder.position = p;
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
Encoder_internal_state_t encoder;
|
||||
#ifdef ENCODER_USE_INTERRUPTS
|
||||
uint8_t interrupts_in_use;
|
||||
#endif
|
||||
public:
|
||||
static Encoder_internal_state_t * interruptArgs[ENCODER_ARGLIST_SIZE];
|
||||
|
||||
// _______ _______
|
||||
// Pin1 ______| |_______| |______ Pin1
|
||||
// negative <--- _______ _______ __ --> positive
|
||||
// Pin2 __| |_______| |_______| Pin2
|
||||
|
||||
// new new old old
|
||||
// pin2 pin1 pin2 pin1 Result
|
||||
// ---- ---- ---- ---- ------
|
||||
// 0 0 0 0 no movement
|
||||
// 0 0 0 1 +1
|
||||
// 0 0 1 0 -1
|
||||
// 0 0 1 1 +2 (assume pin1 edges only)
|
||||
// 0 1 0 0 -1
|
||||
// 0 1 0 1 no movement
|
||||
// 0 1 1 0 -2 (assume pin1 edges only)
|
||||
// 0 1 1 1 +1
|
||||
// 1 0 0 0 +1
|
||||
// 1 0 0 1 -2 (assume pin1 edges only)
|
||||
// 1 0 1 0 no movement
|
||||
// 1 0 1 1 -1
|
||||
// 1 1 0 0 +2 (assume pin1 edges only)
|
||||
// 1 1 0 1 -1
|
||||
// 1 1 1 0 +1
|
||||
// 1 1 1 1 no movement
|
||||
/*
|
||||
// Simple, easy-to-read "documentation" version :-)
|
||||
//
|
||||
void update(void) {
|
||||
uint8_t s = state & 3;
|
||||
if (digitalRead(pin1)) s |= 4;
|
||||
if (digitalRead(pin2)) s |= 8;
|
||||
switch (s) {
|
||||
case 0: case 5: case 10: case 15:
|
||||
break;
|
||||
case 1: case 7: case 8: case 14:
|
||||
position++; break;
|
||||
case 2: case 4: case 11: case 13:
|
||||
position--; break;
|
||||
case 3: case 12:
|
||||
position += 2; break;
|
||||
default:
|
||||
position -= 2; break;
|
||||
}
|
||||
state = (s >> 2);
|
||||
}
|
||||
*/
|
||||
|
||||
private:
|
||||
static void update(Encoder_internal_state_t *arg) {
|
||||
#if defined(__AVR__)
|
||||
// The compiler believes this is just 1 line of code, so
|
||||
// it will inline this function into each interrupt
|
||||
// handler. That's a tiny bit faster, but grows the code.
|
||||
// Especially when used with ENCODER_OPTIMIZE_INTERRUPTS,
|
||||
// the inline nature allows the ISR prologue and epilogue
|
||||
// to only save/restore necessary registers, for very nice
|
||||
// speed increase.
|
||||
asm volatile (
|
||||
"ld r30, X+" "\n\t"
|
||||
"ld r31, X+" "\n\t"
|
||||
"ld r24, Z" "\n\t" // r24 = pin1 input
|
||||
"ld r30, X+" "\n\t"
|
||||
"ld r31, X+" "\n\t"
|
||||
"ld r25, Z" "\n\t" // r25 = pin2 input
|
||||
"ld r30, X+" "\n\t" // r30 = pin1 mask
|
||||
"ld r31, X+" "\n\t" // r31 = pin2 mask
|
||||
"ld r22, X" "\n\t" // r22 = state
|
||||
"andi r22, 3" "\n\t"
|
||||
"and r24, r30" "\n\t"
|
||||
"breq L%=1" "\n\t" // if (pin1)
|
||||
"ori r22, 4" "\n\t" // state |= 4
|
||||
"L%=1:" "and r25, r31" "\n\t"
|
||||
"breq L%=2" "\n\t" // if (pin2)
|
||||
"ori r22, 8" "\n\t" // state |= 8
|
||||
"L%=2:" "ldi r30, lo8(pm(L%=table))" "\n\t"
|
||||
"ldi r31, hi8(pm(L%=table))" "\n\t"
|
||||
"add r30, r22" "\n\t"
|
||||
"adc r31, __zero_reg__" "\n\t"
|
||||
"asr r22" "\n\t"
|
||||
"asr r22" "\n\t"
|
||||
"st X+, r22" "\n\t" // store new state
|
||||
"ld r22, X+" "\n\t"
|
||||
"ld r23, X+" "\n\t"
|
||||
"ld r24, X+" "\n\t"
|
||||
"ld r25, X+" "\n\t"
|
||||
"ijmp" "\n\t" // jumps to update_finishup()
|
||||
// TODO move this table to another static function,
|
||||
// so it doesn't get needlessly duplicated. Easier
|
||||
// said than done, due to linker issues and inlining
|
||||
"L%=table:" "\n\t"
|
||||
"rjmp L%=end" "\n\t" // 0
|
||||
"rjmp L%=plus1" "\n\t" // 1
|
||||
"rjmp L%=minus1" "\n\t" // 2
|
||||
"rjmp L%=plus2" "\n\t" // 3
|
||||
"rjmp L%=minus1" "\n\t" // 4
|
||||
"rjmp L%=end" "\n\t" // 5
|
||||
"rjmp L%=minus2" "\n\t" // 6
|
||||
"rjmp L%=plus1" "\n\t" // 7
|
||||
"rjmp L%=plus1" "\n\t" // 8
|
||||
"rjmp L%=minus2" "\n\t" // 9
|
||||
"rjmp L%=end" "\n\t" // 10
|
||||
"rjmp L%=minus1" "\n\t" // 11
|
||||
"rjmp L%=plus2" "\n\t" // 12
|
||||
"rjmp L%=minus1" "\n\t" // 13
|
||||
"rjmp L%=plus1" "\n\t" // 14
|
||||
"rjmp L%=end" "\n\t" // 15
|
||||
"L%=minus2:" "\n\t"
|
||||
"subi r22, 2" "\n\t"
|
||||
"sbci r23, 0" "\n\t"
|
||||
"sbci r24, 0" "\n\t"
|
||||
"sbci r25, 0" "\n\t"
|
||||
"rjmp L%=store" "\n\t"
|
||||
"L%=minus1:" "\n\t"
|
||||
"subi r22, 1" "\n\t"
|
||||
"sbci r23, 0" "\n\t"
|
||||
"sbci r24, 0" "\n\t"
|
||||
"sbci r25, 0" "\n\t"
|
||||
"rjmp L%=store" "\n\t"
|
||||
"L%=plus2:" "\n\t"
|
||||
"subi r22, 254" "\n\t"
|
||||
"rjmp L%=z" "\n\t"
|
||||
"L%=plus1:" "\n\t"
|
||||
"subi r22, 255" "\n\t"
|
||||
"L%=z:" "sbci r23, 255" "\n\t"
|
||||
"sbci r24, 255" "\n\t"
|
||||
"sbci r25, 255" "\n\t"
|
||||
"L%=store:" "\n\t"
|
||||
"st -X, r25" "\n\t"
|
||||
"st -X, r24" "\n\t"
|
||||
"st -X, r23" "\n\t"
|
||||
"st -X, r22" "\n\t"
|
||||
"L%=end:" "\n"
|
||||
: : "x" (arg) : "r22", "r23", "r24", "r25", "r30", "r31");
|
||||
#else
|
||||
uint8_t p1val = DIRECT_PIN_READ(arg->pin1_register, arg->pin1_bitmask);
|
||||
uint8_t p2val = DIRECT_PIN_READ(arg->pin2_register, arg->pin2_bitmask);
|
||||
uint8_t state = arg->state & 3;
|
||||
if (p1val) state |= 4;
|
||||
if (p2val) state |= 8;
|
||||
arg->state = (state >> 2);
|
||||
switch (state) {
|
||||
case 1: case 7: case 8: case 14:
|
||||
arg->position++;
|
||||
return;
|
||||
case 2: case 4: case 11: case 13:
|
||||
arg->position--;
|
||||
return;
|
||||
case 3: case 12:
|
||||
arg->position += 2;
|
||||
return;
|
||||
case 6: case 9:
|
||||
arg->position -= 2;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
#if defined(__AVR__)
|
||||
// TODO: this must be a no inline function
|
||||
// even noinline does not seem to solve difficult
|
||||
// problems with this. Oh well, it was only meant
|
||||
// to shrink code size - there's no performance
|
||||
// improvement in this, only code size reduction.
|
||||
__attribute__((noinline)) void update_finishup(void) {
|
||||
asm volatile (
|
||||
"ldi r30, lo8(pm(Ltable))" "\n\t"
|
||||
"ldi r31, hi8(pm(Ltable))" "\n\t"
|
||||
"Ltable:" "\n\t"
|
||||
"rjmp L%=end" "\n\t" // 0
|
||||
"rjmp L%=plus1" "\n\t" // 1
|
||||
"rjmp L%=minus1" "\n\t" // 2
|
||||
"rjmp L%=plus2" "\n\t" // 3
|
||||
"rjmp L%=minus1" "\n\t" // 4
|
||||
"rjmp L%=end" "\n\t" // 5
|
||||
"rjmp L%=minus2" "\n\t" // 6
|
||||
"rjmp L%=plus1" "\n\t" // 7
|
||||
"rjmp L%=plus1" "\n\t" // 8
|
||||
"rjmp L%=minus2" "\n\t" // 9
|
||||
"rjmp L%=end" "\n\t" // 10
|
||||
"rjmp L%=minus1" "\n\t" // 11
|
||||
"rjmp L%=plus2" "\n\t" // 12
|
||||
"rjmp L%=minus1" "\n\t" // 13
|
||||
"rjmp L%=plus1" "\n\t" // 14
|
||||
"rjmp L%=end" "\n\t" // 15
|
||||
"L%=minus2:" "\n\t"
|
||||
"subi r22, 2" "\n\t"
|
||||
"sbci r23, 0" "\n\t"
|
||||
"sbci r24, 0" "\n\t"
|
||||
"sbci r25, 0" "\n\t"
|
||||
"rjmp L%=store" "\n\t"
|
||||
"L%=minus1:" "\n\t"
|
||||
"subi r22, 1" "\n\t"
|
||||
"sbci r23, 0" "\n\t"
|
||||
"sbci r24, 0" "\n\t"
|
||||
"sbci r25, 0" "\n\t"
|
||||
"rjmp L%=store" "\n\t"
|
||||
"L%=plus2:" "\n\t"
|
||||
"subi r22, 254" "\n\t"
|
||||
"rjmp L%=z" "\n\t"
|
||||
"L%=plus1:" "\n\t"
|
||||
"subi r22, 255" "\n\t"
|
||||
"L%=z:" "sbci r23, 255" "\n\t"
|
||||
"sbci r24, 255" "\n\t"
|
||||
"sbci r25, 255" "\n\t"
|
||||
"L%=store:" "\n\t"
|
||||
"st -X, r25" "\n\t"
|
||||
"st -X, r24" "\n\t"
|
||||
"st -X, r23" "\n\t"
|
||||
"st -X, r22" "\n\t"
|
||||
"L%=end:" "\n"
|
||||
: : : "r22", "r23", "r24", "r25", "r30", "r31");
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
|
||||
#ifdef ENCODER_USE_INTERRUPTS
|
||||
// this giant function is an unfortunate consequence of Arduino's
|
||||
// attachInterrupt function not supporting any way to pass a pointer
|
||||
// or other context to the attached function.
|
||||
static uint8_t attach_interrupt(uint8_t pin, Encoder_internal_state_t *state) {
|
||||
switch (pin) {
|
||||
#ifdef CORE_INT0_PIN
|
||||
case CORE_INT0_PIN:
|
||||
interruptArgs[0] = state;
|
||||
attachInterrupt(0, isr0, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT1_PIN
|
||||
case CORE_INT1_PIN:
|
||||
interruptArgs[1] = state;
|
||||
attachInterrupt(1, isr1, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT2_PIN
|
||||
case CORE_INT2_PIN:
|
||||
interruptArgs[2] = state;
|
||||
attachInterrupt(2, isr2, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT3_PIN
|
||||
case CORE_INT3_PIN:
|
||||
interruptArgs[3] = state;
|
||||
attachInterrupt(3, isr3, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT4_PIN
|
||||
case CORE_INT4_PIN:
|
||||
interruptArgs[4] = state;
|
||||
attachInterrupt(4, isr4, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT5_PIN
|
||||
case CORE_INT5_PIN:
|
||||
interruptArgs[5] = state;
|
||||
attachInterrupt(5, isr5, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT6_PIN
|
||||
case CORE_INT6_PIN:
|
||||
interruptArgs[6] = state;
|
||||
attachInterrupt(6, isr6, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT7_PIN
|
||||
case CORE_INT7_PIN:
|
||||
interruptArgs[7] = state;
|
||||
attachInterrupt(7, isr7, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT8_PIN
|
||||
case CORE_INT8_PIN:
|
||||
interruptArgs[8] = state;
|
||||
attachInterrupt(8, isr8, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT9_PIN
|
||||
case CORE_INT9_PIN:
|
||||
interruptArgs[9] = state;
|
||||
attachInterrupt(9, isr9, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT10_PIN
|
||||
case CORE_INT10_PIN:
|
||||
interruptArgs[10] = state;
|
||||
attachInterrupt(10, isr10, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT11_PIN
|
||||
case CORE_INT11_PIN:
|
||||
interruptArgs[11] = state;
|
||||
attachInterrupt(11, isr11, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT12_PIN
|
||||
case CORE_INT12_PIN:
|
||||
interruptArgs[12] = state;
|
||||
attachInterrupt(12, isr12, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT13_PIN
|
||||
case CORE_INT13_PIN:
|
||||
interruptArgs[13] = state;
|
||||
attachInterrupt(13, isr13, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT14_PIN
|
||||
case CORE_INT14_PIN:
|
||||
interruptArgs[14] = state;
|
||||
attachInterrupt(14, isr14, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT15_PIN
|
||||
case CORE_INT15_PIN:
|
||||
interruptArgs[15] = state;
|
||||
attachInterrupt(15, isr15, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT16_PIN
|
||||
case CORE_INT16_PIN:
|
||||
interruptArgs[16] = state;
|
||||
attachInterrupt(16, isr16, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT17_PIN
|
||||
case CORE_INT17_PIN:
|
||||
interruptArgs[17] = state;
|
||||
attachInterrupt(17, isr17, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT18_PIN
|
||||
case CORE_INT18_PIN:
|
||||
interruptArgs[18] = state;
|
||||
attachInterrupt(18, isr18, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT19_PIN
|
||||
case CORE_INT19_PIN:
|
||||
interruptArgs[19] = state;
|
||||
attachInterrupt(19, isr19, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT20_PIN
|
||||
case CORE_INT20_PIN:
|
||||
interruptArgs[20] = state;
|
||||
attachInterrupt(20, isr20, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT21_PIN
|
||||
case CORE_INT21_PIN:
|
||||
interruptArgs[21] = state;
|
||||
attachInterrupt(21, isr21, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT22_PIN
|
||||
case CORE_INT22_PIN:
|
||||
interruptArgs[22] = state;
|
||||
attachInterrupt(22, isr22, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT23_PIN
|
||||
case CORE_INT23_PIN:
|
||||
interruptArgs[23] = state;
|
||||
attachInterrupt(23, isr23, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT24_PIN
|
||||
case CORE_INT24_PIN:
|
||||
interruptArgs[24] = state;
|
||||
attachInterrupt(24, isr24, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT25_PIN
|
||||
case CORE_INT25_PIN:
|
||||
interruptArgs[25] = state;
|
||||
attachInterrupt(25, isr25, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT26_PIN
|
||||
case CORE_INT26_PIN:
|
||||
interruptArgs[26] = state;
|
||||
attachInterrupt(26, isr26, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT27_PIN
|
||||
case CORE_INT27_PIN:
|
||||
interruptArgs[27] = state;
|
||||
attachInterrupt(27, isr27, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT28_PIN
|
||||
case CORE_INT28_PIN:
|
||||
interruptArgs[28] = state;
|
||||
attachInterrupt(28, isr28, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT29_PIN
|
||||
case CORE_INT29_PIN:
|
||||
interruptArgs[29] = state;
|
||||
attachInterrupt(29, isr29, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CORE_INT30_PIN
|
||||
case CORE_INT30_PIN:
|
||||
interruptArgs[30] = state;
|
||||
attachInterrupt(30, isr30, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT31_PIN
|
||||
case CORE_INT31_PIN:
|
||||
interruptArgs[31] = state;
|
||||
attachInterrupt(31, isr31, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT32_PIN
|
||||
case CORE_INT32_PIN:
|
||||
interruptArgs[32] = state;
|
||||
attachInterrupt(32, isr32, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT33_PIN
|
||||
case CORE_INT33_PIN:
|
||||
interruptArgs[33] = state;
|
||||
attachInterrupt(33, isr33, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT34_PIN
|
||||
case CORE_INT34_PIN:
|
||||
interruptArgs[34] = state;
|
||||
attachInterrupt(34, isr34, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT35_PIN
|
||||
case CORE_INT35_PIN:
|
||||
interruptArgs[35] = state;
|
||||
attachInterrupt(35, isr35, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT36_PIN
|
||||
case CORE_INT36_PIN:
|
||||
interruptArgs[36] = state;
|
||||
attachInterrupt(36, isr36, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT37_PIN
|
||||
case CORE_INT37_PIN:
|
||||
interruptArgs[37] = state;
|
||||
attachInterrupt(37, isr37, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT38_PIN
|
||||
case CORE_INT38_PIN:
|
||||
interruptArgs[38] = state;
|
||||
attachInterrupt(38, isr38, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT39_PIN
|
||||
case CORE_INT39_PIN:
|
||||
interruptArgs[39] = state;
|
||||
attachInterrupt(39, isr39, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT40_PIN
|
||||
case CORE_INT40_PIN:
|
||||
interruptArgs[40] = state;
|
||||
attachInterrupt(40, isr40, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT41_PIN
|
||||
case CORE_INT41_PIN:
|
||||
interruptArgs[41] = state;
|
||||
attachInterrupt(41, isr41, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT42_PIN
|
||||
case CORE_INT42_PIN:
|
||||
interruptArgs[42] = state;
|
||||
attachInterrupt(42, isr42, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT43_PIN
|
||||
case CORE_INT43_PIN:
|
||||
interruptArgs[43] = state;
|
||||
attachInterrupt(43, isr43, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT44_PIN
|
||||
case CORE_INT44_PIN:
|
||||
interruptArgs[44] = state;
|
||||
attachInterrupt(44, isr44, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT45_PIN
|
||||
case CORE_INT45_PIN:
|
||||
interruptArgs[45] = state;
|
||||
attachInterrupt(45, isr45, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT46_PIN
|
||||
case CORE_INT46_PIN:
|
||||
interruptArgs[46] = state;
|
||||
attachInterrupt(46, isr46, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT47_PIN
|
||||
case CORE_INT47_PIN:
|
||||
interruptArgs[47] = state;
|
||||
attachInterrupt(47, isr47, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT48_PIN
|
||||
case CORE_INT48_PIN:
|
||||
interruptArgs[48] = state;
|
||||
attachInterrupt(48, isr48, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT49_PIN
|
||||
case CORE_INT49_PIN:
|
||||
interruptArgs[49] = state;
|
||||
attachInterrupt(49, isr49, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT50_PIN
|
||||
case CORE_INT50_PIN:
|
||||
interruptArgs[50] = state;
|
||||
attachInterrupt(50, isr50, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT51_PIN
|
||||
case CORE_INT51_PIN:
|
||||
interruptArgs[51] = state;
|
||||
attachInterrupt(51, isr51, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT52_PIN
|
||||
case CORE_INT52_PIN:
|
||||
interruptArgs[52] = state;
|
||||
attachInterrupt(52, isr52, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT53_PIN
|
||||
case CORE_INT53_PIN:
|
||||
interruptArgs[53] = state;
|
||||
attachInterrupt(53, isr53, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT54_PIN
|
||||
case CORE_INT54_PIN:
|
||||
interruptArgs[54] = state;
|
||||
attachInterrupt(54, isr54, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT55_PIN
|
||||
case CORE_INT55_PIN:
|
||||
interruptArgs[55] = state;
|
||||
attachInterrupt(55, isr55, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT56_PIN
|
||||
case CORE_INT56_PIN:
|
||||
interruptArgs[56] = state;
|
||||
attachInterrupt(56, isr56, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT57_PIN
|
||||
case CORE_INT57_PIN:
|
||||
interruptArgs[57] = state;
|
||||
attachInterrupt(57, isr57, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT58_PIN
|
||||
case CORE_INT58_PIN:
|
||||
interruptArgs[58] = state;
|
||||
attachInterrupt(58, isr58, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CORE_INT59_PIN
|
||||
case CORE_INT59_PIN:
|
||||
interruptArgs[59] = state;
|
||||
attachInterrupt(59, isr59, CHANGE);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif // ENCODER_USE_INTERRUPTS
|
||||
|
||||
|
||||
#if defined(ENCODER_USE_INTERRUPTS) && !defined(ENCODER_OPTIMIZE_INTERRUPTS)
|
||||
#ifdef CORE_INT0_PIN
|
||||
static void isr0(void) { update(interruptArgs[0]); }
|
||||
#endif
|
||||
#ifdef CORE_INT1_PIN
|
||||
static void isr1(void) { update(interruptArgs[1]); }
|
||||
#endif
|
||||
#ifdef CORE_INT2_PIN
|
||||
static void isr2(void) { update(interruptArgs[2]); }
|
||||
#endif
|
||||
#ifdef CORE_INT3_PIN
|
||||
static void isr3(void) { update(interruptArgs[3]); }
|
||||
#endif
|
||||
#ifdef CORE_INT4_PIN
|
||||
static void isr4(void) { update(interruptArgs[4]); }
|
||||
#endif
|
||||
#ifdef CORE_INT5_PIN
|
||||
static void isr5(void) { update(interruptArgs[5]); }
|
||||
#endif
|
||||
#ifdef CORE_INT6_PIN
|
||||
static void isr6(void) { update(interruptArgs[6]); }
|
||||
#endif
|
||||
#ifdef CORE_INT7_PIN
|
||||
static void isr7(void) { update(interruptArgs[7]); }
|
||||
#endif
|
||||
#ifdef CORE_INT8_PIN
|
||||
static void isr8(void) { update(interruptArgs[8]); }
|
||||
#endif
|
||||
#ifdef CORE_INT9_PIN
|
||||
static void isr9(void) { update(interruptArgs[9]); }
|
||||
#endif
|
||||
#ifdef CORE_INT10_PIN
|
||||
static void isr10(void) { update(interruptArgs[10]); }
|
||||
#endif
|
||||
#ifdef CORE_INT11_PIN
|
||||
static void isr11(void) { update(interruptArgs[11]); }
|
||||
#endif
|
||||
#ifdef CORE_INT12_PIN
|
||||
static void isr12(void) { update(interruptArgs[12]); }
|
||||
#endif
|
||||
#ifdef CORE_INT13_PIN
|
||||
static void isr13(void) { update(interruptArgs[13]); }
|
||||
#endif
|
||||
#ifdef CORE_INT14_PIN
|
||||
static void isr14(void) { update(interruptArgs[14]); }
|
||||
#endif
|
||||
#ifdef CORE_INT15_PIN
|
||||
static void isr15(void) { update(interruptArgs[15]); }
|
||||
#endif
|
||||
#ifdef CORE_INT16_PIN
|
||||
static void isr16(void) { update(interruptArgs[16]); }
|
||||
#endif
|
||||
#ifdef CORE_INT17_PIN
|
||||
static void isr17(void) { update(interruptArgs[17]); }
|
||||
#endif
|
||||
#ifdef CORE_INT18_PIN
|
||||
static void isr18(void) { update(interruptArgs[18]); }
|
||||
#endif
|
||||
#ifdef CORE_INT19_PIN
|
||||
static void isr19(void) { update(interruptArgs[19]); }
|
||||
#endif
|
||||
#ifdef CORE_INT20_PIN
|
||||
static void isr20(void) { update(interruptArgs[20]); }
|
||||
#endif
|
||||
#ifdef CORE_INT21_PIN
|
||||
static void isr21(void) { update(interruptArgs[21]); }
|
||||
#endif
|
||||
#ifdef CORE_INT22_PIN
|
||||
static void isr22(void) { update(interruptArgs[22]); }
|
||||
#endif
|
||||
#ifdef CORE_INT23_PIN
|
||||
static void isr23(void) { update(interruptArgs[23]); }
|
||||
#endif
|
||||
#ifdef CORE_INT24_PIN
|
||||
static void isr24(void) { update(interruptArgs[24]); }
|
||||
#endif
|
||||
#ifdef CORE_INT25_PIN
|
||||
static void isr25(void) { update(interruptArgs[25]); }
|
||||
#endif
|
||||
#ifdef CORE_INT26_PIN
|
||||
static void isr26(void) { update(interruptArgs[26]); }
|
||||
#endif
|
||||
#ifdef CORE_INT27_PIN
|
||||
static void isr27(void) { update(interruptArgs[27]); }
|
||||
#endif
|
||||
#ifdef CORE_INT28_PIN
|
||||
static void isr28(void) { update(interruptArgs[28]); }
|
||||
#endif
|
||||
#ifdef CORE_INT29_PIN
|
||||
static void isr29(void) { update(interruptArgs[29]); }
|
||||
#endif
|
||||
#ifdef CORE_INT30_PIN
|
||||
static void isr30(void) { update(interruptArgs[30]); }
|
||||
#endif
|
||||
#ifdef CORE_INT31_PIN
|
||||
static void isr31(void) { update(interruptArgs[31]); }
|
||||
#endif
|
||||
#ifdef CORE_INT32_PIN
|
||||
static void isr32(void) { update(interruptArgs[32]); }
|
||||
#endif
|
||||
#ifdef CORE_INT33_PIN
|
||||
static void isr33(void) { update(interruptArgs[33]); }
|
||||
#endif
|
||||
#ifdef CORE_INT34_PIN
|
||||
static void isr34(void) { update(interruptArgs[34]); }
|
||||
#endif
|
||||
#ifdef CORE_INT35_PIN
|
||||
static void isr35(void) { update(interruptArgs[35]); }
|
||||
#endif
|
||||
#ifdef CORE_INT36_PIN
|
||||
static void isr36(void) { update(interruptArgs[36]); }
|
||||
#endif
|
||||
#ifdef CORE_INT37_PIN
|
||||
static void isr37(void) { update(interruptArgs[37]); }
|
||||
#endif
|
||||
#ifdef CORE_INT38_PIN
|
||||
static void isr38(void) { update(interruptArgs[38]); }
|
||||
#endif
|
||||
#ifdef CORE_INT39_PIN
|
||||
static void isr39(void) { update(interruptArgs[39]); }
|
||||
#endif
|
||||
#ifdef CORE_INT40_PIN
|
||||
static void isr40(void) { update(interruptArgs[40]); }
|
||||
#endif
|
||||
#ifdef CORE_INT41_PIN
|
||||
static void isr41(void) { update(interruptArgs[41]); }
|
||||
#endif
|
||||
#ifdef CORE_INT42_PIN
|
||||
static void isr42(void) { update(interruptArgs[42]); }
|
||||
#endif
|
||||
#ifdef CORE_INT43_PIN
|
||||
static void isr43(void) { update(interruptArgs[43]); }
|
||||
#endif
|
||||
#ifdef CORE_INT44_PIN
|
||||
static void isr44(void) { update(interruptArgs[44]); }
|
||||
#endif
|
||||
#ifdef CORE_INT45_PIN
|
||||
static void isr45(void) { update(interruptArgs[45]); }
|
||||
#endif
|
||||
#ifdef CORE_INT46_PIN
|
||||
static void isr46(void) { update(interruptArgs[46]); }
|
||||
#endif
|
||||
#ifdef CORE_INT47_PIN
|
||||
static void isr47(void) { update(interruptArgs[47]); }
|
||||
#endif
|
||||
#ifdef CORE_INT48_PIN
|
||||
static void isr48(void) { update(interruptArgs[48]); }
|
||||
#endif
|
||||
#ifdef CORE_INT49_PIN
|
||||
static void isr49(void) { update(interruptArgs[49]); }
|
||||
#endif
|
||||
#ifdef CORE_INT50_PIN
|
||||
static void isr50(void) { update(interruptArgs[50]); }
|
||||
#endif
|
||||
#ifdef CORE_INT51_PIN
|
||||
static void isr51(void) { update(interruptArgs[51]); }
|
||||
#endif
|
||||
#ifdef CORE_INT52_PIN
|
||||
static void isr52(void) { update(interruptArgs[52]); }
|
||||
#endif
|
||||
#ifdef CORE_INT53_PIN
|
||||
static void isr53(void) { update(interruptArgs[53]); }
|
||||
#endif
|
||||
#ifdef CORE_INT54_PIN
|
||||
static void isr54(void) { update(interruptArgs[54]); }
|
||||
#endif
|
||||
#ifdef CORE_INT55_PIN
|
||||
static void isr55(void) { update(interruptArgs[55]); }
|
||||
#endif
|
||||
#ifdef CORE_INT56_PIN
|
||||
static void isr56(void) { update(interruptArgs[56]); }
|
||||
#endif
|
||||
#ifdef CORE_INT57_PIN
|
||||
static void isr57(void) { update(interruptArgs[57]); }
|
||||
#endif
|
||||
#ifdef CORE_INT58_PIN
|
||||
static void isr58(void) { update(interruptArgs[58]); }
|
||||
#endif
|
||||
#ifdef CORE_INT59_PIN
|
||||
static void isr59(void) { update(interruptArgs[59]); }
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(ENCODER_USE_INTERRUPTS) && defined(ENCODER_OPTIMIZE_INTERRUPTS)
|
||||
#if defined(__AVR__)
|
||||
#if defined(INT0_vect) && CORE_NUM_INTERRUPT > 0
|
||||
ISR(INT0_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(0)]); }
|
||||
#endif
|
||||
#if defined(INT1_vect) && CORE_NUM_INTERRUPT > 1
|
||||
ISR(INT1_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(1)]); }
|
||||
#endif
|
||||
#if defined(INT2_vect) && CORE_NUM_INTERRUPT > 2
|
||||
ISR(INT2_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(2)]); }
|
||||
#endif
|
||||
#if defined(INT3_vect) && CORE_NUM_INTERRUPT > 3
|
||||
ISR(INT3_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(3)]); }
|
||||
#endif
|
||||
#if defined(INT4_vect) && CORE_NUM_INTERRUPT > 4
|
||||
ISR(INT4_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(4)]); }
|
||||
#endif
|
||||
#if defined(INT5_vect) && CORE_NUM_INTERRUPT > 5
|
||||
ISR(INT5_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(5)]); }
|
||||
#endif
|
||||
#if defined(INT6_vect) && CORE_NUM_INTERRUPT > 6
|
||||
ISR(INT6_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(6)]); }
|
||||
#endif
|
||||
#if defined(INT7_vect) && CORE_NUM_INTERRUPT > 7
|
||||
ISR(INT7_vect) { Encoder::update(Encoder::interruptArgs[SCRAMBLE_INT_ORDER(7)]); }
|
||||
#endif
|
||||
#endif // AVR
|
||||
#endif // ENCODER_OPTIMIZE_INTERRUPTS
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Encoder Library - Basic Example
|
||||
* http://www.pjrc.com/teensy/td_libs_Encoder.html
|
||||
*
|
||||
* This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Encoder.h>
|
||||
|
||||
// Change these two numbers to the pins connected to your encoder.
|
||||
// Best Performance: both pins have interrupt capability
|
||||
// Good Performance: only the first pin has interrupt capability
|
||||
// Low Performance: neither pin has interrupt capability
|
||||
Encoder myEnc(5, 6);
|
||||
// avoid using pins with LEDs attached
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("Basic Encoder Test:");
|
||||
}
|
||||
|
||||
long oldPosition = -999;
|
||||
|
||||
void loop() {
|
||||
long newPosition = myEnc.read();
|
||||
if (newPosition != oldPosition) {
|
||||
oldPosition = newPosition;
|
||||
Serial.println(newPosition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Encoder Library - NoInterrupts Example
|
||||
* http://www.pjrc.com/teensy/td_libs_Encoder.html
|
||||
*
|
||||
* This example code is in the public domain.
|
||||
*/
|
||||
|
||||
// If you define ENCODER_DO_NOT_USE_INTERRUPTS *before* including
|
||||
// Encoder, the library will never use interrupts. This is mainly
|
||||
// useful to reduce the size of the library when you are using it
|
||||
// with pins that do not support interrupts. Without interrupts,
|
||||
// your program must call the read() function rapidly, or risk
|
||||
// missing changes in position.
|
||||
#define ENCODER_DO_NOT_USE_INTERRUPTS
|
||||
#include <Encoder.h>
|
||||
|
||||
// Beware of Serial.print() speed. Without interrupts, if you
|
||||
// transmit too much data with Serial.print() it can slow your
|
||||
// reading from Encoder. Arduino 1.0 has improved transmit code.
|
||||
// Using the fastest baud rate also helps. Teensy has USB packet
|
||||
// buffering. But all boards can experience problems if you print
|
||||
// too much and fill up buffers.
|
||||
|
||||
// Change these two numbers to the pins connected to your encoder.
|
||||
// With ENCODER_DO_NOT_USE_INTERRUPTS, no interrupts are ever
|
||||
// used, even if the pin has interrupt capability
|
||||
Encoder myEnc(5, 6);
|
||||
// avoid using pins with LEDs attached
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("Basic NoInterrupts Test:");
|
||||
}
|
||||
|
||||
long position = -999;
|
||||
|
||||
void loop() {
|
||||
long newPos = myEnc.read();
|
||||
if (newPos != position) {
|
||||
position = newPos;
|
||||
Serial.println(position);
|
||||
}
|
||||
// With any substantial delay added, Encoder can only track
|
||||
// very slow motion. You may uncomment this line to see
|
||||
// how badly a delay affects your encoder.
|
||||
//delay(50);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/* Encoder Library - SpeedTest - for measuring maximum Encoder speed
|
||||
* http://www.pjrc.com/teensy/td_libs_Encoder.html
|
||||
*
|
||||
* This example code is in the public domain.
|
||||
*/
|
||||
|
||||
|
||||
// This SpeedTest example provides a simple way to verify how much
|
||||
// CPU time Encoder is consuming. Connect a DC voltmeter to the
|
||||
// output pin and measure the voltage while the encoder is stopped
|
||||
// or running at a very slow speed. Even though the pin is rapidly
|
||||
// pulsing, a DC voltmeter will show the average voltage. Due to
|
||||
// software timing, it will read a number much less than a steady
|
||||
// logic high, but this number will give you a baseline reading
|
||||
// for output with minimal interrupt overhead. Then increase the
|
||||
// encoder speed. The voltage will decrease as the processor spends
|
||||
// more time in Encoder's interrupt routines counting the pulses
|
||||
// and less time pulsing the output pin. When the voltage is
|
||||
// close to zero and will not decrease any farther, you have reached
|
||||
// the absolute speed limit. Or, if using a mechanical system where
|
||||
// you reach a speed limit imposed by your motors or other hardware,
|
||||
// the amount this voltage has decreased, compared to the baseline,
|
||||
// should give you a good approximation of the portion of available
|
||||
// CPU time Encoder is consuming at your maximum speed.
|
||||
|
||||
// Encoder requires low latency interrupt response. Available CPU
|
||||
// time does NOT necessarily prove or guarantee correct performance.
|
||||
// If another library, like NewSoftSerial, is disabling interrupts
|
||||
// for lengthy periods of time, Encoder can be prevented from
|
||||
// properly counting the intput signals while interrupt are disabled.
|
||||
|
||||
|
||||
// This optional setting causes Encoder to use more optimized code,
|
||||
// but the downside is a conflict if any other part of your sketch
|
||||
// or any other library you're using requires attachInterrupt().
|
||||
// It must be defined before Encoder.h is included.
|
||||
//#define ENCODER_OPTIMIZE_INTERRUPTS
|
||||
|
||||
#include <Encoder.h>
|
||||
#include "pins_arduino.h"
|
||||
|
||||
// Change these two numbers to the pins connected to your encoder
|
||||
// or shift register circuit which emulates a quadrature encoder
|
||||
// case 1: both pins are interrupts
|
||||
// case 2: only first pin used as interrupt
|
||||
Encoder myEnc(5, 6);
|
||||
|
||||
// Connect a DC voltmeter to this pin.
|
||||
const int outputPin = 12;
|
||||
|
||||
/* This simple circuit, using a Dual Flip-Flop chip, can emulate
|
||||
quadrature encoder signals. The clock can come from a fancy
|
||||
function generator or a cheap 555 timer chip. The clock
|
||||
frequency can be measured with another board running FreqCount
|
||||
http://www.pjrc.com/teensy/td_libs_FreqCount.html
|
||||
|
||||
+5V
|
||||
| Quadrature Encoder Signal Emulator
|
||||
Clock |
|
||||
Input o----*-------------------------- ---------------------------o Output1
|
||||
| |14 | |
|
||||
| _______|_______ | | _______________
|
||||
| | CD4013 | | | | CD4013 |
|
||||
| 5 | | 1 | | 9 | | 13
|
||||
---------| D Q |-----|----*----| D Q |------o Output2
|
||||
| | | | | | |
|
||||
| | 3 | | | 11 | |
|
||||
| ----|> Clk | ---------|> Clk |
|
||||
| | | | |
|
||||
| 6 | | 8 | |
|
||||
| ----| S | ----| S |
|
||||
| | | | | | |
|
||||
| | 4 | _ | 2 | 10 | _ | 12
|
||||
| *----| R Q |--- *----| R Q |----
|
||||
| | | | | | | |
|
||||
| | |_______________| | |_______________| |
|
||||
| | | | |
|
||||
| | | 7 | |
|
||||
| | | | |
|
||||
--------------------------------------------------------------
|
||||
| | |
|
||||
| | |
|
||||
----- ----- -----
|
||||
--- --- ---
|
||||
- - -
|
||||
*/
|
||||
|
||||
|
||||
void setup() {
|
||||
pinMode(outputPin, OUTPUT);
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
#define REGTYPE unsigned char
|
||||
#elif defined(__PIC32MX__)
|
||||
#define REGTYPE unsigned long
|
||||
#endif
|
||||
|
||||
void loop() {
|
||||
volatile int count = 0;
|
||||
volatile REGTYPE *reg = portOutputRegister(digitalPinToPort(outputPin));
|
||||
REGTYPE mask = digitalPinToBitMask(outputPin);
|
||||
|
||||
while (1) {
|
||||
myEnc.read(); // Read the encoder while interrupts are enabled.
|
||||
noInterrupts();
|
||||
*reg |= mask; // Pulse the pin high, while interrupts are disabled.
|
||||
count = count + 1;
|
||||
*reg &= ~mask;
|
||||
interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Encoder Library - TwoKnobs Example
|
||||
* http://www.pjrc.com/teensy/td_libs_Encoder.html
|
||||
*
|
||||
* This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <Encoder.h>
|
||||
|
||||
// Change these pin numbers to the pins connected to your encoder.
|
||||
// Best Performance: both pins have interrupt capability
|
||||
// Good Performance: only the first pin has interrupt capability
|
||||
// Low Performance: neither pin has interrupt capability
|
||||
Encoder knobLeft(5, 6);
|
||||
Encoder knobRight(7, 8);
|
||||
// avoid using pins with LEDs attached
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("TwoKnobs Encoder Test:");
|
||||
}
|
||||
|
||||
long positionLeft = -999;
|
||||
long positionRight = -999;
|
||||
|
||||
void loop() {
|
||||
long newLeft, newRight;
|
||||
newLeft = knobLeft.read();
|
||||
newRight = knobRight.read();
|
||||
if (newLeft != positionLeft || newRight != positionRight) {
|
||||
Serial.print("Left = ");
|
||||
Serial.print(newLeft);
|
||||
Serial.print(", Right = ");
|
||||
Serial.print(newRight);
|
||||
Serial.println();
|
||||
positionLeft = newLeft;
|
||||
positionRight = newRight;
|
||||
}
|
||||
// if a character is sent from the serial monitor,
|
||||
// reset both back to zero.
|
||||
if (Serial.available()) {
|
||||
Serial.read();
|
||||
Serial.println("Reset both knobs to zero");
|
||||
knobLeft.write(0);
|
||||
knobRight.write(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
ENCODER_USE_INTERRUPTS LITERAL1
|
||||
ENCODER_OPTIMIZE_INTERRUPTS LITERAL1
|
||||
ENCODER_DO_NOT_USE_INTERRUPTS LITERAL1
|
||||
Encoder KEYWORD1
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef direct_pin_read_h_
|
||||
#define direct_pin_read_h_
|
||||
|
||||
#if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__)
|
||||
|
||||
#define IO_REG_TYPE uint8_t
|
||||
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define DIRECT_PIN_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
|
||||
|
||||
#elif defined(__SAM3X8E__)
|
||||
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define DIRECT_PIN_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
|
||||
|
||||
#elif defined(__PIC32MX__)
|
||||
|
||||
#define IO_REG_TYPE uint32_t
|
||||
#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin)))
|
||||
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
|
||||
#define DIRECT_PIN_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#if defined(__AVR__)
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
#define attachInterrupt(num, func, mode) enableInterrupt(num)
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
#define SCRAMBLE_INT_ORDER(num) ((num < 4) ? num + 2 : ((num < 6) ? num - 4 : num))
|
||||
#define DESCRAMBLE_INT_ORDER(num) ((num < 2) ? num + 4 : ((num < 6) ? num - 2 : num))
|
||||
#else
|
||||
#define SCRAMBLE_INT_ORDER(num) (num)
|
||||
#define DESCRAMBLE_INT_ORDER(num) (num)
|
||||
#endif
|
||||
|
||||
static void enableInterrupt(uint8_t num)
|
||||
{
|
||||
switch (DESCRAMBLE_INT_ORDER(num)) {
|
||||
#if defined(EICRA) && defined(EIMSK)
|
||||
case 0:
|
||||
EICRA = (EICRA & 0xFC) | 0x01;
|
||||
EIMSK |= 0x01;
|
||||
return;
|
||||
case 1:
|
||||
EICRA = (EICRA & 0xF3) | 0x04;
|
||||
EIMSK |= 0x02;
|
||||
return;
|
||||
case 2:
|
||||
EICRA = (EICRA & 0xCF) | 0x10;
|
||||
EIMSK |= 0x04;
|
||||
return;
|
||||
case 3:
|
||||
EICRA = (EICRA & 0x3F) | 0x40;
|
||||
EIMSK |= 0x08;
|
||||
return;
|
||||
#elif defined(MCUCR) && defined(GICR)
|
||||
case 0:
|
||||
MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
|
||||
GICR |= (1 << INT0);
|
||||
return;
|
||||
case 1:
|
||||
MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
|
||||
GICR |= (1 << INT1);
|
||||
return;
|
||||
#elif defined(MCUCR) && defined(GIMSK)
|
||||
case 0:
|
||||
MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
|
||||
GIMSK |= (1 << INT0);
|
||||
return;
|
||||
case 1:
|
||||
MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
|
||||
GIMSK |= (1 << INT1);
|
||||
return;
|
||||
#endif
|
||||
#if defined(EICRB) && defined(EIMSK)
|
||||
case 4:
|
||||
EICRB = (EICRB & 0xFC) | 0x01;
|
||||
EIMSK |= 0x10;
|
||||
return;
|
||||
case 5:
|
||||
EICRB = (EICRB & 0xF3) | 0x04;
|
||||
EIMSK |= 0x20;
|
||||
return;
|
||||
case 6:
|
||||
EICRB = (EICRB & 0xCF) | 0x10;
|
||||
EIMSK |= 0x40;
|
||||
return;
|
||||
case 7:
|
||||
EICRB = (EICRB & 0x3F) | 0x40;
|
||||
EIMSK |= 0x80;
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__PIC32MX__)
|
||||
|
||||
#ifdef ENCODER_OPTIMIZE_INTERRUPTS
|
||||
#undef ENCODER_OPTIMIZE_INTERRUPTS
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef ENCODER_OPTIMIZE_INTERRUPTS
|
||||
#undef ENCODER_OPTIMIZE_INTERRUPTS
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
// interrupt pins for known boards
|
||||
|
||||
// Teensy (and maybe others) define these automatically
|
||||
#if !defined(CORE_NUM_INTERRUPT)
|
||||
|
||||
// Wiring boards
|
||||
#if defined(WIRING)
|
||||
#define CORE_NUM_INTERRUPT NUM_EXTERNAL_INTERRUPTS
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 0
|
||||
#define CORE_INT0_PIN EI0
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 1
|
||||
#define CORE_INT1_PIN EI1
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 2
|
||||
#define CORE_INT2_PIN EI2
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 3
|
||||
#define CORE_INT3_PIN EI3
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 4
|
||||
#define CORE_INT4_PIN EI4
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 5
|
||||
#define CORE_INT5_PIN EI5
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 6
|
||||
#define CORE_INT6_PIN EI6
|
||||
#endif
|
||||
#if NUM_EXTERNAL_INTERRUPTS > 7
|
||||
#define CORE_INT7_PIN EI7
|
||||
#endif
|
||||
|
||||
// Arduino Uno, Duemilanove, Diecimila, LilyPad, Mini, Fio, etc...
|
||||
#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__)
|
||||
#define CORE_NUM_INTERRUPT 2
|
||||
#define CORE_INT0_PIN 2
|
||||
#define CORE_INT1_PIN 3
|
||||
|
||||
// Arduino Mega
|
||||
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
#define CORE_NUM_INTERRUPT 6
|
||||
#define CORE_INT0_PIN 2
|
||||
#define CORE_INT1_PIN 3
|
||||
#define CORE_INT2_PIN 21
|
||||
#define CORE_INT3_PIN 20
|
||||
#define CORE_INT4_PIN 19
|
||||
#define CORE_INT5_PIN 18
|
||||
|
||||
// Arduino Leonardo (untested)
|
||||
#elif defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY)
|
||||
#define CORE_NUM_INTERRUPT 4
|
||||
#define CORE_INT0_PIN 3
|
||||
#define CORE_INT1_PIN 2
|
||||
#define CORE_INT2_PIN 0
|
||||
#define CORE_INT3_PIN 1
|
||||
|
||||
// Sanguino (untested)
|
||||
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
|
||||
#define CORE_NUM_INTERRUPT 3
|
||||
#define CORE_INT0_PIN 10
|
||||
#define CORE_INT1_PIN 11
|
||||
#define CORE_INT2_PIN 2
|
||||
|
||||
// Chipkit Uno32 - attachInterrupt may not support CHANGE option
|
||||
#elif defined(__PIC32MX__) && defined(_BOARD_UNO_)
|
||||
#define CORE_NUM_INTERRUPT 5
|
||||
#define CORE_INT0_PIN 38
|
||||
#define CORE_INT1_PIN 2
|
||||
#define CORE_INT2_PIN 7
|
||||
#define CORE_INT3_PIN 8
|
||||
#define CORE_INT4_PIN 35
|
||||
|
||||
// Chipkit Uno32 - attachInterrupt may not support CHANGE option
|
||||
#elif defined(__PIC32MX__) && defined(_BOARD_MEGA_)
|
||||
#define CORE_NUM_INTERRUPT 5
|
||||
#define CORE_INT0_PIN 3
|
||||
#define CORE_INT1_PIN 2
|
||||
#define CORE_INT2_PIN 7
|
||||
#define CORE_INT3_PIN 21
|
||||
#define CORE_INT4_PIN 20
|
||||
|
||||
// http://hlt.media.mit.edu/?p=1229
|
||||
#elif defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
|
||||
#define CORE_NUM_INTERRUPT 1
|
||||
#define CORE_INT0_PIN 2
|
||||
|
||||
// Arduino Due (untested)
|
||||
#elif defined(__SAM3X8E__)
|
||||
#define CORE_NUM_INTERRUPT 54
|
||||
#define CORE_INT0_PIN 0
|
||||
#define CORE_INT1_PIN 1
|
||||
#define CORE_INT2_PIN 2
|
||||
#define CORE_INT3_PIN 3
|
||||
#define CORE_INT4_PIN 4
|
||||
#define CORE_INT5_PIN 5
|
||||
#define CORE_INT6_PIN 6
|
||||
#define CORE_INT7_PIN 7
|
||||
#define CORE_INT8_PIN 8
|
||||
#define CORE_INT9_PIN 9
|
||||
#define CORE_INT10_PIN 10
|
||||
#define CORE_INT11_PIN 11
|
||||
#define CORE_INT12_PIN 12
|
||||
#define CORE_INT13_PIN 13
|
||||
#define CORE_INT14_PIN 14
|
||||
#define CORE_INT15_PIN 15
|
||||
#define CORE_INT16_PIN 16
|
||||
#define CORE_INT17_PIN 17
|
||||
#define CORE_INT18_PIN 18
|
||||
#define CORE_INT19_PIN 19
|
||||
#define CORE_INT20_PIN 20
|
||||
#define CORE_INT21_PIN 21
|
||||
#define CORE_INT22_PIN 22
|
||||
#define CORE_INT23_PIN 23
|
||||
#define CORE_INT24_PIN 24
|
||||
#define CORE_INT25_PIN 25
|
||||
#define CORE_INT26_PIN 26
|
||||
#define CORE_INT27_PIN 27
|
||||
#define CORE_INT28_PIN 28
|
||||
#define CORE_INT29_PIN 29
|
||||
#define CORE_INT30_PIN 30
|
||||
#define CORE_INT31_PIN 31
|
||||
#define CORE_INT32_PIN 32
|
||||
#define CORE_INT33_PIN 33
|
||||
#define CORE_INT34_PIN 34
|
||||
#define CORE_INT35_PIN 35
|
||||
#define CORE_INT36_PIN 36
|
||||
#define CORE_INT37_PIN 37
|
||||
#define CORE_INT38_PIN 38
|
||||
#define CORE_INT39_PIN 39
|
||||
#define CORE_INT40_PIN 40
|
||||
#define CORE_INT41_PIN 41
|
||||
#define CORE_INT42_PIN 42
|
||||
#define CORE_INT43_PIN 43
|
||||
#define CORE_INT44_PIN 44
|
||||
#define CORE_INT45_PIN 45
|
||||
#define CORE_INT46_PIN 46
|
||||
#define CORE_INT47_PIN 47
|
||||
#define CORE_INT48_PIN 48
|
||||
#define CORE_INT49_PIN 49
|
||||
#define CORE_INT50_PIN 50
|
||||
#define CORE_INT51_PIN 51
|
||||
#define CORE_INT52_PIN 52
|
||||
#define CORE_INT53_PIN 53
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(CORE_NUM_INTERRUPT)
|
||||
#error "Interrupts are unknown for this board, please add to this code"
|
||||
#endif
|
||||
#if CORE_NUM_INTERRUPT <= 0
|
||||
#error "Encoder requires interrupt pins, but this board does not have any :("
|
||||
#error "You could try defining ENCODER_DO_NOT_USE_INTERRUPTS as a kludge."
|
||||
#endif
|
||||
|
||||
@@ -81,12 +81,11 @@ char* MyMessage::getStream(char *buffer) const {
|
||||
|
||||
char* MyMessage::getString(char *buffer) const {
|
||||
uint8_t payloadType = miGetPayloadType();
|
||||
if (payloadType == P_STRING) {
|
||||
strncpy(buffer, data, miGetLength());
|
||||
buffer[miGetLength()] = 0;
|
||||
return buffer;
|
||||
} else if (buffer != NULL) {
|
||||
if (payloadType == P_BYTE) {
|
||||
if (buffer != NULL) {
|
||||
if (payloadType == P_STRING) {
|
||||
strncpy(buffer, data, miGetLength());
|
||||
buffer[miGetLength()] = 0;
|
||||
} else if (payloadType == P_BYTE) {
|
||||
itoa(bValue, buffer, 10);
|
||||
} else if (payloadType == P_INT16) {
|
||||
itoa(iValue, buffer, 10);
|
||||
|
||||
@@ -483,7 +483,7 @@ void MySensor::sendBatteryLevel(uint8_t value, bool enableAck) {
|
||||
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
|
||||
}
|
||||
|
||||
void MySensor::present(uint8_t childSensorId, uint8_t sensorType, const char*description, bool enableAck) {
|
||||
void MySensor::present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
|
||||
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description));
|
||||
}
|
||||
|
||||
|
||||
@@ -179,6 +179,7 @@ class MySensor
|
||||
* @param sensorType The sensor type. See sensor typedef in MyMessage.h.
|
||||
* @param description A textual description of the sensor.
|
||||
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
|
||||
* @param description A textual description of the sensor.
|
||||
*/
|
||||
void present(uint8_t sensorId, uint8_t sensorType, const char *description="", bool ack=false);
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
bool signMsg(MyMessage &msg);
|
||||
bool verifyMsg(MyMessage &msg);
|
||||
private:
|
||||
atsha204Class atsha204;
|
||||
ATSHA204Class atsha204;
|
||||
unsigned long timestamp;
|
||||
bool verification_ongoing;
|
||||
uint8_t current_nonce[NONCE_NUMIN_SIZE_PASSTHROUGH+SHA204_SERIAL_SZ+1];
|
||||
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2015 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*******************************
|
||||
*
|
||||
* REVISION HISTORY
|
||||
* Version 1.0 - Developed by Bruce Lacey and GizMoCuz (Domoticz)
|
||||
* Version 1.1 - Modified by hek to incorporate a rotary encode to adjust
|
||||
* light level locally at node
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This sketch provides an example how to implement a dimmable led light node with a rotary
|
||||
* encoder connected for adjusting light level.
|
||||
* The encoder has a click button which turns on/off the light (and remembers last dim-level)
|
||||
* The sketch fades the light (non-blocking) to the desired level.
|
||||
*
|
||||
* Default MOSFET pin is 3
|
||||
*
|
||||
* Arduino Encoder module
|
||||
* ---------------------------
|
||||
* 5V 5V (+)
|
||||
* GND GND (-)
|
||||
* 4 CLK (or putput 1)
|
||||
* 5 DT (or output 1)
|
||||
* 6 SW (Switch/Click)
|
||||
*/
|
||||
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Encoder.h>
|
||||
#include <MySensor.h>
|
||||
#include <Bounce2.h>
|
||||
|
||||
#define LED_PIN 3 // Arduino pin attached to MOSFET Gate pin
|
||||
#define KNOB_ENC_PIN_1 4 // Rotary encoder input pin 1
|
||||
#define KNOB_ENC_PIN_2 5 // Rotary encoder input pin 2
|
||||
#define KNOB_BUTTON_PIN 6 // Rotary encoder button pin
|
||||
#define FADE_DELAY 10 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
|
||||
#define SEND_THROTTLE_DELAY 400 // Number of milliseconds before sending after user stops turning knob
|
||||
#define SN "DimmableLED /w button"
|
||||
#define SV "1.2"
|
||||
|
||||
#define CHILD_ID_LIGHT 1
|
||||
|
||||
#define EEPROM_DIM_LEVEL_LAST 1
|
||||
#define EEPROM_DIM_LEVEL_SAVE 2
|
||||
|
||||
#define LIGHT_OFF 0
|
||||
#define LIGHT_ON 1
|
||||
|
||||
int dimValue;
|
||||
int fadeTo;
|
||||
int fadeDelta;
|
||||
byte oldButtonVal;
|
||||
bool changedByKnob=false;
|
||||
bool sendDimValue=false;
|
||||
unsigned long lastFadeStep;
|
||||
unsigned long sendDimTimeout;
|
||||
char convBuffer[10];
|
||||
|
||||
MySensor gw;
|
||||
MyMessage dimmerMsg(CHILD_ID_LIGHT, V_DIMMER);
|
||||
Encoder knob(KNOB_ENC_PIN_1, KNOB_ENC_PIN_2);
|
||||
Bounce debouncer = Bounce();
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Set knob button pin as input (with debounce)
|
||||
pinMode(KNOB_BUTTON_PIN, INPUT);
|
||||
digitalWrite(KNOB_BUTTON_PIN, HIGH);
|
||||
debouncer.attach(KNOB_BUTTON_PIN);
|
||||
debouncer.interval(5);
|
||||
oldButtonVal = debouncer.read();
|
||||
|
||||
// Set analog led pin to off
|
||||
analogWrite( LED_PIN, 0);
|
||||
|
||||
// Init mysensors library
|
||||
gw.begin(incomingMessage, AUTO, false);
|
||||
|
||||
// Send the Sketch Version Information to the Gateway
|
||||
gw.present(CHILD_ID_LIGHT, S_DIMMER);
|
||||
gw.sendSketchInfo(SN, SV);
|
||||
|
||||
// Retreive our last dim levels from the eprom
|
||||
fadeTo = dimValue = 0;
|
||||
byte oldLevel = loadLevelState(EEPROM_DIM_LEVEL_LAST);
|
||||
Serial.print("Sending in last known light level to controller: ");
|
||||
Serial.println(oldLevel);
|
||||
gw.send(dimmerMsg.set(oldLevel), true);
|
||||
|
||||
Serial.println("Ready to receive messages...");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Process incoming messages (like config and light state from controller)
|
||||
gw.process();
|
||||
|
||||
// Check if someone turned the rotary encode
|
||||
checkRotaryEncoder();
|
||||
|
||||
// Check if someone has pressed the knob button
|
||||
checkButtonClick();
|
||||
|
||||
// Fade light to new dim value
|
||||
fadeStep();
|
||||
}
|
||||
|
||||
void incomingMessage(const MyMessage &message)
|
||||
{
|
||||
if (message.type == V_LIGHT) {
|
||||
// Incoming on/off command sent from controller ("1" or "0")
|
||||
int lightState = message.getString()[0] == '1';
|
||||
int newLevel = 0;
|
||||
if (lightState==LIGHT_ON) {
|
||||
// Pick up last saved dimmer level from the eeprom
|
||||
newLevel = loadLevelState(EEPROM_DIM_LEVEL_SAVE);
|
||||
}
|
||||
// Send dimmer level back to controller with ack enabled
|
||||
gw.send(dimmerMsg.set(0), true);
|
||||
// We do not change any levels here until ack comes back from gateway
|
||||
return;
|
||||
} else if (message.type == V_DIMMER) {
|
||||
// Incoming dim-level command sent from controller (or ack message)
|
||||
fadeTo = atoi(message.getString(convBuffer));
|
||||
// Save received dim value to eeprom (unless turned off). Will be
|
||||
// retreived when a on command comes in
|
||||
if (fadeTo != 0)
|
||||
saveLevelState(EEPROM_DIM_LEVEL_SAVE, fadeTo);
|
||||
}
|
||||
saveLevelState(EEPROM_DIM_LEVEL_LAST, fadeTo);
|
||||
|
||||
Serial.print("New light level received: ");
|
||||
Serial.println(fadeTo);
|
||||
|
||||
if (!changedByKnob)
|
||||
knob.write(fadeTo);
|
||||
|
||||
// Cancel send if user turns knob while message comes in
|
||||
changedByKnob = false;
|
||||
sendDimValue = false;
|
||||
|
||||
// Stard fading to new light level
|
||||
startFade();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void checkRotaryEncoder() {
|
||||
long encoderValue = knob.read();
|
||||
|
||||
if (encoderValue > 100) {
|
||||
encoderValue = 100;
|
||||
knob.write(100);
|
||||
} else if (encoderValue < 0) {
|
||||
encoderValue = 0;
|
||||
knob.write(0);
|
||||
}
|
||||
|
||||
if (encoderValue != fadeTo) {
|
||||
fadeTo = encoderValue;
|
||||
changedByKnob = true;
|
||||
startFade();
|
||||
}
|
||||
}
|
||||
|
||||
void checkButtonClick() {
|
||||
debouncer.update();
|
||||
byte buttonVal = debouncer.read();
|
||||
byte newLevel = 0;
|
||||
if (buttonVal != oldButtonVal && buttonVal == LOW) {
|
||||
if (dimValue==0) {
|
||||
// Turn on light. Set the level to last saved dim value
|
||||
int saved = loadLevelState(EEPROM_DIM_LEVEL_SAVE);
|
||||
newLevel = saved > 0 ? saved : 100;
|
||||
}
|
||||
gw.send(dimmerMsg.set(newLevel),true);
|
||||
}
|
||||
oldButtonVal = buttonVal;
|
||||
}
|
||||
|
||||
void startFade() {
|
||||
fadeDelta = ( fadeTo - dimValue ) < 0 ? -1 : 1;
|
||||
lastFadeStep = millis();
|
||||
}
|
||||
|
||||
// This method provides a graceful none-blocking fade up/down effect
|
||||
void fadeStep() {
|
||||
unsigned long currentTime = millis();
|
||||
if ( dimValue != fadeTo && currentTime > lastFadeStep + FADE_DELAY) {
|
||||
dimValue += fadeDelta;
|
||||
analogWrite( LED_PIN, (int)(dimValue / 100. * 255) );
|
||||
lastFadeStep = currentTime;
|
||||
|
||||
Serial.print("Fading level: ");
|
||||
Serial.println(dimValue);
|
||||
|
||||
if (fadeTo == dimValue && changedByKnob) {
|
||||
sendDimValue = true;
|
||||
sendDimTimeout = currentTime;
|
||||
}
|
||||
}
|
||||
// Wait a few millisecs before sending in new value (if user still turns the knob)
|
||||
if (sendDimValue && currentTime > sendDimTimeout + SEND_THROTTLE_DELAY) {
|
||||
// We're done fading.. send in new dim-value to controller.
|
||||
// Send in new dim value with ack (will be picked up in incomingMessage)
|
||||
gw.send(dimmerMsg.set(dimValue), true); // Send new dimmer value and request ack back
|
||||
sendDimValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure only to store/fetch values in the range 0-100 from eeprom
|
||||
int loadLevelState(byte pos) {
|
||||
return min(max(gw.loadState(pos),0),100);
|
||||
}
|
||||
void saveLevelState(byte pos, byte data) {
|
||||
gw.saveState(pos,min(max(data,0),100));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,864 @@
|
||||
/*
|
||||
MySprinkler for MySensors
|
||||
|
||||
Arduino Multi-Zone Sprinkler Control
|
||||
|
||||
May 31, 2015
|
||||
|
||||
*** Version 2.0
|
||||
|
||||
*** Upgraded to http://MySensors.org version 1.4.1
|
||||
*** Expanded for up to 16 Valves
|
||||
*** Setup for active low relay board or comment out #define ACTIVE_LOW to switch to active high
|
||||
*** Switch to bitshift method vs byte arrays
|
||||
*** Changed RUN_ALL_ZONES Vera device to 0 (was highest valve)
|
||||
*** Added optional LCD display featuring remaining time, date last ran & current time
|
||||
*** Features 'raindrop' and 'clock' icons which indicate sensor is updating valve data and clock respectively
|
||||
*** Added single pushbutton menu to manually select which program to run (All Zones or a Single Zone)
|
||||
*** Added option of naming your Zones programmatically or with Vera (V_VAR3 used to store names)
|
||||
|
||||
Utilizing your Vera home automation controller and the MySensors.org gateway you can
|
||||
control up to a sixteen zone irrigation system with only three digital pins. This sketch
|
||||
will create NUMBER_OF_VALVES + 1 devices on your Vera controller
|
||||
|
||||
This sketch features the following:
|
||||
|
||||
* Allows you to cycle through All zones (RUN_ALL_ZONES) or individual zone (RUN_SINGLE_ZONE) control.
|
||||
* Use the 0th controller to activate RUN_ALL_ZONES (each zone in numeric sequence 1 to n)
|
||||
using Variable1 as the "ON" time in minutes in each of the vera devices created.
|
||||
* Use the individual zone controller to activate a single zone. This feature uses
|
||||
Variable2 as the "ON" time for each individual device/zone.
|
||||
* Connect according to pinout below and uses Shift Registers as to allow the MySensors
|
||||
standard radio configuration and still leave available digital pins
|
||||
* Turning on any zone will stop the current process and begin that particular process.
|
||||
* Turning off any zone will stop the current process and turn off all zones.
|
||||
* To push your new time intervals for your zones, simply change the variable on your Vera and
|
||||
your arduino will call to Vera once a minute and update accordingly. Variables will also be
|
||||
requested when the device is first powered on.
|
||||
* Pushbutton activation to RUN_ALL_ZONES, RUN_SINGLE_ZONE or halt the current program
|
||||
* LED status indicator
|
||||
|
||||
PARTS LIST:
|
||||
Available from the MySensors store - http://www.mysensors.org/store/
|
||||
* Relays (8 channel)
|
||||
* Female Pin Header Connector Strip
|
||||
* Prototype Universal Printed Circuit Boards (PCB)
|
||||
* NRF24L01 Radio
|
||||
* Arduino (I used a Pro Mini)
|
||||
* FTDI USB to TTL Serial Adapter
|
||||
* Capacitors (10uf and .1uf)
|
||||
* 3.3v voltage regulator
|
||||
* Resistors (270 & 10K)
|
||||
* Female Dupont Cables
|
||||
* 1602 LCD (with I2C Interface)
|
||||
* LED
|
||||
* Push button
|
||||
* Shift Register (SN74HC595)
|
||||
* 2 Pole 5mm Pitch PCB Mount Screw Terminal Block
|
||||
* 3 Pole 5mm Pitch PCB Mount Screw Terminal Block
|
||||
* 22-24 gauge wire or similar (I used Cat5/Cat6 cable)
|
||||
* 18 gauge wire (for relay)
|
||||
* Irrigation Power Supply (24-Volt/750 mA Transformer)
|
||||
|
||||
|
||||
INSTRUCTIONS:
|
||||
|
||||
* A step-by-step setup video is available here: http://youtu.be/l4GPRTsuHkI
|
||||
* After assembling your arduino, radio, decoupling capacitors, shift register(s), status LED, pushbutton LCD (I2C connected to
|
||||
A4 and A5) and relays, and load the sketch.
|
||||
* Following the instructions at https://MySensors.org include the device to your MySensors Gateway.
|
||||
* Verify that each new device has a Variable1, Variable2 and Variable3. Populate data accordingly with whole minutes for
|
||||
the RUN_ALL_ZONES routine (Variable1) and the RUN_SINGLE_ZONE routines (Variable 2). The values entered for times may be zero and
|
||||
you may use the defaulet zone names by leaving Variable3 blank.
|
||||
* Once you have entered values for each zone and each variable, save the settings by pressing the red save button on your Vera.
|
||||
* Restart your arduino; verify the settings are loaded into your arduino with the serial monitor; the array will be printed
|
||||
on the serial monitor.
|
||||
* Your arduino should slow-flash, indicating that it is in ready mode.
|
||||
* There are multiple debug serial prints that can be monitored to assure that it is operating properly.
|
||||
* ***THIS SHOULD NO LONGER BE NEEDED*** The standard MySensors library now works. https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads for the I2C library, or use yours
|
||||
|
||||
Contributed by Jim (BulldogLowell@gmail.com) with much contribution from Pete (pete.will@mysensors.org) and is released to the public domain
|
||||
*/
|
||||
//
|
||||
#include <Wire.h>
|
||||
#include <Time.h>
|
||||
#include <MySensor.h>
|
||||
#include <SPI.h>
|
||||
#include <LiquidCrystal.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
|
||||
//
|
||||
#define NUMBER_OF_VALVES 8 // Change this to set your valve count up to 16.
|
||||
#define VALVE_RESET_TIME 7500UL // Change this (in milliseconds) for the time you need your valves to hydraulically reset and change state
|
||||
#define RADIO_ID AUTO // Change this to fix your Radio ID or use Auto
|
||||
|
||||
#define SKETCH_NAME "MySprinkler"
|
||||
#define SKETCH_VERSION "2.0"
|
||||
//
|
||||
#define CHILD_ID_SPRINKLER 0
|
||||
//
|
||||
#define ACTIVE_LOW // comment out this line if your relays are active high
|
||||
//
|
||||
#define DEBUG_ON // comment out to supress serial monitor output
|
||||
//
|
||||
#ifdef ACTIVE_LOW
|
||||
#define BITSHIFT_VALVE_NUMBER ~(1U << (valveNumber-1))
|
||||
#define ALL_VALVES_OFF 0xFFFF
|
||||
#else
|
||||
#define BITSHIFT_VALVE_NUMBER (1U << (valveNumber-1))
|
||||
#define ALL_VALVES_OFF 0U
|
||||
#endif
|
||||
//
|
||||
#ifdef DEBUG_ON
|
||||
#define DEBUG_PRINT(x) Serial.print(x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||
#define SERIAL_START(x) Serial.begin(x)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define SERIAL_START(x)
|
||||
#endif
|
||||
//
|
||||
typedef enum {
|
||||
STAND_BY_ALL_OFF, RUN_SINGLE_ZONE, RUN_ALL_ZONES, CYCLE_COMPLETE, ZONE_SELECT_MENU
|
||||
}
|
||||
SprinklerStates;
|
||||
//
|
||||
SprinklerStates state = STAND_BY_ALL_OFF;
|
||||
SprinklerStates lastState;
|
||||
byte menuState = 0;
|
||||
unsigned long menuTimer;
|
||||
byte countDownTime = 10;
|
||||
//
|
||||
int allZoneTime [NUMBER_OF_VALVES + 1];
|
||||
int valveSoloTime [NUMBER_OF_VALVES + 1];
|
||||
int valveNumber;
|
||||
int lastValve;
|
||||
unsigned long startMillis;
|
||||
const int ledPin = 5;
|
||||
const int waterButtonPin = 3;
|
||||
boolean buttonPushed = false;
|
||||
boolean showTime = true;
|
||||
boolean clockUpdating = false;
|
||||
boolean recentUpdate = true;
|
||||
const char *dayOfWeek[] = {
|
||||
"Null", "Sunday ", "Monday ", "Tuesday ", "Wednesday ", "Thursday ", "Friday ", "Saturday "
|
||||
};
|
||||
// Name your Zones here or use Vera to edit them by adding a name in Variable3...
|
||||
String valveNickName[17] = {
|
||||
"All Zones", "Zone 1", "Zone 2", "Zone 3", "Zone 4", "Zone 5", "Zone 6", "Zone 7", "Zone 8", "Zone 9", "Zone 10", "Zone 11", "Zone 12", "Zone 13", "Zone 14", "Zone 15", "Zone 16"
|
||||
};
|
||||
//
|
||||
time_t lastTimeRun = 0;
|
||||
//Setup Shift Register...
|
||||
const int latchPin = 8;
|
||||
const int clockPin = 4;
|
||||
const int dataPin = 7;
|
||||
//
|
||||
byte clock[8] = {0x0, 0xe, 0x15, 0x17, 0x11, 0xe, 0x0}; // fetching time indicator
|
||||
byte raindrop[8] = {0x4, 0x4, 0xA, 0xA, 0x11, 0xE, 0x0,}; // fetching Valve Data indicator
|
||||
// Set the pins on the I2C chip used for LCD connections:
|
||||
// addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
|
||||
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address to 0x27
|
||||
MySensor gw;
|
||||
//
|
||||
MyMessage msg1valve(CHILD_ID_SPRINKLER, V_LIGHT);
|
||||
MyMessage var1valve(CHILD_ID_SPRINKLER, V_VAR1);
|
||||
MyMessage var2valve(CHILD_ID_SPRINKLER, V_VAR2);
|
||||
//
|
||||
void setup()
|
||||
{
|
||||
SERIAL_START(115200);
|
||||
DEBUG_PRINTLN(F("Initialising..."));
|
||||
pinMode(latchPin, OUTPUT);
|
||||
pinMode(clockPin, OUTPUT);
|
||||
pinMode(dataPin, OUTPUT);
|
||||
pinMode(ledPin, OUTPUT);
|
||||
pinMode(waterButtonPin, INPUT_PULLUP);
|
||||
//pinMode(waterButtonPin, INPUT);
|
||||
attachInterrupt(1, PushButton, RISING); //May need to change for your Arduino model
|
||||
digitalWrite (ledPin, HIGH);
|
||||
DEBUG_PRINTLN(F("Turning All Valves Off..."));
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
//delay(5000);
|
||||
lcd.begin(16, 2); //(16 characters and 2 line display)
|
||||
lcd.clear();
|
||||
lcd.backlight();
|
||||
lcd.createChar(0, clock);
|
||||
lcd.createChar(1, raindrop);
|
||||
//
|
||||
//check for saved date in EEPROM
|
||||
DEBUG_PRINTLN(F("Checking EEPROM for stored date:"));
|
||||
delay(500);
|
||||
if (gw.loadState(0) == 0xFF); // EEPROM flag
|
||||
{
|
||||
DEBUG_PRINTLN(F("Retreiving last run time from EEPROM..."));
|
||||
for (int i = 0; i < 4 ; i++)
|
||||
{
|
||||
lastTimeRun = lastTimeRun << 8;
|
||||
lastTimeRun = lastTimeRun | gw.loadState(i + 1); // assemble 4 bytes into an ussigned long epoch timestamp
|
||||
}
|
||||
}
|
||||
gw.begin(getVariables, RADIO_ID, false); // Change 'false' to 'true' to create a Radio repeating node
|
||||
gw.sendSketchInfo(SKETCH_NAME, SKETCH_VERSION);
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
gw.present(i, S_LIGHT);
|
||||
}
|
||||
DEBUG_PRINTLN(F("Sensor Presentation Complete"));
|
||||
//
|
||||
digitalWrite (ledPin, LOW);
|
||||
DEBUG_PRINTLN(F("Ready..."));
|
||||
//
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print(F(" Syncing Time "));
|
||||
lcd.setCursor(15, 0);
|
||||
lcd.write(byte(0));
|
||||
lcd.setCursor(0, 1);
|
||||
int clockCounter = 0;
|
||||
while (timeStatus() == timeNotSet && clockCounter < 21)
|
||||
{
|
||||
gw.process();
|
||||
gw.requestTime(receiveTime);
|
||||
DEBUG_PRINTLN(F("Requesting time from Gateway:"));
|
||||
delay(1000);
|
||||
lcd.print(".");
|
||||
clockCounter++;
|
||||
if (clockCounter > 16)
|
||||
{
|
||||
DEBUG_PRINTLN(F("Failed initial clock synchronization!"));
|
||||
lcd.clear();
|
||||
lcd.print(F(" Failed Clock "));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F(" Syncronization "));
|
||||
delay(2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//
|
||||
lcd.clear();
|
||||
//Update valve data when first powered on
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
lcd.print(F(" Updating "));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F(" Valve Data: "));
|
||||
lcd.print(i);
|
||||
boolean flashIcon = false;
|
||||
DEBUG_PRINT(F("Calling for Valve "));
|
||||
DEBUG_PRINT(i);
|
||||
DEBUG_PRINTLN(F(" Data..."));
|
||||
while (gw.process() == false)
|
||||
{
|
||||
lcd.setCursor(15, 0);
|
||||
flashIcon = !flashIcon;
|
||||
flashIcon ? lcd.write(byte(1)) : lcd.print(F(" "));
|
||||
gw.request(i, V_VAR1);
|
||||
delay(100);
|
||||
}
|
||||
while (gw.process() == false)
|
||||
{
|
||||
lcd.setCursor(15, 0);
|
||||
flashIcon = !flashIcon;
|
||||
flashIcon ? lcd.write(byte(1)) : lcd.print(F(" "));
|
||||
gw.request(i, V_VAR2);
|
||||
delay(100);
|
||||
}
|
||||
while (gw.process() == false)
|
||||
{
|
||||
lcd.setCursor(15, 0);
|
||||
flashIcon = !flashIcon;
|
||||
flashIcon ? lcd.write(byte(1)) : lcd.print(F(" "));
|
||||
gw.request(i, V_VAR3);
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
lcd.clear();
|
||||
}
|
||||
//
|
||||
void loop()
|
||||
{
|
||||
gw.process();
|
||||
updateClock();
|
||||
updateDisplay();
|
||||
goGetValveTimes();
|
||||
//
|
||||
if (buttonPushed)
|
||||
{
|
||||
menuTimer = millis();
|
||||
DEBUG_PRINTLN(F("Button Pressed"));
|
||||
if (state == STAND_BY_ALL_OFF)
|
||||
{
|
||||
state = ZONE_SELECT_MENU;
|
||||
menuState = 0;
|
||||
}
|
||||
else if (state == ZONE_SELECT_MENU)
|
||||
{
|
||||
menuState++;
|
||||
if (menuState > NUMBER_OF_VALVES)
|
||||
{
|
||||
menuState = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = STAND_BY_ALL_OFF;
|
||||
}
|
||||
buttonPushed = false;
|
||||
}
|
||||
if (state == STAND_BY_ALL_OFF)
|
||||
{
|
||||
slowToggleLED ();
|
||||
if (state != lastState)
|
||||
{
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
DEBUG_PRINTLN(F("State Changed... all Zones off"));
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
delay(50);
|
||||
gw.send(msg1valve.setSensor(i).set(false), false);
|
||||
}
|
||||
lcd.clear();
|
||||
lcd.setCursor(0,0);
|
||||
lcd.print(F("** Irrigation **"));
|
||||
lcd.setCursor(0,1);
|
||||
lcd.print(F("** Halted **"));
|
||||
delay(2000);
|
||||
lastValve = -1;
|
||||
}
|
||||
}
|
||||
//
|
||||
else if (state == RUN_ALL_ZONES)
|
||||
{
|
||||
if (lastValve != valveNumber)
|
||||
{
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
if (i == 0 || i == valveNumber)
|
||||
{
|
||||
gw.send(msg1valve.setSensor(i).set(true), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
gw.send(msg1valve.setSensor(i).set(false), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastValve = valveNumber;
|
||||
fastToggleLed();
|
||||
if (state != lastState)
|
||||
{
|
||||
valveNumber = 1;
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
DEBUG_PRINTLN(F("State Changed, Running All Zones..."));
|
||||
}
|
||||
unsigned long nowMillis = millis();
|
||||
if (nowMillis - startMillis < VALVE_RESET_TIME)
|
||||
{
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
}
|
||||
else if (nowMillis - startMillis < (allZoneTime[valveNumber] * 60000UL))
|
||||
{
|
||||
updateRelays(BITSHIFT_VALVE_NUMBER);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Changing Valves..."));
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
startMillis = millis();
|
||||
valveNumber++;
|
||||
if (valveNumber > NUMBER_OF_VALVES)
|
||||
{
|
||||
state = CYCLE_COMPLETE;
|
||||
startMillis = millis();
|
||||
lastValve = -1;
|
||||
lastTimeRun = now();
|
||||
saveDateToEEPROM(lastTimeRun);
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
gw.send(msg1valve.setSensor(i).set(false), false);
|
||||
}
|
||||
DEBUG_PRINT(F("State = "));
|
||||
DEBUG_PRINTLN(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
else if (state == RUN_SINGLE_ZONE)
|
||||
{
|
||||
fastToggleLed();
|
||||
if (state != lastState)
|
||||
{
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
if (i == 0 || i == valveNumber)
|
||||
{
|
||||
gw.send(msg1valve.setSensor(i).set(true), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
gw.send(msg1valve.setSensor(i).set(false), false);
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F("State Changed, Single Zone Running..."));
|
||||
DEBUG_PRINT(F("Zone: "));
|
||||
DEBUG_PRINTLN(valveNumber);
|
||||
}
|
||||
unsigned long nowMillis = millis();
|
||||
if (nowMillis - startMillis < VALVE_RESET_TIME)
|
||||
{
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
}
|
||||
else if (nowMillis - startMillis < (valveSoloTime [valveNumber] * 60000UL))
|
||||
{
|
||||
updateRelays(BITSHIFT_VALVE_NUMBER);
|
||||
}
|
||||
else
|
||||
{
|
||||
updateRelays(ALL_VALVES_OFF);
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
gw.send(msg1valve.setSensor(i).set(false), false);
|
||||
}
|
||||
state = CYCLE_COMPLETE;
|
||||
startMillis = millis();
|
||||
DEBUG_PRINT(F("State = "));
|
||||
DEBUG_PRINTLN(state);
|
||||
}
|
||||
lastTimeRun = now();
|
||||
}
|
||||
else if (state == CYCLE_COMPLETE)
|
||||
{
|
||||
if (millis() - startMillis < 30000UL)
|
||||
{
|
||||
fastToggleLed();
|
||||
}
|
||||
else
|
||||
{
|
||||
state = STAND_BY_ALL_OFF;
|
||||
}
|
||||
}
|
||||
else if (state = ZONE_SELECT_MENU)
|
||||
{
|
||||
displayMenu();
|
||||
}
|
||||
lastState = state;
|
||||
}
|
||||
//
|
||||
void displayMenu(void)
|
||||
{
|
||||
static byte lastMenuState = -1;
|
||||
static int lastSecond;
|
||||
if (menuState != lastMenuState)
|
||||
{
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print(valveNickName[menuState]);
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F("Starting"));
|
||||
DEBUG_PRINT(valveNickName[menuState]);
|
||||
Serial.print(F(" Starting Shortly"));
|
||||
}
|
||||
int thisSecond = (millis() - menuTimer) / 1000UL;
|
||||
if (thisSecond != lastSecond && thisSecond < 8)
|
||||
{
|
||||
lcd.print(F("."));
|
||||
Serial.print(".");
|
||||
}
|
||||
lastSecond = thisSecond;
|
||||
if (millis() - menuTimer > 10000UL)
|
||||
{
|
||||
startMillis = millis();
|
||||
if (menuState == 0)
|
||||
{
|
||||
valveNumber = 1;
|
||||
state = RUN_ALL_ZONES;
|
||||
}
|
||||
else
|
||||
{
|
||||
valveNumber = menuState;
|
||||
state = RUN_SINGLE_ZONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
lastMenuState = menuState;
|
||||
}
|
||||
//
|
||||
void updateRelays(int value)
|
||||
{
|
||||
digitalWrite(latchPin, LOW);
|
||||
shiftOut(dataPin, clockPin, MSBFIRST, highByte(value));
|
||||
shiftOut(dataPin, clockPin, MSBFIRST, lowByte(value));
|
||||
digitalWrite(latchPin, HIGH);
|
||||
}
|
||||
//
|
||||
void PushButton() //interrupt with debounce
|
||||
{
|
||||
static unsigned long last_interrupt_time = 0;
|
||||
unsigned long interrupt_time = millis();
|
||||
if (interrupt_time - last_interrupt_time > 200)
|
||||
{
|
||||
buttonPushed = true;
|
||||
}
|
||||
last_interrupt_time = interrupt_time;
|
||||
}
|
||||
//
|
||||
void fastToggleLed()
|
||||
{
|
||||
static unsigned long fastLedTimer;
|
||||
if (millis() - fastLedTimer >= 100UL)
|
||||
{
|
||||
digitalWrite(ledPin, !digitalRead(ledPin));
|
||||
fastLedTimer = millis ();
|
||||
}
|
||||
}
|
||||
//
|
||||
void slowToggleLED ()
|
||||
{
|
||||
static unsigned long slowLedTimer;
|
||||
if (millis() - slowLedTimer >= 1250UL)
|
||||
{
|
||||
digitalWrite(ledPin, !digitalRead(ledPin));
|
||||
slowLedTimer = millis ();
|
||||
}
|
||||
}
|
||||
//
|
||||
void getVariables(const MyMessage &message)
|
||||
{
|
||||
boolean zoneTimeUpdate = false;
|
||||
if (message.isAck())
|
||||
{
|
||||
DEBUG_PRINTLN(F("This is an ack from gateway"));
|
||||
}
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
if (message.sensor == i)
|
||||
{
|
||||
if (message.type == V_LIGHT)
|
||||
{
|
||||
int switchState = atoi(message.data);
|
||||
if (switchState == 0)
|
||||
{
|
||||
state = STAND_BY_ALL_OFF;
|
||||
DEBUG_PRINTLN(F("Recieved Instruction to Cancel..."));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
state = RUN_ALL_ZONES;
|
||||
valveNumber = 1;
|
||||
DEBUG_PRINTLN(F("Recieved Instruction to Run All Zones..."));
|
||||
}
|
||||
else
|
||||
{
|
||||
state = RUN_SINGLE_ZONE;
|
||||
valveNumber = i;
|
||||
DEBUG_PRINT(F("Recieved Instruction to Activate Zone: "));
|
||||
DEBUG_PRINTLN(i);
|
||||
}
|
||||
}
|
||||
startMillis = millis();
|
||||
}
|
||||
else if (message.type == V_VAR1)
|
||||
{
|
||||
int variable1 = atoi(message.data);// RUN_ALL_ZONES time
|
||||
DEBUG_PRINT(F("Recieved variable1 valve:"));
|
||||
DEBUG_PRINT(i);
|
||||
DEBUG_PRINT(F(" = "));
|
||||
DEBUG_PRINTLN(variable1);
|
||||
if (variable1 != allZoneTime[i])
|
||||
{
|
||||
allZoneTime[i] = variable1;
|
||||
|
||||
zoneTimeUpdate = true;
|
||||
}
|
||||
}
|
||||
else if (message.type == V_VAR2)
|
||||
{
|
||||
int variable2 = atoi(message.data);// RUN_SINGLE_ZONE time
|
||||
DEBUG_PRINT(F("Recieved variable2 valve:"));
|
||||
DEBUG_PRINT(i);
|
||||
DEBUG_PRINT(F(" = "));
|
||||
DEBUG_PRINTLN(variable2);
|
||||
if (variable2 != valveSoloTime[i])
|
||||
{
|
||||
valveSoloTime[i] = variable2;
|
||||
zoneTimeUpdate = true;
|
||||
}
|
||||
}
|
||||
else if (message.type == V_VAR3)
|
||||
{
|
||||
String newMessage = String(message.data);
|
||||
if (newMessage.length() == 0)
|
||||
{
|
||||
DEBUG_PRINT(F("No Name Recieved for zone "));
|
||||
DEBUG_PRINTLN(i);
|
||||
break;
|
||||
}
|
||||
if (newMessage.length() > 16)
|
||||
{
|
||||
newMessage.substring(0, 16);
|
||||
}
|
||||
valveNickName[i] = "";
|
||||
valveNickName[i] += newMessage;
|
||||
DEBUG_PRINT(F("Recieved new name for zone "));
|
||||
DEBUG_PRINT(i);
|
||||
DEBUG_PRINT(F(" and it is now called: "));
|
||||
DEBUG_PRINTLN(valveNickName[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (zoneTimeUpdate)
|
||||
{
|
||||
//
|
||||
DEBUG_PRINTLN(F("New Zone Times Recieved..."));
|
||||
for (byte i = 0; i <= NUMBER_OF_VALVES; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
DEBUG_PRINT(F("Zone "));
|
||||
DEBUG_PRINT(i);
|
||||
DEBUG_PRINT(F(" individual time: "));
|
||||
DEBUG_PRINT(valveSoloTime[i]);
|
||||
DEBUG_PRINT(F(" group time: "));
|
||||
DEBUG_PRINTLN(allZoneTime[i]);
|
||||
recentUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
recentUpdate = false;
|
||||
}
|
||||
}
|
||||
//
|
||||
void updateDisplay()
|
||||
{
|
||||
static unsigned long lastUpdateTime;
|
||||
static boolean displayToggle = false;
|
||||
//static byte toggleCounter = 0;
|
||||
static SprinklerStates lastDisplayState;
|
||||
if (state != lastDisplayState || millis() - lastUpdateTime >= 3000UL)
|
||||
{
|
||||
displayToggle = !displayToggle;
|
||||
switch (state) {
|
||||
case STAND_BY_ALL_OFF:
|
||||
//
|
||||
fastClear();
|
||||
lcd.setCursor(0, 0);
|
||||
if (displayToggle)
|
||||
{
|
||||
lcd.print(F(" System Ready "));
|
||||
if (clockUpdating)
|
||||
{
|
||||
lcd.setCursor(15, 0);
|
||||
lcd.write(byte(0));
|
||||
}
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(hourFormat12() < 10 ? F(" ") : F(""));
|
||||
lcd.print(hourFormat12());
|
||||
lcd.print(minute() < 10 ? F(":0") : F(":"));
|
||||
lcd.print(minute());
|
||||
lcd.print(isAM() ? F("am") : F("pm"));
|
||||
lcd.print(month() < 10 ? F(" 0") : F(" "));
|
||||
lcd.print(month());
|
||||
lcd.print(day() < 10 ? F("/0") : F("/"));
|
||||
lcd.print(day());
|
||||
lcd.print(F("/"));
|
||||
lcd.print(year() % 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
lcd.print(F(" Last Watered "));
|
||||
if (clockUpdating)
|
||||
{
|
||||
lcd.setCursor(15, 0);
|
||||
lcd.write(byte(0));
|
||||
}
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(dayOfWeek[weekday(lastTimeRun)]);
|
||||
lcd.setCursor(11, 1);
|
||||
lcd.print(month(lastTimeRun) < 10 ? F(" ") : F(""));
|
||||
lcd.print(month(lastTimeRun));
|
||||
lcd.print(day(lastTimeRun) < 10 ? F("/0") : F("/"));
|
||||
lcd.print(day(lastTimeRun));
|
||||
}
|
||||
break;
|
||||
case RUN_SINGLE_ZONE:
|
||||
//
|
||||
fastClear();
|
||||
lcd.setCursor(0, 0);
|
||||
if (displayToggle)
|
||||
{
|
||||
lcd.print(F("Single Zone Mode"));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F(" Zone:"));
|
||||
if (valveNumber < 10) lcd.print(F("0"));
|
||||
lcd.print(valveNumber);
|
||||
lcd.print(F(" Active"));
|
||||
}
|
||||
else
|
||||
{
|
||||
lcd.print(F(" Time Remaining "));
|
||||
lcd.setCursor(0, 1);
|
||||
if (valveSoloTime[valveNumber] == 0)
|
||||
{
|
||||
lcd.print(F(" No Valve Time "));
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long timeRemaining = (valveSoloTime[valveNumber] * 60) - ((millis() - startMillis) / 1000);
|
||||
lcd.print(timeRemaining / 60 < 10 ? " 0" : " ");
|
||||
lcd.print(timeRemaining / 60);
|
||||
lcd.print("min");
|
||||
lcd.print(timeRemaining % 60 < 10 ? " 0" : " ");
|
||||
lcd.print(timeRemaining % 60);
|
||||
lcd.print("sec ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RUN_ALL_ZONES:
|
||||
//
|
||||
fastClear();
|
||||
lcd.setCursor(0, 0);
|
||||
if (displayToggle)
|
||||
{
|
||||
lcd.print(F(" All-Zone Mode "));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F(" Zone:"));
|
||||
if (valveNumber < 10) lcd.print(F("0"));
|
||||
lcd.print(valveNumber);
|
||||
lcd.print(F(" Active "));
|
||||
}
|
||||
else
|
||||
{
|
||||
lcd.print(F(" Time Remaining "));
|
||||
lcd.setCursor(0, 1);
|
||||
int timeRemaining = (allZoneTime[valveNumber] * 60) - ((millis() - startMillis) / 1000);
|
||||
lcd.print((timeRemaining / 60) < 10 ? " 0" : " ");
|
||||
lcd.print(timeRemaining / 60);
|
||||
lcd.print("min");
|
||||
lcd.print(timeRemaining % 60 < 10 ? " 0" : " ");
|
||||
lcd.print(timeRemaining % 60);
|
||||
lcd.print("sec ");
|
||||
}
|
||||
break;
|
||||
case CYCLE_COMPLETE:
|
||||
//
|
||||
if (displayToggle)
|
||||
{
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print(F(" Watering Cycle "));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F(" Complete "));
|
||||
}
|
||||
else
|
||||
{
|
||||
int totalTimeRan = 0;
|
||||
for (int i = 1; i < NUMBER_OF_VALVES + 1; i++)
|
||||
{
|
||||
totalTimeRan += allZoneTime[i];
|
||||
}
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print(F(" Total Time Run "));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(totalTimeRan < 10 ? " 0" : " ");
|
||||
lcd.print(totalTimeRan);
|
||||
lcd.print(" Minutes ");
|
||||
}
|
||||
}
|
||||
lastUpdateTime = millis();
|
||||
}
|
||||
lastDisplayState = state;
|
||||
}
|
||||
void receiveTime(time_t newTime)
|
||||
{
|
||||
DEBUG_PRINTLN(F("Time value received and updated..."));
|
||||
int lastSecond = second();
|
||||
int lastMinute = minute();
|
||||
int lastHour = hour();
|
||||
setTime(newTime);
|
||||
if (((second() != lastSecond) || (minute() != lastMinute) || (hour() != lastHour)) || showTime)
|
||||
{
|
||||
DEBUG_PRINTLN(F("Clock updated...."));
|
||||
DEBUG_PRINT(F("Sensor's time currently set to:"));
|
||||
DEBUG_PRINT(hourFormat12() < 10 ? F(" 0") : F(" "));
|
||||
DEBUG_PRINT(hourFormat12());
|
||||
DEBUG_PRINT(minute() < 10 ? F(":0") : F(":"));
|
||||
DEBUG_PRINT(minute());
|
||||
DEBUG_PRINTLN(isAM() ? F("am") : F("pm"));
|
||||
DEBUG_PRINT(month());
|
||||
DEBUG_PRINT(F("/"));
|
||||
DEBUG_PRINT(day());
|
||||
DEBUG_PRINT(F("/"));
|
||||
DEBUG_PRINTLN(year());
|
||||
DEBUG_PRINTLN(dayOfWeek[weekday()]);
|
||||
showTime = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_PRINTLN(F("Sensor's time did NOT need adjustment greater than 1 second."));
|
||||
}
|
||||
clockUpdating = false;
|
||||
}
|
||||
void fastClear()
|
||||
{
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print(F(" "));
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(F(" "));
|
||||
}
|
||||
//
|
||||
void updateClock()
|
||||
{
|
||||
static unsigned long lastVeraGetTime;
|
||||
if (millis() - lastVeraGetTime >= 3600000UL) // updates clock time and gets zone times from vera once every hour
|
||||
{
|
||||
DEBUG_PRINTLN(F("Requesting time and valve data from Gateway..."));
|
||||
lcd.setCursor(15, 0);
|
||||
lcd.write(byte(0));
|
||||
clockUpdating = true;
|
||||
gw.requestTime(receiveTime);
|
||||
lastVeraGetTime = millis();
|
||||
}
|
||||
}
|
||||
//
|
||||
void saveDateToEEPROM(unsigned long theDate)
|
||||
{
|
||||
DEBUG_PRINTLN(F("Saving Last Run date"));
|
||||
if (gw.loadState(0) != 0xFF)
|
||||
{
|
||||
gw.saveState(0, 0xFF); // EEPROM flag for last date saved stored in EEPROM (location zero)
|
||||
}
|
||||
//
|
||||
for (int i = 1; i < 5; i++)
|
||||
{
|
||||
gw.saveState(5 - i, byte(theDate >> 8 * (i - 1))); // store epoch datestamp in 4 bytes of EEPROM starting in location one
|
||||
}
|
||||
}
|
||||
//
|
||||
void goGetValveTimes()
|
||||
{
|
||||
static unsigned long valveUpdateTime;
|
||||
static byte valveIndex = 1;
|
||||
if (millis() - valveUpdateTime >= 300000UL / NUMBER_OF_VALVES)// update each valve once every 5 mins (distributes the traffic)
|
||||
{
|
||||
DEBUG_PRINTLN(F("Calling for Valve Data..."));
|
||||
lcd.setCursor(15, 0);
|
||||
lcd.write(byte(1)); //lcd.write(1);
|
||||
gw.request(valveIndex, V_VAR1);
|
||||
gw.request(valveIndex, V_VAR2);
|
||||
gw.request(valveIndex, V_VAR3);
|
||||
valveUpdateTime = millis();
|
||||
valveIndex++;
|
||||
if (valveIndex > NUMBER_OF_VALVES + 1)
|
||||
{
|
||||
valveIndex = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/****
|
||||
* Formatted logging to the Serial console.
|
||||
* Compiled in by setting LOGDEBUG
|
||||
*
|
||||
* 2015-05-25 Bruce Lacey V1.0
|
||||
*
|
||||
* Based upon Arduino Playground prior art and should be moved to
|
||||
* the MySensors library at some point as a common debug logging facility
|
||||
*/
|
||||
#ifndef MYSLog_h
|
||||
#define MYSLog_h
|
||||
|
||||
#define LOGDEBUG 1
|
||||
|
||||
#if defined ( LOGDEBUG )
|
||||
#define LOG(fmt, args... ) log( fmt, ## args );
|
||||
#else
|
||||
#define log(fmt, args... )
|
||||
#endif
|
||||
|
||||
void log(const char *fmt, ... ) {
|
||||
char buff[128];
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
vsnprintf(buff, sizeof(buff), fmt, args);
|
||||
va_end (args);
|
||||
buff[sizeof(buff)/sizeof(buff[0])-1]='\0';
|
||||
Serial.print(buff);
|
||||
}
|
||||
|
||||
void log(const __FlashStringHelper *fmt, ... ){
|
||||
char buf[128]; // resulting string limited to 128 chars
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
#ifdef __AVR__
|
||||
vsnprintf_P(buf, sizeof(buf), (const char *)fmt, args); // progmem for AVR
|
||||
#else
|
||||
vsnprintf(buf, sizeof(buf), (const char *)fmt, args); // for the rest of the world
|
||||
#endif
|
||||
va_end(args);
|
||||
Serial.print(buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
/***
|
||||
* This is a simple sketch used to demenstrate and test node-to-node MySensor's communication.
|
||||
* To use this sketch, assemble MySensors nodes - they need nothing more than a radio
|
||||
* 1. Flash each node with the same sketch, open the console and type either 0 or 1 to the respective nodes to set thei ID
|
||||
* 2. You only need to set the node id once, and restart the nodes
|
||||
* 3. To being a ping-pong test, simply type T in the console for one of the nodes.
|
||||
*
|
||||
* 2015-05-25 Bruce Lacey v1.0
|
||||
*/
|
||||
|
||||
#include <MySensor.h>
|
||||
#include <SPI.h>
|
||||
#include "MYSLog.h"
|
||||
|
||||
#define VSN "v1.0"
|
||||
|
||||
// Define two generic nodes with a single child
|
||||
#define YING 200
|
||||
#define YANG 201
|
||||
#define CHILD 1
|
||||
|
||||
// Sensor objects
|
||||
MySensor gw;
|
||||
|
||||
MyMessage mPing(CHILD, V_VAR1); //Ping message
|
||||
MyMessage mPong(CHILD, V_VAR2); //Pong message
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
gw.begin(incomingMessage); // Node ids are written to EEPROM during sensor node configuration by entering a 0 or 1
|
||||
gw.present(CHILD, S_CUSTOM); //
|
||||
|
||||
gw.sendSketchInfo( nodeTypeAsCharRepresentation( gw.getNodeId() ), VSN );
|
||||
LOG(F("\n%sReady.\n"), nodeTypeAsCharRepresentation(gw.getNodeId()));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// Interactive command and control
|
||||
// Entering a number from 0 or 1 will write the node 200 (YING) or 201 (YANG) to EEPROM
|
||||
// Entering T on either node will initiatve a ping-pong test.
|
||||
if (Serial.available()) {
|
||||
byte inChar = Serial.read();
|
||||
uint8_t node = gw.getNodeId();
|
||||
|
||||
// Manual Test Mode
|
||||
if (inChar == 'T' || inChar == 't') {
|
||||
LOG(F("T received - starting test...\n"));
|
||||
MyMessage msg = mPong;
|
||||
msg.sender = (node == YING ? YANG : YING);
|
||||
sendPingOrPongResponse( msg );
|
||||
}
|
||||
else if (inChar == '0' or inChar == '1') {
|
||||
byte nodeID = 200 + (inChar - '0');
|
||||
setNodeId(nodeID);
|
||||
}
|
||||
else {
|
||||
LOG("Invalid input\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Process sensor messages
|
||||
gw.process();
|
||||
}
|
||||
|
||||
void incomingMessage(const MyMessage &message) {
|
||||
|
||||
LOG(F("Received %s from %s\n"), msgTypeAsCharRepresentation((mysensor_data)message.type), nodeTypeAsCharRepresentation(message.sender));
|
||||
|
||||
delay(250);
|
||||
sendPingOrPongResponse( message );
|
||||
}
|
||||
|
||||
void sendPingOrPongResponse( MyMessage msg ) {
|
||||
|
||||
MyMessage response = (msg.type == V_VAR1 ? mPong : mPing);
|
||||
|
||||
LOG(F("Sending %s to %s\n"), msgTypeAsCharRepresentation( (mysensor_data)response.type ), nodeTypeAsCharRepresentation(msg.sender));
|
||||
|
||||
// Set payload to current time in millis to ensure each message is unique
|
||||
response.set( millis() );
|
||||
response.setDestination(msg.sender);
|
||||
gw.send(response);
|
||||
}
|
||||
|
||||
void setNodeId(byte nodeID) {
|
||||
LOG(F("Setting node id to: %i.\n***Please restart the node for changes to take effect.\n"), nodeID);
|
||||
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, (byte)nodeID);
|
||||
}
|
||||
|
||||
const char * msgTypeAsCharRepresentation( mysensor_data mType ) {
|
||||
return mType == V_VAR1 ? "Ping" : "Pong";
|
||||
}
|
||||
|
||||
const char * nodeTypeAsCharRepresentation( uint8_t node ) {
|
||||
return node == YING ? "Ying Node" : "Yang Node";
|
||||
}
|
||||
@@ -73,7 +73,7 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
float pressure = bmp.readSealevelPressure(205)/100; // 205 meters above sealevel
|
||||
float pressure = bmp.readSealevelPressure(205)/100.0; // 205 meters above sealevel
|
||||
float temperature = bmp.readTemperature();
|
||||
|
||||
if (!metric) {
|
||||
|
||||
+128
-83
@@ -1,54 +1,35 @@
|
||||
/**
|
||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||
* between your home built sensors/actuators and HA controller of choice.
|
||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* Copyright (C) 2013-2015 Sensnology AB
|
||||
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
|
||||
*
|
||||
* Documentation: http://www.mysensors.org
|
||||
* Support Forum: http://forum.mysensors.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*******************************
|
||||
*
|
||||
* REVISION HISTORY
|
||||
* Version 1.0 - Thomas Bowman Mørch
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Default sensor sketch for MySensor Sensebender Micro module
|
||||
* Act as a temperature / humidity sensor by default.
|
||||
*
|
||||
* If A0 is held low while powering on, it will enter testmode, which verifies all on-board peripherals
|
||||
*
|
||||
* Battery voltage is repported as child sensorId 199, as well as battery percentage
|
||||
*
|
||||
*/
|
||||
|
||||
// Default sensor sketch for Sensebender Micro module
|
||||
// Act as a temperature / humidity sensor by default.
|
||||
//
|
||||
// If A0 is held low while powering on, it will enter testmode, which verifies all on-board peripherals
|
||||
//
|
||||
// Battery voltage is as battery percentage (Internal message), and optionally as a sensor value (See defines below)
|
||||
|
||||
|
||||
#include <MySensor.h>
|
||||
#include <Wire.h>
|
||||
#include <SI7021.h>
|
||||
#include <SPI.h>
|
||||
#include <SPIFlash.h>
|
||||
#include "utility/SPIFlash.h"
|
||||
#include <EEPROM.h>
|
||||
#include <sha204_lib_return_codes.h>
|
||||
#include <sha204_library.h>
|
||||
#include <RunningAverage.h>
|
||||
#include <avr/power.h>
|
||||
|
||||
// Define a static node address, remove if you want auto address assignment
|
||||
//#define NODE_ADDRESS 3
|
||||
|
||||
// Uncomment the line below, to transmit battery voltage as a normal sensor value
|
||||
//#define BATT_SENSOR 199
|
||||
|
||||
#define RELEASE "1.2"
|
||||
|
||||
#define AVERAGES 2
|
||||
|
||||
// Child sensor ID's
|
||||
#define CHILD_ID_TEMP 1
|
||||
#define CHILD_ID_HUM 2
|
||||
#define CHILD_ID_BATT 199
|
||||
|
||||
// How many milli seconds between each measurement
|
||||
#define MEASURE_INTERVAL 60000
|
||||
@@ -59,7 +40,7 @@
|
||||
// When MEASURE_INTERVAL is 60000 and FORCE_TRANSMIT_INTERVAL is 30, we force a transmission every 30 minutes.
|
||||
// Between the forced transmissions a tranmission will only occur if the measured value differs from the previous measurement
|
||||
|
||||
//Pin definitions
|
||||
// Pin definitions
|
||||
#define TEST_PIN A0
|
||||
#define LED_PIN A2
|
||||
#define ATSHA204_PIN 17 // A3
|
||||
@@ -75,32 +56,53 @@ MySensor gw;
|
||||
// Sensor messages
|
||||
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
|
||||
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
|
||||
MyMessage msgBattery(CHILD_ID_BATT, V_VOLTAGE);
|
||||
|
||||
#ifdef BATT_SENSOR
|
||||
MyMessage msgBatt(BATT_SENSOR, V_VOLTAGE);
|
||||
#endif
|
||||
|
||||
// Global settings
|
||||
int measureCount = 0;
|
||||
int sendBattery = 0;
|
||||
boolean isMetric = true;
|
||||
boolean highfreq = true;
|
||||
|
||||
// Storage of old measurements
|
||||
float lastTemperature = -100;
|
||||
int lastHumidity = -100;
|
||||
long lastBattery = -100;
|
||||
|
||||
RunningAverage raHum(AVERAGES);
|
||||
RunningAverage raTemp(AVERAGES);
|
||||
|
||||
/****************************************************
|
||||
*
|
||||
* Setup code
|
||||
*
|
||||
****************************************************/
|
||||
void setup() {
|
||||
|
||||
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.print(F("Sensebender Micro FW "));
|
||||
Serial.print(RELEASE);
|
||||
Serial.flush();
|
||||
|
||||
// First check if we should boot into test mode
|
||||
|
||||
pinMode(TEST_PIN,INPUT);
|
||||
digitalWrite(TEST_PIN, HIGH); // Enable pullup
|
||||
if (!digitalRead(TEST_PIN)) testMode();
|
||||
|
||||
// Make sure that ATSHA204 is not floating
|
||||
pinMode(ATSHA204_PIN, INPUT);
|
||||
digitalWrite(ATSHA204_PIN, HIGH);
|
||||
|
||||
digitalWrite(TEST_PIN,LOW);
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
|
||||
|
||||
#ifdef NODE_ADDRESS
|
||||
gw.begin(NULL, NODE_ADDRESS, false);
|
||||
#else
|
||||
@@ -110,85 +112,137 @@ void setup() {
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
|
||||
humiditySensor.begin();
|
||||
|
||||
gw.sendSketchInfo("MysensorMicro", "1.0");
|
||||
Serial.flush();
|
||||
Serial.println(F(" - Online!"));
|
||||
gw.sendSketchInfo("Sensebender Micro", RELEASE);
|
||||
|
||||
gw.present(CHILD_ID_TEMP,S_TEMP);
|
||||
gw.present(CHILD_ID_HUM,S_HUM);
|
||||
|
||||
gw.present(CHILD_ID_BATT, S_POWER);
|
||||
switchClock(1<<CLKPS2); // Switch to 1Mhz for the reminder of the sketch, save power.
|
||||
#ifdef BATT_SENSOR
|
||||
gw.present(BATT_SENSOR, S_POWER);
|
||||
#endif
|
||||
|
||||
isMetric = gw.getConfig().isMetric;
|
||||
Serial.print(F("isMetric: ")); Serial.println(isMetric);
|
||||
raHum.clear();
|
||||
raTemp.clear();
|
||||
sendTempHumidityMeasurements(false);
|
||||
sendBattLevel(false);
|
||||
}
|
||||
|
||||
|
||||
// Main loop function
|
||||
/***********************************************
|
||||
*
|
||||
* Main loop function
|
||||
*
|
||||
***********************************************/
|
||||
void loop() {
|
||||
measureCount ++;
|
||||
sendBattery ++;
|
||||
bool forceTransmit = false;
|
||||
|
||||
if (measureCount > FORCE_TRANSMIT_INTERVAL
|
||||
) { // force a transmission
|
||||
if ((measureCount == 5) && highfreq)
|
||||
{
|
||||
clock_prescale_set(clock_div_8); // Switch to 1Mhz for the reminder of the sketch, save power.
|
||||
highfreq = false;
|
||||
}
|
||||
|
||||
if (measureCount > FORCE_TRANSMIT_INTERVAL) { // force a transmission
|
||||
forceTransmit = true;
|
||||
measureCount = 0;
|
||||
}
|
||||
|
||||
|
||||
gw.process();
|
||||
sendBattLevel(forceTransmit);
|
||||
|
||||
sendTempHumidityMeasurements(forceTransmit);
|
||||
if (sendBattery > 60)
|
||||
{
|
||||
sendBattLevel(forceTransmit); // Not needed to send battery info that often
|
||||
sendBattery = 0;
|
||||
}
|
||||
|
||||
gw.sleep(MEASURE_INTERVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Sends temperature and humidity from Si7021 sensor
|
||||
*
|
||||
* Parameters
|
||||
* - force : Forces transmission of a value (even if it's the same as previous measurement)
|
||||
*/
|
||||
*
|
||||
*********************************************/
|
||||
void sendTempHumidityMeasurements(bool force)
|
||||
{
|
||||
if (force) {
|
||||
lastHumidity = -100;
|
||||
lastTemperature = -100;
|
||||
}
|
||||
bool tx = force;
|
||||
|
||||
si7021_env data = humiditySensor.getHumidityAndTemperature();
|
||||
float oldAvgTemp = raTemp.getAverage();
|
||||
float oldAvgHum = raHum.getAverage();
|
||||
|
||||
float temperature = data.celsiusHundredths/100;
|
||||
|
||||
int humidity = data.humidityPercent;
|
||||
raTemp.addValue(data.celsiusHundredths);
|
||||
raHum.addValue(data.humidityPercent);
|
||||
|
||||
float diffTemp = abs(lastTemperature - data.celsiusHundredths/100);
|
||||
float diffHum = abs(oldAvgHum - raHum.getAverage());
|
||||
|
||||
if (lastTemperature != temperature) {
|
||||
Serial.print(F("TempDiff :"));Serial.println(diffTemp);
|
||||
Serial.print(F("HumDiff :"));Serial.println(diffHum);
|
||||
|
||||
if (isnan(diffTemp)) tx = true;
|
||||
if (diffTemp > 0.3) tx = true;
|
||||
if (diffHum >= 0.5) tx = true;
|
||||
|
||||
if (tx) {
|
||||
measureCount = 0;
|
||||
float temperature = (isMetric ? data.celsiusHundredths : data.fahrenheitHundredths) / 100.0;
|
||||
|
||||
int humidity = data.humidityPercent;
|
||||
Serial.print("T: ");Serial.println(temperature);
|
||||
Serial.print("H: ");Serial.println(humidity);
|
||||
|
||||
gw.send(msgTemp.set(temperature,1));
|
||||
lastTemperature = temperature;
|
||||
}
|
||||
if (lastHumidity != humidity) {
|
||||
gw.send(msgHum.set(humidity));
|
||||
lastTemperature = temperature;
|
||||
lastHumidity = humidity;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends battery information (both voltage, and battery percentage)
|
||||
/********************************************
|
||||
*
|
||||
* Sends battery information (battery percentage)
|
||||
*
|
||||
* Parameters
|
||||
* - force : Forces transmission of a value
|
||||
*/
|
||||
*
|
||||
*******************************************/
|
||||
void sendBattLevel(bool force)
|
||||
{
|
||||
if (force) lastBattery = -1;
|
||||
long vcc = readVcc();
|
||||
if (vcc != lastBattery) {
|
||||
lastBattery = vcc;
|
||||
|
||||
#ifdef BATT_SENSOR
|
||||
gw.send(msgBatt.set(vcc));
|
||||
#endif
|
||||
|
||||
// Calculate percentage
|
||||
gw.send(msgBattery.set(vcc));
|
||||
|
||||
vcc = vcc - 1900; // subtract 1.9V from vcc, as this is the lowest voltage we will operate at
|
||||
|
||||
long percent = vcc / 14;
|
||||
long percent = vcc / 14.0;
|
||||
gw.sendBatteryLevel(percent);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************
|
||||
*
|
||||
* Internal battery ADC measuring
|
||||
*
|
||||
*******************************************/
|
||||
long readVcc() {
|
||||
// Read 1.1V reference against AVcc
|
||||
// set the reference to Vcc and the measurement to the internal 1.1V reference
|
||||
@@ -215,17 +269,11 @@ long readVcc() {
|
||||
return result; // Vcc in millivolts
|
||||
}
|
||||
|
||||
void switchClock(unsigned char clk)
|
||||
{
|
||||
cli();
|
||||
|
||||
CLKPR = 1<<CLKPCE; // Set CLKPCE to enable clk switching
|
||||
CLKPR = clk;
|
||||
sei();
|
||||
}
|
||||
|
||||
|
||||
// Verify all peripherals, and signal via the LED if any problems.
|
||||
/****************************************************
|
||||
*
|
||||
* Verify all peripherals, and signal via the LED if any problems.
|
||||
*
|
||||
****************************************************/
|
||||
void testMode()
|
||||
{
|
||||
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
|
||||
@@ -233,7 +281,7 @@ void testMode()
|
||||
byte tests = 0;
|
||||
|
||||
digitalWrite(LED_PIN, HIGH); // Turn on LED.
|
||||
|
||||
Serial.println(F(" - TestMode"));
|
||||
Serial.println(F("Testing peripherals!"));
|
||||
Serial.flush();
|
||||
Serial.print(F("-> SI7021 : "));
|
||||
@@ -305,7 +353,7 @@ void testMode()
|
||||
while (1) // Blink OK pattern!
|
||||
{
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
delay(800);
|
||||
delay(200);
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
delay(200);
|
||||
}
|
||||
@@ -315,10 +363,7 @@ void testMode()
|
||||
Serial.println(F("----> Selftest failed!"));
|
||||
while (1) // Blink FAILED pattern! Rappidly blinking..
|
||||
{
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// Feed this function the Arduino-ized pin number you want to assign to the ATSHA204's SDA pin
|
||||
// This will find the DDRX, PORTX, and PINX registrs it'll need to point to to control that pin
|
||||
// As well as the bit value for each of those registers
|
||||
atsha204Class::atsha204Class(uint8_t pin)
|
||||
ATSHA204Class::ATSHA204Class(uint8_t pin)
|
||||
{
|
||||
device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin
|
||||
uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers
|
||||
@@ -18,7 +18,7 @@ atsha204Class::atsha204Class(uint8_t pin)
|
||||
device_port_IN = portInputRegister(port);
|
||||
}
|
||||
|
||||
void atsha204Class::getSerialNumber(uint8_t * response)
|
||||
void ATSHA204Class::getSerialNumber(uint8_t * response)
|
||||
{
|
||||
uint8_t readCommand[READ_COUNT];
|
||||
uint8_t readResponse[READ_4_RSP_SIZE];
|
||||
@@ -48,7 +48,7 @@ void atsha204Class::getSerialNumber(uint8_t * response)
|
||||
|
||||
/* SWI bit bang functions */
|
||||
|
||||
void atsha204Class::swi_set_signal_pin(uint8_t is_high)
|
||||
void ATSHA204Class::swi_set_signal_pin(uint8_t is_high)
|
||||
{
|
||||
*device_port_DDR |= device_pin;
|
||||
|
||||
@@ -58,7 +58,7 @@ void atsha204Class::swi_set_signal_pin(uint8_t is_high)
|
||||
*device_port_OUT &= ~device_pin;
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
uint8_t ATSHA204Class::swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t i, bit_mask;
|
||||
|
||||
@@ -101,12 +101,12 @@ uint8_t atsha204Class::swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
return SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::swi_send_byte(uint8_t value)
|
||||
uint8_t ATSHA204Class::swi_send_byte(uint8_t value)
|
||||
{
|
||||
return swi_send_bytes(1, &value);
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
uint8_t ATSHA204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
uint8_t i;
|
||||
@@ -212,12 +212,12 @@ uint8_t atsha204Class::swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
|
||||
/* Physical functions */
|
||||
|
||||
void atsha204Class::sha204c_sleep()
|
||||
void ATSHA204Class::sha204c_sleep()
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_SLEEP);
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
uint8_t ATSHA204Class::sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
{
|
||||
uint8_t count_byte;
|
||||
uint8_t i;
|
||||
@@ -249,7 +249,7 @@ uint8_t atsha204Class::sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
|
||||
/* Communication functions */
|
||||
|
||||
uint8_t atsha204Class::sha204c_wakeup(uint8_t *response)
|
||||
uint8_t ATSHA204Class::sha204c_wakeup(uint8_t *response)
|
||||
{
|
||||
swi_set_signal_pin(0);
|
||||
delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH);
|
||||
@@ -277,7 +277,7 @@ uint8_t atsha204Class::sha204c_wakeup(uint8_t *response)
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::sha204c_resync(uint8_t size, uint8_t *response)
|
||||
uint8_t ATSHA204Class::sha204c_resync(uint8_t size, uint8_t *response)
|
||||
{
|
||||
// Try to re-synchronize without sending a Wake token
|
||||
// (step 1 of the re-synchronization process).
|
||||
@@ -298,7 +298,7 @@ uint8_t atsha204Class::sha204c_resync(uint8_t size, uint8_t *response)
|
||||
return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code);
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout)
|
||||
uint8_t ATSHA204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout)
|
||||
{
|
||||
uint8_t ret_code = SHA204_FUNC_FAIL;
|
||||
uint8_t ret_code_resync;
|
||||
@@ -440,7 +440,7 @@ uint8_t atsha204Class::sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_s
|
||||
|
||||
|
||||
/* Marshaling functions */
|
||||
uint8_t atsha204Class::sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
|
||||
uint8_t ATSHA204Class::sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
|
||||
{
|
||||
uint8_t rx_size;
|
||||
|
||||
@@ -457,7 +457,7 @@ uint8_t atsha204Class::sha204m_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint
|
||||
return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY, READ_EXEC_MAX - READ_DELAY);
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t ATSHA204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer)
|
||||
{
|
||||
uint8_t poll_delay, poll_timeout, response_size;
|
||||
@@ -534,7 +534,7 @@ uint8_t atsha204Class::sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t
|
||||
|
||||
/* CRC Calculator and Checker */
|
||||
|
||||
void atsha204Class::sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
|
||||
void ATSHA204Class::sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
|
||||
{
|
||||
uint8_t counter;
|
||||
uint16_t crc_register = 0;
|
||||
@@ -560,7 +560,7 @@ void atsha204Class::sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t
|
||||
crc[1] = (uint8_t) (crc_register >> 8);
|
||||
}
|
||||
|
||||
uint8_t atsha204Class::sha204c_check_crc(uint8_t *response)
|
||||
uint8_t ATSHA204Class::sha204c_check_crc(uint8_t *response)
|
||||
{
|
||||
uint8_t crc[SHA204_CRC_SIZE];
|
||||
uint8_t count = response[SHA204_BUFFER_POS_COUNT];
|
||||
@@ -570,4 +570,4 @@ uint8_t atsha204Class::sha204c_check_crc(uint8_t *response)
|
||||
|
||||
return (crc[0] == response[count] && crc[1] == response[count + 1])
|
||||
? SHA204_SUCCESS : SHA204_BAD_CRC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
|
||||
#define SHA204_SERIAL_SZ 9 // The number of bytes the serial number consists of
|
||||
|
||||
class atsha204Class
|
||||
class ATSHA204Class
|
||||
{
|
||||
private:
|
||||
uint8_t device_pin;
|
||||
@@ -237,7 +237,7 @@ private:
|
||||
uint8_t sha204c_resync(uint8_t size, uint8_t *response);
|
||||
uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer, uint8_t execution_delay, uint8_t execution_timeout);
|
||||
public:
|
||||
atsha204Class(uint8_t pin); // Constructor
|
||||
ATSHA204Class(uint8_t pin); // Constructor
|
||||
void sha204c_sleep();
|
||||
uint8_t sha204m_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// FILE: RunningAverage.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.2.08
|
||||
// DATE: 2015-apr-10
|
||||
// PURPOSE: RunningAverage library for Arduino
|
||||
//
|
||||
// The library stores N individual values in a circular buffer,
|
||||
// to calculate the running average.
|
||||
//
|
||||
// HISTORY:
|
||||
// 0.1.00 - 2011-01-30 initial version
|
||||
// 0.1.01 - 2011-02-28 fixed missing destructor in .h
|
||||
// 0.2.00 - 2012-??-?? Yuval Naveh added trimValue (found on web)
|
||||
// http://stromputer.googlecode.com/svn-history/r74/trunk/Arduino/Libraries/RunningAverage/RunningAverage.cpp
|
||||
// 0.2.01 - 2012-11-21 refactored
|
||||
// 0.2.02 - 2012-12-30 refactored trimValue -> fillValue
|
||||
// 0.2.03 - 2013-11-31 getElement
|
||||
// 0.2.04 - 2014-07-03 added memory protection
|
||||
// 0.2.05 - 2014-12-16 changed float -> double
|
||||
// 0.2.06 - 2015-03-07 all size uint8_t
|
||||
// 0.2.07 - 2015-03-16 added getMin() and getMax() functions (Eric Mulder)
|
||||
// 0.2.08 - 2015-04-10 refactored getMin() and getMax() implementation
|
||||
//
|
||||
// Released to the public domain
|
||||
//
|
||||
|
||||
#include "RunningAverage.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
RunningAverage::RunningAverage(uint8_t size)
|
||||
{
|
||||
_size = size;
|
||||
_ar = (double*) malloc(_size * sizeof(double));
|
||||
if (_ar == NULL) _size = 0;
|
||||
clear();
|
||||
}
|
||||
|
||||
RunningAverage::~RunningAverage()
|
||||
{
|
||||
if (_ar != NULL) free(_ar);
|
||||
}
|
||||
|
||||
// resets all counters
|
||||
void RunningAverage::clear()
|
||||
{
|
||||
_cnt = 0;
|
||||
_idx = 0;
|
||||
_sum = 0.0;
|
||||
_min = NAN;
|
||||
_max = NAN;
|
||||
for (uint8_t i = 0; i < _size; i++)
|
||||
{
|
||||
_ar[i] = 0.0; // keeps addValue simple
|
||||
}
|
||||
}
|
||||
|
||||
// adds a new value to the data-set
|
||||
void RunningAverage::addValue(double value)
|
||||
{
|
||||
if (_ar == NULL) return; // allocation error
|
||||
_sum -= _ar[_idx];
|
||||
_ar[_idx] = value;
|
||||
_sum += _ar[_idx];
|
||||
_idx++;
|
||||
if (_idx == _size) _idx = 0; // faster than %
|
||||
// handle min max
|
||||
if (_cnt == 0) _min = _max = value;
|
||||
else if (value < _min) _min = value;
|
||||
else if (value > _max) _max = value;
|
||||
// update count as last otherwise if( _cnt == 0) above will fail
|
||||
if (_cnt < _size) _cnt++;
|
||||
}
|
||||
|
||||
// returns the average of the data-set added sofar
|
||||
double RunningAverage::getAverage()
|
||||
{
|
||||
if (_cnt == 0) return NAN;
|
||||
return _sum / _cnt;
|
||||
}
|
||||
|
||||
// returns the value of an element if exist, NAN otherwise
|
||||
double RunningAverage::getElement(uint8_t idx)
|
||||
{
|
||||
if (idx >=_cnt ) return NAN;
|
||||
return _ar[idx];
|
||||
}
|
||||
|
||||
// fill the average with a value
|
||||
// the param number determines how often value is added (weight)
|
||||
// number should preferably be between 1 and size
|
||||
void RunningAverage::fillValue(double value, uint8_t number)
|
||||
{
|
||||
clear(); // TODO conditional? if (clr) clear();
|
||||
|
||||
for (uint8_t i = 0; i < number; i++)
|
||||
{
|
||||
addValue(value);
|
||||
}
|
||||
}
|
||||
// END OF FILE
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// FILE: RunningAverage.h
|
||||
// AUTHOR: Rob dot Tillaart at gmail dot com
|
||||
// VERSION: 0.2.08
|
||||
// DATE: 2015-apr-10
|
||||
// PURPOSE: RunningAverage library for Arduino
|
||||
// URL: http://arduino.cc/playground/Main/RunningAverage
|
||||
// HISTORY: See RunningAverage.cpp
|
||||
//
|
||||
// Released to the public domain
|
||||
//
|
||||
// backwards compatibility
|
||||
// clr() clear()
|
||||
// add(x) addValue(x)
|
||||
// avg() getAverage()
|
||||
|
||||
#ifndef RunningAverage_h
|
||||
#define RunningAverage_h
|
||||
|
||||
#define RUNNINGAVERAGE_LIB_VERSION "0.2.08"
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
class RunningAverage
|
||||
{
|
||||
public:
|
||||
RunningAverage(void);
|
||||
RunningAverage(uint8_t);
|
||||
~RunningAverage();
|
||||
|
||||
void clear();
|
||||
void addValue(double);
|
||||
void fillValue(double, uint8_t);
|
||||
|
||||
double getAverage();
|
||||
// returns lowest value added to the data-set since last clear
|
||||
double getMin() { return _min; };
|
||||
// returns highest value added to the data-set since last clear
|
||||
double getMax() { return _max; };
|
||||
|
||||
double getElement(uint8_t idx);
|
||||
uint8_t getSize() { return _size; }
|
||||
uint8_t getCount() { return _cnt; }
|
||||
|
||||
protected:
|
||||
uint8_t _size;
|
||||
uint8_t _cnt;
|
||||
uint8_t _idx;
|
||||
double _sum;
|
||||
double * _ar;
|
||||
double _min;
|
||||
double _max;
|
||||
};
|
||||
|
||||
#endif
|
||||
// END OF FILE
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// FILE: fillValue.pde
|
||||
// AUTHOR: Rob Tillaart
|
||||
// DATE: 2012-12-30
|
||||
//
|
||||
// PUPROSE: show working of fillValue
|
||||
//
|
||||
|
||||
#include "RunningAverage.h"
|
||||
|
||||
RunningAverage myRA(10);
|
||||
int samples = 0;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.print("Demo RunningAverage lib - fillValue ");
|
||||
Serial.print("Version: ");
|
||||
Serial.println(RUNNINGAVERAGE_LIB_VERSION);
|
||||
|
||||
myRA.fillValue(100,5);
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
long rn = random(0, 100);
|
||||
myRA.addValue(rn/100.0);
|
||||
samples++;
|
||||
Serial.print("Running Average: ");
|
||||
Serial.println(myRA.getAverage(), 4);
|
||||
|
||||
if (samples == 300)
|
||||
{
|
||||
samples = 0;
|
||||
myRA.fillValue(100, 10);
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// FILE: runningAverageMinMaxTest.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.00
|
||||
// DATE: 2015-apr-10
|
||||
//
|
||||
// PUPROSE: demo
|
||||
//
|
||||
|
||||
#include "RunningAverage.h"
|
||||
|
||||
RunningAverage myRA(10);
|
||||
int samples = 0;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("\nDemo runningAverageMinMaxTest");
|
||||
Serial.print("Version: ");
|
||||
Serial.println(RUNNINGAVERAGE_LIB_VERSION);
|
||||
myRA.clear(); // explicitly start clean
|
||||
|
||||
Serial.println("\nCNT\tMIN\tAVG\tMAX");
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
long rn = random(0, 1000);
|
||||
myRA.addValue(rn * 0.001);
|
||||
samples++;
|
||||
Serial.print(samples);
|
||||
Serial.print("\t");
|
||||
Serial.print(myRA.getMin(), 3);
|
||||
Serial.print("\t");
|
||||
Serial.print(myRA.getAverage(), 3);
|
||||
Serial.print("\t");
|
||||
Serial.println(myRA.getMax(), 3);
|
||||
|
||||
if (samples == 100)
|
||||
{
|
||||
samples = 0;
|
||||
myRA.clear();
|
||||
Serial.println("\nCNT\tMIN\tAVG\tMAX");
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// FILE: runningAverageHour.pde
|
||||
// AUTHOR: Rob Tillaart
|
||||
// DATE: 2012-12-30
|
||||
//
|
||||
// PUPROSE: show working of runningAverage per hour
|
||||
// in 2 steps - last minute + last hour
|
||||
// 3 or more steps also possible
|
||||
//
|
||||
|
||||
#include "RunningAverage.h"
|
||||
|
||||
RunningAverage raMinute(60);
|
||||
RunningAverage raHour(60);
|
||||
|
||||
int samples = 0;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Demo RunningAverage lib - average per minute & hour");
|
||||
Serial.print("Version: ");
|
||||
Serial.println(RUNNINGAVERAGE_LIB_VERSION);
|
||||
raHour.clear();
|
||||
raMinute.clear();
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
long rn = random(0, 100);
|
||||
raMinute.addValue(rn);
|
||||
samples++;
|
||||
|
||||
if (samples % 60 == 0) raHour.addValue(raMinute.getAverage());
|
||||
|
||||
Serial.print(" raMinute: ");
|
||||
Serial.print(raMinute.getAverage(), 4);
|
||||
Serial.print(" raHour: ");
|
||||
Serial.println(raHour.getAverage(), 4);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// FILE: runningAverageTest.pde
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.1.01
|
||||
// DATE: 2012-12-30
|
||||
//
|
||||
// PUPROSE: show working of runningAverage
|
||||
//
|
||||
|
||||
#include "RunningAverage.h"
|
||||
|
||||
RunningAverage myRA(10);
|
||||
int samples = 0;
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Demo RunningAverage lib");
|
||||
Serial.print("Version: ");
|
||||
Serial.println(RUNNINGAVERAGE_LIB_VERSION);
|
||||
myRA.clear(); // explicitly start clean
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
long rn = random(0, 1000);
|
||||
myRA.addValue(rn * 0.001);
|
||||
samples++;
|
||||
Serial.print(samples);
|
||||
Serial.print("\t Running Average: ");
|
||||
Serial.println(myRA.getAverage(), 3);
|
||||
|
||||
if (samples == 300)
|
||||
{
|
||||
samples = 0;
|
||||
myRA.clear();
|
||||
Serial.println();
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For RunningAverage
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
RunningAverage KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
clear KEYWORD2
|
||||
addValue KEYWORD2
|
||||
getAverage KEYWORD2
|
||||
getMin KEYWORD2
|
||||
getMax KEYWORD2
|
||||
fillValue KEYWORD2
|
||||
getElement KEYWORD2
|
||||
getSize KEYWORD2
|
||||
getCount KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
Reference in New Issue
Block a user