добавление mysensors в процессе не рабочая версия

This commit is contained in:
Dmitry Borisenko
2022-11-30 23:31:27 +01:00
parent 3e95049ce9
commit 9436af94df
304 changed files with 67751 additions and 29 deletions

View File

@@ -0,0 +1,370 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwAVR.h"
bool hwInit(void)
{
#if !defined(MY_DISABLED_SERIAL)
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {}
#endif
#endif
return true;
}
#define WDTO_SLEEP_FOREVER (0xFFu)
volatile uint8_t _wokeUpByInterrupt =
INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu.
volatile uint8_t _wakeUp1Interrupt =
INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp1-callback.
volatile uint8_t _wakeUp2Interrupt =
INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
static uint32_t sleepRemainingMs = 0ul;
void wakeUp1(void)
{
// Disable sleep. When an interrupt occurs after attachInterrupt,
// but before sleeping the CPU would not wake up.
// Ref: http://playground.arduino.cc/Learning/ArduinoSleepCode
sleep_disable();
detachInterrupt(_wakeUp1Interrupt);
// First interrupt occurred will be reported only
if (INVALID_INTERRUPT_NUM == _wokeUpByInterrupt) {
_wokeUpByInterrupt = _wakeUp1Interrupt;
}
}
void wakeUp2(void)
{
sleep_disable();
detachInterrupt(_wakeUp2Interrupt);
// First interrupt occurred will be reported only
if (INVALID_INTERRUPT_NUM == _wokeUpByInterrupt) {
_wokeUpByInterrupt = _wakeUp2Interrupt;
}
}
inline bool interruptWakeUp(void)
{
return _wokeUpByInterrupt != INVALID_INTERRUPT_NUM;
}
void clearPendingInterrupt(const uint8_t interrupt)
{
EIFR = _BV(interrupt);
}
// Watchdog Timer interrupt service routine. This routine is required
// to allow automatic WDIF and WDIE bit clearance in hardware.
ISR (WDT_vect)
{
}
void hwPowerDown(const uint8_t wdto)
{
// Let serial prints finish (debug, log etc)
#ifndef MY_DISABLED_SERIAL
MY_SERIALDEVICE.flush();
#endif
// disable ADC for power saving
ADCSRA &= ~(1 << ADEN);
// save WDT settings
const uint8_t WDTsave = WDTCSR;
if (wdto != WDTO_SLEEP_FOREVER) {
wdt_enable(wdto);
// enable WDT interrupt before system reset
WDTCSR |= (1 << WDCE) | (1 << WDIE);
} else {
// if sleeping forever, disable WDT
wdt_disable();
}
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli();
sleep_enable();
#if defined(__AVR_ATmega328P__)
sleep_bod_disable();
#endif
// Enable interrupts & sleep until WDT or ext. interrupt
sei();
// Directly sleep CPU, to prevent race conditions!
// Ref: chapter 7.7 of ATMega328P datasheet
sleep_cpu();
sleep_disable();
// restore previous WDT settings
cli();
wdt_reset();
// enable WDT changes
WDTCSR |= (1 << WDCE) | (1 << WDE);
// restore saved WDT settings
WDTCSR = WDTsave;
sei();
// enable ADC
ADCSRA |= (1 << ADEN);
}
uint32_t hwInternalSleep(uint32_t ms)
{
// Sleeping with watchdog only supports multiples of 16ms.
// Round up to next multiple of 16ms, to assure we sleep at least the
// requested amount of time. Sleep of 0ms will not sleep at all!
ms += 15u;
while (!interruptWakeUp() && ms >= 16) {
for (uint8_t period = 9u; ; --period) {
const uint16_t comparatorMS = 1 << (period + 4);
if ( ms >= comparatorMS) {
hwPowerDown(period); // 8192ms => 9, 16ms => 0
ms -= comparatorMS;
break;
}
}
}
if (interruptWakeUp()) {
return ms;
}
return 0ul;
}
int8_t hwSleep(uint32_t ms)
{
// Return what woke the mcu.
// Default: no interrupt triggered, timer wake up
int8_t ret = MY_WAKE_UP_BY_TIMER;
sleepRemainingMs = 0ul;
if (ms > 0u) {
// sleep for defined time
sleepRemainingMs = hwInternalSleep(ms);
} else {
// sleep until ext interrupt triggered
hwPowerDown(WDTO_SLEEP_FOREVER);
}
if (interruptWakeUp()) {
ret = static_cast<int8_t>(_wokeUpByInterrupt);
}
// Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
_wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
return ret;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
return hwSleep(interrupt, mode, INVALID_INTERRUPT_NUM, 0u, ms);
}
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
// ATMega328P supports following modes to wake from sleep: LOW, CHANGE, RISING, FALLING
// Datasheet states only LOW can be used with INT0/1 to wake from sleep, which is incorrect.
// Ref: http://gammon.com.au/interrupts
// Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
// and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
cli();
// attach interrupts
_wakeUp1Interrupt = interrupt1;
_wakeUp2Interrupt = interrupt2;
// Attach external interrupt handlers, and clear any pending interrupt flag
// to prevent waking immediately again.
// Ref: https://forum.arduino.cc/index.php?topic=59217.0
if (interrupt1 != INVALID_INTERRUPT_NUM) {
clearPendingInterrupt(interrupt1);
attachInterrupt(interrupt1, wakeUp1, mode1);
}
if (interrupt2 != INVALID_INTERRUPT_NUM) {
clearPendingInterrupt(interrupt2);
attachInterrupt(interrupt2, wakeUp2, mode2);
}
sleepRemainingMs = 0ul;
if (ms > 0u) {
// sleep for defined time
sleepRemainingMs = hwInternalSleep(ms);
} else {
// sleep until ext interrupt triggered
hwPowerDown(WDTO_SLEEP_FOREVER);
}
// Assure any interrupts attached, will get detached when they did not occur.
if (interrupt1 != INVALID_INTERRUPT_NUM) {
detachInterrupt(interrupt1);
}
if (interrupt2 != INVALID_INTERRUPT_NUM) {
detachInterrupt(interrupt2);
}
// Return what woke the mcu.
// Default: no interrupt triggered, timer wake up
int8_t ret = MY_WAKE_UP_BY_TIMER;
if (interruptWakeUp()) {
ret = static_cast<int8_t>(_wokeUpByInterrupt);
}
// Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
_wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
return ret;
}
uint32_t hwGetSleepRemaining(void)
{
return sleepRemainingMs;
}
inline void hwRandomNumberInit(void)
{
// This function initializes the random number generator with a seed
// of 32 bits. This method is good enough to earn FIPS 140-2 conform
// random data. This should reach to generate 32 Bit for randomSeed().
uint32_t seed = 0;
uint32_t timeout = millis() + 20;
// Trigger floating effect of an unconnected pin
pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT_PULLUP);
pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT);
delay(10);
// Generate 32 bits of datas
for (uint8_t i=0; i<32; i++) {
const int pinValue = analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN);
// Wait until the analog value has changed
while ((pinValue == analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)) && (timeout>=millis())) {
seed ^= (millis() << i);
// Check of data generation is slow
if (timeout<=millis()) {
// Trigger pin again
pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT_PULLUP);
pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT);
// Pause a short while
delay(seed % 10);
timeout = millis() + 20;
}
}
}
randomSeed(seed);
}
bool hwUniqueID(unique_id_t *uniqueID)
{
// padding
(void)memset(uniqueID, MY_HWID_PADDING_BYTE, sizeof(unique_id_t));
// no unique ID for non-PB AVR, use HW specifics for diversification
*((uint8_t *)uniqueID) = SIGNATURE_2;
*((uint8_t *)uniqueID + 1) = SIGNATURE_1;
*((uint8_t *)uniqueID + 2) = SIGNATURE_0;
*((uint8_t *)uniqueID + 3) = OSCCAL;
#if defined(__AVR_ATmega328PB__)
// ATMEGA328PB specifics, has unique ID
for(uint8_t idx = 0; idx < 10; idx++) {
*((uint8_t *)uniqueID + 4 + idx) = boot_signature_byte_get(0xE + idx);
}
return true; // unique ID returned
#else
return false; // no unique ID returned
#endif
}
uint16_t hwCPUVoltage(void)
{
// Measure Vcc against 1.1V Vref
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = (_BV(MUX5) | _BV(MUX0));
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = (_BV(MUX3) | _BV(MUX2));
#else
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#endif
// Vref settle
delay(70);
// Do conversion
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA,ADSC)) {};
// return Vcc in mV
return (1125300UL) / ADC;
}
uint16_t hwCPUFrequency(void)
{
cli();
// save WDT & timer settings
const uint8_t WDTsave = WDTCSR;
const uint8_t TCCR1Asave = TCCR1A;
const uint8_t TCCR1Bsave = TCCR1B;
const uint8_t TCCR1Csave = TCCR1C;
// setup timer1
TIFR1 = 0xFF;
TCNT1 = 0;
TCCR1A = 0;
TCCR1C = 0;
// set wdt
wdt_enable(WDTO_500MS);
// enable WDT interrupt mode => first timeout WDIF, 2nd timeout reset
WDTCSR |= (1 << WDIE);
wdt_reset();
// start timer1 with 1024 prescaling
TCCR1B = _BV(CS12) | _BV(CS10);
// wait until wdt interrupt
while (bit_is_clear(WDTCSR,WDIF)) {};
// stop timer
TCCR1B = 0;
// restore WDT settings
wdt_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = WDTsave;
sei();
const uint16_t result = TCNT1 * 2048UL / 100000UL;
// restore timer settings
TCCR1A = TCCR1Asave;
TCCR1B = TCCR1Bsave;
TCCR1C = TCCR1Csave;
// return frequency in 1/10MHz (accuracy +- 10%)
return result;
}
int8_t hwCPUTemperature(void)
{
#if defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328BP__) || defined(__AVR_ATmega32U4__)
// Set the internal reference and mux.
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
ADCSRA |= _BV(ADEN); // enable the ADC
delay(20); // wait for voltages to become stable.
ADCSRA |= _BV(ADSC); // Start the ADC
// Wait until conversion done
while (bit_is_set(ADCSRA, ADSC));
// temperature is in degrees Celsius
return static_cast<int8_t>((ADCW - MY_AVR_TEMPERATURE_OFFSET) / MY_AVR_TEMPERATURE_GAIN);
#else
return -127; // not available
#endif
}
uint16_t hwFreeMem(void)
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

View File

@@ -0,0 +1,92 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef MyHwAVR_h
#define MyHwAVR_h
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/boot.h>
#include <util/atomic.h>
#include <SPI.h>
// Fast IO driver
#include "drivers/DigitalWriteFast/digitalWriteFast.h"
// SOFTSPI
#ifdef MY_SOFTSPI
#include "hal/architecture/AVR/drivers/DigitalIO/DigitalIO.h"
#endif
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
// AVR temperature calibration reference: http://ww1.microchip.com/downloads/en/AppNotes/Atmel-8108-Calibration-of-the-AVRs-Internal-Temperature-Reference_ApplicationNote_AVR122.pdf
#ifndef MY_AVR_TEMPERATURE_OFFSET
#define MY_AVR_TEMPERATURE_OFFSET (324.31f)
#endif
#ifndef MY_AVR_TEMPERATURE_GAIN
#define MY_AVR_TEMPERATURE_GAIN (1.22f)
#endif
// Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
#define hwDigitalRead(__pin) digitalReadFast(__pin)
#define hwPinMode(__pin, __value) pinModeFast(__pin, __value)
bool hwInit(void);
#define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis()
#define hwReadConfig(__pos) eeprom_read_byte((const uint8_t *)__pos)
#define hwWriteConfig(__pos, __val) eeprom_update_byte((uint8_t *)__pos, (uint8_t)__val)
#define hwReadConfigBlock(__buf, __pos, __length) eeprom_read_block((void *)__buf, (const void *)__pos, (uint32_t)__length)
#define hwWriteConfigBlock(__buf, __pos, __length) eeprom_update_block((const void *)__buf, (void *)__pos, (uint32_t)__length)
inline void hwRandomNumberInit(void);
uint32_t hwInternalSleep(uint32_t ms);
#if defined(MY_SOFTSPI)
SoftSPI<MY_SOFT_SPI_MISO_PIN, MY_SOFT_SPI_MOSI_PIN, MY_SOFT_SPI_SCK_PIN, 0> hwSPI; //!< hwSPI
#else
#define hwSPI SPI //!< hwSPI
#endif
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
#endif /* DOXYGEN */
#endif

View File

@@ -0,0 +1,39 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
// Initialize library and handle sketch functions like we want to
int main(void)
{
init();
#if defined(USBCON)
USBDevice.attach();
#endif
_begin(); // Startup MySensors library
for(;;) {
_process(); // Process incoming data
if (loop) {
loop(); // Call sketch loop
}
if (serialEventRun) {
serialEventRun();
}
}
return 0;
}

View File

@@ -0,0 +1,36 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Fast Digital I/O functions
*
*/
#ifndef DigitalIO_h
#define DigitalIO_h 1
//------------------------------------------------------------------------------
/** DigitalPin version YYYYMMDD */
#define DIGITAL_IO_VERSION 20151127
//------------------------------------------------------------------------------
#include "DigitalPin.h"
#include "I2cConstants.h"
#include "PinIO.h"
#include "SoftI2cMaster.h"
#include "SoftSPI.h"
#endif // DigitalIO_h

View File

@@ -0,0 +1,416 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Fast Digital Pin functions
*
* @defgroup digitalPin Fast Pin I/O
* @details Fast Digital I/O functions and template class.
* @{
*/
#ifndef DigitalPin_h
#define DigitalPin_h
#if defined(__AVR__) || defined(DOXYGEN)
#include <avr/io.h>
/** GpioPinMap type */
struct GpioPinMap_t {
volatile uint8_t* pin; /**< address of PIN for this pin */
volatile uint8_t* ddr; /**< address of DDR for this pin */
volatile uint8_t* port; /**< address of PORT for this pin */
uint8_t mask; /**< bit mask for this pin */
};
/** Initializer macro. */
#define GPIO_PIN(reg, bit) {&PIN##reg, &DDR##reg, &PORT##reg, 1 << bit}
// Include pin map for current board.
#include "boards/GpioPinMap.h"
//------------------------------------------------------------------------------
/** generate bad pin number error */
void badPinNumber(void)
__attribute__((error("Pin number is too large or not a constant")));
//------------------------------------------------------------------------------
/** Check for valid pin number
* @param[in] pin Number of pin to be checked.
*/
static inline __attribute__((always_inline))
void badPinCheck(uint8_t pin)
{
if (!__builtin_constant_p(pin) || pin >= NUM_DIGITAL_PINS) {
badPinNumber();
}
}
//------------------------------------------------------------------------------
/** DDR register address
* @param[in] pin Arduino pin number
* @return register address
*/
static inline __attribute__((always_inline))
volatile uint8_t* ddrReg(uint8_t pin)
{
badPinCheck(pin);
return GpioPinMap[pin].ddr;
}
//------------------------------------------------------------------------------
/** Bit mask for pin
* @param[in] pin Arduino pin number
* @return mask
*/
static inline __attribute__((always_inline))
uint8_t pinMask(uint8_t pin)
{
badPinCheck(pin);
return GpioPinMap[pin].mask;
}
//------------------------------------------------------------------------------
/** PIN register address
* @param[in] pin Arduino pin number
* @return register address
*/
static inline __attribute__((always_inline))
volatile uint8_t* pinReg(uint8_t pin)
{
badPinCheck(pin);
return GpioPinMap[pin].pin;
}
//------------------------------------------------------------------------------
/** PORT register address
* @param[in] pin Arduino pin number
* @return register address
*/
static inline __attribute__((always_inline))
volatile uint8_t* portReg(uint8_t pin)
{
badPinCheck(pin);
return GpioPinMap[pin].port;
}
//------------------------------------------------------------------------------
/** Fast write helper.
* @param[in] address I/O register address
* @param[in] mask bit mask for pin
* @param[in] level value for bit
*/
static inline __attribute__((always_inline))
void fastBitWriteSafe(volatile uint8_t* address, uint8_t mask, bool level)
{
uint8_t s;
if (address > reinterpret_cast<uint8_t*>(0X3F)) {
s = SREG;
cli();
}
if (level) {
*address |= mask;
} else {
*address &= ~mask;
}
if (address > reinterpret_cast<uint8_t*>(0X3F)) {
SREG = s;
}
}
//------------------------------------------------------------------------------
/** Read pin value.
* @param[in] pin Arduino pin number
* @return value read
*/
static inline __attribute__((always_inline))
bool fastDigitalRead(uint8_t pin)
{
return *pinReg(pin) & pinMask(pin);
}
//------------------------------------------------------------------------------
/** Toggle a pin.
* @param[in] pin Arduino pin number
*
* If the pin is in output mode toggle the pin level.
* If the pin is in input mode toggle the state of the 20K pullup.
*/
static inline __attribute__((always_inline))
void fastDigitalToggle(uint8_t pin)
{
if (pinReg(pin) > reinterpret_cast<uint8_t*>(0X3F)) {
// must write bit to high address port
*pinReg(pin) = pinMask(pin);
} else {
// will compile to sbi and PIN register will not be read.
*pinReg(pin) |= pinMask(pin);
}
}
//------------------------------------------------------------------------------
/** Set pin value.
* @param[in] pin Arduino pin number
* @param[in] level value to write
*/
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, bool level)
{
fastBitWriteSafe(portReg(pin), pinMask(pin), level);
}
//------------------------------------------------------------------------------
/** Write the DDR register.
* @param[in] pin Arduino pin number
* @param[in] level value to write
*/
static inline __attribute__((always_inline))
void fastDdrWrite(uint8_t pin, bool level)
{
fastBitWriteSafe(ddrReg(pin), pinMask(pin), level);
}
//------------------------------------------------------------------------------
/** Set pin mode.
* @param[in] pin Arduino pin number
* @param[in] mode INPUT, OUTPUT, or INPUT_PULLUP.
*
* The internal pullup resistors will be enabled if mode is INPUT_PULLUP
* and disabled if the mode is INPUT.
*/
static inline __attribute__((always_inline))
void fastPinMode(uint8_t pin, uint8_t mode)
{
fastDdrWrite(pin, mode == OUTPUT);
if (mode != OUTPUT) {
fastDigitalWrite(pin, mode == INPUT_PULLUP);
}
}
#else // defined(__AVR__)
#if defined(CORE_TEENSY)
//------------------------------------------------------------------------------
/** read pin value
* @param[in] pin Arduino pin number
* @return value read
*/
static inline __attribute__((always_inline))
bool fastDigitalRead(uint8_t pin)
{
return *portInputRegister(pin);
}
//------------------------------------------------------------------------------
/** Set pin value
* @param[in] pin Arduino pin number
* @param[in] level value to write
*/
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, bool value)
{
if (value) {
*portSetRegister(pin) = 1;
} else {
*portClearRegister(pin) = 1;
}
}
#elif defined(__SAM3X8E__) || defined(__SAM3X8H__)
//------------------------------------------------------------------------------
/** read pin value
* @param[in] pin Arduino pin number
* @return value read
*/
static inline __attribute__((always_inline))
bool fastDigitalRead(uint8_t pin)
{
return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin;
}
//------------------------------------------------------------------------------
/** Set pin value
* @param[in] pin Arduino pin number
* @param[in] level value to write
*/
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, bool value)
{
if (value) {
g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin;
} else {
g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin;
}
}
#elif defined(ESP8266)
//------------------------------------------------------------------------------
/** Set pin value
* @param[in] pin Arduino pin number
* @param[in] val value to write
*/
static inline __attribute__((always_inline))
void fastDigitalWrite(uint8_t pin, uint8_t val)
{
if (pin < 16) {
if (val) {
GPOS = (1 << pin);
} else {
GPOC = (1 << pin);
}
} else if (pin == 16) {
if (val) {
GP16O |= 1;
} else {
GP16O &= ~1;
}
}
}
//------------------------------------------------------------------------------
/** Read pin value
* @param[in] pin Arduino pin number
* @return value read
*/
static inline __attribute__((always_inline))
bool fastDigitalRead(uint8_t pin)
{
if (pin < 16) {
return GPIP(pin);
} else if (pin == 16) {
return GP16I & 0x01;
}
return 0;
}
#else // CORE_TEENSY
//------------------------------------------------------------------------------
inline void fastDigitalWrite(uint8_t pin, bool value)
{
digitalWrite(pin, value);
}
//------------------------------------------------------------------------------
inline bool fastDigitalRead(uint8_t pin)
{
return digitalRead(pin);
}
#endif // CORE_TEENSY
//------------------------------------------------------------------------------
inline void fastDigitalToggle(uint8_t pin)
{
fastDigitalWrite(pin, !fastDigitalRead(pin));
}
//------------------------------------------------------------------------------
inline void fastPinMode(uint8_t pin, uint8_t mode)
{
pinMode(pin, mode);
}
#endif // __AVR__
//------------------------------------------------------------------------------
/** set pin configuration
* @param[in] pin Arduino pin number
* @param[in] mode mode INPUT or OUTPUT.
* @param[in] level If mode is output, set level high/low.
* If mode is input, enable or disable the pin's 20K pullup.
*/
#define fastPinConfig(pin, mode, level)\
{fastPinMode(pin, mode); fastDigitalWrite(pin, level);}
//==============================================================================
/**
* @class DigitalPin
* @brief Fast digital port I/O
*/
template<uint8_t PinNumber>
class DigitalPin
{
public:
//----------------------------------------------------------------------------
/** Constructor */
DigitalPin() {}
//----------------------------------------------------------------------------
/** Asignment operator.
* @param[in] value If true set the pin's level high else set the
* pin's level low.
*
* @return This DigitalPin instance.
*/
inline DigitalPin & operator = (bool value) __attribute__((always_inline))
{
write(value);
return *this;
}
//----------------------------------------------------------------------------
/** Parenthesis operator.
* @return Pin's level
*/
inline operator bool () const __attribute__((always_inline))
{
return read();
}
//----------------------------------------------------------------------------
/** Set pin configuration.
* @param[in] mode: INPUT or OUTPUT.
* @param[in] level If mode is OUTPUT, set level high/low.
* If mode is INPUT, enable or disable the pin's 20K pullup.
*/
inline __attribute__((always_inline))
void config(uint8_t mode, bool level)
{
fastPinConfig(PinNumber, mode, level);
}
//----------------------------------------------------------------------------
/**
* Set pin level high if output mode or enable 20K pullup if input mode.
*/
inline __attribute__((always_inline))
void high()
{
write(true);
}
//----------------------------------------------------------------------------
/**
* Set pin level low if output mode or disable 20K pullup if input mode.
*/
inline __attribute__((always_inline))
void low()
{
write(false);
}
//----------------------------------------------------------------------------
/**
* Set pin mode.
* @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP.
*
* The internal pullup resistors will be enabled if mode is INPUT_PULLUP
* and disabled if the mode is INPUT.
*/
inline __attribute__((always_inline))
void mode(uint8_t mode)
{
fastPinMode(PinNumber, mode);
}
//----------------------------------------------------------------------------
/** @return Pin's level. */
inline __attribute__((always_inline))
bool read() const
{
return fastDigitalRead(PinNumber);
}
//----------------------------------------------------------------------------
/** Toggle a pin.
*
* If the pin is in output mode toggle the pin's level.
* If the pin is in input mode toggle the state of the 20K pullup.
*/
inline __attribute__((always_inline))
void toggle()
{
fastDigitalToggle(PinNumber);
}
//----------------------------------------------------------------------------
/** Write the pin's level.
* @param[in] value If true set the pin's level high else set the
* pin's level low.
*/
inline __attribute__((always_inline))
void write(bool value)
{
fastDigitalWrite(PinNumber, value);
}
};
#endif // DigitalPin_h
/** @} */

View File

@@ -0,0 +1,62 @@
/* Arduino I2C Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino I2C Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino I2C Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file I2cConstants.h
* @brief Two Wire Interface constants.
*
* @defgroup twoWire I2C constants
* @details Two Wire Interface library.
* @{
*/
#ifndef I2cConstants_h
#define I2cConstants_h
#include <inttypes.h>
/** Option argument for transfer() of transferContinue() to continue a
an I2C operation. */
const uint8_t I2C_CONTINUE = 0;
/** Option argument for transfer() of transferContinue() to end a
transfer with a STOP condition */
const uint8_t I2C_STOP = 1;
/** Option argument for transfer() of transferContinue() to end a
transfer with a repeated START condition */
const uint8_t I2C_REP_START = 2;
/** Set I2C bus speed to 100 kHz. Used by TwiMaster class. */
const uint8_t I2C_100KHZ = 0;
/** Set I2C bus speed to 400 kHz. Used by TwiMaster class. */
const uint8_t I2C_400KHZ = 1;
/** Bit to OR with address for a read operation. */
const uint8_t I2C_READ = 1;
/** Bit to OR with address for write operation. */
const uint8_t I2C_WRITE = 0;
/** Disable internal pull-ups on SDA and SCL. Used by TwiMaster class. */
const uint8_t I2C_NO_PULLUPS = 0;
/** Enable internal pull-ups on SDA and SCL. Used by TwiMaster class. */
const uint8_t I2C_INTERNAL_PULLUPS = 1;
#endif // I2cConstants_h
/** @} */

View File

@@ -0,0 +1,75 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Digital AVR port I/O with runtime pin number.
*
* @defgroup runtimeDigital Runtime Pin I/O
* @details Two Wire Interface library.
* @{
*/
#if defined(__AVR__) || defined(DOXYGEN) // AVR only
#include "PinIO.h"
#include <util/atomic.h>
#include <Arduino.h>
//==============================================================================
/** Constructor
* @param[in] pin Pin assigned to this object.
*/
PinIO::PinIO(uint8_t pin)
{
begin(pin);
}
//------------------------------------------------------------------------------
/** Initialize pin bit mask and port address.
* @param[in] pin Arduino board pin number.
* @return true for success or false if invalid pin number.
*/
bool PinIO::begin(uint8_t pin)
{
if (pin >= NUM_DIGITAL_PINS) {
return false;
}
uint8_t port = digitalPinToPort(pin);
pinReg_ = portInputRegister(port);
bit_ = digitalPinToBitMask(pin);
mask_ = ~bit_;
portReg_ = pinReg_ + 2;
return true;
}
//------------------------------------------------------------------------------
/** Configure the pin.
*
* @param[in] mode: INPUT or OUTPUT.
* @param[in] level If mode is OUTPUT, set level high/low.
* If mode is INPUT, enable or disable the pin's 20K pullup.
*
* This function may be used with interrupts enabled or disabled.
* The previous interrupt state will be restored.
*/
void PinIO::config(uint8_t mode, bool level)
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
modeI(mode);
writeI(level);
}
}
#endif // __AVR__
/** @} */

View File

@@ -0,0 +1,194 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Digital AVR port I/O with runtime pin number.
*
* @defgroup runtimeDigital Runtime Pin I/O
* @details Two Wire Interface library.
* @{
*/
#ifndef PinIO_h
#define PinIO_h
#if defined(__AVR__) || defined(DOXYGEN) // AVR only
#include <Arduino.h>
#include <util/atomic.h>
#include <avr/io.h>
//------------------------------------------------------------------------------
/**
* @class PinIO
* @brief AVR port I/O with runtime pin numbers.
*/
class PinIO
{
public:
/** Create a PinIO object with no assigned pin. */
// cppcheck-suppress uninitMemberVar
PinIO() : bit_(0), mask_(0XFF) {}
explicit PinIO(uint8_t pin);
bool begin(uint8_t pin);
void config(uint8_t mode, bool data);
//----------------------------------------------------------------------------
/** @return Pin's level */
inline __attribute__((always_inline))
bool read()
{
return *pinReg_ & bit_;
}
//----------------------------------------------------------------------------
/** toggle a pin
*
* If the pin is in output mode toggle the pin's level.
* If the pin is in input mode toggle the state of the 20K pullup.
*/
inline __attribute__((always_inline))
void toggle()
{
*pinReg_ = bit_;
}
//============================================================================
/**
* Set pin high if output mode or enable 20K pullup if input mode.
*
* This function must be called with interrupts disabled.
* This function will not change the interrupt state.
*/
inline __attribute__((always_inline))
void highI()
{
writeI(1);
}
/**
* Set pin low if output mode or disable 20K pullup if input mode.
*
* This function must be called with interrupts disabled.
* This function will not change the interrupt state.
*/
inline __attribute__((always_inline))
void lowI()
{
writeI(0);
}
/** Set pin mode.
*
* @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP.
*
* The internal pullup resistors will be enabled if mode is INPUT_PULLUP
* and disabled if the mode is INPUT.
*
* This function must be called with interrupts disabled.
* This function will not change the interrupt state.
*/
inline __attribute__((always_inline))
void modeI(uint8_t mode)
{
volatile uint8_t* ddrReg = pinReg_ + 1;
*ddrReg = mode == OUTPUT ? *ddrReg | bit_ : *ddrReg & mask_;
if (mode != OUTPUT) {
writeI(mode == INPUT_PULLUP);
}
}
/** Write pin.
*
* @param[in] level If output mode set pin high if true else low.
* If input mode enable 20K pullup if true else disable pullup.
*
* This function must be called with interrupts disabled.
* This function will not change the interrupt state.
*/
inline __attribute__((always_inline))
void writeI(bool level)
{
*portReg_ = level ? *portReg_ | bit_ : *portReg_ & mask_;
}
//============================================================================
/**
* Set pin level high if output mode or enable 20K pullup if input mode.
*
* This function will enable interrupts. This function should not be
* called in an ISR or where interrupts are disabled.
*/
inline __attribute__((always_inline))
void high()
{
ATOMIC_BLOCK(ATOMIC_FORCEON) {
highI();
}
}
/**
* Set pin level low if output mode or disable 20K pullup if input mode.
*
* This function will enable interrupts. This function should not be
* called in an ISR or where interrupts are disabled.
*/
inline __attribute__((always_inline))
void low()
{
ATOMIC_BLOCK(ATOMIC_FORCEON) {
lowI();
}
}
/**
* Set pin mode.
*
* @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP.
*
* The internal pullup resistors will be enabled if mode is INPUT_PULLUP
* and disabled if the mode is INPUT.
*
* This function will enable interrupts. This function should not be
* called in an ISR or where interrupts are disabled.
*/
inline __attribute__((always_inline))
void mode(uint8_t mode)
{
ATOMIC_BLOCK(ATOMIC_FORCEON) {
modeI(mode);
}
}
/** Write pin.
*
* @param[in] level If output mode set pin high if true else low.
* If input mode enable 20K pullup if true else disable pullup.
*
* This function will enable interrupts. This function should not be
* called in an ISR or where interrupts are disabled.
*/
inline __attribute__((always_inline))
void write(bool level)
{
ATOMIC_BLOCK(ATOMIC_FORCEON) {
writeI(level);
}
}
//----------------------------------------------------------------------------
private:
uint8_t bit_;
uint8_t mask_;
volatile uint8_t* pinReg_;
volatile uint8_t* portReg_;
};
#endif // __AVR__
#endif // PinIO_h
/** @} */

View File

@@ -0,0 +1,252 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if defined(__AVR__) || defined(DOXYGEN) // AVR only
/**
* @file
* @brief AVR Software I2C library
*
* @defgroup softI2C Software I2C
* @details Software Two Wire Interface library.
* @{
*/
#include "SoftI2cMaster.h"
//------------------------------------------------------------------------------
/**
* Start an I2C transfer with possible continuation.
*
* @param[in] addrRW I2C slave address plus R/W bit.
* The I2C slave address is in the high seven bits
* and is ORed with on of the following:
* - I2C_READ for a read transfer.
* - I2C_WRITE for a write transfer.
* .
* @param[in,out] buf Source or destination for transfer.
* @param[in] nbytes Number of bytes to transfer (may be zero).
* @param[in] option Option for ending the transfer, one of:
* - I2C_STOP end the transfer with an I2C stop
* condition.
* - I2C_REP_START end the transfer with an I2C
* repeated start condition.
* - I2C_CONTINUE allow additional transferContinue()
* calls.
* .
* @return true for success else false.
*/
bool I2cMasterBase::transfer(uint8_t addrRW,
void *buf, size_t nbytes, uint8_t option)
{
if (_state != STATE_REP_START) {
start();
}
if (!write(addrRW)) {
_state = (addrRW & I2C_READ) ? STATE_RX_ADDR_NACK : STATE_TX_ADDR_NACK;
return false;
}
_state = (addrRW & I2C_READ) ? STATE_RX_DATA : STATE_TX_DATA;
return transferContinue(buf, nbytes, option);
}
//------------------------------------------------------------------------------
/**
* Continue an I2C transfer.
*
* @param[in,out] buf Source or destination for transfer.
* @param[in] nbytes Number of bytes to transfer (may be zero).
* @param[in] option Option for ending the transfer, one of:
* - I2C_STOP end the transfer with an I2C stop
* condition.
* - I2C_REP_START end the transfer with an I2C
* repeated start condition.
* - I2C_CONTINUE allow additional transferContinue()
* calls.
* .
* @return true for success else false.
*/
bool I2cMasterBase::transferContinue(void *buf, size_t nbytes, uint8_t option)
{
uint8_t* p = reinterpret_cast<uint8_t*>(buf);
if (_state == STATE_RX_DATA) {
for (size_t i = 0; i < nbytes; i++) {
p[i] = read(i == (nbytes - 1) && option != I2C_CONTINUE);
}
} else if (_state == STATE_TX_DATA) {
for (size_t i = 0; i < nbytes; i++) {
if (!write(p[i])) {
_state = STATE_TX_DATA_NACK;
return false;
}
}
} else {
return false;
}
if (option == I2C_STOP) {
stop();
_state = STATE_STOP;
} else if (option == I2C_REP_START) {
start();
_state = STATE_STOP;
}
return true;
}
//==============================================================================
// WARNING don't change SoftI2cMaster unless you verify the change with a scope
//------------------------------------------------------------------------------
/**
* Constructor, initialize SCL/SDA pins and set the bus high.
*
* @param[in] sdaPin The software SDA pin number.
*
* @param[in] sclPin The software SCL pin number.
*/
SoftI2cMaster::SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin)
{
begin(sclPin, sdaPin);
}
//------------------------------------------------------------------------------
/**
* Initialize SCL/SDA pins and set the bus high.
*
* @param[in] sdaPin The software SDA pin number.
*
* @param[in] sclPin The software SCL pin number.
*/
void SoftI2cMaster::begin(uint8_t sclPin, uint8_t sdaPin)
{
uint8_t port;
// Get bit mask and address of scl registers.
_sclBit = digitalPinToBitMask(sclPin);
port = digitalPinToPort(sclPin);
_sclDDR = portModeRegister(port);
volatile uint8_t* sclOutReg = portOutputRegister(port);
// Get bit mask and address of sda registers.
_sdaBit = digitalPinToBitMask(sdaPin);
port = digitalPinToPort(sdaPin);
_sdaDDR = portModeRegister(port);
_sdaInReg = portInputRegister(port);
volatile uint8_t* sdaOutReg = portOutputRegister(port);
// Clear PORT bit for scl and sda.
uint8_t s = SREG;
noInterrupts();
*sclOutReg &= ~_sclBit;
*sdaOutReg &= ~_sdaBit;
SREG = s;
// Set scl and sda high.
writeScl(HIGH);
writeSda(HIGH);
}
//------------------------------------------------------------------------------
/* Read a byte and send ACK if more reads follow else NACK to terminate read.
*
* @param[in] last Set true to terminate the read else false.
*
* @return The byte read from the I2C bus.
*/
uint8_t SoftI2cMaster::read(uint8_t last)
{
uint8_t b = 0;
// Set sda to high Z mode for read.
writeSda(HIGH);
// Read a byte.
for (uint8_t i = 0; i < 8; i++) {
// Don't change this loop unless you verify the change with a scope.
b <<= 1;
sclDelay(16);
writeScl(HIGH);
sclDelay(12);
if (readSda()) {
b |= 1;
}
writeScl(LOW);
}
// send ACK or NACK
writeSda(last);
sclDelay(12);
writeScl(HIGH);
sclDelay(18);
writeScl(LOW);
writeSda(LOW);
return b;
}
//------------------------------------------------------------------------------
/* Issue a start condition. */
void SoftI2cMaster::start()
{
if (!readSda()) {
writeSda(HIGH);
writeScl(HIGH);
sclDelay(20);
}
writeSda(LOW);
sclDelay(20);
writeScl(LOW);
}
//------------------------------------------------------------------------------
/* Issue a stop condition. */
void SoftI2cMaster::stop(void)
{
writeSda(LOW);
sclDelay(20);
writeScl(HIGH);
sclDelay(20);
writeSda(HIGH);
sclDelay(20);
}
//------------------------------------------------------------------------------
/*
* Write a byte.
*
* @param[in] data The byte to send.
*
* @return The value true, 1, if the slave returned an ACK or false for NACK.
*/
bool SoftI2cMaster::write(uint8_t data)
{
// write byte
for (uint8_t m = 0X80; m != 0; m >>= 1) {
// don't change this loop unless you verify the change with a scope
writeSda(m & data);
sclDelay(8);
writeScl(HIGH);
sclDelay(18);
writeScl(LOW);
}
sclDelay(8);
// Go to sda high Z mode for input.
writeSda(HIGH);
writeScl(HIGH);
sclDelay(16);
// Get ACK or NACK.
uint8_t rtn = readSda();
// pull scl low.
writeScl(LOW);
// Pull sda low.
writeSda(LOW);
return rtn == 0;
}
#endif // __AVR__
/** @} */

View File

@@ -0,0 +1,314 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef SOFT_I2C_MASTER_H
#define SOFT_I2C_MASTER_H
/**
* @file
* @brief AVR Software I2C library
*
* @defgroup softI2C Software I2C
* @details Software Two Wire Interface library.
* @{
*/
#if defined(__AVR__) || defined(DOXYGEN) // AVR only
#include <Arduino.h>
#include <util/delay_basic.h>
#include "DigitalPin.h"
#include "I2cConstants.h"
//------------------------------------------------------------------------------
// State codes.
/** Stop condition transmitted. */
const uint8_t STATE_STOP = 0;
/** Repeated start condition transmitted. */
const uint8_t STATE_REP_START = 1;
/** Read data transfer active. */
const uint8_t STATE_RX_DATA = 2;
/** Write data transfer active. */
const uint8_t STATE_TX_DATA = 3;
/** Slave address plus read bit transmitted, NACK received. */
const uint8_t STATE_RX_ADDR_NACK = 4;
/** Slave address plus write bit transmitted, NACK received. */
const uint8_t STATE_TX_ADDR_NACK = 5;
/** Data byte transmitted, NACK received. */
const uint8_t STATE_TX_DATA_NACK = 6;
//==============================================================================
/**
* @class I2cMasterBase
* @brief Base class for FastI2cMaster, SoftI2cMaster
*/
class I2cMasterBase
{
public:
I2cMasterBase() : _state(STATE_STOP) {}
/** Read a byte
*
* @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue()
*
* @param[in] last send a NACK to terminate read if last is true else
* send an ACK to continue the read.
*
* @return byte read from I2C bus
*/
virtual uint8_t read(uint8_t last) = 0;
/** Issue a start condition
*
* @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue()
*/
virtual void start() = 0;
/** Issue a stop condition.
*
* @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue()
*/
virtual void stop() = 0;
bool transfer(uint8_t addressRW, void *buf,
size_t nbyte, uint8_t option = I2C_STOP);
bool transferContinue(void *buf, size_t nbyte, uint8_t option = I2C_STOP);
/** Write a byte
*
* @note This function should only be used by experts. Data should be
* accessed by calling transfer() and transferContinue()
*
* @param[in] data byte to write
* @return true for ACK or false for NACK */
virtual bool write(uint8_t data) = 0;
private:
uint8_t _state;
};
//==============================================================================
/**
* @class SoftI2cMaster
* @brief AVR Software I2C master class
*/
class SoftI2cMaster : public I2cMasterBase
{
public:
SoftI2cMaster() {}
SoftI2cMaster(uint8_t sclPin, uint8_t sdaPin);
void begin(uint8_t sclPin, uint8_t sdaPin);
uint8_t read(uint8_t last);
void start();
void stop(void);
bool write(uint8_t b);
private:
uint8_t _sclBit;
uint8_t _sdaBit;
volatile uint8_t* _sclDDR;
volatile uint8_t* _sdaDDR;
volatile uint8_t* _sdaInReg;
//----------------------------------------------------------------------------
bool readSda()
{
return *_sdaInReg & _sdaBit;
}
//----------------------------------------------------------------------------
void sclDelay(uint8_t n)
{
_delay_loop_1(n);
}
//----------------------------------------------------------------------------
void writeScl(bool value)
{
uint8_t s = SREG;
noInterrupts();
if (value == LOW) {
// Pull scl low.
*_sclDDR |= _sclBit;
} else {
// Put scl in high Z input mode.
*_sclDDR &= ~_sclBit;
}
SREG = s;
}
//----------------------------------------------------------------------------
void writeSda(bool value)
{
uint8_t s = SREG;
noInterrupts();
if (value == LOW) {
// Pull sda low.
*_sdaDDR |= _sdaBit;
} else {
// Put sda in high Z input mode.
*_sdaDDR &= ~_sdaBit;
}
SREG = s;
}
};
//==============================================================================
// Template based fast software I2C
//------------------------------------------------------------------------------
/**
* @class FastI2cMaster
* @brief AVR Fast software I2C master class.
*/
template<uint8_t sclPin, uint8_t sdaPin>
class FastI2cMaster : public I2cMasterBase
{
public:
//----------------------------------------------------------------------------
FastI2cMaster()
{
begin();
}
//----------------------------------------------------------------------------
/** Initialize I2C bus pins. */
void begin()
{
fastDigitalWrite(sclPin, LOW);
fastDigitalWrite(sdaPin, LOW);
sclWrite(HIGH);
sdaWrite(HIGH);
}
//----------------------------------------------------------------------------
uint8_t read(uint8_t last)
{
uint8_t data = 0;
sdaWrite(HIGH);
readBit(7, &data);
readBit(6, &data);
readBit(5, &data);
readBit(4, &data);
readBit(3, &data);
readBit(2, &data);
readBit(1, &data);
readBit(0, &data);
// send ACK or NACK
sdaWrite(last);
sclDelay(4);
sclWrite(HIGH);
sclDelay(6);
sclWrite(LOW);
sdaWrite(LOW);
return data;
}
//----------------------------------------------------------------------------
void start()
{
if (!fastDigitalRead(sdaPin)) {
// It's a repeat start.
sdaWrite(HIGH);
sclDelay(8);
sclWrite(HIGH);
sclDelay(8);
}
sdaWrite(LOW);
sclDelay(8);
sclWrite(LOW);
sclDelay(8);
}
//----------------------------------------------------------------------------
void stop(void)
{
sdaWrite(LOW);
sclDelay(8);
sclWrite(HIGH);
sclDelay(8);
sdaWrite(HIGH);
sclDelay(8);
}
//----------------------------------------------------------------------------
bool write(uint8_t data)
{
// write byte
writeBit(7, data);
writeBit(6, data);
writeBit(5, data);
writeBit(4, data);
writeBit(3, data);
writeBit(2, data);
writeBit(1, data);
writeBit(0, data);
// get ACK or NACK
sdaWrite(HIGH);
sclWrite(HIGH);
sclDelay(5);
bool rtn = fastDigitalRead(sdaPin);
sclWrite(LOW);
sdaWrite(LOW);
return rtn == 0;
}
private:
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void sclWrite(bool value)
{
fastDdrWrite(sclPin, !value);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void sdaWrite(bool value)
{
fastDdrWrite(sdaPin, !value);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void readBit(uint8_t bit, uint8_t* data)
{
sclWrite(HIGH);
sclDelay(5);
if (fastDigitalRead(sdaPin)) {
*data |= 1 << bit;
}
sclWrite(LOW);
if (bit) {
sclDelay(6);
}
}
//----------------------------------------------------------------------------
void sclDelay(uint8_t n)
{
_delay_loop_1(n);
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void writeBit(uint8_t bit, uint8_t data)
{
uint8_t mask = 1 << bit;
sdaWrite(data & mask);
sclWrite(HIGH);
sclDelay(5);
sclWrite(LOW);
sclDelay(5);
}
};
#endif // __AVR__
#endif // SOFT_I2C_MASTER_H
/** @} */

View File

@@ -0,0 +1,180 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Software SPI.
*
* @defgroup softSPI Software SPI
* @details Software SPI Template Class.
* @{
*/
#ifndef SoftSPI_h
#define SoftSPI_h
#include "DigitalPin.h"
//------------------------------------------------------------------------------
/** Nop for timing. */
#define nop asm volatile ("nop\n\t")
//------------------------------------------------------------------------------
/** Pin Mode for MISO is input.*/
#define MISO_MODE INPUT
/** Pullups disabled for MISO are disabled. */
#define MISO_LEVEL false
/** Pin Mode for MOSI is output.*/
#define MOSI_MODE OUTPUT
/** Pin Mode for SCK is output. */
#define SCK_MODE OUTPUT
//------------------------------------------------------------------------------
/**
* @class SoftSPI
* @brief Fast software SPI.
*/
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin, uint8_t Mode = 0>
class SoftSPI
{
public:
//----------------------------------------------------------------------------
/** Initialize SoftSPI pins. */
void begin()
{
fastPinConfig(MisoPin, MISO_MODE, MISO_LEVEL);
fastPinConfig(MosiPin, MOSI_MODE, !MODE_CPHA(Mode));
fastPinConfig(SckPin, SCK_MODE, MODE_CPOL(Mode));
}
//----------------------------------------------------------------------------
/** Soft SPI receive byte.
* @return Data byte received.
*/
inline __attribute__((always_inline))
uint8_t receive()
{
uint8_t data = 0;
receiveBit(7, &data);
receiveBit(6, &data);
receiveBit(5, &data);
receiveBit(4, &data);
receiveBit(3, &data);
receiveBit(2, &data);
receiveBit(1, &data);
receiveBit(0, &data);
return data;
}
//----------------------------------------------------------------------------
/** Soft SPI send byte.
* @param[in] data Data byte to send.
*/
inline __attribute__((always_inline))
void send(uint8_t data)
{
sendBit(7, data);
sendBit(6, data);
sendBit(5, data);
sendBit(4, data);
sendBit(3, data);
sendBit(2, data);
sendBit(1, data);
sendBit(0, data);
}
//----------------------------------------------------------------------------
/** Soft SPI transfer byte.
* @param[in] txData Data byte to send.
* @return Data byte received.
*/
inline __attribute__((always_inline))
uint8_t transfer(uint8_t txData)
{
uint8_t rxData = 0;
transferBit(7, &rxData, txData);
transferBit(6, &rxData, txData);
transferBit(5, &rxData, txData);
transferBit(4, &rxData, txData);
transferBit(3, &rxData, txData);
transferBit(2, &rxData, txData);
transferBit(1, &rxData, txData);
transferBit(0, &rxData, txData);
return rxData;
}
private:
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
bool MODE_CPHA(uint8_t mode)
{
return (mode & 1) != 0;
}
inline __attribute__((always_inline))
bool MODE_CPOL(uint8_t mode)
{
return (mode & 2) != 0;
}
inline __attribute__((always_inline))
void receiveBit(uint8_t bit, uint8_t* data)
{
if (MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
}
nop;
nop;
fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) {
*data |= 1 << bit;
}
if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
}
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void sendBit(uint8_t bit, uint8_t data)
{
if (MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
}
fastDigitalWrite(MosiPin, data & (1 << bit));
fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
nop;
nop;
if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
}
}
//----------------------------------------------------------------------------
inline __attribute__((always_inline))
void transferBit(uint8_t bit, uint8_t* rxData, uint8_t txData)
{
if (MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, !MODE_CPOL(Mode));
}
fastDigitalWrite(MosiPin, txData & (1 << bit));
fastDigitalWrite(SckPin,
MODE_CPHA(Mode) ? MODE_CPOL(Mode) : !MODE_CPOL(Mode));
if (fastDigitalRead(MisoPin)) {
*rxData |= 1 << bit;
}
if (!MODE_CPHA(Mode)) {
fastDigitalWrite(SckPin, MODE_CPOL(Mode));
}
}
//----------------------------------------------------------------------------
};
#endif // SoftSPI_h
/** @} */

View File

@@ -0,0 +1,37 @@
#ifndef AvrDevelopersGpioPinMap_h
#define AvrDevelopersGpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(B, 0), // D0
GPIO_PIN(B, 1), // D1
GPIO_PIN(B, 2), // D2
GPIO_PIN(B, 3), // D3
GPIO_PIN(B, 4), // D4
GPIO_PIN(B, 5), // D5
GPIO_PIN(B, 6), // D6
GPIO_PIN(B, 7), // D7
GPIO_PIN(D, 0), // D8
GPIO_PIN(D, 1), // D9
GPIO_PIN(D, 2), // D10
GPIO_PIN(D, 3), // D11
GPIO_PIN(D, 4), // D12
GPIO_PIN(D, 5), // D13
GPIO_PIN(D, 6), // D14
GPIO_PIN(D, 7), // D15
GPIO_PIN(C, 0), // D16
GPIO_PIN(C, 1), // D17
GPIO_PIN(C, 2), // D18
GPIO_PIN(C, 3), // D19
GPIO_PIN(C, 4), // D20
GPIO_PIN(C, 5), // D21
GPIO_PIN(C, 6), // D22
GPIO_PIN(C, 7), // D23
GPIO_PIN(A, 7), // D24
GPIO_PIN(A, 6), // D25
GPIO_PIN(A, 5), // D26
GPIO_PIN(A, 4), // D27
GPIO_PIN(A, 3), // D28
GPIO_PIN(A, 2), // D29
GPIO_PIN(A, 1), // D30
GPIO_PIN(A, 0) // D31
};
#endif // AvrDevelopersGpioPinMap_h

View File

@@ -0,0 +1,37 @@
#ifndef BobuinoGpioPinMap_h
#define BobuinoGpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(B, 0), // D0
GPIO_PIN(B, 1), // D1
GPIO_PIN(B, 2), // D2
GPIO_PIN(B, 3), // D3
GPIO_PIN(B, 4), // D4
GPIO_PIN(B, 5), // D5
GPIO_PIN(B, 6), // D6
GPIO_PIN(B, 7), // D7
GPIO_PIN(D, 0), // D8
GPIO_PIN(D, 1), // D9
GPIO_PIN(D, 2), // D10
GPIO_PIN(D, 3), // D11
GPIO_PIN(D, 4), // D12
GPIO_PIN(D, 5), // D13
GPIO_PIN(D, 6), // D14
GPIO_PIN(D, 7), // D15
GPIO_PIN(C, 0), // D16
GPIO_PIN(C, 1), // D17
GPIO_PIN(C, 2), // D18
GPIO_PIN(C, 3), // D19
GPIO_PIN(C, 4), // D20
GPIO_PIN(C, 5), // D21
GPIO_PIN(C, 6), // D22
GPIO_PIN(C, 7), // D23
GPIO_PIN(A, 0), // D24
GPIO_PIN(A, 1), // D25
GPIO_PIN(A, 2), // D26
GPIO_PIN(A, 3), // D27
GPIO_PIN(A, 4), // D28
GPIO_PIN(A, 5), // D29
GPIO_PIN(A, 6), // D30
GPIO_PIN(A, 7) // D31
};
#endif // BobuinoGpioPinMap_h

View File

@@ -0,0 +1,64 @@
/* Arduino DigitalIO Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino DigitalIO Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino DigitalIO Library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef GpioPinMap_h
#define GpioPinMap_h
#if defined(__AVR_ATmega168__)\
||defined(__AVR_ATmega168P__)\
||defined(__AVR_ATmega328P__)
// 168 and 328 Arduinos
#include "UnoGpioPinMap.h"
#elif defined(__AVR_ATmega1280__)\
|| defined(__AVR_ATmega2560__)
// Mega ADK
#include "MegaGpioPinMap.h"
#elif defined(__AVR_ATmega32U4__)
#ifdef CORE_TEENSY
#include "Teensy2GpioPinMap.h"
#else // CORE_TEENSY
// Leonardo or Yun
#include "LeonardoGpioPinMap.h"
#endif // CORE_TEENSY
#elif defined(__AVR_AT90USB646__)\
|| defined(__AVR_AT90USB1286__)
// Teensy++ 1.0 & 2.0
#include "Teensy2ppGpioPinMap.h"
#elif defined(__AVR_ATmega1284P__)\
|| defined(__AVR_ATmega1284__)\
|| defined(__AVR_ATmega644P__)\
|| defined(__AVR_ATmega644__)\
|| defined(__AVR_ATmega64__)\
|| defined(__AVR_ATmega32__)\
|| defined(__AVR_ATmega324__)\
|| defined(__AVR_ATmega16__)
#ifdef ARDUINO_1284P_AVR_DEVELOPERS
#include "AvrDevelopersGpioPinMap.h"
#elif defined(ARDUINO_1284P_BOBUINO)
#include "BobuinoGpioPinMap.h"
#elif defined(ARDUINO_1284P_SLEEPINGBEAUTY)
#include "SleepingBeautyGpioPinMap.h"
#elif defined(ARDUINO_1284P_STANDARD)
#include "Standard1284GpioPinMap.h"
#else // ARDUINO_1284P_SLEEPINGBEAUTY
#error Undefined variant 1284, 644, 324
#endif // ARDUINO_1284P_SLEEPINGBEAUTY
#else // 1284P, 1284, 644
#error Unknown board type.
#endif // end all boards
#endif // GpioPinMap_h

View File

@@ -0,0 +1,35 @@
#ifndef LeonardoGpioPinMap_h
#define LeonardoGpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(D, 2), // D0
GPIO_PIN(D, 3), // D1
GPIO_PIN(D, 1), // D2
GPIO_PIN(D, 0), // D3
GPIO_PIN(D, 4), // D4
GPIO_PIN(C, 6), // D5
GPIO_PIN(D, 7), // D6
GPIO_PIN(E, 6), // D7
GPIO_PIN(B, 4), // D8
GPIO_PIN(B, 5), // D9
GPIO_PIN(B, 6), // D10
GPIO_PIN(B, 7), // D11
GPIO_PIN(D, 6), // D12
GPIO_PIN(C, 7), // D13
GPIO_PIN(B, 3), // D14
GPIO_PIN(B, 1), // D15
GPIO_PIN(B, 2), // D16
GPIO_PIN(B, 0), // D17
GPIO_PIN(F, 7), // D18
GPIO_PIN(F, 6), // D19
GPIO_PIN(F, 5), // D20
GPIO_PIN(F, 4), // D21
GPIO_PIN(F, 1), // D22
GPIO_PIN(F, 0), // D23
GPIO_PIN(D, 4), // D24
GPIO_PIN(D, 7), // D25
GPIO_PIN(B, 4), // D26
GPIO_PIN(B, 5), // D27
GPIO_PIN(B, 6), // D28
GPIO_PIN(D, 6) // D29
};
#endif // LeonardoGpioPinMap_h

View File

@@ -0,0 +1,75 @@
#ifndef MegaGpioPinMap_h
#define MegaGpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(E, 0), // D0
GPIO_PIN(E, 1), // D1
GPIO_PIN(E, 4), // D2
GPIO_PIN(E, 5), // D3
GPIO_PIN(G, 5), // D4
GPIO_PIN(E, 3), // D5
GPIO_PIN(H, 3), // D6
GPIO_PIN(H, 4), // D7
GPIO_PIN(H, 5), // D8
GPIO_PIN(H, 6), // D9
GPIO_PIN(B, 4), // D10
GPIO_PIN(B, 5), // D11
GPIO_PIN(B, 6), // D12
GPIO_PIN(B, 7), // D13
GPIO_PIN(J, 1), // D14
GPIO_PIN(J, 0), // D15
GPIO_PIN(H, 1), // D16
GPIO_PIN(H, 0), // D17
GPIO_PIN(D, 3), // D18
GPIO_PIN(D, 2), // D19
GPIO_PIN(D, 1), // D20
GPIO_PIN(D, 0), // D21
GPIO_PIN(A, 0), // D22
GPIO_PIN(A, 1), // D23
GPIO_PIN(A, 2), // D24
GPIO_PIN(A, 3), // D25
GPIO_PIN(A, 4), // D26
GPIO_PIN(A, 5), // D27
GPIO_PIN(A, 6), // D28
GPIO_PIN(A, 7), // D29
GPIO_PIN(C, 7), // D30
GPIO_PIN(C, 6), // D31
GPIO_PIN(C, 5), // D32
GPIO_PIN(C, 4), // D33
GPIO_PIN(C, 3), // D34
GPIO_PIN(C, 2), // D35
GPIO_PIN(C, 1), // D36
GPIO_PIN(C, 0), // D37
GPIO_PIN(D, 7), // D38
GPIO_PIN(G, 2), // D39
GPIO_PIN(G, 1), // D40
GPIO_PIN(G, 0), // D41
GPIO_PIN(L, 7), // D42
GPIO_PIN(L, 6), // D43
GPIO_PIN(L, 5), // D44
GPIO_PIN(L, 4), // D45
GPIO_PIN(L, 3), // D46
GPIO_PIN(L, 2), // D47
GPIO_PIN(L, 1), // D48
GPIO_PIN(L, 0), // D49
GPIO_PIN(B, 3), // D50
GPIO_PIN(B, 2), // D51
GPIO_PIN(B, 1), // D52
GPIO_PIN(B, 0), // D53
GPIO_PIN(F, 0), // D54
GPIO_PIN(F, 1), // D55
GPIO_PIN(F, 2), // D56
GPIO_PIN(F, 3), // D57
GPIO_PIN(F, 4), // D58
GPIO_PIN(F, 5), // D59
GPIO_PIN(F, 6), // D60
GPIO_PIN(F, 7), // D61
GPIO_PIN(K, 0), // D62
GPIO_PIN(K, 1), // D63
GPIO_PIN(K, 2), // D64
GPIO_PIN(K, 3), // D65
GPIO_PIN(K, 4), // D66
GPIO_PIN(K, 5), // D67
GPIO_PIN(K, 6), // D68
GPIO_PIN(K, 7) // D69
};
#endif // MegaGpioPinMap_h

View File

@@ -0,0 +1,37 @@
#ifndef SleepingBeautyGpioPinMap_h
#define SleepingBeautyGpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(D, 0), // D0
GPIO_PIN(D, 1), // D1
GPIO_PIN(D, 2), // D2
GPIO_PIN(D, 3), // D3
GPIO_PIN(B, 0), // D4
GPIO_PIN(B, 1), // D5
GPIO_PIN(B, 2), // D6
GPIO_PIN(B, 3), // D7
GPIO_PIN(D, 6), // D8
GPIO_PIN(D, 5), // D9
GPIO_PIN(B, 4), // D10
GPIO_PIN(B, 5), // D11
GPIO_PIN(B, 6), // D12
GPIO_PIN(B, 7), // D13
GPIO_PIN(C, 7), // D14
GPIO_PIN(C, 6), // D15
GPIO_PIN(A, 5), // D16
GPIO_PIN(A, 4), // D17
GPIO_PIN(A, 3), // D18
GPIO_PIN(A, 2), // D19
GPIO_PIN(A, 1), // D20
GPIO_PIN(A, 0), // D21
GPIO_PIN(D, 4), // D22
GPIO_PIN(D, 7), // D23
GPIO_PIN(C, 2), // D24
GPIO_PIN(C, 3), // D25
GPIO_PIN(C, 4), // D26
GPIO_PIN(C, 5), // D27
GPIO_PIN(C, 1), // D28
GPIO_PIN(C, 0), // D29
GPIO_PIN(A, 6), // D30
GPIO_PIN(A, 7) // D31
};
#endif // SleepingBeautyGpioPinMap_h

View File

@@ -0,0 +1,37 @@
#ifndef Standard1284GpioPinMap_h
#define Standard1284GpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(B, 0), // D0
GPIO_PIN(B, 1), // D1
GPIO_PIN(B, 2), // D2
GPIO_PIN(B, 3), // D3
GPIO_PIN(B, 4), // D4
GPIO_PIN(B, 5), // D5
GPIO_PIN(B, 6), // D6
GPIO_PIN(B, 7), // D7
GPIO_PIN(D, 0), // D8
GPIO_PIN(D, 1), // D9
GPIO_PIN(D, 2), // D10
GPIO_PIN(D, 3), // D11
GPIO_PIN(D, 4), // D12
GPIO_PIN(D, 5), // D13
GPIO_PIN(D, 6), // D14
GPIO_PIN(D, 7), // D15
GPIO_PIN(C, 0), // D16
GPIO_PIN(C, 1), // D17
GPIO_PIN(C, 2), // D18
GPIO_PIN(C, 3), // D19
GPIO_PIN(C, 4), // D20
GPIO_PIN(C, 5), // D21
GPIO_PIN(C, 6), // D22
GPIO_PIN(C, 7), // D23
GPIO_PIN(A, 0), // D24
GPIO_PIN(A, 1), // D25
GPIO_PIN(A, 2), // D26
GPIO_PIN(A, 3), // D27
GPIO_PIN(A, 4), // D28
GPIO_PIN(A, 5), // D29
GPIO_PIN(A, 6), // D30
GPIO_PIN(A, 7) // D31
};
#endif // Standard1284GpioPinMap_h

View File

@@ -0,0 +1,30 @@
#ifndef Teensy2GpioPinMap_h
#define Teensy2GpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(B, 0), // D0
GPIO_PIN(B, 1), // D1
GPIO_PIN(B, 2), // D2
GPIO_PIN(B, 3), // D3
GPIO_PIN(B, 7), // D4
GPIO_PIN(D, 0), // D5
GPIO_PIN(D, 1), // D6
GPIO_PIN(D, 2), // D7
GPIO_PIN(D, 3), // D8
GPIO_PIN(C, 6), // D9
GPIO_PIN(C, 7), // D10
GPIO_PIN(D, 6), // D11
GPIO_PIN(D, 7), // D12
GPIO_PIN(B, 4), // D13
GPIO_PIN(B, 5), // D14
GPIO_PIN(B, 6), // D15
GPIO_PIN(F, 7), // D16
GPIO_PIN(F, 6), // D17
GPIO_PIN(F, 5), // D18
GPIO_PIN(F, 4), // D19
GPIO_PIN(F, 1), // D20
GPIO_PIN(F, 0), // D21
GPIO_PIN(D, 4), // D22
GPIO_PIN(D, 5), // D23
GPIO_PIN(E, 6), // D24
};
#endif // Teensy2GpioPinMap_h

View File

@@ -0,0 +1,51 @@
#ifndef Teensypp2GpioPinMap_h
#define Teensypp2GpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(D, 0), // D0
GPIO_PIN(D, 1), // D1
GPIO_PIN(D, 2), // D2
GPIO_PIN(D, 3), // D3
GPIO_PIN(D, 4), // D4
GPIO_PIN(D, 5), // D5
GPIO_PIN(D, 6), // D6
GPIO_PIN(D, 7), // D7
GPIO_PIN(E, 0), // D8
GPIO_PIN(E, 1), // D9
GPIO_PIN(C, 0), // D10
GPIO_PIN(C, 1), // D11
GPIO_PIN(C, 2), // D12
GPIO_PIN(C, 3), // D13
GPIO_PIN(C, 4), // D14
GPIO_PIN(C, 5), // D15
GPIO_PIN(C, 6), // D16
GPIO_PIN(C, 7), // D17
GPIO_PIN(E, 6), // D18
GPIO_PIN(E, 7), // D19
GPIO_PIN(B, 0), // D20
GPIO_PIN(B, 1), // D21
GPIO_PIN(B, 2), // D22
GPIO_PIN(B, 3), // D23
GPIO_PIN(B, 4), // D24
GPIO_PIN(B, 5), // D25
GPIO_PIN(B, 6), // D26
GPIO_PIN(B, 7), // D27
GPIO_PIN(A, 0), // D28
GPIO_PIN(A, 1), // D29
GPIO_PIN(A, 2), // D30
GPIO_PIN(A, 3), // D31
GPIO_PIN(A, 4), // D32
GPIO_PIN(A, 5), // D33
GPIO_PIN(A, 6), // D34
GPIO_PIN(A, 7), // D35
GPIO_PIN(E, 4), // D36
GPIO_PIN(E, 5), // D37
GPIO_PIN(F, 0), // D38
GPIO_PIN(F, 1), // D39
GPIO_PIN(F, 2), // D40
GPIO_PIN(F, 3), // D41
GPIO_PIN(F, 4), // D42
GPIO_PIN(F, 5), // D43
GPIO_PIN(F, 6), // D44
GPIO_PIN(F, 7), // D45
};
#endif // Teensypp2GpioPinMap_h

View File

@@ -0,0 +1,25 @@
#ifndef UnoGpioPinMap_h
#define UnoGpioPinMap_h
static const GpioPinMap_t GpioPinMap[] = {
GPIO_PIN(D, 0), // D0
GPIO_PIN(D, 1), // D1
GPIO_PIN(D, 2), // D2
GPIO_PIN(D, 3), // D3
GPIO_PIN(D, 4), // D4
GPIO_PIN(D, 5), // D5
GPIO_PIN(D, 6), // D6
GPIO_PIN(D, 7), // D7
GPIO_PIN(B, 0), // D8
GPIO_PIN(B, 1), // D9
GPIO_PIN(B, 2), // D10
GPIO_PIN(B, 3), // D11
GPIO_PIN(B, 4), // D12
GPIO_PIN(B, 5), // D13
GPIO_PIN(C, 0), // D14
GPIO_PIN(C, 1), // D15
GPIO_PIN(C, 2), // D16
GPIO_PIN(C, 3), // D17
GPIO_PIN(C, 4), // D18
GPIO_PIN(C, 5) // D19
};
#endif // UnoGpioPinMap_h

View File

@@ -0,0 +1,31 @@
/*
* Optimized digital functions for AVR microcontrollers
* based on http://code.google.com/p/digitalwritefast
*/
#ifndef __digitalWriteFast_h_
#define __digitalWriteFast_h_
#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined (__AVR_ATmega168__)
#if defined(__AVR_ATmega328PB__)
#define __digitalPinToPortReg(__pin) (((__pin) <= 7) ? &PORTD : (((__pin) >= 8 && (__pin) <= 13) ? &PORTB : (((__pin) >= 14 && (__pin) <= 19) ? &PORTC : &PORTE)))
#define __digitalPinToDDRReg(__pin) (((__pin) <= 7) ? &DDRD : (((__pin) >= 8 && (__pin) <= 13) ? &DDRB : (((__pin) >= 14 && (__pin) <= 19) ? &DDRC : &DDRE)))
#define __digitalPinToPINReg(__pin) (((__pin) <= 7) ? &PIND : (((__pin) >= 8 && (__pin) <= 13) ? &PINB : (((__pin) >= 14 && (__pin) <= 19) ? &PINC : &PINE)))
#define __digitalPinToBit(__pin) (((__pin) <= 7) ? (__pin) : (((__pin) >= 8 && (__pin) <= 13) ? (__pin) - 8 : (((__pin) >= 14 && (__pin) <= 19) ? (__pin) - 14 : (((__pin) >= 20 && (__pin) <= 21) ? (__pin) - 18 : (__pin) - 22))))
#else
#define __digitalPinToPortReg(__pin) (((__pin) <= 7) ? &PORTD : (((__pin) >= 8 && (__pin) <= 13) ? &PORTB : &PORTC))
#define __digitalPinToDDRReg(__pin) (((__pin) <= 7) ? &DDRD : (((__pin) >= 8 && (__pin) <= 13) ? &DDRB : &DDRC))
#define __digitalPinToPINReg(__pin) (((__pin) <= 7) ? &PIND : (((__pin) >= 8 && (__pin) <= 13) ? &PINB : &PINC))
#define __digitalPinToBit(__pin) (((__pin) <= 7) ? (__pin) : (((__pin) >= 8 && (__pin) <= 13) ? (__pin) - 8 : (__pin) - 14))
#endif
#define digitalWriteFast(__pin, __value) do { if (__builtin_constant_p(__pin) && __builtin_constant_p(__value)) { bitWrite(*__digitalPinToPortReg(__pin), (uint8_t)__digitalPinToBit(__pin), (__value)); } else { digitalWrite((__pin), (__value)); } } while (0)
#define pinModeFast(__pin, __mode) do { if (__builtin_constant_p(__pin) && __builtin_constant_p(__mode) && (__mode!=INPUT_PULLUP)) { bitWrite(*__digitalPinToDDRReg(__pin), (uint8_t)__digitalPinToBit(__pin), (__mode)); } else { pinMode((__pin), (__mode)); } } while (0)
#define digitalReadFast(__pin) ( (bool) (__builtin_constant_p(__pin) ) ? (( bitRead(*__digitalPinToPINReg(__pin), (uint8_t)__digitalPinToBit(__pin))) ) : digitalRead((__pin)) )
#else
// for all other archs use built-in pin access functions
#define digitalWriteFast(__pin, __value) digitalWrite(__pin, __value)
#define pinModeFast(__pin, __value) pinMode(__pin, __value)
#define digitalReadFast(__pin) digitalRead(__pin)
#endif
#endif

View File

@@ -0,0 +1,144 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Arduino core for ESP32: https://github.com/espressif/arduino-esp32
*
* MySensors ESP32 implementation, Copyright (C) 2017-2018 Olivier Mauti <olivier@mysensors.org>
*
*/
#include "MyHwESP32.h"
bool hwInit(void)
{
#if !defined(MY_DISABLED_SERIAL)
MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {}
#endif
#endif
return EEPROM.begin(MY_EEPROM_SIZE);
}
void hwReadConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *dst = static_cast<uint8_t *>(buf);
int offs = reinterpret_cast<int>(addr);
while (length-- > 0) {
*dst++ = EEPROM.read(offs++);
}
}
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *src = static_cast<uint8_t *>(buf);
int offs = reinterpret_cast<int>(addr);
while (length-- > 0) {
EEPROM.write(offs++, *src++);
}
EEPROM.commit();
}
uint8_t hwReadConfig(const int addr)
{
uint8_t value;
hwReadConfigBlock(&value, reinterpret_cast<void *>(addr), 1);
return value;
}
void hwWriteConfig(const int addr, uint8_t value)
{
if (hwReadConfig(addr) != value) {
hwWriteConfigBlock(&value, reinterpret_cast<void *>(addr), 1);
}
}
bool hwUniqueID(unique_id_t *uniqueID)
{
uint64_t val = ESP.getEfuseMac();
(void)memcpy((void *)uniqueID, (void *)&val, 8);
(void)memset((void *)(uniqueID + 8), MY_HWID_PADDING_BYTE, 8); // padding
return true;
}
ssize_t hwGetentropy(void *__buffer, size_t __length)
{
// cut length if > 256
if (__length > 256) {
__length = 256;
}
uint8_t *dst = (uint8_t *)__buffer;
// get random numbers
for (size_t i = 0; i < __length; i++) {
dst[i] = (uint8_t)esp_random();
}
return __length;
}
int8_t hwSleep(uint32_t ms)
{
// TODO: Not supported!
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
// TODO: Not supported!
(void)interrupt;
(void)mode;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
// TODO: Not supported!
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
uint16_t hwCPUVoltage(void)
{
// in mV
return FUNCTION_NOT_SUPPORTED;
}
uint16_t hwCPUFrequency(void)
{
// in 1/10Mhz
return static_cast<uint16_t>(ESP.getCpuFreqMHz() * 10);
}
int8_t hwCPUTemperature(void)
{
// CPU temperature in °C
return static_cast<int8_t>((temperatureRead() - MY_ESP32_TEMPERATURE_OFFSET) /
MY_ESP32_TEMPERATURE_GAIN);
}
uint16_t hwFreeMem(void)
{
return static_cast<uint16_t>(ESP.getFreeHeap());
}

View File

@@ -0,0 +1,107 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Arduino core for ESP32: https://github.com/espressif/arduino-esp32
*
* MySensors ESP32 implementation, Copyright (C) 2017-2018 Olivier Mauti <olivier@mysensors.org>
*
* Radio wiring ESP32(Node32s): RF24, RFM69, RFM95:
*
* | IO | RF24 | RFM69 | RFM95 |
* |------|------|-------|-------|
* | MOSI | 23 | 23 | 23 |
* | MISO | 19 | 19 | 19 |
* | SCK | 18 | 18 | 18 |
* | CSN | 5 | 5 | 5 |
* | CE | 17 | - | - |
* | RST | - | 17 | 17 |
* | IRQ | 16* | 16 | 16 |
* * = optional
*
*/
#ifndef MyHwESP32_h
#define MyHwESP32_h
#include <WiFi.h>
#include "EEPROM.h"
#include <SPI.h>
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
#ifndef MY_ESP32_TEMPERATURE_OFFSET
#define MY_ESP32_TEMPERATURE_OFFSET (0.0f)
#endif
#ifndef MY_ESP32_TEMPERATURE_GAIN
#define MY_ESP32_TEMPERATURE_GAIN (1.0f)
#endif
#define MY_EEPROM_SIZE 1024
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
#define hwDigitalRead(__pin) digitalRead(__pin)
#define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwWatchdogReset()
#define hwReboot() ESP.restart()
#define hwMillis() millis()
#define hwMicros() micros()
#define hwRandomNumberInit() randomSeed(esp_random())
#define hwGetSleepRemaining() (0ul)
bool hwInit(void);
void hwReadConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(const int addr);
ssize_t hwGetentropy(void *__buffer, size_t __length);
#define MY_HW_HAS_GETENTROPY
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
/**
* Restore interrupt state.
* Helper function for MY_CRITICAL_SECTION.
*/
static __inline__ void __psRestore(const uint32_t *__s)
{
XTOS_RESTORE_INTLEVEL(*__s);
}
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION for ( uint32_t __psSaved __attribute__((__cleanup__(__psRestore))) = XTOS_DISABLE_ALL_INTERRUPTS, __ToDo = 1; __ToDo ; __ToDo = 0 )
#endif /* DOXYGEN */
#endif

View File

@@ -0,0 +1,57 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "Arduino.h"
TaskHandle_t loopTaskHandle = NULL;
#if CONFIG_AUTOSTART_ARDUINO
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
bool loopTaskWDTEnabled;
void loopTask(void *pvParameters)
{
_begin(); // Startup MySensors library
for(;;) {
if(loopTaskWDTEnabled) {
esp_task_wdt_reset();
}
_process(); // Process incoming data
loop();
}
}
extern "C" void app_main()
{
loopTaskWDTEnabled = false;
initArduino();
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);
}
#endif

View File

@@ -0,0 +1,156 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwESP8266.h"
bool hwInit(void)
{
#if !defined(MY_DISABLED_SERIAL)
MY_SERIALDEVICE.begin(MY_BAUD_RATE, SERIAL_8N1, MY_ESP8266_SERIAL_MODE, 1);
MY_SERIALDEVICE.setDebugOutput(true);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {}
#endif
#endif
EEPROM.begin(EEPROM_size);
return true;
}
void hwReadConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *dst = static_cast<uint8_t *>(buf);
int pos = reinterpret_cast<int>(addr);
while (length-- > 0) {
*dst++ = EEPROM.read(pos++);
}
}
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *src = static_cast<uint8_t *>(buf);
int pos = reinterpret_cast<int>(addr);
while (length-- > 0) {
EEPROM.write(pos++, *src++);
}
// see implementation, commit only executed if diff
EEPROM.commit();
}
uint8_t hwReadConfig(const int addr)
{
uint8_t value;
hwReadConfigBlock(&value, reinterpret_cast<void *>(addr), 1);
return value;
}
void hwWriteConfig(const int addr, uint8_t value)
{
hwWriteConfigBlock(&value, reinterpret_cast<void *>(addr), 1);
}
bool hwUniqueID(unique_id_t *uniqueID)
{
// padding
(void)memset((uint8_t *)uniqueID, MY_HWID_PADDING_BYTE, sizeof(unique_id_t));
uint32_t val = ESP.getChipId();
(void)memcpy((uint8_t *)uniqueID, &val, 4);
val = ESP.getFlashChipId();
(void)memcpy((uint8_t *)uniqueID + 4, &val, 4);
return true;
}
ssize_t hwGetentropy(void *__buffer, size_t __length)
{
// cut length if > 256
if (__length > 256) {
__length = 256;
}
uint8_t *dst = (uint8_t *)__buffer;
// Start random number generator
for (size_t i = 0; i < __length; i++) {
dst[i] = (uint8_t)RANDOM_REG32;
}
return __length;
}
int8_t hwSleep(uint32_t ms)
{
// TODO: Not supported!
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
// TODO: Not supported!
(void)interrupt;
(void)mode;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
// TODO: Not supported!
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
#if defined(MY_SPECIAL_DEBUG)
// settings for getVcc()
ADC_MODE(ADC_VCC);
#else
// [default] settings for analogRead(A0)
ADC_MODE(ADC_TOUT);
#endif
uint16_t hwCPUVoltage(void)
{
#if defined(MY_SPECIAL_DEBUG)
// in mV, requires ADC_VCC set
return ESP.getVcc();
#else
// not possible
return FUNCTION_NOT_SUPPORTED;
#endif
}
uint16_t hwCPUFrequency(void)
{
// in 1/10Mhz
return ESP.getCpuFreqMHz()*10;
}
int8_t hwCPUTemperature(void)
{
return -127; // not available
}
uint16_t hwFreeMem(void)
{
return ESP.getFreeHeap();
}

View File

@@ -0,0 +1,82 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef MyHwESP8266_h
#define MyHwESP8266_h
#include <SPI.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
#define EEPROM_size (1024)
// Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
#define hwDigitalRead(__pin) digitalRead(__pin)
#define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwWatchdogReset() wdt_reset()
#define hwReboot() ESP.restart()
#define hwMillis() millis()
// The use of randomSeed switch to pseudo random number. Keep hwRandomNumberInit empty
#define hwRandomNumberInit()
#define hwGetSleepRemaining() (0ul)
bool hwInit(void);
void hwReadConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(const int addr);
ssize_t hwGetentropy(void *__buffer, size_t __length);
//#define MY_HW_HAS_GETENTROPY
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
/**
* Restore interrupt state.
* Helper function for MY_CRITICAL_SECTION.
*/
static __inline__ void __psRestore(const uint32_t *__s)
{
xt_wsr_ps( *__s );
}
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION for ( uint32_t __psSaved __attribute__((__cleanup__(__psRestore))) = xt_rsil(15), __ToDo = 1; __ToDo ; __ToDo = 0 )
#endif /* DOXYGEN */
#endif // #ifdef ARDUINO_ARCH_ESP8266

View File

@@ -0,0 +1,348 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
//This may be used to change user task stack size:
//#define CONT_STACKSIZE 4096
#include <Arduino.h>
#include "Schedule.h"
extern "C" {
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
#include "cont.h"
}
#include <core_version.h>
#include "gdb_hooks.h"
#define LOOP_TASK_PRIORITY 1
#define LOOP_QUEUE_SIZE 1
#define OPTIMISTIC_YIELD_TIME_US 16000
extern "C" void call_user_start();
extern void loop();
extern void setup();
extern void(*__init_array_start)(void);
extern void(*__init_array_end)(void);
/* Not static, used in Esp.cpp */
struct rst_info resetInfo;
/* Not static, used in core_esp8266_postmortem.c and other places.
* Placed into noinit section because we assign value to this variable
* before .bss is zero-filled, and need to preserve the value.
*/
cont_t* g_pcont __attribute__((section(".noinit")));
/* Event queue used by the main (arduino) task */
static os_event_t s_loop_queue[LOOP_QUEUE_SIZE];
/* Used to implement optimistic_yield */
static uint32_t s_micros_at_task_start;
/* For ets_intr_lock_nest / ets_intr_unlock_nest
* Max nesting seen by SDK so far is 2.
*/
#define ETS_INTR_LOCK_NEST_MAX 7
static uint16_t ets_intr_lock_stack[ETS_INTR_LOCK_NEST_MAX];
static byte ets_intr_lock_stack_ptr = 0;
extern "C" {
extern const uint32_t __attribute__((section(".ver_number"))) core_version =
ARDUINO_ESP8266_GIT_VER;
const char* core_release =
#ifdef ARDUINO_ESP8266_RELEASE
ARDUINO_ESP8266_RELEASE;
#else
NULL;
#endif
} // extern "C"
void initVariant() __attribute__((weak));
void initVariant()
{
}
void preloop_update_frequency() __attribute__((weak));
void preloop_update_frequency()
{
#if defined(F_CPU) && (F_CPU == 160000000L)
REG_SET_BIT(0x3ff00014, BIT(0));
ets_update_cpu_frequency(160);
#endif
}
extern "C" bool can_yield()
{
return cont_can_yield(g_pcont);
}
static inline void esp_yield_within_cont() __attribute__((always_inline));
static void esp_yield_within_cont()
{
cont_yield(g_pcont);
run_scheduled_recurrent_functions();
}
extern "C" void esp_yield()
{
if (can_yield()) {
esp_yield_within_cont();
}
}
extern "C" void esp_schedule()
{
// always on CONT stack here
ets_post(LOOP_TASK_PRIORITY, 0, 0);
}
extern "C" void __yield()
{
if (can_yield()) {
esp_schedule();
esp_yield_within_cont();
} else {
panic();
}
}
extern "C" void yield(void) __attribute__((weak, alias("__yield")));
extern "C" void optimistic_yield(uint32_t interval_us)
{
if (can_yield() &&
(system_get_time() - s_micros_at_task_start) > interval_us) {
yield();
}
}
// Replace ets_intr_(un)lock with nestable versions
extern "C" void IRAM_ATTR ets_intr_lock()
{
if (ets_intr_lock_stack_ptr < ETS_INTR_LOCK_NEST_MAX) {
ets_intr_lock_stack[ets_intr_lock_stack_ptr++] = xt_rsil(3);
} else {
xt_rsil(3);
}
}
extern "C" void IRAM_ATTR ets_intr_unlock()
{
if (ets_intr_lock_stack_ptr > 0) {
xt_wsr_ps(ets_intr_lock_stack[--ets_intr_lock_stack_ptr]);
} else {
xt_rsil(0);
}
}
// Save / Restore the PS state across the rom ets_post call as the rom code
// does not implement this correctly.
extern "C" bool ets_post_rom(uint8 prio, ETSSignal sig, ETSParam par);
extern "C" bool IRAM_ATTR ets_post(uint8 prio, ETSSignal sig, ETSParam par)
{
uint32_t saved;
asm volatile ("rsr %0,ps":"=a" (saved));
bool rc = ets_post_rom(prio, sig, par);
xt_wsr_ps(saved);
return rc;
}
extern "C" void __loop_end(void)
{
run_scheduled_functions();
run_scheduled_recurrent_functions();
}
extern "C" void loop_end(void) __attribute__((weak, alias("__loop_end")));
static void loop_wrapper()
{
static bool setup_done = false;
preloop_update_frequency();
if (!setup_done) {
_begin(); // Startup MySensors library
setup_done = true;
}
_process(); // Process incoming data
loop();
run_scheduled_functions();
esp_schedule();
}
static void loop_task(os_event_t *events)
{
(void)events;
s_micros_at_task_start = system_get_time();
cont_run(g_pcont, &loop_wrapper);
if (cont_check(g_pcont) != 0) {
panic();
}
}
extern "C" {
struct object {
long placeholder[10];
};
void __register_frame_info(const void *begin, struct object *ob);
extern char __eh_frame[];
}
static void do_global_ctors(void)
{
static struct object ob;
__register_frame_info(__eh_frame, &ob);
void(**p)(void) = &__init_array_end;
while (p != &__init_array_start) {
(*--p)();
}
}
extern "C" {
extern void __unhandled_exception(const char *str);
static void __unhandled_exception_cpp()
{
#ifndef __EXCEPTIONS
abort();
#else
static bool terminating;
if (terminating) {
abort();
}
terminating = true;
/* Use a trick from vterminate.cc to get any std::exception what() */
try {
__throw_exception_again;
} catch (const std::exception& e) {
__unhandled_exception(e.what());
} catch (...) {
__unhandled_exception("");
}
#endif
}
}
void init_done()
{
system_set_os_print(1);
gdb_init();
std::set_terminate(__unhandled_exception_cpp);
do_global_ctors();
esp_schedule();
}
/* This is the entry point of the application.
* It gets called on the default stack, which grows down from the top
* of DRAM area.
* .bss has not been zeroed out yet, but .data and .rodata are in place.
* Cache is not enabled, so only ROM and IRAM functions can be called.
* Peripherals (except for SPI0 and UART0) are not initialized.
* This function does not return.
*/
/*
A bit of explanation for this entry point:
SYS is the SDK task/context used by the upperlying system to run its
administrative tasks (at least WLAN and lwip's receive callbacks and
Ticker). NONOS-SDK is designed to run user's non-threaded code in
another specific task/context with its own stack in BSS.
Some clever fellows found that the SYS stack was a large and quite unused
piece of ram that we could use for the user's stack instead of using user's
main memory, thus saving around 4KB on ram/heap.
A problem arose later, which is that this stack can heavily be used by
the SDK for some features. One of these features is WPS. We still don't
know if other features are using this, or if this memory is going to be
used in future SDK releases.
WPS beeing flawed by its poor security, or not beeing used by lots of
users, it has been decided that we are still going to use that memory for
user's stack and disable the use of WPS.
app_entry() jumps to app_entry_custom() defined as "weakref" calling
itself a weak customizable function, allowing to use another one when
this is required (see core_esp8266_app_entry_noextra4k.cpp, used by WPS).
(note: setting app_entry() itself as "weak" is not sufficient and always
ends up with the other "noextra4k" one linked, maybe because it has a
default ENTRY(app_entry) value in linker scripts).
References:
https://github.com/esp8266/Arduino/pull/4553
https://github.com/esp8266/Arduino/pull/4622
https://github.com/esp8266/Arduino/issues/4779
https://github.com/esp8266/Arduino/pull/4889
*/
extern "C" void app_entry_redefinable(void) __attribute__((weak));
extern "C" void app_entry_redefinable(void)
{
/* Allocate continuation context on this SYS stack,
and save pointer to it. */
cont_t s_cont __attribute__((aligned(16)));
g_pcont = &s_cont;
/* Call the entry point of the SDK code. */
call_user_start();
}
static void app_entry_custom(void) __attribute__((weakref("app_entry_redefinable")));
extern "C" void app_entry(void)
{
return app_entry_custom();
}
extern "C" void preinit(void) __attribute__((weak));
extern "C" void preinit(void)
{
/* do nothing by default */
}
extern "C" void user_init(void)
{
struct rst_info *rtc_info_ptr = system_get_rst_info();
memcpy((void *)&resetInfo, (void *)rtc_info_ptr, sizeof(resetInfo));
uart_div_modify(0, UART_CLK_FREQ / (115200));
init(); // in core_esp8266_wiring.c, inits hw regs and sdk timer
initVariant();
cont_init(g_pcont);
preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable.
ets_task(loop_task,
LOOP_TASK_PRIORITY, s_loop_queue,
LOOP_QUEUE_SIZE);
system_init_done_cb(&init_done);
}

View File

@@ -0,0 +1,165 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwLinuxGeneric.h"
static SoftEeprom eeprom;
static FILE *randomFp = NULL;
bool hwInit(void)
{
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#ifdef MY_GATEWAY_SERIAL
#ifdef MY_LINUX_SERIAL_GROUPNAME
if (!MY_SERIALDEVICE.setGroupPerm(MY_LINUX_SERIAL_GROUPNAME)) {
logError("Unable to change permission for serial port device.\n");
exit(1);
}
#endif
#endif
if (eeprom.init(conf.eeprom_file, conf.eeprom_size) != 0) {
exit(1);
}
return true;
}
void hwReadConfigBlock(void *buf, void *addr, size_t length)
{
eeprom.readBlock(buf, addr, length);
}
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
{
eeprom.writeBlock(buf, addr, length);
}
uint8_t hwReadConfig(const int addr)
{
return eeprom.readByte(addr);
}
void hwWriteConfig(const int addr, uint8_t value)
{
eeprom.writeByte(addr, value);
}
void hwRandomNumberInit(void)
{
uint32_t seed=0;
if (randomFp != NULL) {
fclose(randomFp);
}
if (!(randomFp = fopen("/dev/urandom", "r"))) {
logError("Cannot open '/dev/urandom'.\n");
exit(2);
}
while (hwGetentropy(&seed, sizeof(seed)) != sizeof(seed));
randomSeed(seed);
}
ssize_t hwGetentropy(void *__buffer, size_t __length)
{
return(fread(__buffer, 1, __length, randomFp));
}
uint32_t hwMillis(void)
{
return millis();
}
bool hwUniqueID(unique_id_t *uniqueID)
{
// not implemented yet
(void)uniqueID;
return false;
}
// Not supported!
int8_t hwSleep(uint32_t ms)
{
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
// Not supported!
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
(void)interrupt;
(void)mode;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
// Not supported!
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
uint16_t hwCPUVoltage(void)
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}
uint16_t hwCPUFrequency(void)
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}
int8_t hwCPUTemperature(void)
{
return -127; // not implemented yet
}
uint16_t hwFreeMem(void)
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}
void hwDigitalWrite(uint8_t pin, uint8_t value)
{
digitalWrite(pin, value);
}
int hwDigitalRead(uint8_t pin)
{
return digitalRead(pin);
}
void hwPinMode(uint8_t pin, uint8_t mode)
{
pinMode(pin, mode);
}

View File

@@ -0,0 +1,116 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef MyHwLinuxGeneric_h
#define MyHwLinuxGeneric_h
#include <cstdlib>
#include <pthread.h>
#include "SerialPort.h"
#include "StdInOutStream.h"
#include <SPI.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
#include <unistd.h>
#include "SoftEeprom.h"
#include "log.h"
#include "config.h"
#define CRYPTO_LITTLE_ENDIAN
#ifdef MY_LINUX_SERIAL_PORT
#ifdef MY_LINUX_SERIAL_IS_PTY
SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PORT, true);
#else
SerialPort Serial = SerialPort(MY_LINUX_SERIAL_PORT, false);
#endif
#else
StdInOutStream Serial = StdInOutStream();
#endif
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
// Define these as macros (do nothing)
#define hwWatchdogReset()
#define hwReboot()
#define hwGetSleepRemaining() (0ul)
inline void hwDigitalWrite(uint8_t, uint8_t);
inline int hwDigitalRead(uint8_t);
inline void hwPinMode(uint8_t, uint8_t);
bool hwInit(void);
inline void hwReadConfigBlock(void *buf, void *addr, size_t length);
inline void hwWriteConfigBlock(void *buf, void *addr, size_t length);
inline uint8_t hwReadConfig(const int addr);
inline void hwWriteConfig(const int addr, uint8_t value);
inline void hwRandomNumberInit(void);
ssize_t hwGetentropy(void *__buffer, size_t __length);
#define MY_HW_HAS_GETENTROPY
inline uint32_t hwMillis(void);
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
#ifdef MY_RF24_IRQ_PIN
static pthread_mutex_t hw_mutex = PTHREAD_MUTEX_INITIALIZER;
static __inline__ void __hwUnlock(const uint8_t *__s)
{
pthread_mutex_unlock(&hw_mutex);
(void)__s;
}
static __inline__ void __hwLock()
{
pthread_mutex_lock(&hw_mutex);
}
#endif
#if defined(DOXYGEN)
#define ATOMIC_BLOCK_CLEANUP
#elif defined(MY_RF24_IRQ_PIN)
#define ATOMIC_BLOCK_CLEANUP uint8_t __atomic_loop \
__attribute__((__cleanup__( __hwUnlock ))) = 1
#else
#define ATOMIC_BLOCK_CLEANUP
#endif /* DOXYGEN */
#if defined(DOXYGEN)
#define ATOMIC_BLOCK
#elif defined(MY_RF24_IRQ_PIN)
#define ATOMIC_BLOCK for ( ATOMIC_BLOCK_CLEANUP, __hwLock(); \
__atomic_loop ; __atomic_loop = 0 )
#else
#define ATOMIC_BLOCK
#endif /* DOXYGEN */
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION ATOMIC_BLOCK
#endif /* DOXYGEN */
#endif

View File

@@ -0,0 +1,488 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
// Initialize library and handle sketch functions like we want to
#include <stdio.h>
#include <csignal>
#include <cstdlib>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <getopt.h>
#include "log.h"
#include "config.h"
#include "MySensorsCore.h"
void handle_sigint(int sig)
{
if (sig == SIGINT) {
logNotice("Received SIGINT\n\n");
} else if (sig == SIGTERM) {
logNotice("Received SIGTERM\n\n");
} else {
return;
}
#ifdef MY_RF24_IRQ_PIN
detachInterrupt(MY_RF24_IRQ_PIN);
#endif
#if defined(MY_GATEWAY_SERIAL)
MY_SERIALDEVICE.end();
#endif
logClose();
exit(EXIT_SUCCESS);
}
static int daemonize(void)
{
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
logError("fork: %s\n", strerror(errno));
return -1;
}
/* If we got a good PID, then we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* At this point we are executing as the child process */
/* Change the file mode mask */
umask(0);
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
logError("setsid: %s\n", strerror(errno));
return -1;
}
/* Change the current working directory. This prevents the current
directory from being locked; hence not being able to remove it. */
if ((chdir("/")) < 0) {
logError("chdir(\"/\"): %s\n", strerror(errno));
return -1;
}
if (freopen( "/dev/null", "r", stdin) == NULL) {
logError("freopen: %s\n", strerror(errno));
}
if (freopen( "/dev/null", "r", stdout) == NULL) {
logError("freopen: %s\n", strerror(errno));
}
if (freopen( "/dev/null", "r", stderr) == NULL) {
logError("freopen: %s\n", strerror(errno));
}
return 0;
}
void print_usage()
{
printf("Usage: mysgw [options]\n\n" \
"Options:\n" \
" -c, --config-file Config file. [" MY_LINUX_CONFIG_FILE "]\n" \
" -h, --help Display a short summary of all program options.\n" \
" -q, --quiet Quiet mode, disable log messages written to the terminal.\n" \
" --daemon Run as a daemon.\n" \
" --gen-soft-hmac-key Generate and print a soft hmac key.\n" \
" --gen-soft-serial-key Generate and print a soft serial key.\n" \
" --gen-aes-key Generate and print an aes encryption key.\n");
}
void print_soft_sign_hmac_key(uint8_t *key_ptr = NULL)
{
uint8_t key[32];
if (key_ptr == NULL) {
hwReadConfigBlock(&key, reinterpret_cast<void*>EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
key_ptr = key;
}
printf("soft_hmac_key=");
for (int i = 0; i < 32; i++) {
printf("%02X", key_ptr[i]);
}
printf("\n\n");
printf("The next line is intended to be used in SecurityPersonalizer.ino:\n");
printf("#define MY_HMAC_KEY ");
for (int i=0; i<32; i++) {
printf("%#02X", key_ptr[i]);
if (i < 31) {
printf(",");
}
}
printf("\n\n");
}
void generate_soft_sign_hmac_key(char *config_file = NULL)
{
uint8_t key[32];
printf("Generating key...");
while (hwGetentropy(&key, sizeof(key)) != sizeof(key));
printf(" done.\n");
printf("To use the new key, update the value in %s witn:\n",
config_file?config_file:MY_LINUX_CONFIG_FILE);
print_soft_sign_hmac_key(key);
#if defined(MY_SIGNING_SIMPLE_PASSWD)
printf("Note: The gateway was built with simplified signing using the password: %s\n" \
" Any key set with soft_hmac_key option in the config file is ignored.\n\n",
MY_SIGNING_SIMPLE_PASSWD);
#elif !defined(MY_SIGNING_FEATURE)
printf("Note: The gateway was not built with signing support.\n" \
" Any key set with soft_hmac_key option in the config file is ignored.\n\n");
#endif
}
void set_soft_sign_hmac_key(char *key_str)
{
uint8_t key[32];
if (strlen(key_str) != 64) {
logWarning("Invalid HMAC key!\n");
} else {
for (int i = 0; i < 64; ++i) {
int n;
char c = key_str[i];
if (c <= '9') {
n = c - '0';
} else if (c >= 'a') {
n = c - 'a' + 10;
} else {
n = c - 'A' + 10;
}
if ((i & 0x1) == 0) {
key[i/2] = n * 16;
} else {
key[i/2] += n;
}
}
hwWriteConfigBlock(&key, reinterpret_cast<void*>EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS, 32);
}
}
void print_soft_sign_serial_key(uint8_t *key_ptr = NULL)
{
uint8_t key[9];
if (key_ptr == NULL) {
hwReadConfigBlock(&key, reinterpret_cast<void*>EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
key_ptr = key;
}
printf("soft_serial_key=");
for (int i = 0; i < 9; i++) {
printf("%02X", key_ptr[i]);
}
printf("\n\n");
printf("The next line is intended to be used in SecurityPersonalizer.ino:\n");
printf("#define MY_SOFT_SERIAL ");
for (int i=0; i<9; i++) {
printf("%#02X", key_ptr[i]);
if (i < 8) {
printf(",");
}
}
printf("\n\n");
}
void generate_soft_sign_serial_key(char *config_file = NULL)
{
uint8_t key[9];
printf("Generating key...");
while (hwGetentropy(&key, sizeof(key)) != sizeof(key));
printf(" done.\n");
printf("To use the new key, update the value in %s witn:\n",
config_file?config_file:MY_LINUX_CONFIG_FILE);
print_soft_sign_serial_key(key);
#if defined(MY_SIGNING_SIMPLE_PASSWD)
printf("Note: The gateway was built with simplified signing using the password: %s\n" \
" Any key set with soft_serial_key option in the config file is ignored.\n\n",
MY_SIGNING_SIMPLE_PASSWD);
#elif !defined(MY_SIGNING_FEATURE)
printf("Note: The gateway was not built with signing support.\n" \
" Any key set with soft_serial_key option in the config file is ignored.\n\n");
#endif
}
void set_soft_sign_serial_key(char *key_str)
{
uint8_t key[9];
if (strlen(key_str) != 18) {
logWarning("Invalid soft serial key!\n");
} else {
for (int i = 0; i < 18; ++i) {
int n;
char c = key_str[i];
if (c <= '9') {
n = c - '0';
} else if (c >= 'a') {
n = c - 'a' + 10;
} else {
n = c - 'A' + 10;
}
if ((i & 0x1) == 0) {
key[i/2] = n * 16;
} else {
key[i/2] += n;
}
}
hwWriteConfigBlock(&key, reinterpret_cast<void*>EEPROM_SIGNING_SOFT_SERIAL_ADDRESS, 9);
}
}
void print_aes_key(uint8_t *key_ptr = NULL)
{
uint8_t key[16];
if (key_ptr == NULL) {
hwReadConfigBlock(&key, reinterpret_cast<void*>EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
key_ptr = key;
}
printf("aes_key=");
for (int i = 0; i < 16; i++) {
printf("%02X", key_ptr[i]);
}
printf("\n\n");
printf("The next line is intended to be used in SecurityPersonalizer.ino:\n");
printf("#define MY_AES_KEY ");
for (int i=0; i<16; i++) {
printf("%#02X", key_ptr[i]);
if (i < 15) {
printf(",");
}
}
printf("\n\n");
}
void generate_aes_key(char *config_file = NULL)
{
uint8_t key[16];
printf("Generating key...");
while (hwGetentropy(&key, sizeof(key)) != sizeof(key));
printf(" done.\n");
printf("To use the new key, update the value in %s witn:\n",
config_file?config_file:MY_LINUX_CONFIG_FILE);
print_aes_key(key);
#if defined(MY_ENCRYPTION_SIMPLE_PASSWD)
printf("Note: The gateway was built with simplified encryption using the password: %s\n" \
" Any key set with aes_key option in the config file is ignored.\n\n",
MY_ENCRYPTION_SIMPLE_PASSWD);
#elif !defined(MY_ENCRYPTION_FEATURE)
printf("Note: The gateway was not built with encryption support.\n" \
" Any key set with aes_key option in the config file is ignored.\n\n");
#endif
}
void set_aes_key(char *key_str)
{
uint8_t key[16];
if (strlen(key_str) != 32) {
logWarning("Invalid AES key!\n");
} else {
for (int i = 0; i < 32; ++i) {
int n;
char c = key_str[i];
if (c <= '9') {
n = c - '0';
} else if (c >= 'a') {
n = c - 'a' + 10;
} else {
n = c - 'A' + 10;
}
if ((i & 0x1) == 0) {
key[i/2] = n * 16;
} else {
key[i/2] += n;
}
}
hwWriteConfigBlock(&key, reinterpret_cast<void*>EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
}
}
int main(int argc, char *argv[])
{
int opt, daemon = 0, quiet = 0;
char *config_file = NULL;
bool gen_soft_sign_hmac_key = false;
bool gen_soft_sign_serial_key = false;
bool gen_aes_key = false;
/* register the signal handler */
signal(SIGINT, handle_sigint);
signal(SIGTERM, handle_sigint);
signal(SIGPIPE, handle_sigint);
hwRandomNumberInit();
static struct option long_options[] = {
{"config-file", required_argument, 0, 'c'},
{"daemon", no_argument, 0, 'J'},
{"help", no_argument, 0, 'h'},
{"quiet", no_argument, 0, 'q'},
{"gen-soft-hmac-key", no_argument, 0, 'A'},
{"gen-soft-serial-key", no_argument, 0, 'B'},
{"gen-aes-key", no_argument, 0, 'C'},
{0, 0, 0, 0}
};
int long_index = 0;
while ((opt = getopt_long(argc, argv,"chqABCJ", long_options, &long_index )) != -1) {
switch (opt) {
case 'c':
config_file = strdup(optarg);
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case 'q':
quiet = 1;
break;
case 'A':
gen_soft_sign_hmac_key = true;
break;
case 'B':
gen_soft_sign_serial_key = true;
break;
case 'C':
gen_aes_key = true;
break;
case 'J':
daemon = 1;
break;
default:
print_usage();
exit(EXIT_SUCCESS);
}
}
if (gen_soft_sign_hmac_key || gen_soft_sign_serial_key || gen_aes_key) {
if (gen_soft_sign_hmac_key) {
generate_soft_sign_hmac_key(config_file);
}
if (gen_soft_sign_serial_key) {
generate_soft_sign_serial_key(config_file);
}
if (gen_aes_key) {
generate_aes_key(config_file);
}
exit(EXIT_SUCCESS);
}
if (daemon) {
if (daemonize() != 0) {
exit(EXIT_FAILURE);
}
quiet = 1;
}
if (config_parse(config_file?config_file:MY_LINUX_CONFIG_FILE) != 0) {
exit(EXIT_FAILURE);
}
logSetQuiet(quiet);
logSetLevel(conf.verbose);
if (conf.log_file) {
if (logSetFile(conf.log_filepath) != 0) {
logError("Failed to open log file.\n");
}
}
if (conf.log_pipe) {
if (logSetPipe(conf.log_pipe_file) != 0) {
logError("Failed to open log pipe.\n");
}
}
if (conf.syslog) {
logSetSyslog(LOG_CONS, LOG_USER);
}
logInfo("Starting gateway...\n");
logInfo("Protocol version - %s\n", MYSENSORS_LIBRARY_VERSION);
_begin(); // Startup MySensors library
// EEPROM is initialized within _begin()
// any operation on it must be done hereafter
#if defined(MY_SIGNING_FEATURE) && !defined(MY_SIGNING_SIMPLE_PASSWD)
// Check if we need to update the signing keys in EEPROM
if (conf.soft_hmac_key) {
set_soft_sign_hmac_key(conf.soft_hmac_key);
} else {
logError("soft_hmac_key was not found in %s\n", config_file?config_file:MY_LINUX_CONFIG_FILE);
exit(EXIT_FAILURE);
}
if (conf.soft_serial_key) {
set_soft_sign_serial_key(conf.soft_serial_key);
} else {
logError("soft_serial_key was not found in %s\n", config_file?config_file:MY_LINUX_CONFIG_FILE);
exit(EXIT_FAILURE);
}
#endif
#if defined(MY_ENCRYPTION_FEATURE) && !defined(MY_ENCRYPTION_SIMPLE_PASSWD)
// Check if we need to update the encryption key in EEPROM
if (conf.aes_key) {
set_aes_key(conf.aes_key);
} else {
logError("aes_key was not found in %s\n", config_file?config_file:MY_LINUX_CONFIG_FILE);
exit(EXIT_FAILURE);
}
#endif
if (config_file) {
free(config_file);
}
for (;;) {
_process(); // Process incoming data
if (loop) {
loop(); // Call sketch loop
}
}
return 0;
}

View File

@@ -0,0 +1,80 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "BCM.h"
#include <stdlib.h>
#include "log.h"
// Declare a single default instance
BCMClass BCM = BCMClass();
uint8_t BCMClass::initialized = 0;
BCMClass::~BCMClass()
{
if (initialized) {
bcm2835_close();
initialized = 0;
}
}
uint8_t BCMClass::init()
{
if (!bcm2835_init()) {
logError("Failed to initialized bcm2835.\n");
exit(1);
}
initialized = 1;
return 1;
}
void BCMClass::pinMode(uint8_t gpio, uint8_t mode)
{
if (!initialized) {
init();
}
bcm2835_gpio_fsel(gpio, mode);
}
void BCMClass::digitalWrite(uint8_t gpio, uint8_t value)
{
if (!initialized) {
init();
}
bcm2835_gpio_write(gpio, value);
// Delay to allow any change in state to be reflected in the LEVn, register bit.
delayMicroseconds(1);
}
uint8_t BCMClass::digitalRead(uint8_t gpio)
{
if (!initialized) {
init();
}
return bcm2835_gpio_lev(gpio);
}
uint8_t BCMClass::isInitialized()
{
return initialized;
}

View File

@@ -0,0 +1,92 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef BCM_h
#define BCM_h
#include <stdint.h>
#include "bcm2835.h"
#define INPUT BCM2835_GPIO_FSEL_INPT
#define OUTPUT BCM2835_GPIO_FSEL_OUTP
/**
* @brief BCM class
*/
class BCMClass
{
public:
/**
* @brief BCMClass destructor.
*/
~BCMClass();
/**
* @brief Initializes BCM.
*
* @return 1 if successful, else exits the program.
*/
uint8_t init();
/**
* @brief Configures the specified pin to behave either as an input or an output.
*
* @param gpio The GPIO pin number.
* @param mode INPUT or OUTPUT.
*/
void pinMode(uint8_t gpio, uint8_t mode);
/**
* @brief Write a high or a low value for the given pin.
*
* @param gpio The GPIO pin number.
* @param value HIGH or LOW.
*/
void digitalWrite(uint8_t gpio, uint8_t value);
/**
* @brief Reads the value from a specified pin.
*
* @param gpio The GPIO pin number.
* @return HIGH or LOW.
*/
uint8_t digitalRead(uint8_t gpio);
/**
* @brief Returns the same GPIO, no conversion is required.
*
* @param gpio The GPIO pin number.
* @return The GPIO pin number.
*/
inline uint8_t digitalPinToInterrupt(uint8_t gpio);
/**
* @brief Checks if SPI was initialized.
*
* @return 1 if initialized, else 0.
*/
uint8_t isInitialized();
private:
static uint8_t initialized; //!< @brief BCM initialized flag.
};
uint8_t BCMClass::digitalPinToInterrupt(uint8_t gpio)
{
return gpio;
}
extern BCMClass BCM;
#endif

View File

@@ -0,0 +1,167 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "RPi.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "log.h"
const static int phys_to_gpio_rev1[41] = {-1, -1, -1, 0, -1, 1, -1, 4, 14, -1, 15, 17, 18, 21, -1, 22, 23, -1, 24, 10, -1, 9, 25, 11, 8, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
const static int phys_to_gpio_rev2[41] = {-1, -1, -1, 2, -1, 3, -1, 4, 14, -1, 15, 17, 18, 27, -1, 22, 23, -1, 24, 10, -1, 9, 25, 11, 8, -1, 7, -1, -1, 5, -1, 6, 12, 13, -1, 19, 16, 26, 20, -1, 21};
// Declare a single default instance
RPiClass RPi = RPiClass();
const int* RPiClass::phys_to_gpio = NULL;
void RPiClass::pinMode(uint8_t physPin, uint8_t mode)
{
uint8_t gpioPin;
if (physToGPIO(physPin, &gpioPin) != 0) {
logError("pinMode: invalid pin: %d\n", physPin);
return;
}
BCM.pinMode(gpioPin, mode);
}
void RPiClass::digitalWrite(uint8_t physPin, uint8_t value)
{
uint8_t gpioPin;
if (physToGPIO(physPin, &gpioPin) != 0) {
logError("digitalWrite: invalid pin: %d\n", physPin);
return;
}
BCM.digitalWrite(gpioPin, value);
}
uint8_t RPiClass::digitalRead(uint8_t physPin)
{
uint8_t gpioPin;
if (physToGPIO(physPin, &gpioPin) != 0) {
logError("digitalRead: invalid pin: %d\n", physPin);
return 0;
}
return BCM.digitalRead(gpioPin);
}
uint8_t RPiClass::digitalPinToInterrupt(uint8_t physPin)
{
uint8_t gpioPin;
if (physToGPIO(physPin, &gpioPin) != 0) {
logError("digitalPinToInterrupt: invalid pin: %d\n", physPin);
return 0;
}
return gpioPin;
}
int RPiClass::rpiGpioLayout()
{
/*
* Based on wiringPi Copyright (c) 2012 Gordon Henderson.
*/
FILE *fd;
char line[120];
char *c;
if ((fd = fopen("/proc/cpuinfo", "r")) == NULL) {
return -1;
}
while (fgets(line, 120, fd) != NULL) {
if (strncmp(line, "Revision", 8) == 0) {
fclose(fd);
// Chop trailing CR/NL
for (c = &line[strlen(line) - 1]; (*c == '\n') || (*c == '\r'); --c) {
*c = 0;
}
// Scan to the first character of the revision number
for (c = line; *c; ++c) {
if (*c == ':') {
// Chop spaces
++c;
while (isspace(*c)) {
++c;
}
// Check hex digit at start
if (!isxdigit(*c)) {
return -1;
}
// Check bogus revision line (too small)
if (strlen(c) < 4) {
return -1;
}
// Isolate last 4 characters: (in-case of overvolting or new encoding scheme)
c = c + strlen(c) - 4;
if ((strcmp(c, "0002") == 0) || (strcmp(c, "0003") == 0) ||
(strcmp(c, "0004") == 0) || (strcmp(c, "0005") == 0) ||
(strcmp(c, "0006") == 0) || (strcmp(c, "0007") == 0) ||
(strcmp(c, "0008") == 0) || (strcmp(c, "0009") == 0) ||
(strcmp(c, "000d") == 0) || (strcmp(c, "000e") == 0) ||
(strcmp(c, "000f") == 0)) {
return 1;
} else {
return 2;
}
}
}
}
}
fclose(fd);
return -1;
}
int RPiClass::physToGPIO(uint8_t physPin, uint8_t *gpio)
{
if (phys_to_gpio == NULL) {
if (rpiGpioLayout() == 1) {
// A, B, Rev 1, 1.1
phys_to_gpio = &phys_to_gpio_rev1[0];
} else {
// A2, B2, A+, B+, CM, Pi2, Pi3, Zero
phys_to_gpio = &phys_to_gpio_rev2[0];
}
}
if (gpio == NULL || physPin > 40) {
return -1;
}
int pin = *(phys_to_gpio+physPin);
if (pin == -1) {
return -1;
} else {
*gpio = pin;
}
return 0;
}

View File

@@ -0,0 +1,82 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef RPi_h
#define RPi_h
#include <stdint.h>
#include "BCM.h"
/**
* @brief RPi class
*/
class RPiClass
{
public:
/**
* @brief Configures the specified pin to behave either as an input or an output.
*
* @param physPin The physical number of the pin.
* @param mode INPUT or OUTPUT.
*/
void pinMode(uint8_t physPin, uint8_t mode);
/**
* @brief Write a high or a low value for the given pin.
*
* @param physPin The physical number of the pin.
* @param value HIGH or LOW.
*/
void digitalWrite(uint8_t physPin, uint8_t value);
/**
* @brief Reads the value from a specified pin.
*
* @param physPin The physical number of the pin.
* @return HIGH or LOW.
*/
uint8_t digitalRead(uint8_t physPin);
/**
* @brief Translate the physical pin number to the GPIO number for use in interrupt.
*
* @param physPin The physical number of the pin.
* @return The GPIO pin number.
*/
uint8_t digitalPinToInterrupt(uint8_t physPin);
/**
* @brief Translate the physical pin number to the GPIO number.
*
* @param physPin The physical number of the pin.
* @param gpio Pointer to write the GPIO pin number when success.
* @return -1 if FAILURE or 0 if SUCCESS.
*/
static int physToGPIO(uint8_t physPin, uint8_t *gpio);
private:
static const int *phys_to_gpio; //!< @brief Pointer to array of GPIO pins numbers.
/**
* @brief Get the gpio layout.
*
* @return The gpio layout number.
*/
static int rpiGpioLayout(void);
};
extern RPiClass RPi;
#endif

View File

@@ -0,0 +1,110 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
*/
#include "SPIBCM.h"
#include <pthread.h>
#include <stdlib.h>
#include "log.h"
static pthread_mutex_t spiMutex = PTHREAD_MUTEX_INITIALIZER;
// Declare a single default instance
SPIBCMClass SPIBCM = SPIBCMClass();
uint8_t SPIBCMClass::initialized = 0;
void SPIBCMClass::begin()
{
if (!initialized) {
if (!BCM.isInitialized()) {
BCM.init();
}
if (!bcm2835_spi_begin()) {
logError("You need root privilege to use SPI.\n");
exit(1);
}
}
initialized++; // reference count
}
void SPIBCMClass::end()
{
if (initialized) {
initialized--;
}
if (!initialized) {
// End the SPI
bcm2835_spi_end();
}
}
void SPIBCMClass::setBitOrder(uint8_t bit_order)
{
bcm2835_spi_setBitOrder(bit_order);
}
void SPIBCMClass::setDataMode(uint8_t data_mode)
{
bcm2835_spi_setDataMode(data_mode);
}
void SPIBCMClass::setClockDivider(uint16_t divider)
{
bcm2835_spi_setClockDivider(divider);
}
void SPIBCMClass::chipSelect(int csn_pin)
{
if (csn_pin == RPI_GPIO_P1_26) {
csn_pin = BCM2835_SPI_CS1;
} else if (csn_pin == RPI_GPIO_P1_24) {
csn_pin = BCM2835_SPI_CS0;
} else {
csn_pin = BCM2835_SPI_CS0;
}
bcm2835_spi_chipSelect(csn_pin);
delayMicroseconds(5);
}
void SPIBCMClass::beginTransaction(SPISettings settings)
{
pthread_mutex_lock(&spiMutex);
setBitOrder(settings.border);
setDataMode(settings.dmode);
setClockDivider(settings.cdiv);
}
void SPIBCMClass::endTransaction()
{
pthread_mutex_unlock(&spiMutex);
}
void SPIBCMClass::usingInterrupt(uint8_t interruptNumber)
{
(void)interruptNumber;
}
void SPIBCMClass::notUsingInterrupt(uint8_t interruptNumber)
{
(void)interruptNumber;
}

View File

@@ -0,0 +1,262 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
*/
#ifndef SPIBCM_h
#define SPIBCM_h
#include <stdio.h>
#include "bcm2835.h"
#include "BCM.h"
#define SPI_HAS_TRANSACTION
#define SPI_CLOCK_BASE 256000000
// SPI Clock divider
#define SPI_CLOCK_DIV1 BCM2835_SPI_CLOCK_DIVIDER_1
#define SPI_CLOCK_DIV2 BCM2835_SPI_CLOCK_DIVIDER_2
#define SPI_CLOCK_DIV4 BCM2835_SPI_CLOCK_DIVIDER_4
#define SPI_CLOCK_DIV8 BCM2835_SPI_CLOCK_DIVIDER_8
#define SPI_CLOCK_DIV16 BCM2835_SPI_CLOCK_DIVIDER_16
#define SPI_CLOCK_DIV32 BCM2835_SPI_CLOCK_DIVIDER_32
#define SPI_CLOCK_DIV64 BCM2835_SPI_CLOCK_DIVIDER_64
#define SPI_CLOCK_DIV128 BCM2835_SPI_CLOCK_DIVIDER_128
#define SPI_CLOCK_DIV256 BCM2835_SPI_CLOCK_DIVIDER_256
#define SPI_CLOCK_DIV512 BCM2835_SPI_CLOCK_DIVIDER_512
#define SPI_CLOCK_DIV1024 BCM2835_SPI_CLOCK_DIVIDER_1024
#define SPI_CLOCK_DIV2048 BCM2835_SPI_CLOCK_DIVIDER_2048
#define SPI_CLOCK_DIV4096 BCM2835_SPI_CLOCK_DIVIDER_4096
#define SPI_CLOCK_DIV8192 BCM2835_SPI_CLOCK_DIVIDER_8192
#define SPI_CLOCK_DIV16384 BCM2835_SPI_CLOCK_DIVIDER_16384
#define SPI_CLOCK_DIV32768 BCM2835_SPI_CLOCK_DIVIDER_32768
#define SPI_CLOCK_DIV65536 BCM2835_SPI_CLOCK_DIVIDER_65536
// SPI Data mode
#define SPI_MODE0 BCM2835_SPI_MODE0
#define SPI_MODE1 BCM2835_SPI_MODE1
#define SPI_MODE2 BCM2835_SPI_MODE2
#define SPI_MODE3 BCM2835_SPI_MODE3
#define LSBFIRST BCM2835_SPI_BIT_ORDER_LSBFIRST
#define MSBFIRST BCM2835_SPI_BIT_ORDER_MSBFIRST
const uint8_t SS = 24;
const uint8_t MOSI = 19;
const uint8_t MISO = 21;
const uint8_t SCK = 23;
/**
* SPISettings class
*/
class SPISettings
{
public:
/**
* @brief SPISettings constructor.
*
* Default clock speed is 8Mhz.
*/
SPISettings()
{
init(SPI_CLOCK_DIV32, MSBFIRST, SPI_MODE0);
}
/**
* @brief SPISettings constructor.
*
* @param clock SPI clock speed in Hz.
* @param bitOrder SPI bit order.
* @param dataMode SPI data mode.
*/
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
{
uint16_t divider;
if (clock >= SPI_CLOCK_BASE) {
divider = SPI_CLOCK_DIV1;
} else if (clock >= SPI_CLOCK_BASE / 2) {
divider = SPI_CLOCK_DIV2;
} else if (clock >= SPI_CLOCK_BASE / 4) {
divider = SPI_CLOCK_DIV4;
} else if (clock >= SPI_CLOCK_BASE / 8) {
divider = SPI_CLOCK_DIV8;
} else if (clock >= SPI_CLOCK_BASE / 16) {
divider = SPI_CLOCK_DIV16;
} else if (clock >= SPI_CLOCK_BASE / 32) {
divider = SPI_CLOCK_DIV32;
} else if (clock >= SPI_CLOCK_BASE / 64) {
divider = SPI_CLOCK_DIV64;
} else if (clock >= SPI_CLOCK_BASE / 128) {
divider = SPI_CLOCK_DIV128;
} else if (clock >= SPI_CLOCK_BASE / 256) {
divider = SPI_CLOCK_DIV256;
} else if (clock >= SPI_CLOCK_BASE / 512) {
divider = SPI_CLOCK_DIV512;
} else if (clock >= SPI_CLOCK_BASE / 1024) {
divider = SPI_CLOCK_DIV1024;
} else if (clock >= SPI_CLOCK_BASE / 2048) {
divider = SPI_CLOCK_DIV2048;
} else if (clock >= SPI_CLOCK_BASE / 4096) {
divider = SPI_CLOCK_DIV4096;
} else if (clock >= SPI_CLOCK_BASE / 8192) {
divider = SPI_CLOCK_DIV8192;
} else if (clock >= SPI_CLOCK_BASE / 16384) {
divider = SPI_CLOCK_DIV16384;
} else if (clock >= SPI_CLOCK_BASE / 32768) {
divider = SPI_CLOCK_DIV32768;
} else if (clock >= SPI_CLOCK_BASE / 65536) {
divider = SPI_CLOCK_DIV65536;
} else {
// Default to 8Mhz
divider = SPI_CLOCK_DIV32;
}
init(divider, bitOrder, dataMode);
}
uint16_t cdiv; //!< @brief SPI clock divider.
uint8_t border; //!< @brief SPI bit order.
uint8_t dmode; //!< @brief SPI data mode.
private:
/**
* @brief Initialized class members.
*
* @param divider SPI clock divider.
* @param bitOrder SPI bit order.
* @param dataMode SPI data mode.
*/
void init(uint16_t divider, uint8_t bitOrder, uint8_t dataMode)
{
cdiv = divider;
border = bitOrder;
dmode = dataMode;
}
friend class SPIBCMClass;
};
/**
* SPIBCM class
*/
class SPIBCMClass
{
public:
/**
* @brief Send and receive a byte.
*
* @param data to send.
* @return byte received.
*/
inline static uint8_t transfer(uint8_t data);
/**
* @brief Send and receive a number of bytes.
*
* @param tbuf Sending buffer.
* @param rbuf Receive buffer.
* @param len Buffer length.
*/
inline static void transfernb(char* tbuf, char* rbuf, uint32_t len);
/**
* @brief Send and receive a number of bytes.
*
* @param buf Buffer to read from and write to.
* @param len Buffer length.
*/
inline static void transfern(char* buf, uint32_t len);
/**
* @brief Start SPI operations.
*/
static void begin();
/**
* @brief End SPI operations.
*/
static void end();
/**
* @brief Sets the SPI bit order.
*
* @param bit_order The desired bit order.
*/
static void setBitOrder(uint8_t bit_order);
/**
* @brief Sets the SPI data mode.
*
* @param data_mode The desired data mode.
*/
static void setDataMode(uint8_t data_mode);
/**
* @brief Sets the SPI clock divider and therefore the SPI clock speed.
*
* @param divider The desired SPI clock divider.
*/
static void setClockDivider(uint16_t divider);
/**
* @brief Sets the chip select pin.
*
* @param csn_pin Specifies the CS pin.
*/
static void chipSelect(int csn_pin);
/**
* @brief Start SPI transaction.
*
* @param settings for SPI.
*/
static void beginTransaction(SPISettings settings);
/**
* @brief End SPI transaction.
*/
static void endTransaction();
/**
* @brief Not implemented.
*
* @param interruptNumber ignored parameter.
*/
static void usingInterrupt(uint8_t interruptNumber);
/**
* @brief Not implemented.
*
* @param interruptNumber ignored parameter.
*/
static void notUsingInterrupt(uint8_t interruptNumber);
private:
static uint8_t initialized; //!< @brief SPI initialized flag.
};
uint8_t SPIBCMClass::transfer(uint8_t data)
{
return bcm2835_spi_transfer(data);
}
void SPIBCMClass::transfernb(char* tbuf, char* rbuf, uint32_t len)
{
bcm2835_spi_transfernb( tbuf, rbuf, len);
}
void SPIBCMClass::transfern(char* buf, uint32_t len)
{
transfernb(buf, buf, len);
}
extern SPIBCMClass SPIBCM;
#endif

View File

@@ -0,0 +1,213 @@
/*
TwoWire.h - TWI/I2C library for Arduino & Wiring
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support
Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support
Modified October 2016 by Marcelo Aquino <marceloaqno@gmail.org> for Raspberry Pi
*/
#include "Wire.h"
#include <stdlib.h>
#include <pthread.h>
#include "bcm2835.h"
#include "log.h"
static pthread_mutex_t i2cMutex = PTHREAD_MUTEX_INITIALIZER;
uint8_t TwoWire::rxBuffer[BUFFER_LENGTH];
uint8_t TwoWire::rxBufferIndex = 0;
uint8_t TwoWire::rxBufferLength = 0;
uint8_t TwoWire::txAddress = 0;
uint8_t TwoWire::txBuffer[BUFFER_LENGTH];
uint8_t TwoWire::txBufferIndex = 0;
uint8_t TwoWire::txBufferLength = 0;
uint8_t TwoWire::transmitting = 0;
void TwoWire::begin()
{
if (!bcm2835_i2c_begin()) {
logError("You need root privilege to use I2C.\n");
exit(1);
}
}
void TwoWire::begin(uint8_t address)
{
begin();
bcm2835_i2c_setSlaveAddress(address);
}
void TwoWire::begin(int address)
{
begin(static_cast<uint8_t>(address));
}
void TwoWire::end()
{
bcm2835_i2c_end();
}
void TwoWire::setClock(uint32_t clock)
{
bcm2835_i2c_set_baudrate(clock);
}
void TwoWire::beginTransmission(uint8_t address)
{
pthread_mutex_lock(&i2cMutex);
// indicate that we are transmitting
transmitting = 1;
// set address of targeted slave
txAddress = address;
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
}
void TwoWire::beginTransmission(int address)
{
beginTransmission(static_cast<uint8_t>(address));
}
uint8_t TwoWire::endTransmission(void)
{
// transmit buffer
bcm2835_i2c_setSlaveAddress(txAddress);
uint8_t ret = bcm2835_i2c_write(reinterpret_cast<const char *>(txBuffer), txBufferLength);
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
// indicate that we are done transmitting
transmitting = 0;
pthread_mutex_unlock(&i2cMutex);
if (ret == BCM2835_I2C_REASON_OK) {
return 0; // success
} else if (ret == BCM2835_I2C_REASON_ERROR_NACK) {
return 3; // error: data send, nack received
}
return 4; // other error
}
size_t TwoWire::requestFrom(uint8_t address, size_t quantity)
{
// clamp to buffer length
if (quantity > BUFFER_LENGTH) {
quantity = BUFFER_LENGTH;
}
rxBufferIndex = 0;
rxBufferLength = 0;
bcm2835_i2c_setSlaveAddress(address);
uint8_t ret = bcm2835_i2c_read(reinterpret_cast<char *>(rxBuffer), quantity);
if (ret == BCM2835_I2C_REASON_OK) {
rxBufferLength = quantity;
}
return rxBufferLength;
}
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
{
return requestFrom(address, static_cast<size_t>(quantity));
}
uint8_t TwoWire::requestFrom(int address, int quantity)
{
return requestFrom(static_cast<uint8_t>(address), static_cast<size_t>(quantity));
}
size_t TwoWire::write(uint8_t data)
{
if (transmitting) {
// in master transmitter mode
// don't bother if buffer is full
if (txBufferLength >= BUFFER_LENGTH) {
setWriteError();
return 0;
}
// put byte in tx buffer
txBuffer[txBufferIndex] = data;
++txBufferIndex;
// update amount in buffer
txBufferLength = txBufferIndex;
return 1;
} else {
return write(&data, 1);
}
}
size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
if (transmitting) {
// in master transmitter mode
for (size_t i = 0; i < quantity; ++i) {
write(data[i]);
}
} else {
uint8_t rc = bcm2835_i2c_write(reinterpret_cast<const char *>(data), quantity);
if (rc != BCM2835_I2C_REASON_OK) {
return 0;
}
}
return quantity;
}
int TwoWire::available()
{
return rxBufferLength - rxBufferIndex;
}
int TwoWire::read()
{
int value = -1;
if (rxBufferIndex < rxBufferLength) {
value = rxBuffer[rxBufferIndex];
++rxBufferIndex;
}
return value;
}
int TwoWire::peek()
{
if (rxBufferIndex < rxBufferLength) {
return rxBuffer[rxBufferIndex];
}
return -1;
}
void TwoWire::flush()
{
rxBufferIndex = 0;
rxBufferLength = 0;
txBufferIndex = 0;
txBufferLength = 0;
}
TwoWire Wire = TwoWire();

View File

@@ -0,0 +1,94 @@
/*
TwoWire.h - TWI/I2C library for Arduino & Wiring
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
Modified December 2014 by Ivan Grokhotkov (ivan@esp8266.com) - esp8266 support
Modified April 2015 by Hrsto Gochkov (ficeto@ficeto.com) - alternative esp8266 support
Modified October 2016 by Marcelo Aquino <marceloaqno@gmail.org> for Raspberry Pi
*/
#ifndef Wire_h
#define Wire_h
#if !DOXYGEN
#include <stdint.h>
#include "Stream.h"
#include "BCM.h"
#define BUFFER_LENGTH 32
class TwoWire : public Stream
{
private:
static uint8_t rxBuffer[];
static uint8_t rxBufferIndex;
static uint8_t rxBufferLength;
static uint8_t txAddress;
static uint8_t txBuffer[];
static uint8_t txBufferIndex;
static uint8_t txBufferLength;
static uint8_t transmitting;
public:
void begin();
void begin(uint8_t address);
void begin(int address);
void end();
void setClock(uint32_t clock);
void beginTransmission(uint8_t address);
void beginTransmission(int address);
uint8_t endTransmission(void);
size_t requestFrom(uint8_t address, size_t size);
uint8_t requestFrom(uint8_t address, uint8_t quantity);
uint8_t requestFrom(int address, int quantity);
size_t write(uint8_t data);
size_t write(const uint8_t *data, size_t quantity);
int available();
int read();
int peek();
void flush();
inline size_t write(unsigned long n)
{
return write((uint8_t)n);
}
inline size_t write(long n)
{
return write((uint8_t)n);
}
inline size_t write(unsigned int n)
{
return write((uint8_t)n);
}
inline size_t write(int n)
{
return write((uint8_t)n);
}
using Print::write;
};
extern TwoWire Wire;
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef Arduino_h
#define Arduino_h
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <string>
#include <algorithm>
#include "stdlib_noniso.h"
#ifdef LINUX_ARCH_RASPBERRYPI
#include "RPi.h"
#define pinMode(pin, direction) RPi.pinMode(pin, direction)
#define digitalWrite(pin, value) RPi.digitalWrite(pin, value)
#define digitalRead(pin) RPi.digitalRead(pin)
#define digitalPinToInterrupt(pin) RPi.digitalPinToInterrupt(pin)
#else
#include "GPIO.h"
#define pinMode(pin, direction) GPIO.pinMode(pin, direction)
#define digitalWrite(pin, value) GPIO.digitalWrite(pin, value)
#define digitalRead(pin) GPIO.digitalRead(pin)
#define digitalPinToInterrupt(pin) GPIO.digitalPinToInterrupt(pin)
#endif
#include "interrupt.h"
#undef PSTR
#define PSTR(x) (x)
#undef F
#define F(x) (x)
#define PROGMEM __attribute__(( section(".progmem.data") ))
#define vsnprintf_P(...) vsnprintf( __VA_ARGS__ )
#define snprintf_P(...) snprintf( __VA_ARGS__ )
#define memcpy_P memcpy
#define pgm_read_byte(p) (*(p))
#define pgm_read_dword(p) (*(p))
#define pgm_read_byte_near(p) (*(p))
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105
#define EULER 2.718281828459045235360287471352
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
#define radians(deg) ((deg)*DEG_TO_RAD)
#define degrees(rad) ((rad)*RAD_TO_DEG)
#define sq(x) ((x)*(x))
#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))
#define lowByte(w) ((uint8_t) ((w) & 0xff))
#define highByte(w) ((uint8_t) ((w) >> 8))
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
#define bit(b) (1UL << (b))
#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define random(...) GET_MACRO(_0, ##__VA_ARGS__, randMinMax, randMax, rand)(__VA_ARGS__)
#ifndef delay
#define delay _delay_milliseconds
#endif
#ifndef delayMicroseconds
#define delayMicroseconds _delay_microseconds
#endif
using std::string;
using std::min;
using std::max;
using std::abs;
typedef uint8_t byte;
typedef string String;
typedef char __FlashStringHelper;
void yield(void);
unsigned long millis(void);
unsigned long micros(void);
void _delay_milliseconds(unsigned int millis);
void _delay_microseconds(unsigned int micro);
void randomSeed(unsigned long seed);
long randMax(long howbig);
long randMinMax(long howsmall, long howbig);
#endif

View File

@@ -0,0 +1,54 @@
/*
Client.h - Base class that provides Client
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#ifndef client_h
#define client_h
#include "Stream.h"
#include "IPAddress.h"
#if !DOXYGEN
class Client : public Stream
{
public:
virtual int connect(IPAddress ip, uint16_t port) = 0;
virtual int connect(const char *host, uint16_t port) = 0;
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t *buf, size_t size) = 0;
virtual int available() = 0;
virtual int read() = 0;
virtual int read(uint8_t *buf, size_t size) = 0;
virtual int peek() = 0;
virtual void flush() = 0;
virtual void stop() = 0;
virtual uint8_t connected() = 0;
virtual operator bool() = 0;
protected:
uint8_t* rawIPAddress(IPAddress& addr)
{
return addr.raw_address();
};
};
#endif
#endif

View File

@@ -0,0 +1,311 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved.
*/
#include "EthernetClient.h"
#include <cstdio>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/tcp.h>
#include <errno.h>
#include "log.h"
EthernetClient::EthernetClient() : _sock(-1)
{
}
EthernetClient::EthernetClient(int sock) : _sock(sock)
{
}
int EthernetClient::connect(const char* host, uint16_t port)
{
struct addrinfo hints, *servinfo, *localinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
char port_str[6];
bool use_bind = (_srcip != 0);
close();
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
sprintf(port_str, "%hu", port);
if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) {
logError("getaddrinfo: %s\n", gai_strerror(rv));
return -1;
}
if (use_bind) {
if ((rv = getaddrinfo(_srcip.toString().c_str(), port_str, &hints, &localinfo)) != 0) {
logError("getaddrinfo: %s\n", gai_strerror(rv));
return -1;
}
}
// loop through all the results and connect to the first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((_sock = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
logError("socket: %s\n", strerror(errno));
continue;
}
if (use_bind) {
if (::bind(_sock, localinfo->ai_addr, localinfo->ai_addrlen) == -1) {
close();
logError("bind: %s\n", strerror(errno));
return -1;
}
}
if (::connect(_sock, p->ai_addr, p->ai_addrlen) == -1) {
close();
logError("connect: %s\n", strerror(errno));
continue;
}
break;
}
if (p == NULL) {
logError("failed to connect\n");
return -1;
}
void *addr = &(((struct sockaddr_in*)p->ai_addr)->sin_addr);
inet_ntop(p->ai_family, addr, s, sizeof s);
logDebug("connected to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
if (use_bind) {
freeaddrinfo(localinfo); // all done with this structure
}
return 1;
}
int EthernetClient::connect(IPAddress ip, uint16_t port)
{
return connect(ip.toString().c_str(), port);
}
size_t EthernetClient::write(uint8_t b)
{
return write(&b, 1);
}
size_t EthernetClient::write(const uint8_t *buf, size_t size)
{
int bytes = 0;
if (_sock == -1) {
return 0;
}
while (size > 0) {
int rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT);
if (rc == -1) {
logError("send: %s\n", strerror(errno));
close();
break;
}
bytes += rc;
size -= rc;
}
return bytes;
}
size_t EthernetClient::write(const char *str)
{
if (str == NULL) {
return 0;
}
return write((const uint8_t *)str, strlen(str));
}
size_t EthernetClient::write(const char *buffer, size_t size)
{
return write((const uint8_t *)buffer, size);
}
int EthernetClient::available()
{
int count = 0;
if (_sock != -1) {
ioctl(_sock, SIOCINQ, &count);
}
return count;
}
int EthernetClient::read()
{
uint8_t b;
if ( recv(_sock, &b, 1, MSG_DONTWAIT) > 0 ) {
// recv worked
return b;
} else {
// No data available
return -1;
}
}
int EthernetClient::read(uint8_t *buf, size_t bytes)
{
return recv(_sock, buf, bytes, MSG_DONTWAIT);
}
int EthernetClient::peek()
{
uint8_t b;
if (recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT) > 0) {
return b;
} else {
return -1;
}
}
void EthernetClient::flush()
{
int count = 0;
if (_sock != -1) {
while (true) {
ioctl(_sock, SIOCOUTQ, &count);
if (count == 0) {
return;
}
usleep(1000);
}
}
}
void EthernetClient::stop()
{
if (_sock == -1) {
return;
}
// attempt to close the connection gracefully (send a FIN to other side)
shutdown(_sock, SHUT_RDWR);
timeval startTime, curTime;
gettimeofday(&startTime, NULL);
// wait up to a second for the connection to close
do {
uint8_t s = status();
if (s == ETHERNETCLIENT_W5100_CLOSED) {
break; // exit the loop
}
usleep(1000);
gettimeofday(&curTime, NULL);
} while (((curTime.tv_sec - startTime.tv_sec) * 1000000) + (curTime.tv_usec - startTime.tv_usec) <
1000000);
// free up the socket descriptor
::close(_sock);
_sock = -1;
}
uint8_t EthernetClient::status()
{
if (_sock == -1) {
return ETHERNETCLIENT_W5100_CLOSED;
}
struct tcp_info tcp_info;
int tcp_info_length = sizeof(tcp_info);
if ( getsockopt( _sock, SOL_TCP, TCP_INFO, (void *)&tcp_info,
(socklen_t *)&tcp_info_length ) == 0 ) {
switch (tcp_info.tcpi_state) {
case TCP_ESTABLISHED:
return ETHERNETCLIENT_W5100_ESTABLISHED;
case TCP_SYN_SENT:
return ETHERNETCLIENT_W5100_SYNSENT;
case TCP_SYN_RECV:
return ETHERNETCLIENT_W5100_SYNRECV;
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
return ETHERNETCLIENT_W5100_FIN_WAIT;
case TCP_TIME_WAIT:
return TCP_TIME_WAIT;
case TCP_CLOSE:
return ETHERNETCLIENT_W5100_CLOSED;
case TCP_CLOSE_WAIT:
return ETHERNETCLIENT_W5100_CLOSING;
case TCP_LAST_ACK:
return ETHERNETCLIENT_W5100_LAST_ACK;
case TCP_LISTEN:
return ETHERNETCLIENT_W5100_LISTEN;
case TCP_CLOSING:
return ETHERNETCLIENT_W5100_CLOSING;
}
}
return ETHERNETCLIENT_W5100_CLOSED;
}
uint8_t EthernetClient::connected()
{
return status() == ETHERNETCLIENT_W5100_ESTABLISHED || available();
}
void EthernetClient::close()
{
if (_sock != -1) {
::close(_sock);
_sock = -1;
}
}
void EthernetClient::bind(IPAddress ip)
{
_srcip = ip;
}
int EthernetClient::getSocketNumber()
{
return _sock;
}
// the next function allows us to use the client returned by
// EthernetServer::available() as the condition in an if-statement.
EthernetClient::operator bool()
{
return _sock != -1;
}
bool EthernetClient::operator==(const EthernetClient& rhs)
{
return _sock == rhs._sock && _sock != -1 && rhs._sock != -1;
}

View File

@@ -0,0 +1,211 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved.
*/
#ifndef EthernetClient_h
#define EthernetClient_h
#include "Client.h"
#include "IPAddress.h"
// State codes from W5100 library
#define ETHERNETCLIENT_W5100_CLOSED 0x00
#define ETHERNETCLIENT_W5100_LISTEN 0x14
#define ETHERNETCLIENT_W5100_SYNSENT 0x15
#define ETHERNETCLIENT_W5100_SYNRECV 0x16
#define ETHERNETCLIENT_W5100_ESTABLISHED 0x17
#define ETHERNETCLIENT_W5100_FIN_WAIT 0x18
#define ETHERNETCLIENT_W5100_CLOSING 0x1A
#define ETHERNETCLIENT_W5100_TIME_WAIT 0x1B
#define ETHERNETCLIENT_W5100_CLOSE_WAIT 0x1C
#define ETHERNETCLIENT_W5100_LAST_ACK 0x1D
/**
* EthernetClient class
*/
class EthernetClient : public Client
{
public:
/**
* @brief EthernetClient constructor.
*/
EthernetClient();
/**
* @brief EthernetClient constructor.
*
* @param sock Network socket.
*/
explicit EthernetClient(int sock);
/**
* @brief Initiate a connection with host:port.
*
* @param host name to resolve or a stringified dotted IP address.
* @param port to connect to.
* @return 1 if SUCCESS or -1 if FAILURE.
*/
virtual int connect(const char *host, uint16_t port);
/**
* @brief Initiate a connection with ip:port.
*
* @param ip to connect to.
* @param port to connect to.
* @return 1 if SUCCESS or -1 if FAILURE.
*/
virtual int connect(IPAddress ip, uint16_t port);
/**
* @brief Write a byte.
*
* @param b byte to write.
* @return 0 if FAILURE or 1 if SUCCESS.
*/
virtual size_t write(uint8_t b);
/**
* @brief Write at most 'size' bytes.
*
* @param buf Buffer to read from.
* @param size of the buffer.
* @return 0 if FAILURE or the number of bytes sent.
*/
virtual size_t write(const uint8_t *buf, size_t size);
/**
* @brief Write a null-terminated string.
*
* @param str String to write.
* @return 0 if FAILURE or number of characters sent.
*/
size_t write(const char *str);
/**
* @brief Write at most 'size' characters.
*
* @param buffer to read from.
* @param size of the buffer.
* @return 0 if FAILURE or the number of characters sent.
*/
size_t write(const char *buffer, size_t size);
/**
* @brief Returns the number of bytes available for reading.
*
* @return number of bytes available.
*/
virtual int available();
/**
* @brief Read a byte.
*
* @return -1 if no data, else the first byte available.
*/
virtual int read();
/**
* @brief Read a number of bytes and store in a buffer.
*
* @param buf buffer to write to.
* @param bytes number of bytes to read.
* @return -1 if no data or number of read bytes.
*/
virtual int read(uint8_t *buf, size_t bytes);
/**
* @brief Returns the next byte of the read queue without removing it from the queue.
*
* @return -1 if no data, else the first byte of incoming data available.
*/
virtual int peek();
/**
* @brief Waits until all outgoing bytes in buffer have been sent.
*/
virtual void flush();
/**
* @brief Close the connection gracefully.
*
* Send a FIN and wait 1s for a response. If no response close it forcefully.
*/
virtual void stop();
/**
* @brief Connection status.
*
* @return state according to W5100 library codes.
*/
uint8_t status();
/**
* @brief Whether or not the client is connected.
*
* Note that a client is considered connected if the connection has been closed but
* there is still unread data.
*
* @return 1 if the client is connected, 0 if not.
*/
virtual uint8_t connected();
/**
* @brief Close the connection.
*/
void close();
/**
* @brief Bind the conection to the specified local ip.
*/
void bind(IPAddress ip);
/**
* @brief Get the internal socket file descriptor.
*
* @return an integer, that is the socket number.
*/
int getSocketNumber();
/**
* @brief Overloaded cast operators.
*
* Allow EthernetClient objects to be used where a bool is expected.
*/
virtual operator bool();
/**
* @brief Overloaded cast operators.
*
*/
virtual bool operator==(const bool value)
{
return bool() == value;
}
/**
* @brief Overloaded cast operators.
*
*/
virtual bool operator!=(const bool value)
{
return bool() != value;
}
/**
* @brief Overloaded cast operators.
*
*/
virtual bool operator==(const EthernetClient& rhs);
/**
* @brief Overloaded cast operators.
*
*/
virtual bool operator!=(const EthernetClient& rhs)
{
return !this->operator==(rhs);
};
friend class EthernetServer;
private:
int _sock; //!< @brief Network socket file descriptor.
IPAddress _srcip; //!< @brief Local ip to bind to.
};
#endif

View File

@@ -0,0 +1,211 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved.
*/
#include "EthernetServer.h"
#include <cstdio>
#include <sys/socket.h>
#include <cstring>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include "log.h"
#include "EthernetClient.h"
EthernetServer::EthernetServer(uint16_t port, uint16_t max_clients) : port(port),
max_clients(max_clients), sockfd(-1)
{
clients.reserve(max_clients);
}
void EthernetServer::begin()
{
begin(IPAddress(0,0,0,0));
}
void EthernetServer::begin(IPAddress address)
{
struct addrinfo hints, *servinfo, *p;
int yes=1;
int rv;
char ipstr[INET_ADDRSTRLEN];
char portstr[6];
if (sockfd != -1) {
close(sockfd);
sockfd = -1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
sprintf(portstr, "%d", port);
if ((rv = getaddrinfo(address.toString().c_str(), portstr, &hints, &servinfo)) != 0) {
logError("getaddrinfo: %s\n", gai_strerror(rv));
return;
}
// loop through all the results and bind to the first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
logError("socket: %s\n", strerror(errno));
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
logError("setsockopt: %s\n", strerror(errno));
freeaddrinfo(servinfo);
return;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
logError("bind: %s\n", strerror(errno));
continue;
}
break;
}
if (p == NULL) {
logError("Failed to bind!\n");
freeaddrinfo(servinfo);
return;
}
if (listen(sockfd, ETHERNETSERVER_BACKLOG) == -1) {
logError("listen: %s\n", strerror(errno));
freeaddrinfo(servinfo);
return;
}
freeaddrinfo(servinfo);
fcntl(sockfd, F_SETFL, O_NONBLOCK);
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
void *addr = &(ipv4->sin_addr);
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
logDebug("Listening for connections on %s:%s\n", ipstr, portstr);
}
bool EthernetServer::hasClient()
{
// Check if any client has disconnected
for (size_t i = 0; i < clients.size(); ++i) {
EthernetClient client(clients[i]);
if (!client.connected()) {
// Checks if this disconnected client is also on the new clients list
for (std::list<int>::iterator it = new_clients.begin(); it != new_clients.end(); ++it) {
if (*it == clients[i]) {
new_clients.erase(it);
break;
}
}
client.stop();
clients[i] = clients.back();
clients.pop_back();
logDebug("Ethernet client disconnected.\n");
}
}
_accept();
return !new_clients.empty();
}
EthernetClient EthernetServer::available()
{
if (new_clients.empty()) {
return EthernetClient();
} else {
int sock = new_clients.front();
new_clients.pop_front();
return EthernetClient(sock);
}
}
size_t EthernetServer::write(uint8_t b)
{
return write(&b, 1);
}
size_t EthernetServer::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
for (size_t i = 0; i < clients.size(); ++i) {
EthernetClient client(clients[i]);
if (client.status() == ETHERNETCLIENT_W5100_ESTABLISHED) {
n += client.write(buffer, size);
}
}
return n;
}
size_t EthernetServer::write(const char *str)
{
if (str == NULL) {
return 0;
}
return write((const uint8_t *)str, strlen(str));
}
size_t EthernetServer::write(const char *buffer, size_t size)
{
return write((const uint8_t *)buffer, size);
}
void EthernetServer::_accept()
{
int new_fd;
socklen_t sin_size;
struct sockaddr_storage client_addr;
char ipstr[INET_ADDRSTRLEN];
sin_size = sizeof client_addr;
new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
if (new_fd == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
logError("accept: %s\n", strerror(errno));
}
return;
}
if (clients.size() == max_clients) {
// no free slots, search for a dead client
close(new_fd);
logDebug("Max number of ethernet clients reached.\n");
return;
}
new_clients.push_back(new_fd);
clients.push_back(new_fd);
void *addr = &(((struct sockaddr_in*)&client_addr)->sin_addr);
inet_ntop(client_addr.ss_family, addr, ipstr, sizeof ipstr);
logDebug("New connection from %s\n", ipstr);
}

View File

@@ -0,0 +1,121 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved.
*/
#ifndef EthernetServer_h
#define EthernetServer_h
#include <list>
#include <vector>
#include "Server.h"
#include "IPAddress.h"
#ifdef ETHERNETSERVER_MAX_CLIENTS
#define ETHERNETSERVER_BACKLOG ETHERNETSERVER_MAX_CLIENTS //!< Maximum length to which the queue of pending connections may grow.
#else
#define ETHERNETSERVER_MAX_CLIENTS 10 //!< Default value for max_clients.
#define ETHERNETSERVER_BACKLOG 10 //!< Maximum length to which the queue of pending connections may grow.
#endif
class EthernetClient;
/**
* @brief EthernetServer class
*/
class EthernetServer : public Server
{
public:
/**
* @brief EthernetServer constructor.
*
* @param port number for the socket addresses.
* @param max_clients The maximum number allowed for connected clients.
*/
EthernetServer(uint16_t port, uint16_t max_clients = ETHERNETSERVER_MAX_CLIENTS);
/**
* @brief Listen for inbound connection request.
*
*/
virtual void begin();
/**
* @brief Listen on the specified ip for inbound connection request.
*
* @param addr IP address to bind to.
*/
void begin(IPAddress addr);
/**
* @brief Verifies if a new client has connected.
*
* @return @c true if a new client has connected, else @c false.
*/
bool hasClient();
/**
* @brief Get the new connected client.
*
* @return a EthernetClient object; if no new client has connected, this object will evaluate to false.
*/
EthernetClient available();
/**
* @brief Write a byte to all clients.
*
* @param b byte to send.
* @return 0 if FAILURE or 1 if SUCCESS.
*/
virtual size_t write(uint8_t b);
/**
* @brief Write at most 'size' bytes to all clients.
*
* @param buffer to read from.
* @param size of the buffer.
* @return 0 if FAILURE else number of bytes sent.
*/
virtual size_t write(const uint8_t *buffer, size_t size);
/**
* @brief Write a null-terminated string to all clients.
*
* @param str String to write.
* @return 0 if FAILURE else number of characters sent.
*/
size_t write(const char *str);
/**
* @brief Write at most 'size' characters to all clients.
*
* @param buffer to read from.
* @param size of the buffer.
* @return 0 if FAILURE else the number of characters sent.
*/
size_t write(const char *buffer, size_t size);
private:
uint16_t port; //!< @brief Port number for the network socket.
std::list<int> new_clients; //!< Socket list of new connected clients.
std::vector<int> clients; //!< @brief Socket list of connected clients.
uint16_t max_clients; //!< @brief The maximum number of allowed clients.
int sockfd; //!< @brief Network socket used to accept connections.
/**
* @brief Accept new clients if the total of connected clients is below max_clients.
*
*/
void _accept();
};
#endif

View File

@@ -0,0 +1,211 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "GPIO.h"
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "log.h"
// Declare a single default instance
GPIOClass GPIO = GPIOClass();
GPIOClass::GPIOClass()
{
FILE *f;
DIR* dp;
char file[64];
dp = opendir("/sys/class/gpio");
if (dp == NULL) {
logError("Could not open /sys/class/gpio directory");
exit(1);
}
lastPinNum = 0;
while (true) {
dirent *de = readdir(dp);
if (de == NULL) {
break;
}
if (strncmp("gpiochip", de->d_name, 8) == 0) {
snprintf(file, sizeof(file), "/sys/class/gpio/%s/base", de->d_name);
f = fopen(file, "r");
int base;
if (fscanf(f, "%d", &base) == EOF) {
logError("Failed to open %s\n", file);
base = 0;
}
fclose(f);
snprintf(file, sizeof(file), "/sys/class/gpio/%s/ngpio", de->d_name);
f = fopen(file, "r");
int ngpio;
if (fscanf(f, "%d", &ngpio) == EOF) {
logError("Failed to open %s\n", file);
ngpio = 0;
}
fclose(f);
int max = ngpio + base - 1;
if (lastPinNum < max) {
lastPinNum = max;
}
}
}
closedir(dp);
exportedPins = new uint8_t[lastPinNum + 1];
for (int i = 0; i < lastPinNum + 1; ++i) {
exportedPins[i] = 0;
}
}
GPIOClass::GPIOClass(const GPIOClass& other)
{
lastPinNum = other.lastPinNum;
exportedPins = new uint8_t[lastPinNum + 1];
for (int i = 0; i < lastPinNum + 1; ++i) {
exportedPins[i] = other.exportedPins[i];
}
}
GPIOClass::~GPIOClass()
{
FILE *f;
for (int i = 0; i < lastPinNum + 1; ++i) {
if (exportedPins[i]) {
f = fopen("/sys/class/gpio/unexport", "w");
fprintf(f, "%d\n", i);
fclose(f);
}
}
delete [] exportedPins;
}
void GPIOClass::pinMode(uint8_t pin, uint8_t mode)
{
FILE *f;
if (pin > lastPinNum) {
return;
}
f = fopen("/sys/class/gpio/export", "w");
fprintf(f, "%d\n", pin);
fclose(f);
int counter = 0;
char file[128];
sprintf(file, "/sys/class/gpio/gpio%d/direction", pin);
while ((f = fopen(file,"w")) == NULL) {
// Wait 10 seconds for the file to be accessible if not open on first attempt
sleep(1);
counter++;
if (counter > 10) {
logError("Could not open /sys/class/gpio/gpio%u/direction", pin);
exit(1);
}
}
if (mode == INPUT) {
fprintf(f, "in\n");
} else {
fprintf(f, "out\n");
}
exportedPins[pin] = 1;
fclose(f);
}
void GPIOClass::digitalWrite(uint8_t pin, uint8_t value)
{
FILE *f;
char file[128];
if (pin > lastPinNum) {
return;
}
if (0 == exportedPins[pin]) {
pinMode(pin, OUTPUT);
}
sprintf(file, "/sys/class/gpio/gpio%d/value", pin);
f = fopen(file, "w");
if (value == 0) {
fprintf(f, "0\n");
} else {
fprintf(f, "1\n");
}
fclose(f);
}
uint8_t GPIOClass::digitalRead(uint8_t pin)
{
FILE *f;
char file[128];
if (pin > lastPinNum) {
return 0;
}
if (0 == exportedPins[pin]) {
pinMode(pin, INPUT);
}
sprintf(file, "/sys/class/gpio/gpio%d/value", pin);
f = fopen(file, "r");
int i;
if (fscanf(f, "%d", &i) == EOF) {
logError("digitalRead: failed to read pin %u\n", pin);
i = 0;
}
fclose(f);
return i;
}
uint8_t GPIOClass::digitalPinToInterrupt(uint8_t pin)
{
return pin;
}
GPIOClass& GPIOClass::operator=(const GPIOClass& other)
{
if (this != &other) {
lastPinNum = other.lastPinNum;
exportedPins = new uint8_t[lastPinNum + 1];
for (int i = 0; i < lastPinNum + 1; ++i) {
exportedPins[i] = other.exportedPins[i];
}
}
return *this;
}

View File

@@ -0,0 +1,91 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef GPIO_h
#define GPIO_h
#include <stdint.h>
#define INPUT 0
#define OUTPUT 1
#define LOW 0
#define HIGH 1
/**
* @brief GPIO class
*/
class GPIOClass
{
public:
/**
* @brief GPIOClass constructor.
*/
GPIOClass();
/**
* @brief GPIOClass copy constructor.
*/
GPIOClass(const GPIOClass& other);
/**
* @brief GPIOClass destructor.
*/
~GPIOClass();
/**
* @brief Configures the specified pin to behave either as an input or an output.
*
* @param pin The number of the pin.
* @param mode INPUT or OUTPUT.
*/
void pinMode(uint8_t pin, uint8_t mode);
/**
* @brief Write a high or a low value for the given pin.
*
* @param pin number.
* @param value HIGH or LOW.
*/
void digitalWrite(uint8_t pin, uint8_t value);
/**
* @brief Reads the value from a specified pin.
*
* @param pin The number of the pin.
* @return HIGH or LOW.
*/
uint8_t digitalRead(uint8_t pin);
/**
* @brief Arduino compatibility function, returns the same given pin.
*
* @param pin The number of the pin.
* @return The same parameter pin number.
*/
uint8_t digitalPinToInterrupt(uint8_t pin);
/**
* @brief Overloaded assign operator.
*
*/
GPIOClass& operator=(const GPIOClass& other);
private:
int lastPinNum; //!< @brief Highest pin number supported.
uint8_t *exportedPins; //!< @brief Array with information of which pins were exported.
};
extern GPIOClass GPIO;
#endif

View File

@@ -0,0 +1,110 @@
/*
* IPAddress.cpp - Base class that provides IPAddress
* Copyright (c) 2011 Adrian McEwen. All right reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Modified by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#include <cstdio>
#include <cstring>
#include "IPAddress.h"
IPAddress::IPAddress()
{
_address.dword = 0;
}
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet,
uint8_t fourth_octet)
{
_address.bytes[0] = first_octet;
_address.bytes[1] = second_octet;
_address.bytes[2] = third_octet;
_address.bytes[3] = fourth_octet;
}
IPAddress::IPAddress(uint32_t address)
{
_address.dword = address;
}
IPAddress::IPAddress(const uint8_t *address)
{
memcpy(_address.bytes, address, sizeof(_address.bytes));
}
bool IPAddress::fromString(const char *address)
{
// TODO: add support for "a", "a.b", "a.b.c" formats
uint16_t acc = 0; // Accumulator
uint8_t dots = 0;
while (*address) {
char c = *address++;
if (c >= '0' && c <= '9') {
acc = acc * 10 + (c - '0');
if (acc > 255) {
// Value out of [0..255] range
return false;
}
} else if (c == '.') {
if (dots == 3) {
// Too much dots (there must be 3 dots)
return false;
}
_address.bytes[dots++] = acc;
acc = 0;
} else {
// Invalid char
return false;
}
}
if (dots != 3) {
// Too few dots (there must be 3 dots)
return false;
}
_address.bytes[3] = acc;
return true;
}
IPAddress& IPAddress::operator=(const uint8_t *address)
{
memcpy(_address.bytes, address, sizeof(_address.bytes));
return *this;
}
IPAddress& IPAddress::operator=(uint32_t address)
{
_address.dword = address;
return *this;
}
bool IPAddress::operator==(const uint8_t* addr) const
{
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
}
std::string IPAddress::toString()
{
char szRet[16];
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2],
_address.bytes[3]);
return std::string(szRet);
}

View File

@@ -0,0 +1,165 @@
/*
* IPAddress.h - Base class that provides IPAddress
* Copyright (c) 2011 Adrian McEwen. All right reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Modified by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#ifndef IPAddress_h
#define IPAddress_h
#include <stdint.h>
#include <string>
/**
* @brief A class to make it easier to handle and pass around IP addresses
*/
class IPAddress
{
private:
union {
uint8_t bytes[4]; //!< IPv4 address as an array
uint32_t dword; //!< IPv4 address in 32 bits format
} _address;
/**
* @brief Access the raw byte array containing the address.
*
* Because this returns a pointer to the internal structure rather than a copy of the address
* this function should only be used when you know that the usage of the returned uint8_t* will
* be transient and not stored.
*
* @return pointer to the internal structure.
*/
uint8_t* raw_address()
{
return _address.bytes;
}
public:
/**
* @brief IPAddress constructor.
*/
IPAddress();
/**
* @brief IPAddress constructor.
*
* @param first_octet first octet of the IPv4 address.
* @param second_octet second octet of the IPv4 address.
* @param third_octet third octet of the IPv4 address.
* @param fourth_octet fourth octet of the IPv4 address.
*/
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
/**
* @brief IPAddress constructor.
*
* @param address to be set from a 32 bits integer.
*/
explicit IPAddress(uint32_t address);
/**
* @brief IPAddress constructor.
*
* @param address to be set from a byte array.
*/
explicit IPAddress(const uint8_t *address);
/**
* @brief Set the IP from a array of characters.
*
* @param address to be set.
*/
bool fromString(const char *address);
/**
* @brief Set the IP from a string class type.
*
* @param address to be set.
*/
bool fromString(const std::string &address)
{
return fromString(address.c_str());
}
/**
* @brief Overloaded cast operator
*
* Allow IPAddress objects to be used where a pointer to a four-byte uint8_t array is expected
*/
operator uint32_t() const
{
return _address.dword;
}
/**
* @brief Overloaded cast operator
*
*/
bool operator==(const IPAddress& addr) const
{
return _address.dword == addr._address.dword;
}
/**
* @brief Overloaded cast operator
*
*/
bool operator==(uint32_t addr) const
{
return _address.dword == addr;
}
/**
* @brief Overloaded cast operator
*
*/
bool operator==(const uint8_t* addr) const;
/**
* @brief Overloaded index operator.
*
* Allow getting and setting individual octets of the address.
*
*/
uint8_t operator[](int index) const
{
return _address.bytes[index];
}
/**
* @brief Overloaded index operator
*
*/
uint8_t& operator[](int index)
{
return _address.bytes[index];
}
/**
* @brief Overloaded copy operators.
*
* Allow initialisation of IPAddress objects from byte array.
*/
IPAddress& operator=(const uint8_t *address);
/**
* @brief Overloaded copy operator.
*
* Allow initialisation of IPAddress objects from a 32 bits integer.
*/
IPAddress& operator=(uint32_t address);
/**
* @brief Convert the IP address to a string.
*
* @return A stringified IP address
*/
std::string toString();
friend class Client;
};
#endif

View File

@@ -0,0 +1,272 @@
/*
Print.cpp - Base class that provides print() and println()
Copyright (c) 2008 David A. Mellis. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 23 November 2006 by David A. Mellis
Modified December 2014 by Ivan Grokhotkov
Modified May 2015 by Michael C. Miller - esp8266 progmem support
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#include <cstdlib>
#include <cstdio>
#include <cstdarg>
#include <cmath>
#include "Print.h"
// Public Methods //////////////////////////////////////////////////////////////
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while(size--) {
n += write(*buffer++);
}
return n;
}
size_t
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
Print::printf(const char *format, ...)
{
va_list arg;
va_start(arg, format);
char temp[64];
char* buffer = temp;
size_t len = vsnprintf(temp, sizeof(temp), format, arg);
va_end(arg);
if (len > sizeof(temp) - 1) {
buffer = new char[len + 1];
if (!buffer) {
return 0;
}
va_start(arg, format);
vsnprintf(buffer, len + 1, format, arg);
va_end(arg);
}
len = write((const uint8_t*) buffer, len);
if (buffer != temp) {
delete[] buffer;
}
return len;
}
size_t Print::print(const std::string &s)
{
return write(s.c_str(), s.length());
}
size_t Print::print(const char str[])
{
return write(str);
}
size_t Print::print(char c)
{
return write(c);
}
size_t Print::print(unsigned char b, int base)
{
return print((unsigned long) b, base);
}
size_t Print::print(int n, int base)
{
return print((long) n, base);
}
size_t Print::print(unsigned int n, int base)
{
return print((unsigned long) n, base);
}
size_t Print::print(long n, int base)
{
if(base == 0) {
return write(n);
} else if(base == 10) {
if(n < 0) {
int t = print('-');
n = -n;
return printNumber(n, 10) + t;
}
return printNumber(n, 10);
} else {
return printNumber(n, base);
}
}
size_t Print::print(unsigned long n, int base)
{
if(base == 0) {
return write(n);
} else {
return printNumber(n, base);
}
}
size_t Print::print(double n, int digits)
{
return printFloat(n, digits);
}
size_t Print::println(void)
{
return print("\r\n");
}
size_t Print::println(const std::string &s)
{
size_t n = print(s);
n += println();
return n;
}
size_t Print::println(const char c[])
{
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(char c)
{
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(unsigned char b, int base)
{
size_t n = print(b, base);
n += println();
return n;
}
size_t Print::println(int num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned int num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(double num, int digits)
{
size_t n = print(num, digits);
n += println();
return n;
}
// Private Methods /////////////////////////////////////////////////////////////
size_t Print::printNumber(unsigned long n, uint8_t base)
{
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if(base < 2) {
base = 10;
}
do {
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while(n);
return write(str);
}
size_t Print::printFloat(double number, uint8_t digits)
{
size_t n = 0;
if(std::isnan(number)) {
return print("nan");
}
if(std::isinf(number)) {
return print("inf");
}
if(number > 4294967040.0) {
return print("ovf"); // constant determined empirically
}
if(number < -4294967040.0) {
return print("ovf"); // constant determined empirically
}
// Handle negative numbers
if(number < 0.0) {
n += print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for(uint8_t i = 0; i < digits; ++i) {
rounding /= 10.0;
}
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long) number;
double remainder = number - (double) int_part;
n += print(int_part);
// Print the decimal point, but only if there are digits beyond
if(digits > 0) {
n += print(".");
}
// Extract digits from the remainder one at a time
while(digits-- > 0) {
remainder *= 10.0;
int toPrint = int(remainder);
n += print(toPrint);
remainder -= toPrint;
}
return n;
}

View File

@@ -0,0 +1,98 @@
/*
Print.h - Base class that provides print() and println()
Copyright (c) 2008 David A. Mellis. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#ifndef Print_h
#define Print_h
#include <stdint.h>
#include <string>
#include <string.h>
#if !DOXYGEN
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
class Print
{
private:
int write_error;
size_t printNumber(unsigned long n, uint8_t base);
size_t printFloat(double number, uint8_t digits);
protected:
void setWriteError(int err = 1)
{
write_error = err;
}
public:
Print() : write_error(0) {}
int getWriteError()
{
return write_error;
}
void clearWriteError()
{
setWriteError(0);
}
virtual size_t write(uint8_t) = 0;
size_t write(const char *str)
{
if (str == NULL) {
return 0;
}
return write((const uint8_t *) str, strlen(str));
}
virtual size_t write(const uint8_t *buffer, size_t size);
size_t write(const char *buffer, size_t size)
{
return write((const uint8_t *) buffer, size);
}
size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
size_t print(const std::string &);
size_t print(const char[]);
size_t print(char);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(double, int = 2);
size_t println(const std::string &s);
size_t println(const char[]);
size_t println(char);
size_t println(unsigned char, int = DEC);
size_t println(int, int = DEC);
size_t println(unsigned int, int = DEC);
size_t println(long, int = DEC);
size_t println(unsigned long, int = DEC);
size_t println(double, int = 2);
size_t println(void);
};
#endif
#endif

View File

@@ -0,0 +1,31 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef _SPI_H_
#define _SPI_H_
#ifdef LINUX_SPI_BCM
#include "SPIBCM.h"
#define SPI SPIBCM
#elif LINUX_SPI_SPIDEV
#include "SPIDEV.h"
#define SPI SPIDEV
#endif
#endif

View File

@@ -0,0 +1,368 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
*/
#include "SPIDEV.h"
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "log.h"
static pthread_mutex_t spiMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutexattr_t attr;
// Declare a single default instance
SPIDEVClass SPIDEV = SPIDEVClass();
uint8_t SPIDEVClass::initialized = 0;
int SPIDEVClass::fd = -1;
std::string SPIDEVClass::device = SPI_SPIDEV_DEVICE;
uint8_t SPIDEVClass::mode = SPI_MODE0;
uint32_t SPIDEVClass::speed = SPI_CLOCK_BASE;
uint8_t SPIDEVClass::bit_order = MSBFIRST;
struct spi_ioc_transfer SPIDEVClass::tr = {0,0,0,0,0,8,0,0,0,0}; // 8 bits_per_word, 0 cs_change
SPIDEVClass::SPIDEVClass()
{
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&spiMutex, &attr);
}
void SPIDEVClass::begin(int busNo)
{
if (!initialized) {
/* set spidev accordingly to busNo like:
* busNo = 23 -> /dev/spidev2.3
*
* a bit messy but simple
* */
device[11] += (busNo / 10) % 10;
device[13] += busNo % 10;
init();
}
initialized++; // reference count
}
void SPIDEVClass::end()
{
if (initialized) {
initialized--;
}
if (!initialized) {
if (!(fd < 0)) {
close(fd);
fd = -1;
}
}
}
void SPIDEVClass::setBitOrder(uint8_t border)
{
pthread_mutex_lock(&spiMutex);
/*
* bit order
*/
bit_order = border;
int ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &bit_order);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &bit_order);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIDEVClass::setDataMode(uint8_t data_mode)
{
pthread_mutex_lock(&spiMutex);
/*
* spi mode
*/
mode = data_mode;
int ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
logError("Can't set SPI mode.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1) {
logError("Can't set SPI mode.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIDEVClass::setClockDivider(uint16_t divider)
{
if (divider == 0) {
return;
}
pthread_mutex_lock(&spiMutex);
/*
* max speed hz
*/
speed = SPI_CLOCK_BASE / divider;
int ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIDEVClass::chipSelect(int csn_chip)
{
if (csn_chip >= 0 && csn_chip <= 9) {
device[13] = '0' + (csn_chip % 10);
init();
}
}
uint8_t SPIDEVClass::transfer(uint8_t data)
{
int ret;
uint8_t tx[1] = {data};
uint8_t rx[1] = {0};
pthread_mutex_lock(&spiMutex);
tr.tx_buf = (unsigned long)&tx[0];
tr.rx_buf = (unsigned long)&rx[0];
tr.len = 1;
tr.speed_hz = speed;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
logError("Can't send spi message.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
return rx[0];
}
void SPIDEVClass::transfernb(char* tbuf, char* rbuf, uint32_t len)
{
int ret;
pthread_mutex_lock(&spiMutex);
tr.tx_buf = (unsigned long)tbuf;
tr.rx_buf = (unsigned long)rbuf;
tr.len = len;
tr.speed_hz = speed;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
logError("Can't send spi message.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}
void SPIDEVClass::transfern(char* buf, uint32_t len)
{
transfernb(buf, buf, len);
}
void SPIDEVClass::beginTransaction(SPISettings settings)
{
int ret;
pthread_mutex_lock(&spiMutex);
/*
* spi mode
*/
if (settings.dmode != mode) {
mode = settings.dmode;
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
logError("Can't set spi mode.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1) {
logError("Can't set spi mode.\n");
abort();
}
}
/*
* speed
*/
if (settings.clock != speed) {
speed = settings.clock;
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
}
/*
* bit order
*/
if (settings.border != bit_order) {
bit_order = settings.border;
ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &bit_order);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &bit_order);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
}
}
void SPIDEVClass::endTransaction()
{
pthread_mutex_unlock(&spiMutex);
}
void SPIDEVClass::usingInterrupt(uint8_t interruptNumber)
{
(void)interruptNumber;
}
void SPIDEVClass::notUsingInterrupt(uint8_t interruptNumber)
{
(void)interruptNumber;
}
void SPIDEVClass::init()
{
pthread_mutex_lock(&spiMutex);
if (fd >= 0) {
close(fd);
}
fd = open(device.c_str(), O_RDWR);
if (fd < 0) {
logError("Can't open SPI device: %s\n", device.c_str());
abort();
}
/*
* spi mode
*/
int ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
logError("Can't set SPI mode.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1) {
logError("Can't set SPI mode.\n");
abort();
}
/*
* bits per word
*/
uint8_t bits = 8; // 8 bits per word
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1) {
logError("Can't set SPI bits per word.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1) {
logError("Can't set SPI bits per word.\n");
abort();
}
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1) {
logError("Can't set SPI max speed hz.\n");
abort();
}
/*
* bit order
*/
ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &bit_order);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
ret = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &bit_order);
if (ret == -1) {
logError("Can't set SPI bit order.\n");
abort();
}
pthread_mutex_unlock(&spiMutex);
}

View File

@@ -0,0 +1,212 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on TMRh20 RF24 library, Copyright (c) 2015 Charles-Henri Hallard <tmrh20@gmail.com>
*/
#ifndef SPIDEV_h
#define SPIDEV_h
#include <stdint.h>
#include <string>
#include <linux/spi/spidev.h>
#define SPI_HAS_TRANSACTION
#define MSBFIRST 0
#define LSBFIRST SPI_LSB_FIRST
#define SPI_CLOCK_BASE 16000000 // 16Mhz
#define SPI_CLOCK_DIV1 1
#define SPI_CLOCK_DIV2 2
#define SPI_CLOCK_DIV4 4
#define SPI_CLOCK_DIV8 8
#define SPI_CLOCK_DIV16 16
#define SPI_CLOCK_DIV32 32
#define SPI_CLOCK_DIV64 64
#define SPI_CLOCK_DIV128 128
#define SPI_CLOCK_DIV256 256
// SPI Data mode
#define SPI_MODE0 SPI_MODE_0
#define SPI_MODE1 SPI_MODE_1
#define SPI_MODE2 SPI_MODE_2
#define SPI_MODE3 SPI_MODE_3
#ifndef SPI_SPIDEV_DEVICE
#define SPI_SPIDEV_DEVICE "/dev/spidev0.0"
#endif
// Default to Raspberry Pi
const uint8_t SS = 24;
const uint8_t MOSI = 19;
const uint8_t MISO = 21;
const uint8_t SCK = 23;
/**
* SPISettings class
*/
class SPISettings
{
public:
/**
* @brief SPISettings constructor.
*/
SPISettings()
{
init(SPI_CLOCK_BASE, MSBFIRST, SPI_MODE0);
}
/**
* @brief SPISettings constructor.
*
* @param clock SPI clock speed in Hz.
* @param bitOrder SPI bit order.
* @param dataMode SPI data mode.
*/
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
{
init(clock, bitOrder, dataMode);
}
uint32_t clock; //!< @brief SPI clock.
uint8_t border; //!< @brief SPI bit order.
uint8_t dmode; //!< @brief SPI data mode.
private:
/**
* @brief Initialized class members.
*
* @param clk SPI clock.
* @param bitOrder SPI bit order.
* @param dataMode SPI data mode.
*/
void init(uint32_t clk, uint8_t bitOrder, uint8_t dataMode)
{
clock = clk;
border = bitOrder;
dmode = dataMode;
}
friend class SPIDEVClass;
};
/**
* SPIDEV class
*/
class SPIDEVClass
{
public:
/**
* @brief SPIDEVClass constructor.
*/
SPIDEVClass();
/**
* @brief Start SPI operations.
*/
static void begin(int busNo=0);
/**
* @brief End SPI operations.
*/
static void end();
/**
* @brief Sets the SPI bit order.
*
* @param bit_order The desired bit order.
*/
static void setBitOrder(uint8_t bit_order);
/**
* @brief Sets the SPI data mode.
*
* @param data_mode The desired data mode.
*/
static void setDataMode(uint8_t data_mode);
/**
* @brief Sets the SPI clock divider and therefore the SPI clock speed.
*
* @param divider The desired SPI clock divider.
*/
static void setClockDivider(uint16_t divider);
/**
* @brief Sets the chip select pin.
*
* @param csn_chip Specifies the CS chip.
*/
static void chipSelect(int csn_chip);
/**
* @brief Transfer a single byte
*
* @param data Byte to send
* @return Data returned via spi
*/
static uint8_t transfer(uint8_t data);
/**
* @brief Transfer a buffer of data
*
* @param tbuf Transmit buffer
* @param rbuf Receive buffer
* @param len Length of the data
*/
static void transfernb(char* tbuf, char* rbuf, uint32_t len);
/**
* @brief Transfer a buffer of data without an rx buffer
*
* @param buf Pointer to a buffer of data
* @param len Length of the data
*/
static void transfern(char* buf, uint32_t len);
/**
* @brief Start SPI transaction.
*
* @param settings for SPI.
*/
static void beginTransaction(SPISettings settings);
/**
* @brief End SPI transaction.
*/
static void endTransaction();
/**
* @brief Not implemented.
*
* @param interruptNumber ignored parameter.
*/
static void usingInterrupt(uint8_t interruptNumber);
/**
* @brief Not implemented.
*
* @param interruptNumber ignored parameter.
*/
static void notUsingInterrupt(uint8_t interruptNumber);
private:
static uint8_t initialized; //!< @brief SPI initialized flag.
static int fd; //!< @brief SPI device file descriptor.
static std::string device; //!< @brief Default SPI device.
static uint8_t mode; //!< @brief SPI mode.
static uint32_t speed; //!< @brief SPI speed.
static uint8_t bit_order; //!< @brief SPI bit order.
static struct spi_ioc_transfer tr; //!< @brief Auxiliar struct for data transfer.
static void init();
};
extern SPIDEVClass SPIDEV;
#endif

View File

@@ -0,0 +1,285 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <termios.h>
#include <grp.h>
#include <errno.h>
#include <sys/stat.h>
#include "log.h"
#include "SerialPort.h"
SerialPort::SerialPort(const char *port, bool isPty) : serialPort(std::string(port)), isPty(isPty)
{
sd = -1;
}
void SerialPort::begin(int bauds)
{
if (!open(bauds)) {
logError("Failed to open serial port.\n");
exit(1);
}
logDebug("Serial port %s (%d baud) created\n", serialPort.c_str(), bauds);
}
bool SerialPort::open(int bauds)
{
speed_t speed;
struct termios options;
if (isPty) {
sd = posix_openpt(O_RDWR | O_NOCTTY | O_NDELAY);
if (sd < 0) {
logError("Couldn't open a PTY: %s\n", strerror(errno));
return false;
}
if (grantpt(sd) != 0) {
logError("Couldn't grant permission to the PTY: %s\n", strerror(errno));
return false;
}
if (unlockpt(sd) != 0) {
logError("Couldn't unlock the PTY: %s\n", strerror(errno));
return false;
}
/* create a symlink with predictable name to the PTY device */
unlink(serialPort.c_str()); // remove the symlink if it already exists
if (symlink(ptsname(sd), serialPort.c_str()) != 0) {
logError("Couldn't create a symlink '%s' to PTY! (%d) %s\n", serialPort.c_str(), errno,
strerror(errno));
return false;
}
} else {
if ((sd = ::open(serialPort.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) == -1) {
logError("Unable to open the serial port %s\n", serialPort.c_str());
return false;
}
// nonblocking mode
fcntl(sd, F_SETFL, FNDELAY);
}
switch (bauds) {
case 50:
speed = B50 ;
break ;
case 75:
speed = B75 ;
break ;
case 110:
speed = B110 ;
break ;
case 134:
speed = B134 ;
break ;
case 150:
speed = B150 ;
break ;
case 200:
speed = B200 ;
break ;
case 300:
speed = B300 ;
break ;
case 600:
speed = B600 ;
break ;
case 1200:
speed = B1200 ;
break ;
case 1800:
speed = B1800 ;
break ;
case 2400:
speed = B2400 ;
break ;
case 9600:
speed = B9600 ;
break ;
case 19200:
speed = B19200 ;
break ;
case 38400:
speed = B38400 ;
break ;
case 57600:
speed = B57600 ;
break ;
case 115200:
speed = B115200 ;
break ;
default:
speed = B115200 ;
break ;
}
// Get the current options of the port
if (tcgetattr(sd, &options) < 0) {
logError("Couldn't get term attributes: %s\n", strerror(errno));
return false;
}
// Clear all the options
bzero(&options, sizeof(options));
// Set the baud rate
cfsetispeed(&options, speed);
cfsetospeed(&options, speed);
// Configure the device : 8 bits, no parity, no control
options.c_cflag |= ( CLOCAL | CREAD | CS8);
// Ignore framing errors, parity errors and BREAK condition on input.
options.c_iflag |= ( IGNPAR | IGNBRK );
// Timer unused
options.c_cc[VTIME]=0;
// At least on character before satisfy reading
options.c_cc[VMIN]=0;
// Set parameters
if (tcsetattr(sd, TCSANOW, &options) < 0) {
logError("Couldn't set term attributes: %s\n", strerror(errno));
return false;
}
// flush
if (tcflush(sd, TCIOFLUSH) < 0) {
logError("Couldn't flush serial: %s\n", strerror(errno));
return false;
}
usleep(10000);
return true;
}
bool SerialPort::setGroupPerm(const char *groupName)
{
const mode_t ttyPermissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if (sd != -1 && groupName != NULL) {
struct group *devGrp = getgrnam(groupName);
if (devGrp == NULL) {
logError("getgrnam: %s failed. (%d) %s\n", groupName, errno, strerror(errno));
return false;
}
const char *dev;
if (isPty) {
dev = ptsname(sd);
} else {
dev = serialPort.c_str();
}
int ret = chown(dev, -1, devGrp->gr_gid);
if (ret == -1) {
logError("Could not change PTY owner! (%d) %s\n", errno, strerror(errno));
return false;
}
ret = chmod(dev, ttyPermissions);
if (ret != 0) {
logError("Could not change PTY permissions! (%d) %s\n", errno, strerror(errno));
return false;
}
return true;
}
return false;
}
int SerialPort::available()
{
int nbytes = 0;
if (ioctl(sd, FIONREAD, &nbytes) < 0) {
logError("Failed to get byte count on serial.\n");
exit(-1);
}
return nbytes;
}
int SerialPort::read()
{
unsigned char c;
int ret = ::read(sd, &c, 1);
if (ret < 0) {
logError("Serial - read failed: %s\n", strerror(errno));
} else if (ret == 1) {
return c;
}
return -1;
}
size_t SerialPort::write(uint8_t b)
{
int ret = ::write(sd, &b, 1);
if (ret < 0) {
logError("Serial - write failed: %s\n", strerror(errno));
}
return ret;
}
size_t SerialPort::write(const uint8_t *buffer, size_t size)
{
int ret = ::write(sd, buffer, size);
if (ret < 0) {
logError("Serial - write failed: %s\n", strerror(errno));
}
return ret;
}
int SerialPort::peek()
{
FILE * f = fdopen(sd, "r+");
int c = getc(f);
if (c == EOF) {
return -1;
}
ungetc(c, f);
return c;
}
void SerialPort::flush()
{
// Waits until all output written to sd has been transmitted
if (tcdrain(sd) < 0) {
logError("Couldn't flush serial: %s\n", strerror(errno));
}
}
void SerialPort::end()
{
close(sd);
if (isPty) {
unlink(serialPort.c_str()); // remove the symlink
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef SerialPort_h
#define SerialPort_h
#include <string>
#include <stdbool.h>
#include "Stream.h"
/**
* SerialPort Class
* Class that provides the functionality of arduino Serial library
*/
class SerialPort : public Stream
{
private:
int sd; //!< @brief file descriptor number.
std::string serialPort; //!< @brief tty name.
bool isPty; //!< @brief true if serial is pseudo terminal.
public:
/**
* @brief SerialPort constructor.
*/
SerialPort(const char *port, bool isPty = false);
/**
* @brief Open the serial port and set the data rate in bits per second (baud).
*
* This function will terminate the program on an error.
*
* @param bauds bits per second.
*/
void begin(int bauds);
/**
* @brief Open the serial port and set the data rate in bits per second (baud).
*
* @param bauds bits per second.
* @return @c true if no errors, else @c false.
*/
bool open(int bauds = 115200);
/**
* @brief Grant access to the specified system group for the serial device.
*
* @param groupName system group name.
*/
bool setGroupPerm(const char *groupName);
/**
* @brief Get the number of bytes available.
*
* Get the numberof bytes (characters) available for reading from
* the serial port.
*
* @return number of bytes avalable to read.
*/
int available();
/**
* @brief Reads 1 byte of incoming serial data.
*
* @return first byte of incoming serial data available.
*/
int read();
/**
* @brief Writes a single byte to the serial port.
*
* @param b byte to write.
* @return number of bytes written.
*/
size_t write(uint8_t b);
/**
* @brief Writes binary data to the serial port.
*
* @param buffer to write.
* @param size of the buffer.
* @return number of bytes written.
*/
size_t write(const uint8_t *buffer, size_t size);
/**
* @brief
*
* Returns the next byte (character) of incoming serial data without removing it from
* the internal serial buffer.
*
* @return -1 if no data else character in the buffer.
*/
int peek();
/**
* @brief Remove any data remaining on the serial buffer.
*/
void flush();
/**
* @brief Disables serial communication.
*/
void end();
};
#endif

View File

@@ -0,0 +1,33 @@
/*
Server.h - Base class that provides Server
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef server_h
#define server_h
#include "Print.h"
#if !DOXYGEN
class Server : public Print
{
public:
virtual void begin() =0;
};
#endif
#endif

View File

@@ -0,0 +1,181 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "SoftEeprom.h"
SoftEeprom::SoftEeprom() : _length(0), _fileName(NULL), _values(NULL)
{
}
SoftEeprom::SoftEeprom(const SoftEeprom& other)
{
_fileName = strdup(other._fileName);
_length = other._length;
_values = new uint8_t[_length];
for (size_t i = 0; i < _length; ++i) {
_values[i] = other._values[i];
}
}
SoftEeprom::~SoftEeprom()
{
destroy();
}
int SoftEeprom::init(const char *fileName, size_t length)
{
struct stat fileInfo;
destroy();
_fileName = strdup(fileName);
if (_fileName == NULL) {
logError("Error: %s\n", strerror(errno));
return -1;
}
_length = length;
_values = new uint8_t[_length];
if (stat(_fileName, &fileInfo) != 0) {
//File does not exist. Create it.
logInfo("EEPROM file %s does not exist, creating new file.\n", _fileName);
std::ofstream myFile(_fileName, std::ios::out | std::ios::binary);
if (!myFile) {
logError("Unable to create config file %s.\n", _fileName);
return -1;
}
// Fill the eeprom with 1s
for (size_t i = 0; i < _length; ++i) {
_values[i] = 0xFF;
}
myFile.write((const char*)_values, _length);
myFile.close();
} else if (fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) {
logError("EEPROM file %s is not the correct size of %zu. Please remove the file and a new one will be created.\n",
_fileName, _length);
destroy();
return -1;
} else {
//Read config into local memory.
std::ifstream myFile(_fileName, std::ios::in | std::ios::binary);
if (!myFile) {
logError("Unable to open EEPROM file %s for reading.\n", _fileName);
return -1;
}
myFile.read((char*)_values, _length);
myFile.close();
}
return 0;
}
void SoftEeprom::destroy()
{
if (_values) {
delete[] _values;
_values = NULL;
}
if (_fileName) {
free(_fileName);
_fileName = NULL;
}
_length = 0;
}
void SoftEeprom::readBlock(void* buf, void* addr, size_t length)
{
unsigned long int offs = reinterpret_cast<unsigned long int>(addr);
if (!length) {
logError("EEPROM being read without being initialized!\n");
return;
}
if (offs + length <= _length) {
memcpy(buf, _values+offs, length);
}
}
void SoftEeprom::writeBlock(void* buf, void* addr, size_t length)
{
unsigned long int offs = reinterpret_cast<unsigned long int>(addr);
if (!length) {
logError("EEPROM being written without being initialized!\n");
return;
}
if (offs + length <= _length) {
if (memcmp(_values+offs, buf, length) == 0) {
return;
}
memcpy(_values+offs, buf, length);
std::ofstream myFile(_fileName, std::ios::out | std::ios::in | std::ios::binary);
if (!myFile) {
logError("Unable to write config to file %s.\n", _fileName);
return;
}
myFile.seekp(offs);
myFile.write((const char*)buf, length);
myFile.close();
}
}
uint8_t SoftEeprom::readByte(int addr)
{
uint8_t value = 0xFF;
readBlock(&value, reinterpret_cast<void*>(addr), 1);
return value;
}
void SoftEeprom::writeByte(int addr, uint8_t value)
{
uint8_t curr = readByte(addr);
if (curr != value) {
writeBlock(&value, reinterpret_cast<void*>(addr), 1);
}
}
SoftEeprom& SoftEeprom::operator=(const SoftEeprom& other)
{
if (this != &other) {
delete[] _values;
free(_fileName);
_fileName = strdup(other._fileName);
_length = other._length;
_values = new uint8_t[_length];
for (size_t i = 0; i < _length; ++i) {
_values[i] = other._values[i];
}
}
return *this;
}

View File

@@ -0,0 +1,104 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
/**
* This a software emulation of EEPROM that uses a file for data storage.
* A copy of the eeprom values are also held in memory for faster reading.
*/
#ifndef SoftEeprom_h
#define SoftEeprom_h
#include <stdint.h>
/**
* SoftEeprom class
*/
class SoftEeprom
{
public:
/**
* @brief SoftEeprom constructor.
*/
SoftEeprom();
/**
* @brief SoftEeprom copy constructor.
*/
SoftEeprom(const SoftEeprom& other);
/**
* @brief SoftEeprom destructor.
*/
~SoftEeprom();
/**
* @brief Initializes the eeprom class.
*
* @param fileName filepath where the data is saved.
* @param length eeprom size in bytes.
* @return 0 if SUCCESS or -1 if FAILURE.
*/
int init(const char *fileName, size_t length);
/**
* @brief Clear all allocated memory variables.
*
*/
void destroy();
/**
* @brief Read a block of bytes from eeprom.
*
* @param buf buffer to copy to.
* @param addr eeprom address to read from.
* @param length number of bytes to read.
*/
void readBlock(void* buf, void* addr, size_t length);
/**
* @brief Write a block of bytes to eeprom.
*
* @param buf buffer to read from.
* @param addr eeprom address to write to.
* @param length number of bytes to write.
*/
void writeBlock(void* buf, void* addr, size_t length);
/**
* @brief Read a byte from eeprom.
*
* @param addr eeprom address to read from.
* @return the read byte.
*/
uint8_t readByte(int addr);
/**
* @brief Write a byte to eeprom.
*
* @param addr eeprom address to write to.
* @param value to write.
*/
void writeByte(int addr, uint8_t value);
/**
* @brief Overloaded assign operator.
*
*/
SoftEeprom& operator=(const SoftEeprom& other);
private:
size_t _length; //!< @brief Eeprom max size.
char *_fileName; //!< @brief file where the eeprom values are stored.
uint8_t *_values; //!< @brief copy of the eeprom values held in memory for a faster reading.
};
#endif

View File

@@ -0,0 +1,56 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include <stdio.h>
#include "StdInOutStream.h"
void StdInOutStream::begin(int baud)
{
(void)baud;
}
int StdInOutStream::available()
{
return 1;
}
int StdInOutStream::read()
{
return getchar();
}
size_t StdInOutStream::write(uint8_t b)
{
return (size_t)::printf("%c", b);
}
int StdInOutStream::peek()
{
return -1;
}
void StdInOutStream::flush()
{
fflush(stdout);
}
void StdInOutStream::end()
{
flush();
}

View File

@@ -0,0 +1,76 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef StdInOutStream_h
#define StdInOutStream_h
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Stream.h"
/**
* @brief A class that prints to stdout and reads from stdin
*/
class StdInOutStream : public Stream
{
public:
/**
* @brief This function does nothing.
*
* @param baud Ignored parameter.
*/
void begin(int baud);
/**
* @brief This function does nothing.
*
* @return always returns 1.
*/
int available();
/**
* @brief Reads 1 key pressed from the keyboard.
*
* @return key character pressed cast to an int.
*/
int read();
/**
* @brief Writes a single byte to stdout.
*
* @param b byte to write.
* @return -1 if error else, number of bytes written.
*/
size_t write(uint8_t b);
/**
* @brief Not supported.
*
* @return always returns -1.
*/
int peek();
/**
* @brief Flush stdout.
*/
void flush();
/**
* @brief Nothing to do, flush stdout.
*/
void end();
};
#endif

View File

@@ -0,0 +1,292 @@
/*
Stream.cpp - adds parsing methods to Stream class
Copyright (c) 2008 David A. Mellis. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Created July 2011
parsing functions based on TextFinder library by Michael Margolis
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#include <Arduino.h>
#include <Stream.h>
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
// private method to read stream with timeout
int Stream::timedRead()
{
_startMillis = millis();
do {
int c;
c = read();
if(c >= 0) {
return c;
}
yield();
} while(millis() - _startMillis < _timeout);
return -1; // -1 indicates timeout
}
// private method to peek stream with timeout
int Stream::timedPeek()
{
_startMillis = millis();
do {
int c;
c = peek();
if(c >= 0) {
return c;
}
yield();
} while(millis() - _startMillis < _timeout);
return -1; // -1 indicates timeout
}
// returns peek of the next digit in the stream or -1 if timeout
// discards non-numeric characters
int Stream::peekNextDigit()
{
while(1) {
int c;
c = timedPeek();
if(c < 0) {
return c; // timeout
}
if(c == '-') {
return c;
}
if(c >= '0' && c <= '9') {
return c;
}
read(); // discard non-numeric
}
}
// Public Methods
//////////////////////////////////////////////////////////////
void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait
{
_timeout = timeout;
}
// find returns true if the target string is found
bool Stream::find(const char *target)
{
return findUntil(target, (char*) "");
}
// reads data from the stream until the target string of given length is found
// returns true if target string is found, false if timed out
bool Stream::find(const char *target, size_t length)
{
return findUntil(target, length, NULL, 0);
}
// as find but search ends if the terminator string is found
bool Stream::findUntil(const char *target, const char *terminator)
{
return findUntil(target, strlen(target), terminator, strlen(terminator));
}
// reads data from the stream until the target string of the given length is found
// search terminated if the terminator string is found
// returns true if target string is found, false if terminated or timed out
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator,
size_t termLen)
{
size_t index = 0; // maximum target string length is 64k bytes!
size_t termIndex = 0;
int c;
if(*target == 0) {
return true; // return true if target is a null string
}
while((c = timedRead()) > 0) {
if(c != target[index]) {
index = 0; // reset index if any char does not match
}
if(c == target[index]) {
//////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1);
if(++index >= targetLen) { // return true if all chars in the target match
return true;
}
}
if(termLen > 0 && c == terminator[termIndex]) {
if(++termIndex >= termLen) {
return false; // return false if terminate string found before target string
}
} else {
termIndex = 0;
}
}
return false;
}
// returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// function is terminated by the first character that is not a digit.
long Stream::parseInt()
{
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
}
// as above but a given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
long Stream::parseInt(char skipChar)
{
bool isNegative = false;
long value = 0;
int c;
c = peekNextDigit();
// ignore non numeric leading characters
if(c < 0) {
return 0; // zero returned if timeout
}
do {
if(c == skipChar) {
// ignore this charactor
} else if(c == '-') {
isNegative = true;
} else if(c >= '0' && c <= '9') { // is c a digit?
value = value * 10 + c - '0';
}
read(); // consume the character we got with peek
c = timedPeek();
} while((c >= '0' && c <= '9') || c == skipChar);
if(isNegative) {
value = -value;
}
return value;
}
// as parseInt but returns a floating point value
float Stream::parseFloat()
{
return parseFloat(NO_SKIP_CHAR);
}
// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
float Stream::parseFloat(char skipChar)
{
bool isNegative = false;
bool isFraction = false;
long value = 0;
int c;
float fraction = 1.0;
c = peekNextDigit();
// ignore non numeric leading characters
if(c < 0) {
return 0; // zero returned if timeout
}
do {
if(c == skipChar) {
// ignore
} else if(c == '-') {
isNegative = true;
} else if(c == '.') {
isFraction = true;
} else if(c >= '0' && c <= '9') { // is c a digit?
value = value * 10 + c - '0';
if(isFraction) {
fraction *= 0.1;
}
}
read(); // consume the character we got with peek
c = timedPeek();
} while((c >= '0' && c <= '9') || c == '.' || c == skipChar);
if(isNegative) {
value = -value;
}
if(isFraction) {
return value * fraction;
} else {
return value;
}
}
// read characters from stream into buffer
// terminates if length characters have been read, or timeout (see setTimeout)
// returns the number of characters placed in the buffer
// the buffer is NOT null terminated.
//
size_t Stream::readBytes(char *buffer, size_t length)
{
size_t count = 0;
while(count < length) {
int c = timedRead();
if(c < 0) {
break;
}
*buffer++ = (char) c;
count++;
}
return count;
}
// as readBytes with terminator character
// terminates if length characters have been read, timeout, or if the terminator character detected
// returns the number of characters placed in the buffer (0 means no valid data found)
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length)
{
if(length < 1) {
return 0;
}
size_t index = 0;
while(index < length) {
int c = timedRead();
if(c < 0 || c == terminator) {
break;
}
*buffer++ = (char) c;
index++;
}
return index; // return number of characters, not including null terminator
}
std::string Stream::readString()
{
std::string ret;
int c = timedRead();
while(c >= 0) {
ret += (char) c;
c = timedRead();
}
return ret;
}
std::string Stream::readStringUntil(char terminator)
{
std::string ret;
int c = timedRead();
while(c >= 0 && c != terminator) {
ret += (char) c;
c = timedRead();
}
return ret;
}

View File

@@ -0,0 +1,138 @@
/*
Stream.h - base class for character-based streams.
Copyright (c) 2010 David A. Mellis. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
parsing functions based on TextFinder library by Michael Margolis
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#ifndef Stream_h
#define Stream_h
#include <inttypes.h>
#include <string>
#include "Print.h"
// compatability macros for testing
/*
#define getInt() parseInt()
#define getInt(skipChar) parseInt(skipchar)
#define getFloat() parseFloat()
#define getFloat(skipChar) parseFloat(skipChar)
#define getString( pre_string, post_string, buffer, length)
readBytesBetween( pre_string, terminator, buffer, length)
*/
#if !DOXYGEN
class Stream: public Print
{
protected:
unsigned long
_timeout; // number of milliseconds to wait for the next char before aborting timed read
unsigned long _startMillis; // used for timeout measurement
int timedRead(); // private method to read stream with timeout
int timedPeek(); // private method to peek stream with timeout
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
public:
virtual int available() = 0;
virtual int read() = 0;
virtual int peek() = 0;
virtual void flush() = 0;
Stream()
{
_timeout = 1000;
_startMillis = 0;
}
// parsing methods
void setTimeout(unsigned long
timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
bool find(const char *target); // reads data from the stream until the target string is found
bool find(uint8_t *target)
{
return find((char *) target);
}
// returns true if target string is found, false if timed out (see setTimeout)
bool find(const char *target, size_t
length); // reads data from the stream until the target string of given length is found
bool find(const uint8_t *target, size_t length)
{
return find((char *) target, length);
}
// returns true if target string is found, false if timed out
bool find(char target)
{
return find (&target, 1);
}
bool findUntil(const char *target,
const char *terminator); // as find but search ends if the terminator string is found
bool findUntil(const uint8_t *target, const char *terminator)
{
return findUntil((char *) target, terminator);
}
bool findUntil(const char *target, size_t targetLen, const char *terminate,
size_t termLen); // as above but search ends if the terminate string is found
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen)
{
return findUntil((char *) target, targetLen, terminate, termLen);
}
long parseInt(); // returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// integer is terminated by the first character that is not a digit.
float parseFloat(); // float version of parseInt
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
virtual size_t readBytes(uint8_t *buffer, size_t length)
{
return readBytes((char *) buffer, length);
}
// terminates if length characters have been read or timeout (see setTimeout)
// returns the number of characters placed in the buffer (0 means no valid data found)
size_t readBytesUntil(char terminator, char *buffer,
size_t length); // as readBytes with terminator character
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length)
{
return readBytesUntil(terminator, (char *) buffer, length);
}
// terminates if length characters have been read, timeout, or if the terminator character detected
// returns the number of characters placed in the buffer (0 means no valid data found)
// Arduino String functions to be added here
std::string readString();
std::string readStringUntil(char terminator);
protected:
long parseInt(char skipChar); // as above but the given skipChar is ignored
// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
float parseFloat(char skipChar); // as above but the given skipChar is ignored
};
#endif
#endif

View File

@@ -0,0 +1,96 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include "Arduino.h"
// For millis()
static unsigned long millis_at_start = 0;
void yield(void) {}
unsigned long millis(void)
{
timeval curTime;
if (millis_at_start == 0) {
gettimeofday(&curTime, NULL);
millis_at_start = curTime.tv_sec;
}
gettimeofday(&curTime, NULL);
return ((curTime.tv_sec - millis_at_start) * 1000) + (curTime.tv_usec / 1000);
}
unsigned long micros()
{
timeval curTime;
if (millis_at_start == 0) {
gettimeofday(&curTime, NULL);
millis_at_start = curTime.tv_sec;
}
gettimeofday(&curTime, NULL);
return ((curTime.tv_sec - millis_at_start) * 1000000) + (curTime.tv_usec);
}
void _delay_milliseconds(unsigned int millis)
{
struct timespec sleeper;
sleeper.tv_sec = (time_t)(millis / 1000);
sleeper.tv_nsec = (long)(millis % 1000) * 1000000;
nanosleep(&sleeper, NULL);
}
void _delay_microseconds(unsigned int micro)
{
struct timespec sleeper;
sleeper.tv_sec = (time_t)(micro / 1000000);
sleeper.tv_nsec = (long)(micro % 1000000) * 1000;
nanosleep(&sleeper, NULL);
}
void randomSeed(unsigned long seed)
{
if (seed != 0) {
srand(seed);
}
}
long randMax(long howbig)
{
if (howbig == 0) {
return 0;
}
return rand() % howbig;
}
long randMinMax(long howsmall, long howbig)
{
if (howsmall >= howbig) {
return howsmall;
}
long diff = howbig - howsmall;
return randMax(diff) + howsmall;
}

View File

@@ -0,0 +1,304 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on mosquitto project, Copyright (c) 2012 Roger Light <roger@atchoo.org>
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "log.h"
static int _config_create(const char *config_file);
static int _config_parse_int(char *token, const char *name, int *value);
static int _config_parse_string(char *token, const char *name, char **value);
int config_parse(const char *config_file)
{
FILE *fptr;
char buf[1024];
struct stat fileInfo;
if (stat(config_file, &fileInfo) != 0) {
//File does not exist. Create it.
logInfo("Config file %s does not exist, creating new file.\n", config_file);
_config_create(config_file);
}
fptr = fopen(config_file, "rt");
if (!fptr) {
logError("Error opening config file \"%s\".\n", config_file);
return -1;
}
conf.verbose = 7;
conf.log_pipe = 0;
conf.log_pipe_file = NULL;
conf.syslog = 0;
conf.eeprom_file = NULL;
conf.eeprom_size = 0;
conf.soft_hmac_key = NULL;
conf.soft_serial_key = NULL;
conf.aes_key = NULL;
while (fgets(buf, 1024, fptr)) {
if (buf[0] != '#' && buf[0] != 10 && buf[0] != 13) {
while (buf[strlen(buf)-1] == 10 || buf[strlen(buf)-1] == 13) {
buf[strlen(buf)-1] = 0;
}
if (!strncmp(buf, "verbose=", 8)) {
char *verbose = NULL;
if (_config_parse_string(&(buf[8]), "verbose", &verbose)) {
fclose(fptr);
return -1;
} else {
if (!strncmp(verbose, "err", 3)) {
conf.verbose = 3;
} else if (!strncmp(verbose, "warn", 4)) {
conf.verbose = 4;
} else if (!strncmp(verbose, "notice", 6)) {
conf.verbose = 5;
} else if (!strncmp(verbose, "info", 4)) {
conf.verbose = 6;
} else if (!strncmp(verbose, "debug", 5)) {
conf.verbose = 7;
} else {
logError("Invalid value for verbose in configuration.\n");
fclose(fptr);
free(verbose);
return -1;
}
free(verbose);
}
} else if (!strncmp(buf, "log_file=", 9)) {
if (_config_parse_int(&(buf[9]), "log_file", &conf.log_file)) {
fclose(fptr);
return -1;
} else {
if (conf.log_file != 0 && conf.log_file != 1) {
logError("log_file must be 1 or 0 in configuration.\n");
fclose(fptr);
return -1;
}
}
} else if (!strncmp(buf, "log_filepath=", 13)) {
if (_config_parse_string(&(buf[13]), "log_filepath", &conf.log_filepath)) {
fclose(fptr);
return -1;
}
} else if (!strncmp(buf, "log_pipe=", 9)) {
if (_config_parse_int(&(buf[9]), "log_pipe", &conf.log_pipe)) {
fclose(fptr);
return -1;
} else {
if (conf.log_pipe != 0 && conf.log_pipe != 1) {
logError("log_pipe must be 1 or 0 in configuration.\n");
fclose(fptr);
return -1;
}
}
} else if (!strncmp(buf, "log_pipe_file=", 14)) {
if (_config_parse_string(&(buf[14]), "log_pipe_file", &conf.log_pipe_file)) {
fclose(fptr);
return -1;
}
} else if (!strncmp(buf, "syslog=", 7)) {
if (_config_parse_int(&(buf[7]), "syslog", &conf.syslog)) {
fclose(fptr);
return -1;
} else {
if (conf.syslog != 0 && conf.syslog != 1) {
logError("syslog must be 1 or 0 in configuration.\n");
fclose(fptr);
return -1;
}
}
} else if (!strncmp(buf, "eeprom_file=", 12)) {
if (_config_parse_string(&(buf[12]), "eeprom_file", &conf.eeprom_file)) {
fclose(fptr);
return -1;
}
} else if (!strncmp(buf, "eeprom_size=", 12)) {
if (_config_parse_int(&(buf[12]), "eeprom_size", &conf.eeprom_size)) {
fclose(fptr);
return -1;
} else {
if (conf.eeprom_size <= 0) {
logError("eeprom_size value must be greater than 0 in configuration.\n");
fclose(fptr);
return -1;
}
}
} else if (!strncmp(buf, "soft_hmac_key=", 14)) {
if (_config_parse_string(&(buf[14]), "soft_hmac_key", &conf.soft_hmac_key)) {
fclose(fptr);
return -1;
}
} else if (!strncmp(buf, "soft_serial_key=", 16)) {
if (_config_parse_string(&(buf[16]), "soft_serial_key", &conf.soft_serial_key)) {
fclose(fptr);
return -1;
}
} else if (!strncmp(buf, "aes_key=", 8)) {
if (_config_parse_string(&(buf[8]), "aes_key", &conf.aes_key)) {
fclose(fptr);
return -1;
}
} else {
logWarning("Unknown config option \"%s\".\n", buf);
}
}
}
fclose(fptr);
if (!conf.eeprom_file) {
logError("No eeprom_file found in configuration.\n");
return -1;
}
if (conf.log_file && !conf.log_filepath) {
logError("log_filepath must be set if you enable log_file in configuration.\n");
return -1;
}
if (conf.log_pipe && !conf.log_pipe_file) {
logError("log_pipe_file must be set if you enable log_pipe in configuration.\n");
return -1;
}
return 0;
}
void config_cleanup(void)
{
if (conf.log_filepath) {
free(conf.log_filepath);
}
if (conf.log_pipe_file) {
free(conf.log_pipe_file);
}
if (conf.eeprom_file) {
free(conf.eeprom_file);
}
if (conf.soft_hmac_key) {
free(conf.soft_hmac_key);
}
if (conf.soft_serial_key) {
free(conf.soft_serial_key);
}
if (conf.aes_key) {
free(conf.aes_key);
}
}
int _config_create(const char *config_file)
{
FILE *myFile;
int ret;
const char default_conf[] = "# Logging\n" \
"# Verbosity: debug,info,notice,warn,err\n" \
"verbose=debug\n" \
"\n" \
"# Enable logging to a file.\n" \
"log_file=0\n" \
"# Log file path.\n" \
"log_filepath=/tmp/mysgw.log\n" \
"\n" \
"# Enable logging to a named pipe.\n" \
"# Use this option to view your gateway's log messages\n" \
"# from the log_pipe_file defined bellow.\n" \
"# To do so, run the following command on another terminal:\n" \
"# cat \"log_pipe_file\"\n" \
"log_pipe=0\n" \
"log_pipe_file=/tmp/mysgw.pipe\n" \
"\n" \
"# Enable logging to syslog.\n" \
"syslog=0\n" \
"\n" \
"# EEPROM settings\n" \
"eeprom_file=/etc/mysensors.eeprom\n" \
"eeprom_size=1024\n" \
"\n" \
"# Software signing settings\n" \
"# Note: The gateway must have been built with signing\n" \
"# support to use the options below.\n" \
"#\n" \
"# To generate a HMAC key run mysgw with: --gen-soft-hmac-key\n" \
"# copy the new key in the line below and uncomment it.\n" \
"#soft_hmac_key=\n" \
"# To generate a serial key run mysgw with: --gen-soft-serial-key\n" \
"# copy the new key in the line below and uncomment it.\n" \
"#soft_serial_key=\n" \
"\n" \
"# Encryption settings\n" \
"# Note: The gateway must have been built with encryption\n" \
"# support to use the options below.\n" \
"#\n" \
"# To generate a AES key run mysgw with: --gen-aes-key\n" \
"# copy the new key in the line below and uncomment it.\n" \
"#aes_key=\n";
myFile = fopen(config_file, "w");
if (!myFile) {
logError("Unable to create config file %s.\n", config_file);
return -1;
}
ret = fputs(default_conf, myFile);
fclose(myFile);
return (ret > 0);
}
int _config_parse_int(char *token, const char *name, int *value)
{
if (token) {
*value = atoi(token);
} else {
logError("Empty %s value in configuration.\n", name);
return 1;
}
return 0;
}
int _config_parse_string(char *token, const char *name, char **value)
{
if (token) {
if (*value) {
logError("Duplicate %s value in configuration.\n", name);
return 1;
}
while (token[0] == ' ' || token[0] == '\t') {
token++;
}
*value = strdup(token);
if (!*value) {
logError("Out of memory.\n");
return 1;
}
} else {
logError("Empty %s value in configuration.\n", name);
return 1;
}
return 0;
}

View File

@@ -0,0 +1,48 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef CONFIG_H
#define CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
struct config {
int verbose;
int log_file;
char *log_filepath;
int log_pipe;
char *log_pipe_file;
int syslog;
char *eeprom_file;
int eeprom_size;
char *soft_hmac_key;
char *soft_serial_key;
char *aes_key;
} conf;
int config_parse(const char *config_file);
void config_cleanup(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,252 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on wiringPi Copyright (c) 2012 Gordon Henderson.
*/
#include "interrupt.h"
#include <pthread.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stropts.h>
#include <errno.h>
#include <sched.h>
#include "log.h"
struct ThreadArgs {
void (*func)();
int gpioPin;
};
volatile bool interruptsEnabled = true;
static pthread_mutex_t intMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t *threadIds[64] = {NULL};
// sysFds:
// Map a file descriptor from the /sys/class/gpio/gpioX/value
static int sysFds[64] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
/*
* Part of wiringPi: Simple way to get your program running at high priority
* with realtime schedulling.
*/
int piHiPri(const int pri)
{
struct sched_param sched ;
memset (&sched, 0, sizeof(sched)) ;
if (pri > sched_get_priority_max (SCHED_RR)) {
sched.sched_priority = sched_get_priority_max (SCHED_RR) ;
} else {
sched.sched_priority = pri ;
}
return sched_setscheduler (0, SCHED_RR, &sched) ;
}
void *interruptHandler(void *args)
{
int fd;
struct pollfd polls;
char c;
struct ThreadArgs *arguments = (struct ThreadArgs *)args;
int gpioPin = arguments->gpioPin;
void (*func)() = arguments->func;
delete arguments;
(void)piHiPri(55); // Only effective if we run as root
if ((fd = sysFds[gpioPin]) == -1) {
logError("Failed to attach interrupt for pin %d\n", gpioPin);
return NULL;
}
// Setup poll structure
polls.fd = fd;
polls.events = POLLPRI | POLLERR;
while (1) {
// Wait for it ...
int ret = poll(&polls, 1, -1);
if (ret < 0) {
logError("Error waiting for interrupt: %s\n", strerror(errno));
break;
}
// Do a dummy read to clear the interrupt
// A one character read appars to be enough.
if (lseek (fd, 0, SEEK_SET) < 0) {
logError("Interrupt handler error: %s\n", strerror(errno));
break;
}
if (read (fd, &c, 1) < 0) {
logError("Interrupt handler error: %s\n", strerror(errno));
break;
}
// Call user function.
pthread_mutex_lock(&intMutex);
if (interruptsEnabled) {
pthread_mutex_unlock(&intMutex);
func();
} else {
pthread_mutex_unlock(&intMutex);
}
}
close(fd);
return NULL;
}
void attachInterrupt(uint8_t gpioPin, void (*func)(), uint8_t mode)
{
FILE *fd;
char fName[40];
char c;
int count, i;
if (threadIds[gpioPin] == NULL) {
threadIds[gpioPin] = new pthread_t;
} else {
// Cancel the existing thread for that pin
pthread_cancel(*threadIds[gpioPin]);
// Wait a bit
usleep(1000);
}
// Export pin for interrupt
if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) {
logError("attachInterrupt: Unable to export pin %d for interrupt: %s\n", gpioPin, strerror(errno));
exit(1);
}
fprintf(fd, "%d\n", gpioPin);
fclose(fd);
// Wait a bit the system to create /sys/class/gpio/gpio<GPIO number>
usleep(1000);
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ;
if ((fd = fopen (fName, "w")) == NULL) {
logError("attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n",
gpioPin, strerror(errno));
exit(1) ;
}
fprintf(fd, "in\n") ;
fclose(fd) ;
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ;
if ((fd = fopen(fName, "w")) == NULL) {
logError("attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", gpioPin,
strerror(errno));
exit(1) ;
}
switch (mode) {
case CHANGE:
fprintf(fd, "both\n");
break;
case FALLING:
fprintf(fd, "falling\n");
break;
case RISING:
fprintf(fd, "rising\n");
break;
case NONE:
fprintf(fd, "none\n");
break;
default:
logError("attachInterrupt: Invalid mode\n");
fclose(fd);
return;
}
fclose(fd);
if (sysFds[gpioPin] == -1) {
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin);
if ((sysFds[gpioPin] = open(fName, O_RDWR)) < 0) {
logError("Error reading pin %d: %s\n", gpioPin, strerror(errno));
exit(1);
}
}
// Clear any initial pending interrupt
ioctl(sysFds[gpioPin], FIONREAD, &count);
for (i = 0; i < count; ++i) {
if (read(sysFds[gpioPin], &c, 1) == -1) {
logError("attachInterrupt: failed to read pin status: %s\n", strerror(errno));
}
}
struct ThreadArgs *threadArgs = new struct ThreadArgs;
threadArgs->func = func;
threadArgs->gpioPin = gpioPin;
// Create a thread passing the pin and function
pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs);
}
void detachInterrupt(uint8_t gpioPin)
{
// Cancel the thread
if (threadIds[gpioPin] != NULL) {
pthread_cancel(*threadIds[gpioPin]);
delete threadIds[gpioPin];
threadIds[gpioPin] = NULL;
}
// Close filehandle
if (sysFds[gpioPin] != -1) {
close(sysFds[gpioPin]);
sysFds[gpioPin] = -1;
}
FILE *fp = fopen("/sys/class/gpio/unexport", "w");
if (fp == NULL) {
logError("Unable to unexport pin %d for interrupt\n", gpioPin);
exit(1);
}
fprintf(fp, "%d", gpioPin);
fclose(fp);
}
void interrupts()
{
pthread_mutex_lock(&intMutex);
interruptsEnabled = true;
pthread_mutex_unlock(&intMutex);
}
void noInterrupts()
{
pthread_mutex_lock(&intMutex);
interruptsEnabled = false;
pthread_mutex_unlock(&intMutex);
}

View File

@@ -0,0 +1,45 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Based on wiringPi Copyright (c) 2012 Gordon Henderson.
*/
#ifndef interrupt_h
#define interrupt_h
#include <stdint.h>
#define CHANGE 1
#define FALLING 2
#define RISING 3
#define NONE 4
#ifdef __cplusplus
extern "C" {
#endif
void attachInterrupt(uint8_t gpioPin, void(*func)(), uint8_t mode);
void detachInterrupt(uint8_t gpioPin);
void interrupts();
void noInterrupts();
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,278 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "log.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
static const char *_log_level_colors[] = {
"\x1b[1;5;91m", "\x1b[1;91m", "\x1b[91m", "\x1b[31m", "\x1b[33m", "\x1b[34m", "\x1b[32m", "\x1b[36m"
};
static const char *_log_level_names[] = {
"EMERGENCY", "ALERT", "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
static uint8_t _log_quiet = 0;
static uint8_t _log_level = LOG_DEBUG;
static uint8_t _log_syslog = 0;
static uint8_t _log_pipe = 0;
static char *_log_pipe_file = NULL;
static int _log_pipe_fd = -1;
static FILE *_log_file_fp = NULL;
void logSetQuiet(uint8_t enable)
{
_log_quiet = enable ? 1 : 0;
}
void logSetLevel(int level)
{
if (level < LOG_EMERG || level > LOG_DEBUG) {
return;
}
_log_level = level;
}
void logSetSyslog(int options, int facility)
{
openlog(NULL, options, facility);
_log_syslog = 1;
}
int logSetPipe(char *pipe_file)
{
if (pipe_file == NULL) {
return -1;
}
_log_pipe_file = strdup(pipe_file);
if (_log_pipe_file == NULL) {
return -1;
}
int ret = mkfifo(_log_pipe_file, 0666);
if (ret == 0) {
_log_pipe = 1;
}
return ret;
}
int logSetFile(char *file)
{
if (file == NULL) {
return -1;
}
_log_file_fp = fopen(file, "a");
if (_log_file_fp == NULL) {
return errno;
}
return 0;
}
void logClose(void)
{
if (_log_syslog) {
closelog();
_log_syslog = 0;
}
if (_log_pipe) {
if (_log_pipe_fd > 0) {
close(_log_pipe_fd);
}
/* remove the FIFO */
unlink(_log_pipe_file);
_log_pipe = 0;
}
if (_log_pipe_file != NULL) {
free(_log_pipe_file);
_log_pipe_file = NULL;
}
if (_log_file_fp != NULL) {
fclose(_log_file_fp);
_log_file_fp = NULL;
}
}
void vlog(int level, const char *fmt, va_list args)
{
if (_log_level < level) {
return;
}
if (!_log_quiet || _log_file_fp != NULL) {
/* Get current time */
time_t t = time(NULL);
struct tm *lt = localtime(&t);
char date[16];
date[strftime(date, sizeof(date), "%b %d %H:%M:%S", lt)] = '\0';
if (_log_file_fp != NULL) {
fprintf(_log_file_fp, "%s %-5s ", date, _log_level_names[level]);
vfprintf(_log_file_fp, fmt, args);
fflush(_log_file_fp);
}
if (!_log_quiet) {
#ifdef LOG_DISABLE_COLOR
(void)_log_level_colors;
fprintf(stderr, "%s %-5s ", date, _log_level_names[level]);
vfprintf(stderr, fmt, args);
#else
fprintf(stderr, "%s %s%-5s\x1b[0m ", date, _log_level_colors[level], _log_level_names[level]);
vfprintf(stderr, fmt, args);
#endif
}
}
if (_log_syslog) {
vsyslog(level, fmt, args);
}
if (_log_pipe) {
if (_log_pipe_fd < 0) {
_log_pipe_fd = open(_log_pipe_file, O_WRONLY | O_NONBLOCK);
}
if (_log_pipe_fd > 0) {
if (vdprintf(_log_pipe_fd, fmt, args) < 0) {
close(_log_pipe_fd);
_log_pipe_fd = -1;
}
}
}
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logEmergency(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_EMERG, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logAlert(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_ALERT, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logCritical(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_CRIT, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logError(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_ERR, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logWarning(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_WARNING, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logNotice(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_NOTICE, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logInfo(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_INFO, fmt, args);
va_end(args);
}
void
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
logDebug(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vlog(LOG_DEBUG, fmt, args);
va_end(args);
}

View File

@@ -0,0 +1,59 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef LOG_H
#define LOG_H
#include <stdarg.h>
#include <stdio.h>
#include <syslog.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define vlogError(...) vlog(LOG_ERR, __VA_ARGS__)
#define vlogWarning(...) vlog(LOG_WARNING, __VA_ARGS__)
#define vlogNotice(...) vlog(LOG_NOTICE, __VA_ARGS__)
#define vlogInfo(...) vlog(LOG_INFO, __VA_ARGS__)
#define vlogDebug(...) vlog(LOG_DEBUG, __VA_ARGS__)
void logSetQuiet(uint8_t enable);
void logSetLevel(int level);
void logSetSyslog(int options, int facility);
int logSetPipe(char *pipe_file);
int logSetFile(char *file);
void logClose(void);
void vlog(int level, const char *fmt, va_list args);
void logEmergency(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logAlert(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logCritical(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logError(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logWarning(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logNotice(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logInfo(const char *fmt, ...) __attribute__((format(printf,1,2)));
void logDebug(const char *fmt, ...) __attribute__((format(printf,1,2)));
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,158 @@
/*
noniso.cpp - replacements for non-ISO functions used by Arduino core
Copyright © 2016 Ivan Grokhotkov
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.
Modified August 2016 by Marcelo Aquino <marceloaqno@gmail.org> for MySensors use
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "stdlib_noniso.h"
char* utoa(unsigned value, char* result, int base)
{
if(base < 2 || base > 16) {
*result = 0;
return result;
}
char* out = result;
unsigned quotient = value;
do {
const unsigned tmp = quotient / base;
*out = "0123456789abcdef"[quotient - (tmp * base)];
++out;
quotient = tmp;
} while(quotient);
reverse(result, out);
*out = 0;
return result;
}
char* itoa(int value, char* result, int base)
{
if(base < 2 || base > 16) {
*result = 0;
return result;
}
char* out = result;
int quotient = abs(value);
do {
const int tmp = quotient / base;
*out = "0123456789abcdef"[quotient - (tmp * base)];
++out;
quotient = tmp;
} while(quotient);
// Apply negative sign
if(value < 0) {
*out++ = '-';
}
reverse(result, out);
*out = 0;
return result;
}
int atoi(const char* s)
{
return (int) atol(s);
}
long atol(const char* s)
{
char * tmp;
return strtol(s, &tmp, 10);
}
double atof(const char* s)
{
char * tmp;
return strtod(s, &tmp);
}
void reverse(char* begin, char* end)
{
char *is = begin;
char *ie = end - 1;
while(is < ie) {
char tmp = *ie;
*ie = *is;
*is = tmp;
++is;
--ie;
}
}
char* ltoa(long value, char* result, int base)
{
if(base < 2 || base > 16) {
*result = 0;
return result;
}
char* out = result;
long quotient = abs(value);
do {
const long tmp = quotient / base;
*out = "0123456789abcdef"[quotient - (tmp * base)];
++out;
quotient = tmp;
} while(quotient);
// Apply negative sign
if(value < 0) {
*out++ = '-';
}
reverse(result, out);
*out = 0;
return result;
}
char* ultoa(unsigned long value, char* result, int base)
{
if(base < 2 || base > 16) {
*result = 0;
return result;
}
char* out = result;
unsigned long quotient = value;
do {
const unsigned long tmp = quotient / base;
*out = "0123456789abcdef"[quotient - (tmp * base)];
++out;
quotient = tmp;
} while(quotient);
reverse(result, out);
*out = 0;
return result;
}
char* dtostrf (double val, signed char width, unsigned char prec, char *s)
{
sprintf(s,"%*.*f", width, prec, val);
return s;
}

View File

@@ -0,0 +1,52 @@
/*
stdlib_noniso.h - nonstandard (but usefull) conversion functions
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STDLIB_NONISO_H
#define STDLIB_NONISO_H
#ifdef __cplusplus
extern "C" {
#endif
int atoi(const char *s);
long atol(const char* s);
double atof(const char* s);
char* itoa (int val, char *s, int radix);
char* ltoa (long val, char *s, int radix);
char* utoa (unsigned int val, char *s, int radix);
char* ultoa (unsigned long val, char *s, int radix);
char* dtostrf (double val, signed char width, unsigned char prec, char *s);
void reverse(char* begin, char* end);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View File

@@ -0,0 +1,71 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwHAL.h"
void hwDebugPrint(const char *fmt, ...)
{
#ifndef MY_DISABLED_SERIAL
#if !defined(__linux__)
char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
#ifdef MY_GATEWAY_SERIAL
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";"), C_INTERNAL,
I_LOG_MESSAGE);
MY_DEBUGDEVICE.print(fmtBuffer);
#endif
// prepend timestamp
MY_DEBUGDEVICE.print(hwMillis());
MY_DEBUGDEVICE.print(" ");
va_list args;
va_start(args, fmt);
vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
#ifdef MY_GATEWAY_SERIAL
// Truncate message if this is gateway node
fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
#endif
va_end(args);
MY_DEBUGDEVICE.print(fmtBuffer);
MY_DEBUGDEVICE.flush();
#else
va_list args;
va_start(args, fmt);
vlogDebug(fmt, args);
va_end(args);
#endif
#else
(void)fmt;
#endif
}
#if defined(DEBUG_OUTPUT_ENABLED)
static char hwDebugPrintStr[65];
static void hwDebugBuf2Str(const uint8_t *buf, size_t sz)
{
if (sz > 32) {
sz = 32; //clamp to 32 bytes
}
for (uint8_t i = 0; i < sz; i++) {
hwDebugPrintStr[i * 2] = convertI2H(buf[i] >> 4);
hwDebugPrintStr[(i * 2) + 1] = convertI2H(buf[i]);
}
hwDebugPrintStr[sz * 2] = '\0';
}
#endif

View File

@@ -0,0 +1,187 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
/**
* @file MyHwHAL.h
*
* MySensors hardware abstraction layer
*/
#ifndef MyHwHAL_h
#define MyHwHAL_h
/**
* @def INVALID_INTERRUPT_NUM
* @brief Invalid interrupt
*/
#define INVALID_INTERRUPT_NUM (0xFFu)
/**
* @def MY_HWID_PADDING_BYTE
* @brief HwID padding byte
*/
#define MY_HWID_PADDING_BYTE (0xAAu)
/**
* @def IRQ_HANDLER_ATTR
* @brief ESP8266/ESP32 IRQ handlers need to be stored in IRAM
*/
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR
#else
#define IRQ_HANDLER_ATTR
#endif
// Implement these as functions or macros
/*
#define hwInit() MY_SERIALDEVICE.begin(BAUD_RATE)
#define hwWatchdogReset() wdt_reset()
#define hwReboot() wdt_enable(WDTO_15MS); while (1)
#define hwMillis() millis()
#define hwDigitalWrite(__pin, __value)
#define hwDigitalRead(__pin)
#define hwPinMode(__pin, __value)
void hwReadConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(const int addr);
*/
/**
* @def MY_HW_HAS_GETENTROPY
* @brief Define this, if hwGetentropy is implemented
*
* ssize_t hwGetentropy(void *__buffer, size_t __length);
*/
//#define MY_HW_HAS_GETENTROPY
/// @brief unique ID
typedef uint8_t unique_id_t[16];
/**
* Sleep for a defined time, using minimum power.
* @param ms Time to sleep, in [ms].
* @return MY_WAKE_UP_BY_TIMER.
*/
int8_t hwSleep(uint32_t ms);
/**
* Sleep for a defined time, using minimum power, or until woken by interrupt.
* @param interrupt Interrupt number, which can wake the mcu from sleep.
* @param mode Interrupt mode, as passed to attachInterrupt.
* @param ms Time to sleep, in [ms].
* @return MY_WAKE_UP_BY_TIMER when woken by timer, or interrupt number when woken by interrupt.
*/
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms);
/**
* Sleep for a defined time, using minimum power, or until woken by one of the interrupts.
* @param interrupt1 Interrupt1 number, which can wake the mcu from sleep.
* @param mode1 Interrupt1 mode, as passed to attachInterrupt.
* @param interrupt2 Interrupt2 number, which can wake the mcu from sleep.
* @param mode2 Interrupt2 mode, as passed to attachInterrupt.
* @param ms Time to sleep, in [ms].
* @return MY_WAKE_UP_BY_TIMER when woken by timer, or interrupt number when woken by interrupt.
*/
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms);
/**
* Retrieve unique hardware ID
* @param uniqueID unique ID
* @return True if unique ID successfully retrieved
*/
bool hwUniqueID(unique_id_t *uniqueID);
/**
* CPU voltage
* @return CPU voltage in mV
*/
uint16_t hwCPUVoltage(void);
/**
* CPU frequency
* @return CPU frequency in 1/10Mhz
*/
uint16_t hwCPUFrequency(void);
/**
* CPU temperature (if available)
* Adjust calibration parameters via MY_<ARCH>_TEMPERATURE_OFFSET and MY_<ARCH>_TEMPERATURE_GAIN
* @return CPU temperature in °C, -127 if not available
*/
int8_t hwCPUTemperature(void);
/**
* Report free memory (if function available)
* @return free memory in bytes
*/
uint16_t hwFreeMem(void);
#if defined(DEBUG_OUTPUT_ENABLED)
/**
* Debug print
* @param fmt
*/
void hwDebugPrint(const char *fmt, ...);
/**
* Convert buffer to hex string
* @param buf
* @param sz
*/
static void hwDebugBuf2Str(const uint8_t *buf, size_t sz) __attribute__((unused));
#endif
/**
* @def MY_CRITICAL_SECTION
* @brief Creates a block of code that is guaranteed to be executed atomically.
* Upon entering the block all interrupts are disabled, and re-enabled upon
* exiting the block from any exit path.
* A typical example that requires atomic access is a 16 (or more) bit variable
* that is shared between the main execution path and an ISR, on an 8-bit
* platform (e.g AVR):
* @code
* volatile uint16_t val = 0;
*
* void interrupHandler()
* {
* val = ~val;
* }
*
* void loop()
* {
* uint16_t copy_val;
* MY_CRITICAL_SECTION
* {
* copy_val = val;
* }
* }
* @endcode
* All code within the MY_CRITICAL_SECTION block will be protected from being
* interrupted during execution.
*/
#ifdef DOXYGEN
#define MY_CRITICAL_SECTION
#define MY_HW_HAS_GETENTROPY
#endif /* DOXYGEN */
#endif // #ifdef MyHw_h

View File

@@ -0,0 +1,533 @@
/*
* 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-2019 Sensnology AB
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/MySensors/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.
*/
#include "MyHwNRF5.h"
volatile uint8_t _wokeUpByInterrupt = INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu.
volatile uint8_t _wakeUp1Interrupt =
INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp1-callback.
volatile uint8_t _wakeUp2Interrupt =
INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
void wakeUp1(void) // place to send the interrupts
{
_wokeUpByInterrupt = _wakeUp1Interrupt;
}
void wakeUp2(void) // place to send the second interrupts
{
_wokeUpByInterrupt = _wakeUp2Interrupt;
}
void hwReadConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *dst = static_cast<uint8_t *>(buf);
const int offs = reinterpret_cast<int>(addr);
(void)NVRAM.read_block(dst, offs, length);
}
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *src = static_cast<uint8_t *>(buf);
const int offs = reinterpret_cast<int>(addr);
(void)NVRAM.write_block(src, offs, length);
}
uint8_t hwReadConfig(const int addr)
{
return NVRAM.read(addr);
}
void hwWriteConfig(const int addr, uint8_t value)
{
(void)NVRAM.write(addr, value);
}
bool hwInit(void)
{
#ifdef MY_LOCK_MCU
#ifdef NRF51
// Lock MCU
if((uint32_t)((NRF_UICR->RBPCONF & UICR_RBPCONF_PALL_Msk) >> UICR_RBPCONF_PALL_Pos) !=
UICR_RBPCONF_PALL_Enabled) {
Flash.write((uint32_t *)&NRF_UICR->RBPCONF, (NRF_UICR->RBPCONF & ~UICR_RBPCONF_PALL_Msk));
hwReboot();
}
#else
// Lock MCU
if((uint32_t)((NRF_UICR->APPROTECT & UICR_APPROTECT_PALL_Msk) >> UICR_APPROTECT_PALL_Pos) !=
UICR_APPROTECT_PALL_Enabled) {
Flash.write((uint32_t *)&NRF_UICR->APPROTECT, (NRF_UICR->APPROTECT & ~UICR_APPROTECT_PALL_Msk));
hwReboot();
}
#endif
#endif
#if defined(NRF51) && defined(CONFIG_ENABLE_PINRESET)
// Enabling reset for NRF51 isn't handled by arduino-nrf5. Enable it, if requested.
NRF_POWER->RESET = POWER_RESET_RESET_Enabled;
NRF_POWER->RAMON |= (POWER_RAMON_ONRAM0_RAM0On << POWER_RAMON_ONRAM0_Pos) |
(POWER_RAMON_ONRAM1_RAM1On << POWER_RAMON_ONRAM1_Pos);
#endif
// Clock is manged by sleep modes. Radio depends on HFCLK.
// Force to start HFCLK
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
;
// Enable low latency sleep mode
NRF_POWER->TASKS_CONSTLAT = 1;
// Enable cache on >= NRF52
#ifndef NRF51
NRF_NVMC->ICACHECNF = NVMC_ICACHECNF_CACHEEN_Msk;
#endif
// Suspend UART
NRF_UART0->TASKS_STOPRX = 1;
NRF_UART0->TASKS_STOPTX = 1;
NRF_UART0->TASKS_SUSPEND = 1;
#ifdef MY_DISABLED_SERIAL
// Disable UART, when not configured
#ifdef NRF51
NRF_UART0->POWER = 0;
#endif
#else
// Configure UART
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {
}
#endif
#endif
return true;
}
static nrf_ecb_t hwRngData;
static int8_t hwRndDataReadPos = -1;
void hwRandomNumberInit(void)
{
// Start HWRNG
#ifdef NRF51
NRF_RNG->POWER = 1;
#endif
// Enable "more random" numbers
NRF_RNG->CONFIG = RNG_CONFIG_DERCEN_Enabled << RNG_CONFIG_DERCEN_Pos;
NRF_RNG->TASKS_START = 1;
NRF_RNG->EVENTS_VALRDY = 0;
uint32_t seed = 0;
for (uint8_t i = 0; i < 4; i++) {
// Wait for an random number
while (NRF_RNG->EVENTS_VALRDY == 0) {
yield();
}
seed = (seed << 8) | (uint32_t)NRF_RNG->VALUE;
NRF_RNG->EVENTS_VALRDY = 0;
}
randomSeed(seed);
// Fill ESB data structure for fast random data generation
uint8_t *ecbstruct = (uint8_t *)&hwRngData;
for (uint8_t i = 0; i<sizeof(hwRngData); i++) {
while (NRF_RNG->EVENTS_VALRDY == 0) {
yield();
}
*(ecbstruct + i) = NRF_RNG->VALUE;
NRF_RNG->EVENTS_VALRDY = 0;
}
hwRndDataReadPos = 0;
// Stop HWRNG
NRF_RNG->TASKS_STOP = 1;
#ifdef NRF51
NRF_RNG->POWER = 0;
#endif
}
ssize_t hwGetentropy(void *__buffer, size_t __length)
{
if (hwRndDataReadPos<0) {
// Not initialized
hwRandomNumberInit();
}
// cut length if > 256
if (__length > 256) {
__length = 256;
}
uint8_t *dst = (uint8_t *)__buffer;
// Start random number generator
for (size_t i = 0; i < __length; i++) {
dst[i] = hwRngData.ciphertext[hwRndDataReadPos & 0xfu];
MY_CRITICAL_SECTION {
if (hwRndDataReadPos >= ((int8_t)sizeof(hwRngData.ciphertext)-1))
{
// Retry until no error
bool need_data = true;
while (need_data) {
// Stop if another task is running
NRF_ECB->TASKS_STOPECB = 1;
NRF_ECB->EVENTS_ERRORECB = 0;
NRF_ECB->EVENTS_ENDECB = 0;
uint32_t ptrbackup = NRF_ECB->ECBDATAPTR;
NRF_ECB->ECBDATAPTR = (uint32_t)&hwRngData;
NRF_ECB->TASKS_STARTECB = 1;
while (!NRF_ECB->EVENTS_ENDECB);
NRF_ECB->ECBDATAPTR = ptrbackup;
if (NRF_ECB->EVENTS_ERRORECB == 0) {
need_data = false;
}
}
hwRndDataReadPos=0;
for (uint8_t pos = 0; pos < sizeof(hwRngData.ciphertext); pos++) {
hwRngData.cleartext[pos] ^= hwRngData.ciphertext[pos];
}
} else
{
hwRndDataReadPos++;
}
}
}
return __length;
}
void hwWatchdogReset(void)
{
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
}
void hwReboot(void)
{
NVIC_SystemReset();
while (true)
;
}
static volatile bool nrf5_rtc_event_triggered;
static volatile bool nrf5_pwr_hfclk;
void hwSleepPrepare(uint32_t ms)
{
// Enable low power sleep mode
NRF_POWER->TASKS_LOWPWR = 1;
// Reset RTC trigger flag
nrf5_rtc_event_triggered = false;
if (ms > 0) {
// Configure RTC
#ifdef NRF51
MY_HW_RTC->POWER = 1;
#endif
// Reset RTC
MY_HW_RTC->TASKS_CLEAR = 1;
// Calculate sleep time and prescaler
if (ms<512000) {
// prescaler 0, 30.517 μs resolution -> max 512 s sleep
MY_HW_RTC->PRESCALER = 0;
// Set compare register to 1/30.517 µs to guarantee event triggering
// A minimum of 2 ticks must be guaranteed
// (1000/32768)<<12 == 125
MY_HW_RTC->CC[0] = max(((ms << 12) / 125), 2);
} else {
// 8 Hz -> max 582.542 hours sleep.
MY_HW_RTC->PRESCALER = 4095;
// Set compare register to 1/125ms
// A minimum of 2 ticks must be guaranteed
MY_HW_RTC->CC[0] = max((ms / 125), 2);
}
MY_HW_RTC->INTENSET = RTC_INTENSET_COMPARE0_Msk;
MY_HW_RTC->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
MY_HW_RTC->EVENTS_COMPARE[0] = 0;
MY_HW_RTC->TASKS_START = 1;
NVIC_SetPriority(MY_HW_RTC_IRQN, 15);
NVIC_ClearPendingIRQ(MY_HW_RTC_IRQN);
NVIC_EnableIRQ(MY_HW_RTC_IRQN);
} else {
NRF_RTC1->TASKS_STOP = 1;
}
// Stop HFCLK
nrf5_pwr_hfclk = NRF_CLOCK->EVENTS_HFCLKSTARTED;
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
// Idle serial device
#ifndef MY_DISABLED_SERIAL
NRF_UART0->TASKS_STOPRX = 1;
NRF_UART0->TASKS_STOPTX = 1;
NRF_UART0->TASKS_SUSPEND = 1;
#endif
// Clear NVRAM log, if needed and a time frame of 4 seconds available
if (ms > 40000) {
// preserve some bytes for writing to NVRAM
NVRAM.clean_up(32);
}
}
void hwSleepEnd(uint32_t ms)
{
// Start HFCLK
if (nrf5_pwr_hfclk) {
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
;
// Enable low latency sleep mode
NRF_POWER->TASKS_CONSTLAT = 1;
}
if (ms > 0) {
// Stop RTC
#ifdef NRF51
MY_HW_RTC->POWER = 0;
#endif
MY_HW_RTC->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
MY_HW_RTC->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk;
MY_HW_RTC->TASKS_STOP = 1;
NVIC_DisableIRQ(MY_HW_RTC_IRQN);
} else {
// Start Arduino RTC for millis()
NRF_RTC1->TASKS_START = 1;
}
// Start serial device
#ifndef MY_DISABLED_SERIAL
NRF_UART0->TASKS_STARTRX = 1;
NRF_UART0->TASKS_STARTTX = 1;
#endif
}
// Halt CPU until next interrupt event
inline void hwWaitForInterrupt(void)
{
__DSB();
__WFI();
}
// Sleep in System ON mode
inline void hwSleep(void)
{
__WFE();
__SEV();
__WFE();
}
int8_t hwSleep(uint32_t ms)
{
hwSleepPrepare(ms);
while (nrf5_rtc_event_triggered == false) {
hwSleep();
}
hwSleepEnd(ms);
return MY_WAKE_UP_BY_TIMER;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
return hwSleep(interrupt, mode, INVALID_INTERRUPT_NUM, 0u, ms);
}
int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2,
uint8_t mode2, uint32_t ms)
{
// Disable interrupts until going to sleep, otherwise interrupts occurring
// between attachInterrupt()
// and sleep might cause the MCU to not wakeup from sleep as interrupt has
// already be handled!
MY_CRITICAL_SECTION {
// attach interrupts
_wakeUp1Interrupt = interrupt1;
_wakeUp2Interrupt = interrupt2;
if (interrupt1 != INVALID_INTERRUPT_NUM)
{
attachInterrupt(interrupt1, wakeUp1, mode1);
}
if (interrupt2 != INVALID_INTERRUPT_NUM)
{
attachInterrupt(interrupt2, wakeUp2, mode2);
}
// Reset attribute
_wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
}
// Prepare Timer and Hardware
hwSleepPrepare(ms);
// Sleep until timeout or interrupt
while ((nrf5_rtc_event_triggered == false) and
(_wokeUpByInterrupt == INVALID_INTERRUPT_NUM)) {
hwSleep();
}
// Assure any interrupts attached, will get detached when they did not occur.
if (interrupt1 != INVALID_INTERRUPT_NUM) {
detachInterrupt(interrupt1);
}
if (interrupt2 != INVALID_INTERRUPT_NUM) {
detachInterrupt(interrupt2);
}
// Wake up Hardware
hwSleepEnd(ms);
// Return what woke the mcu.
int8_t ret =
MY_WAKE_UP_BY_TIMER; // default: no interrupt triggered, timer wake up
if (_wokeUpByInterrupt != INVALID_INTERRUPT_NUM) {
ret = static_cast<int8_t>(_wokeUpByInterrupt);
}
// Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
_wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
return ret;
}
extern "C" {
// RTC interrupt handler
void MY_HW_RTC_IRQ_HANDLER(void)
{
if (MY_HW_RTC->EVENTS_COMPARE[0] > 0) {
nrf5_rtc_event_triggered = true;
NRF_RESET_EVENT(MY_HW_RTC->EVENTS_COMPARE[0]);
}
}
}
bool hwUniqueID(unique_id_t *uniqueID)
{
uint32_t *buffer = (uint32_t *)uniqueID;
buffer[0] = NRF_FICR->DEVICEID[0];
buffer[1] = NRF_FICR->DEVICEID[1];
buffer[2] = NRF_FICR->DEVICEADDR[0];
buffer[3] = NRF_FICR->DEVICEADDR[1];
return true;
}
uint16_t hwCPUVoltage(void)
{
// VDD is prescaled 1/3 and compared with the internal 1.2V reference
#if defined(NRF_ADC)
// NRF51:
// Sampling is done with lowest resolution to minimize the time
// 20uS@260uA
// Concurrent resource: disable
uint32_t lpcomp_enabled = NRF_LPCOMP->ENABLE;
NRF_LPCOMP->ENABLE = 0;
// Enable and configure ADC
NRF_ADC->ENABLE = 1;
NRF_ADC->CONFIG = (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos) |
(ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
(ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
(ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
(ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos);
NRF_ADC->EVENTS_END = 0;
NRF_ADC->TASKS_START = 1;
while(!NRF_ADC->EVENTS_END);
NRF_ADC->EVENTS_END = 0;
int32_t sample = (int32_t)NRF_ADC->RESULT;
NRF_ADC->TASKS_STOP = 1;
NRF_ADC->ENABLE = 0;
// Restore LPCOMP state
NRF_LPCOMP->ENABLE = lpcomp_enabled;
return (sample*3600)/255;
#elif defined(NRF_SAADC)
// NRF52:
// Sampling time 3uS@700uA
int32_t sample;
NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;
NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_8bit << SAADC_RESOLUTION_VAL_Pos;
NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_VDD << SAADC_CH_PSELP_PSELP_Pos;
NRF_SAADC->CH[0].CONFIG = (SAADC_CH_CONFIG_BURST_Disabled << SAADC_CH_CONFIG_BURST_Pos) |
(SAADC_CH_CONFIG_MODE_SE << SAADC_CH_CONFIG_MODE_Pos) |
(SAADC_CH_CONFIG_TACQ_3us << SAADC_CH_CONFIG_TACQ_Pos) |
(SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
(SAADC_CH_CONFIG_GAIN_Gain1_6 << SAADC_CH_CONFIG_GAIN_Pos) |
(SAADC_CH_CONFIG_RESN_Bypass << SAADC_CH_CONFIG_RESN_Pos) |
(SAADC_CH_CONFIG_RESP_Bypass << SAADC_CH_CONFIG_RESP_Pos);
NRF_SAADC->OVERSAMPLE = SAADC_OVERSAMPLE_OVERSAMPLE_Bypass << SAADC_OVERSAMPLE_OVERSAMPLE_Pos;
NRF_SAADC->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task << SAADC_SAMPLERATE_MODE_Pos;
NRF_SAADC->RESULT.MAXCNT = 1;
NRF_SAADC->RESULT.PTR = (uint32_t)&sample;
NRF_SAADC->EVENTS_STARTED = 0;
NRF_SAADC->TASKS_START = 1;
while (!NRF_SAADC->EVENTS_STARTED);
NRF_SAADC->EVENTS_STARTED = 0;
NRF_SAADC->EVENTS_END = 0;
NRF_SAADC->TASKS_SAMPLE = 1;
while (!NRF_SAADC->EVENTS_END);
NRF_SAADC->EVENTS_END = 0;
NRF_SAADC->EVENTS_STOPPED = 0;
NRF_SAADC->TASKS_STOP = 1;
while (!NRF_SAADC->EVENTS_STOPPED);
NRF_SAADC->EVENTS_STOPPED = 1;
NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);
return (sample*3600)/255;
#else
// unknown MCU
return 0;
#endif
}
uint16_t hwCPUFrequency(void)
{
#if defined(VARIANT_MCK)
return (VARIANT_MCK) / 100000UL;
#elif defined(F_CPU)
return (F_CPU) / 100000UL;
#else
return 160;
#endif
}
int8_t hwCPUTemperature(void)
{
return -127; // not implemented yet
}
uint16_t hwFreeMem(void)
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}

View File

@@ -0,0 +1,208 @@
/*
* 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-2019 Sensnology AB
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/MySensors/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.
*/
#ifndef MyHwNRF5_h
#define MyHwNRF5_h
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
// Define NRF5_SOFTDEVICE when SoftDevice found
#if defined(S110) || defined(S130) || defined(S132) || defined(S140)
#ifndef SOFTDEVICE_PRESENT
#define SOFTDEVICE_PRESENT
#endif
#endif
// Define ARDUINO_ARCH_NRF5, if not defined
#ifndef ARDUINO_ARCH_NRF5
#define ARDUINO_ARCH_NRF5
#endif
#include "hal/architecture/NRF5/drivers/nrf5_wiring_digital.c"
#include "hal/architecture/NRF5/drivers/wdt.h"
#include "hal/architecture/NRF5/drivers/nrf_temp.h"
#include "drivers/NVM/NVRAM.cpp"
#include "drivers/NVM/VirtualPage.cpp"
#include <avr/dtostrf.h>
#include <nrf.h>
#include <SPI.h>
// mapping
#ifndef strncpy_P
#define strncpy_P strncpy
#endif
#ifndef snprintf_P
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
#endif
#ifndef vsnprintf_P
#define vsnprintf_P vsnprintf
#endif
#ifndef printf_P
#define printf_P printf
#endif
// redefine 8 bit types of inttypes.h until fix of https://github.com/sandeepmistry/arduino-nRF5/issues/197
#undef PRId8
#undef PRIi8
#undef PRIo8
#undef PRIu8
#undef PRIx8
#undef PRIX8
#undef PRIdLEAST8
#undef PRIiLEAST8
#undef PRIoLEAST8
#undef PRIuLEAST8
#undef PRIxLEAST8
#undef PRIXLEAST8
#undef PRIdFAST8
#undef PRIiFAST8
#undef PRIoFAST8
#undef PRIuFAST8
#undef PRIxFAST8
#undef PRIXFAST8
#define PRId8 "hd"
#define PRIi8 "hi"
#define PRIo8 "ho"
#define PRIu8 "hu"
#define PRIx8 "hx"
#define PRIX8 "hX"
#define PRIdLEAST8 "hd"
#define PRIiLEAST8 "hi"
#define PRIoLEAST8 "ho"
#define PRIuLEAST8 "hu"
#define PRIxLEAST8 "hx"
#define PRIXLEAST8 "hX"
#define PRIdFAST8 "hd"
#define PRIiFAST8 "hi"
#define PRIoFAST8 "ho"
#define PRIuFAST8 "hu"
#define PRIxFAST8 "hx"
#define PRIXFAST8 "hX"
// Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
#define hwDigitalRead(__pin) digitalRead(__pin)
#define hwPinMode(__pin, __value) nrf5_pinMode(__pin, __value)
#define hwMillis() millis()
// TODO: Can nrf5 determine time slept?
#define hwGetSleepRemaining() (0ul)
bool hwInit(void);
void hwWatchdogReset(void);
void hwReboot(void);
void hwReadConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(const int addr);
void hwRandomNumberInit(void);
ssize_t hwGetentropy(void *__buffer, size_t __length);
#define MY_HW_HAS_GETENTROPY
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
/**
* Disable all interrupts.
* Helper function for MY_CRITICAL_SECTION.
*/
static __inline__ uint8_t __disableIntsRetVal(void)
{
__disable_irq();
return 1;
}
/**
* Restore priority mask register.
* Helper function for MY_CRITICAL_SECTION.
*/
static __inline__ void __priMaskRestore(const uint32_t *priMask)
{
__set_PRIMASK(*priMask);
}
/**
* Reset events and read back on nRF52
* http://infocenter.nordicsemi.com/pdf/nRF52_Series_Migration_v1.0.pdf
*/
#if __CORTEX_M == 0x04
#define NRF_RESET_EVENT(event) \
event = 0; \
(void)event
#else
#define NRF_RESET_EVENT(event) event = 0
#endif
/**
* RTC to use for hwSleep
* RTC1 is used by arduino-nRF5
* RTC2 is used on nRF52
* RTC0 is used on nRF51. This conflicts with SoftDevice!
*/
#ifdef NRF_RTC2
#define MY_HW_RTC NRF_RTC2
#define MY_HW_RTC_IRQ_HANDLER RTC2_IRQHandler
#define MY_HW_RTC_IRQN RTC2_IRQn
#else
#define MY_HW_RTC NRF_RTC0
#define MY_HW_RTC_IRQ_HANDLER RTC0_IRQHandler
#define MY_HW_RTC_IRQN RTC0_IRQn
#endif
/** Datastructure for AES ECB unit
*/
typedef struct {
/** AES Key
*/
uint8_t key[16];
/** Unencrypted data
*/
uint8_t cleartext[16];
/** Encrypted data
*/
uint8_t ciphertext[16];
} nrf_ecb_t;
#if !defined(DOXYGEN) && !defined(CPPCHECK)
#define MY_CRITICAL_SECTION \
for (uint32_t __savePriMask \
__attribute__((__cleanup__(__priMaskRestore))) = __get_PRIMASK(), \
__ToDo = __disableIntsRetVal(); \
__ToDo; __ToDo = 0)
#else
#define MY_CRITICAL_SECTION
#endif /* DOXYGEN || CPPCHECK */
#endif // #ifndef MyHwNRF5_h

View File

@@ -0,0 +1,39 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
// Initialize library and handle sketch functions like we want to
int main(void)
{
init();
#if defined(USBCON)
USBDevice.attach();
#endif
_begin(); // Startup MySensors library
for(;;) {
_process(); // Process incoming data
if (loop) {
loop(); // Call sketch loop
}
if (serialEventRun) {
serialEventRun();
}
}
return 0;
}

View File

@@ -0,0 +1,165 @@
/*
Flash.cpp - Flash library
Original Copyright (c) 2017 Frank Holtz. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "drivers/NVM/Flash.h"
#include <nrf.h>
FlashClass Flash;
uint32_t FlashClass::page_size() const
{
return (size_t)NRF_FICR->CODEPAGESIZE;
}
uint8_t FlashClass::page_size_bits() const
{
#if defined(NRF51)
return 10;
#elif defined(NRF52)
return 12;
#endif
}
uint32_t FlashClass::page_count() const
{
return (uint32_t)NRF_FICR->CODESIZE;
}
uint32_t FlashClass::specified_erase_cycles() const
{
return FLASH_ERASE_CYCLES;
}
uint32_t *FlashClass::page_address(size_t page)
{
return (uint32_t *)(page << page_size_bits());
}
uint32_t *FlashClass::top_app_page_address()
{
#if !defined(MCUBOOT_PRESENT)
// Bootcode at the top of the flash memory?
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.0.0%2Flib_bootloader.html
if (NRF_UICR->NRFFW[0]<0xFFFFFFFF) {
// Return pointer calculated by SoftDevice/bootloader
return (uint32_t *)NRF_UICR->NRFFW[0];
}
#endif
// Return flash length
return (uint32_t *)(Flash.page_count() << Flash.page_size_bits());
}
void FlashClass::erase(uint32_t *address, size_t size)
{
size_t end_address = (size_t)address + size;
// align address
address =
(uint32_t *)((size_t)address & (size_t)((size_t)(~0) - FLASH_PAGE_SIZE));
// Wrong parameters?
if ((size_t)address >= end_address) {
return;
}
// get old nvm controller state
uint32_t old_config = NRF_NVMC->CONFIG;
// Enable erasing flash
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
// Erase page(s)
while ((size_t)address < end_address) {
wait_for_ready();
// Erase one 1k/4k page
NRF_NVMC->ERASEPAGE = (size_t)(address);
address = (uint32_t *)((size_t)address + FLASH_PAGE_SIZE);
}
// Disable erasing
wait_for_ready();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
// Restore old state
wait_for_ready();
NRF_NVMC->CONFIG = old_config;
// Go back if controller is ready
wait_for_ready();
}
void FlashClass::erase_all()
{
// Enable erasing flash
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
// Erase Flash and UICR
NRF_NVMC->ERASEALL = 1;
wait_for_ready();
// Disable erasing
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
}
void FlashClass::write(uint32_t *address, uint32_t value)
{
// Compare word
if (*address != value) {
// Enable write
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
// Write word
*address = value;
// Disable write
wait_for_ready();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
}
}
void FlashClass::write_block(uint32_t *dst_address, uint32_t *src_address,
uint16_t word_count)
{
// Enable write
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
while (word_count > 0) {
// cppcheck-suppress duplicateConditionalAssign
if (*dst_address != *src_address) {
*dst_address = *src_address;
}
word_count--;
dst_address++;
src_address++;
}
// Disable write
wait_for_ready();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
}
void FlashClass::wait_for_ready()
{
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
};
}

View File

@@ -0,0 +1,42 @@
/*
* nRF5 output modes
*/
// Standard 0, Standard 1
#ifndef OUTPUT_S0S1
#define OUTPUT_S0S1 (0x10)
#endif
// High Drive 0, Standard 1
#ifndef OUTPUT_H0S1
#define OUTPUT_H0S1 (0x11)
#endif
// Standard 0, High Drive 1
#ifndef OUTPUT_S0H1
#define OUTPUT_S0H1 (0x12)
#endif
// High Drive both
#ifndef OUTPUT_H0H1
#define OUTPUT_H0H1 (0x13)
#endif
// Disconnected 0, Standard 1
#ifndef OUTPUT_D0S1
#define OUTPUT_D0S1 (0x14)
#endif
// Disconnected 0, High Drive 1
#ifndef OUTPUT_D0H1
#define OUTPUT_D0H1 (0x15)
#endif
// Standard 0, Disconnected 1
#ifndef OUTPUT_S0D1
#define OUTPUT_S0D1 (0x16)
#endif
// High Drive 0, Disconnected 1
#ifndef OUTPUT_H0D1
#define OUTPUT_H0D1 (0x17)
#endif

View File

@@ -0,0 +1,102 @@
/*
Copyright (c) 2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2017 Frank Holtz All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrf.h"
#include "Arduino.h"
#include "nrf5_wiring_constants.h"
#ifdef __cplusplus
extern "C" {
#endif
void nrf5_pinMode(uint32_t ulPin, uint32_t ulMode)
{
if (ulPin >= PINS_COUNT) {
return;
}
#ifdef ARDUINO_ARCH_NRF52
// Arduino: https://github.com/arduino-org/arduino-core-nrf52
ulPin = g_APinDescription[ulPin].ulPin;
#else
// Sandeep Mistry: https://github.com/sandeepmistry/arduino-nRF5
ulPin = g_ADigitalPinMap[ulPin];
#endif
// Set pin mode according to chapter '22.6.3 I/O Pin Configuration'
switch (ulMode) {
case INPUT:
// Set pin to input mode
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
case INPUT_PULLUP:
// Set pin to input mode with pull-up resistor enabled
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
case INPUT_PULLDOWN:
// Set pin to input mode with pull-down resistor enabled
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
case OUTPUT:
// Set pin to output mode
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
default:
// calculate nRF specific output modes
if ((ulMode >= OUTPUT_S0S1) && (ulMode <= OUTPUT_H0D1)) {
// Set pin to given output mode
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)(ulMode - OUTPUT_S0S1) << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
}
break;
}
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2012 - 2018, Nordic Semiconductor ASA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NRF_TEMP_H__
#define NRF_TEMP_H__
#ifdef __cplusplus
extern "C" {
#endif
#define MASK_SIGN (0x00000200UL) //!< MASK_SIGN
#define MASK_SIGN_EXTENSION (0xFFFFFC00UL) //!< MASK_SIGN_EXTENSION
/**
* @brief Function for preparing the temp module for temperature measurement.
*
* This function initializes the TEMP module and writes to the hidden configuration register.
*/
static __INLINE void nrf_temp_init(void)
{
/**@note Workaround for PAN_028 rev2.0A anomaly 31 - TEMP: Temperature offset value has to be manually loaded to the TEMP module */
*(uint32_t *) 0x4000C504 = 0;
}
/**
* @brief Function for reading temperature measurement.
*
* The function reads the 10 bit 2's complement value and transforms it to a 32 bit 2's complement value.
*/
static __INLINE int32_t nrf_temp_read(void)
{
/**@note Workaround for PAN_028 rev2.0A anomaly 28 - TEMP: Negative measured values are not represented correctly */
return ((NRF_TEMP->TEMP & MASK_SIGN) != 0) ? (int32_t)(NRF_TEMP->TEMP | MASK_SIGN_EXTENSION) :
(NRF_TEMP->TEMP);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,186 @@
/* Copyright (c) 2002, 2004 Marek Michalkiewicz
Copyright (c) 2005, 2006, 2007 Eric B. Weddington
Copyright (c) 2016 Frank Holtz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* $Id$ */
/*
avr/wdt.h - macros for AVR watchdog timer
*/
#ifndef _NRF5_WDT_H_
#define _NRF5_WDT_H_
#include <nrf.h>
#include <stdint.h>
/** \file */
/** \defgroup avr_watchdog <avr/wdt.h>: Watchdog timer handling
\code #include <avr/wdt.h> \endcode
@ingroup internals
This header file declares the interface to some inline macros
handling the watchdog timer like present in many AVR devices.
In order to prevent the watchdog timer configuration from being
accidentally altered by a crashing application, a special timed
sequence is required in order to change it. The macros within
this header file handle the required sequence automatically
before changing any value. Interrupts will be disabled during
the manipulation.
*/
/**
\ingroup avr_watchdog
Reset the watchdog timer. When the watchdog timer is enabled,
a call to this instruction is required before the timer expires,
otherwise a watchdog-initiated device reset will occur.
*/
#define wdt_reset() NRF_WDT->RR[0] = WDT_RR_RR_Reload
/**
\ingroup avr_watchdog
Enable the watchdog timer, configuring it for expiry after
\c timeout (ms).
The WDT is running in sleep mode.
See also the symbolic constants \c WDTO_15MS et al.
*/
#define wdt_enable(timeout) \
NRF_WDT->CONFIG = NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | ( WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos); \
NRF_WDT->CRV = (32768*timeout)/1000; \
NRF_WDT->RREN |= WDT_RREN_RR0_Msk; \
NRF_WDT->TASKS_START = 1
/**
\ingroup avr_watchdog
Disable the watchdog timer. On nRF5 the timer cannot disabled.
The period is set to 36h of CPU run time. The WDT is stopped in sleep mode.
*/
#define wdt_disable() \
NRF_WDT->CONFIG = NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | ( WDT_CONFIG_SLEEP_Pause << WDT_CONFIG_SLEEP_Pos); \
NRF_WDT->CRV = 4294967295
/**
\ingroup avr_watchdog
Symbolic constants for the watchdog timeout.
Possible timeout values are: 15 ms, 30 ms, 60 ms, 120 ms, 250 ms,
500 ms, 1 s, 2 s, 4s, 8s. (Not all devices allow 4 s or 8 s.)
Symbolic constants are formed by the prefix
\c WDTO_, followed by the time.
Example that would select a watchdog timer expiry of approximately
500 ms:
\code
wdt_enable(WDTO_500MS);
\endcode
*/
#define WDTO_15MS 15
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_30MS 30
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_60MS 60
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_120MS 120
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_250MS 250
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_500MS 500
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_1S 1000
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_2S 2000
/** \ingroup avr_watchdog
See \c WDTO_15MS
Note: This is only available on the
ATtiny2313,
ATtiny24, ATtiny44, ATtiny84, ATtiny84A,
ATtiny25, ATtiny45, ATtiny85,
ATtiny261, ATtiny461, ATtiny861,
ATmega48, ATmega88, ATmega168,
ATmega48P, ATmega88P, ATmega168P, ATmega328P,
ATmega164P, ATmega324P, ATmega644P, ATmega644,
ATmega640, ATmega1280, ATmega1281, ATmega2560, ATmega2561,
ATmega8HVA, ATmega16HVA, ATmega32HVB,
ATmega406, ATmega1284P,
AT90PWM1, AT90PWM2, AT90PWM2B, AT90PWM3, AT90PWM3B, AT90PWM216, AT90PWM316,
AT90PWM81, AT90PWM161,
AT90USB82, AT90USB162,
AT90USB646, AT90USB647, AT90USB1286, AT90USB1287,
ATtiny48, ATtiny88,
nRF51822, nRF52832
*/
#define WDTO_4S 4000
/** \ingroup avr_watchdog
See \c WDTO_15MS
Note: This is only available on the
ATtiny2313,
ATtiny24, ATtiny44, ATtiny84, ATtiny84A,
ATtiny25, ATtiny45, ATtiny85,
ATtiny261, ATtiny461, ATtiny861,
ATmega48, ATmega48A, ATmega48PA, ATmega88, ATmega168,
ATmega48P, ATmega88P, ATmega168P, ATmega328P,
ATmega164P, ATmega324P, ATmega644P, ATmega644,
ATmega640, ATmega1280, ATmega1281, ATmega2560, ATmega2561,
ATmega8HVA, ATmega16HVA, ATmega32HVB,
ATmega406, ATmega1284P,
ATmega2564RFR2, ATmega256RFR2, ATmega1284RFR2, ATmega128RFR2, ATmega644RFR2, ATmega64RFR2
AT90PWM1, AT90PWM2, AT90PWM2B, AT90PWM3, AT90PWM3B, AT90PWM216, AT90PWM316,
AT90PWM81, AT90PWM161,
AT90USB82, AT90USB162,
AT90USB646, AT90USB647, AT90USB1286, AT90USB1287,
ATtiny48, ATtiny88,
ATxmega16a4u, ATxmega32a4u,
ATxmega16c4, ATxmega32c4,
ATxmega128c3, ATxmega192c3, ATxmega256c3,
nRF51822, nRF52832
*/
#define WDTO_8S 8000
#endif /* _NRF5_WDT_H_ */

View File

@@ -0,0 +1,256 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwSAMD.h"
/*
int8_t pinIntTrigger = 0;
void wakeUp() //place to send the interrupts
{
pinIntTrigger = 1;
}
void wakeUp2() //place to send the second interrupts
{
pinIntTrigger = 2;
}
// Watchdog Timer interrupt service routine. This routine is required
// to allow automatic WDIF and WDIE bit clearance in hardware.
ISR (WDT_vect)
{
// WDIE & WDIF is cleared in hardware upon entering this ISR
wdt_disable();
}
*/
void hwReadConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *dst = static_cast<uint8_t *>(buf);
const int offs = reinterpret_cast<int>(addr);
(void)eep.read(offs, dst, length);
}
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *src = static_cast<uint8_t *>(buf);
const int offs = reinterpret_cast<int>(addr);
// use update() instead of write() to reduce e2p wear off
(void)eep.update(offs, src, length);
}
uint8_t hwReadConfig(const int addr)
{
return eep.read(addr);
}
void hwWriteConfig(const int addr, uint8_t value)
{
(void)eep.update(addr, value);
}
bool hwInit(void)
{
#if !defined(MY_DISABLED_SERIAL)
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {}
#endif
#endif
SYSCTRL->VREF.reg |= SYSCTRL_VREF_TSEN; // Enable the temperature sensor
while (ADC->STATUS.bit.SYNCBUSY ==
1); // Wait for synchronization of registers between the clock domains
const uint8_t eepInit = eep.begin(MY_EXT_EEPROM_TWI_CLOCK, &Wire);
#if defined(SENSEBENDER_GW_SAMD_V1)
// check connection to external EEPROM - only sensebender GW
return eepInit==0;
#else
(void)eepInit;
return true;
#endif
}
void hwWatchdogReset(void)
{
// TODO: Not supported!
}
void hwReboot(void)
{
NVIC_SystemReset();
while (true);
}
int8_t hwSleep(uint32_t ms)
{
// TODO: Not supported!
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
// TODO: Not supported!
(void)interrupt;
(void)mode;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
// TODO: Not supported!
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
bool hwUniqueID(unique_id_t *uniqueID)
{
(void)memcpy((uint8_t *)uniqueID, (uint32_t *)0x0080A00C, 4);
(void)memcpy((uint8_t *)uniqueID + 4, (uint32_t *)0x0080A040, 12);
return true;
}
// Wait for synchronization of registers between the clock domains
static __inline__ void syncADC() __attribute__((always_inline, unused));
static void syncADC()
{
while (ADC->STATUS.bit.SYNCBUSY);
}
uint16_t hwCPUVoltage(void)
{
// Set ADC reference to internal 1v
ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val;
syncADC();
// Set to 10 bits reading resolution
ADC->CTRLB.reg = ADC_CTRLB_RESSEL_10BIT | ADC_CTRLB_PRESCALER_DIV256;
syncADC();
// Select MUXPOS as SCALEDIOVCC/4 channel, and MUXNEG as internal ground
ADC->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC_Val;
ADC->INPUTCTRL.bit.MUXNEG = ADC_INPUTCTRL_MUXNEG_GND_Val;
syncADC();
// enable ADC
ADC->CTRLA.bit.ENABLE = 1;
syncADC();
// start conversion
ADC->SWTRIG.bit.START = 1;
// clear the Data Ready flag
ADC->INTFLAG.bit.RESRDY = 1;
syncADC();
// start conversion again, since The first conversion after the reference is changed must not be used.
ADC->SWTRIG.bit.START = 1;
// waiting for conversion to complete
while (!ADC->INTFLAG.bit.RESRDY);
syncADC();
const uint32_t valueRead = ADC->RESULT.reg;
// disable ADC
ADC->CTRLA.bit.ENABLE = 0;
syncADC();
// value is 1/4 scaled, multiply by 4
return valueRead * 4;
}
uint16_t hwCPUFrequency(void)
{
// TODO: currently reporting compile time frequency (in 1/10MHz)
return F_CPU / 100000UL;
}
int8_t hwCPUTemperature(void)
{
// taken from https://github.com/arduino/ArduinoCore-samd/pull/277
// Set to 12 bits resolution
ADC->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT | ADC_CTRLB_PRESCALER_DIV256;
syncADC();
// Ensure we are sampling slowly
ADC->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(0x3f);
syncADC();
// Set ADC reference to internal 1v
ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val;
syncADC();
// Select MUXPOS as temperature channel, and MUXNEG as internal ground
ADC->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_TEMP_Val;
ADC->INPUTCTRL.bit.MUXNEG = ADC_INPUTCTRL_MUXNEG_GND_Val;
syncADC();
// Enable ADC
ADC->CTRLA.bit.ENABLE = 1;
syncADC();
// Start ADC conversion
ADC->SWTRIG.bit.START = 1;
// Clear the Data Ready flag
ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY;
syncADC();
// Start conversion again, since The first conversion after the reference is changed must not be used.
ADC->SWTRIG.bit.START = 1;
// Wait until ADC conversion is done
while (!(ADC->INTFLAG.bit.RESRDY));
syncADC();
// Get result
// This is signed so that the math later is done signed
const int32_t adcReading = ADC->RESULT.reg;
// Clear result ready flag
ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY;
syncADC();
// Disable ADC
ADC->CTRLA.bit.ENABLE = 0;
syncADC();
// Factory room temperature readings
const uint8_t roomInteger = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_INT_ADDR &
FUSES_ROOM_TEMP_VAL_INT_Msk)
>> FUSES_ROOM_TEMP_VAL_INT_Pos;
const uint8_t roomDecimal = (*(uint32_t *)FUSES_ROOM_TEMP_VAL_DEC_ADDR &
FUSES_ROOM_TEMP_VAL_DEC_Msk)
>> FUSES_ROOM_TEMP_VAL_DEC_Pos;
const int32_t roomReading = ((*(uint32_t *)FUSES_ROOM_ADC_VAL_ADDR & FUSES_ROOM_ADC_VAL_Msk) >>
FUSES_ROOM_ADC_VAL_Pos);
const int32_t roomTemperature = 1000 * roomInteger + 100 * roomDecimal;
// Factory hot temperature readings
const uint8_t hotInteger = (*(uint32_t *)FUSES_HOT_TEMP_VAL_INT_ADDR & FUSES_HOT_TEMP_VAL_INT_Msk)
>>
FUSES_HOT_TEMP_VAL_INT_Pos;
const uint8_t hotDecimal = (*(uint32_t *)FUSES_HOT_TEMP_VAL_DEC_ADDR & FUSES_HOT_TEMP_VAL_DEC_Msk)
>>
FUSES_HOT_TEMP_VAL_DEC_Pos;
const int32_t hotReading = ((*(uint32_t *)FUSES_HOT_ADC_VAL_ADDR & FUSES_HOT_ADC_VAL_Msk) >>
FUSES_HOT_ADC_VAL_Pos);
const int32_t hotTemperature = 1000 * hotInteger + 100 * hotDecimal;
// Linear interpolation of temperature using factory room temperature and hot temperature
const int32_t temperature = roomTemperature + ((hotTemperature - roomTemperature) *
(adcReading - roomReading)) / (hotReading - roomReading);
return static_cast<int8_t>(((temperature / 1000) - MY_SAMD_TEMPERATURE_OFFSET) /
MY_SAMD_TEMPERATURE_GAIN);
}
uint16_t hwFreeMem(void)
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}

View File

@@ -0,0 +1,144 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef MyHwSAMD_h
#define MyHwSAMD_h
#include <SPI.h>
#include <avr/dtostrf.h>
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE SerialUSB
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
#ifndef MY_SAMD_TEMPERATURE_OFFSET
#define MY_SAMD_TEMPERATURE_OFFSET (0.0f)
#endif
#ifndef MY_SAMD_TEMPERATURE_GAIN
#define MY_SAMD_TEMPERATURE_GAIN (1.0f)
#endif
// defines for sensebender gw variant.h
#define MY_EXT_EEPROM_I2C_ADDRESS (0x50u)
#define MY_EXT_EEPROM_SIZE (kbits_512)
#define MY_EXT_EEPROM_PAGE_SIZE (32u)
extEEPROM eep(MY_EXT_EEPROM_SIZE, 1, MY_EXT_EEPROM_PAGE_SIZE,
MY_EXT_EEPROM_I2C_ADDRESS); //device size, number of devices, page size
#define MY_EXT_EEPROM_TWI_CLOCK (eep.twiClock100kHz) // can be set to 400kHz with precaution if other i2c devices on bus
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
#define vsnprintf_P(s, n, f, ...) vsnprintf((s), (n), (f), __VA_ARGS__)
// redefine 8 bit types of inttypes.h (as of SAMD board defs 1.8.1)
#undef PRId8
#undef PRIi8
#undef PRIo8
#undef PRIu8
#undef PRIx8
#undef PRIX8
#undef PRIdLEAST8
#undef PRIiLEAST8
#undef PRIoLEAST8
#undef PRIuLEAST8
#undef PRIxLEAST8
#undef PRIXLEAST8
#undef PRIdFAST8
#undef PRIiFAST8
#undef PRIoFAST8
#undef PRIuFAST8
#undef PRIxFAST8
#undef PRIXFAST8
#define PRId8 "d"
#define PRIi8 "i"
#define PRIo8 "o"
#define PRIu8 "u"
#define PRIx8 "x"
#define PRIX8 "X"
#define PRIdLEAST8 "d"
#define PRIiLEAST8 "i"
#define PRIoLEAST8 "o"
#define PRIuLEAST8 "u"
#define PRIxLEAST8 "x"
#define PRIXLEAST8 "X"
#define PRIdFAST8 "d"
#define PRIiFAST8 "i"
#define PRIoFAST8 "o"
#define PRIuFAST8 "u"
#define PRIxFAST8 "x"
#define PRIXFAST8 "X"
// Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
#define hwDigitalRead(__pin) digitalRead(__pin)
#define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwMillis() millis()
#define hwRandomNumberInit() randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN))
#define hwGetSleepRemaining() (0ul)
bool hwInit(void);
void hwWatchdogReset(void);
void hwReboot(void);
void hwReadConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(const int addr);
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
/**
* Disable all interrupts.
* Helper function for MY_CRITICAL_SECTION.
*/
static __inline__ uint8_t __disableIntsRetVal(void)
{
__disable_irq();
return 1;
}
/**
* Restore priority mask register.
* Helper function for MY_CRITICAL_SECTION.
*/
static __inline__ void __priMaskRestore(const uint32_t *priMask)
{
__set_PRIMASK(*priMask);
}
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION for ( uint32_t __savePriMask __attribute__((__cleanup__(__priMaskRestore))) = __get_PRIMASK(), __ToDo = __disableIntsRetVal(); __ToDo ; __ToDo = 0 )
#endif /* DOXYGEN */
#endif // #ifdef ARDUINO_ARCH_SAMD

View File

@@ -0,0 +1,43 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
// Initialize library and handle sketch functions like we want to
extern "C" void __libc_init_array(void);
int main(void)
{
init();
#if defined(USBCON)
__libc_init_array();
USBDevice.init();
USBDevice.attach();
#endif
_begin(); // Startup MySensors library
for(;;) {
_process(); // Process incoming data
if (loop) {
loop(); // Call sketch loop
}
if (serialEventRun) {
serialEventRun();
}
}
return 0;
}

View File

@@ -0,0 +1,194 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwSTM32F1.h"
/*
* Pinout STM32F103C8 dev board:
* http://wiki.stm32duino.com/images/a/ae/Bluepillpinout.gif
*
* Wiring RFM69 radio / SPI1
* --------------------------------------------------
* CLK PA5
* MISO PA6
* MOSI PA7
* CSN PA4
* CE NA
* IRQ PA3 (default)
*
* Wiring RF24 radio / SPI1
* --------------------------------------------------
* CLK PA5
* MISO PA6
* MOSI PA7
* CSN PA4
* CE PB0 (default)
* IRQ NA
*
*/
bool hwInit(void)
{
#if !defined(MY_DISABLED_SERIAL)
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {}
#endif
#endif
if (EEPROM.init() == EEPROM_OK) {
uint16 cnt;
EEPROM.count(&cnt);
if(cnt>=EEPROM.maxcount()) {
// tmp, WIP: format eeprom if full
EEPROM.format();
}
return true;
}
return false;
}
void hwReadConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *dst = static_cast<uint8_t *>(buf);
int pos = reinterpret_cast<int>(addr);
while (length-- > 0) {
*dst++ = EEPROM.read(pos++);
}
}
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
{
uint8_t *src = static_cast<uint8_t *>(buf);
int pos = reinterpret_cast<int>(addr);
while (length-- > 0) {
EEPROM.write(pos++, *src++);
}
}
uint8_t hwReadConfig(const int addr)
{
uint8_t value;
hwReadConfigBlock(&value, reinterpret_cast<void *>(addr), 1);
return value;
}
void hwWriteConfig(const int addr, uint8_t value)
{
hwWriteConfigBlock(&value, reinterpret_cast<void *>(addr), 1);
}
int8_t hwSleep(uint32_t ms)
{
// TODO: Not supported!
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
// TODO: Not supported!
(void)interrupt;
(void)mode;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
// TODO: Not supported!
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
void hwRandomNumberInit(void)
{
// use internal temperature sensor as noise source
adc_reg_map *regs = ADC1->regs;
regs->CR2 |= ADC_CR2_TSVREFE;
regs->SMPR1 |= ADC_SMPR1_SMP16;
uint32_t seed = 0;
uint16_t currentValue = 0;
uint16_t newValue = 0;
for (uint8_t i = 0; i < 32; i++) {
const uint32_t timeout = hwMillis() + 20;
while (timeout >= hwMillis()) {
newValue = adc_read(ADC1, 16);
if (newValue != currentValue) {
currentValue = newValue;
break;
}
}
seed ^= ( (newValue + hwMillis()) & 7) << i;
}
randomSeed(seed);
regs->CR2 &= ~ADC_CR2_TSVREFE; // disable VREFINT and temp sensor
}
bool hwUniqueID(unique_id_t *uniqueID)
{
(void)memcpy((uint8_t *)uniqueID, (uint32_t *)0x1FFFF7E0, 16); // FlashID + ChipID
return true;
}
uint16_t hwCPUVoltage(void)
{
adc_reg_map *regs = ADC1->regs;
regs->CR2 |= ADC_CR2_TSVREFE; // enable VREFINT and temp sensor
regs->SMPR1 = ADC_SMPR1_SMP17; // sample rate for VREFINT ADC channel
adc_calibrate(ADC1);
const uint16_t vdd = adc_read(ADC1, 17);
regs->CR2 &= ~ADC_CR2_TSVREFE; // disable VREFINT and temp sensor
return (uint16_t)(1200u * 4096u / vdd);
}
uint16_t hwCPUFrequency(void)
{
return F_CPU/100000UL;
}
int8_t hwCPUTemperature(void)
{
adc_reg_map *regs = ADC1->regs;
regs->CR2 |= ADC_CR2_TSVREFE; // enable VREFINT and Temperature sensor
regs->SMPR1 |= ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
adc_calibrate(ADC1);
//const uint16_t adc_temp = adc_read(ADC1, 16);
//const uint16_t vref = 1200 * 4096 / adc_read(ADC1, 17);
// calibrated at 25°C, ADC output = 1430mV, avg slope = 4.3mV / °C, increasing temp ~ lower voltage
const int8_t temp = static_cast<int8_t>((1430.0 - (adc_read(ADC1, 16) * 1200 / adc_read(ADC1,
17))) / 4.3 + 25.0);
regs->CR2 &= ~ADC_CR2_TSVREFE; // disable VREFINT and temp sensor
return (temp - MY_STM32F1_TEMPERATURE_OFFSET) / MY_STM32F1_TEMPERATURE_GAIN;
}
uint16_t hwFreeMem(void)
{
//Not yet implemented
return FUNCTION_NOT_SUPPORTED;
}

View File

@@ -0,0 +1,93 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#ifndef MyHwSTM32F1_h
#define MyHwSTM32F1_h
#include <libmaple/iwdg.h>
#include <itoa.h>
#include <EEPROM.h>
#include <SPI.h>
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
#ifndef MY_STM32F1_TEMPERATURE_OFFSET
#define MY_STM32F1_TEMPERATURE_OFFSET (0.0f)
#endif
#ifndef MY_STM32F1_TEMPERATURE_GAIN
#define MY_STM32F1_TEMPERATURE_GAIN (1.0f)
#endif
// SS default
#ifndef SS
#define SS PA4
#endif
// mapping
#define snprintf_P snprintf
#define vsnprintf_P vsnprintf
#define strncpy_P strncpy
#define printf_P printf
#define yield() // not defined
#ifndef digitalPinToInterrupt
#define digitalPinToInterrupt(__pin) (__pin)
#endif
#define hwDigitalWrite(__pin, __value) digitalWrite(__pin, __value)
#define hwDigitalRead(__pin) digitalRead(__pin)
#define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwWatchdogReset() iwdg_feed()
#define hwReboot() nvic_sys_reset()
#define hwMillis() millis()
#define hwGetSleepRemaining() (0ul)
extern void serialEventRun(void) __attribute__((weak));
bool hwInit(void);
void hwRandomNumberInit(void);
void hwReadConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfigBlock(void *buf, void *addr, size_t length);
void hwWriteConfig(const int addr, uint8_t value);
uint8_t hwReadConfig(const int addr);
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
#ifndef DOXYGEN
#define MY_CRITICAL_SECTION
#endif /* DOXYGEN */
#endif

View File

@@ -0,0 +1,41 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
// Force init to be called *first*, i.e. before static object allocation.
// Otherwise, statically allocated objects that need libmaple may fail.
__attribute__(( constructor (101))) void premain()
{
init();
}
// Initialize library and handle sketch functions like we want to
int main(void)
{
_begin(); // Startup MySensors library
for(;;) {
_process(); // Process incoming data
if (loop) { // Call sketch loop
loop();
}
if (serialEventRun) {
serialEventRun();
}
}
return 0;
}

View File

@@ -0,0 +1,174 @@
/**
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
#include "MyHwTeensy3.h"
/*
int8_t pinIntTrigger = 0;
void wakeUp() //place to send the interrupts
{
pinIntTrigger = 1;
}
void wakeUp2() //place to send the second interrupts
{
pinIntTrigger = 2;
}
// Watchdog Timer interrupt service routine. This routine is required
// to allow automatic WDIF and WDIE bit clearance in hardware.
ISR (WDT_vect)
{
// WDIE & WDIF is cleared in hardware upon entering this ISR
wdt_disable();
}
*/
bool hwInit(void)
{
#if !defined(MY_DISABLED_SERIAL)
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {}
#endif
#endif
return true;
}
void hwWatchdogReset(void)
{
// TODO: Not supported!
}
void hwReboot(void)
{
SCB_AIRCR = 0x05FA0004;
while (true);
}
int8_t hwSleep(uint32_t ms)
{
// TODO: Not supported!
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
{
// TODO: Not supported!
(void)interrupt;
(void)mode;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
const uint8_t mode2,
uint32_t ms)
{
// TODO: Not supported!
(void)interrupt1;
(void)mode1;
(void)interrupt2;
(void)mode2;
(void)ms;
return MY_SLEEP_NOT_POSSIBLE;
}
bool hwUniqueID(unique_id_t *uniqueID)
{
#if defined(__MKL26Z64__)
(void)memcpy((uint8_t *)uniqueID, &SIM_UIDMH, 12);
(void)memset((uint8_t *)uniqueID + 12, MY_HWID_PADDING_BYTE, 4);
#else
(void)memcpy((uint8_t *)uniqueID, &SIM_UIDH, 16);
#endif
return true;
}
uint16_t hwCPUVoltage(void)
{
analogReference(DEFAULT);
analogReadResolution(12);
analogReadAveraging(32);
#if defined(__MK20DX128__) || defined(__MK20DX256__)
// Teensy 3.0/3.1/3.2
return (uint16_t)(1195u * 4096u / analogRead(39));
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
// Teensy 3.6
return (uint16_t)(1195u * 4096u / analogRead(71));
#elif defined(__MKL26Z64__)
// Teensy LC
// not supported
return FUNCTION_NOT_SUPPORTED;
#else
// not supported
return FUNCTION_NOT_SUPPORTED;
#endif
}
uint16_t hwCPUFrequency(void)
{
// TODO: currently reporting compile time frequency (in 1/10MHz)
return F_CPU / 100000UL;
}
int8_t hwCPUTemperature(void)
{
return -127; // not implemented yet
}
uint16_t hwFreeMem(void)
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}
#if defined(MY_HW_HAS_GETENTROPY)
ssize_t hwGetentropy(void *__buffer, const size_t __length)
{
SIM_SCGC6 |= SIM_SCGC6_RNGA; // enable RNG
RNG_CR &= ~RNG_CR_SLP_MASK;
RNG_CR |= RNG_CR_HA_MASK; //high assurance, not needed
size_t pos = 0;
while (pos < __length) {
RNG_CR |= RNG_CR_GO_MASK;
while (!(RNG_SR & RNG_SR_OREG_LVL(0xF)));
const uint32_t rndVar = RNG_OR;
const uint8_t bsize = (__length - pos) > sizeof(rndVar) ? sizeof(rndVar) : (__length - pos);
(void)memcpy((uint8_t *)__buffer + pos, &rndVar, bsize);
pos += bsize;
}
SIM_SCGC6 &= ~SIM_SCGC6_RNGA; // disable RNG
return pos;
}
#endif
void hwRandomNumberInit(void)
{
#if defined(MY_HW_HAS_GETENTROPY)
// use HW RNG present on Teensy3.5/3.6
// init RNG
uint32_t seed = 0;
hwGetentropy(&seed, sizeof(seed));
randomSeed(seed);
#else
randomSeed(analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN));
#endif
}

View File

@@ -0,0 +1,91 @@
/**
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* Radio wiring Teensy3.x: RF24, RFM69, RFM95:
* MISO 12
* MOSI 11
* SCK 13
* CSN 10
* CE 9 (RF24)
* IRQ 8 (opt. RF24, RFM69, RFM95)
*/
#ifndef MyHwTeensy3_h
#define MyHwTeensy3_h
#include <SPI.h>
#include "util/atomic.h"
#ifdef __cplusplus
#include <Arduino.h>
#endif
#define CRYPTO_LITTLE_ENDIAN
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
#ifndef MY_DEBUGDEVICE
#define MY_DEBUGDEVICE MY_SERIALDEVICE
#endif
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define RNG_CR_GO_MASK 0x1u
#define RNG_CR_HA_MASK 0x2u
#define RNG_CR_INTM_MASK 0x4u
#define RNG_CR_CLRI_MASK 0x8u
#define RNG_CR_SLP_MASK 0x10u
#define RNG_SR_OREG_LVL_MASK 0xFF00u
#define RNG_SR_OREG_LVL_SHIFT 8
#define RNG_SR_OREG_LVL(x) (((uint32_t)(((uint32_t)(x))<<RNG_SR_OREG_LVL_SHIFT))&RNG_SR_OREG_LVL_MASK)
#define SIM_SCGC6_RNGA ((uint32_t)0x00000200)
#endif
// Define these as macros to save valuable space
#define hwDigitalWrite(__pin, __value) digitalWriteFast(__pin, __value)
#define hwDigitalRead(__pin) digitalReadFast(__pin)
#define hwPinMode(__pin, __value) pinMode(__pin, __value)
#define hwMillis() millis()
#define hwGetSleepRemaining() (0ul)
void hwRandomNumberInit(void);
bool hwInit(void);
void hwWatchdogReset(void);
void hwReboot(void);
// Teensy 3.x implements avr-libc EEPROM API
#define hwReadConfig(__pos) eeprom_read_byte((const uint8_t *)__pos)
#define hwWriteConfig(__pos, __val) eeprom_update_byte((uint8_t *)__pos, (uint8_t)__val)
#define hwReadConfigBlock(__buf, __pos, __length) eeprom_read_block((void *)__buf, (const void *)__pos, (uint32_t)__length)
#define hwWriteConfigBlock(__buf, __pos, __length) eeprom_update_block((const void *)__buf, (void *)__pos, (uint32_t)__length)
// SOFTSPI
#ifdef MY_SOFTSPI
#error Soft SPI is not available on this architecture!
#endif
#define hwSPI SPI //!< hwSPI
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define MY_HW_HAS_GETENTROPY
#endif
#define MY_CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
#endif

View File

@@ -0,0 +1,31 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*/
int main(void)
{
_begin(); // Startup MySensors library
for(;;) {
_process(); // Process incoming data
if (loop) {
loop(); // Call sketch loop
}
yield();
}
return 0;
}

View File

@@ -0,0 +1,57 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
*/
#include "MyCryptoAVR.h"
// SHA256 implementation in ASM, see MyASM.S
void SHA256(uint8_t *dest, const uint8_t *data, size_t dataLength)
{
sha256((sha256_hash_t *)dest, data, dataLength << 3); // data length in bits
}
// SHA256HMAC
void SHA256HMAC(uint8_t *dest, const uint8_t *key, size_t keyLength, const uint8_t *data,
size_t dataLength)
{
hmac_sha256(dest, key, keyLength << 3, data, dataLength << 3);
}
// AES
AES_ctx aes_ctx;
void AES128CBCInit(const uint8_t *key)
{
AES_init_ctx(&aes_ctx, key);
}
void AES128CBCEncrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength)
{
AES_ctx_set_iv(&aes_ctx, iv);
AES_CBC_encrypt_buffer(&aes_ctx, buffer, dataLength);
}
void AES128CBCDecrypt(uint8_t *iv, uint8_t *buffer, const size_t dataLength)
{
AES_ctx_set_iv(&aes_ctx, iv);
AES_CBC_decrypt_buffer(&aes_ctx, buffer, dataLength);
}

View File

@@ -0,0 +1,29 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
*/
#ifndef MyCryptoGeneric_h
#define MyCryptoGeneric_h
#include "hal/crypto/MyCryptoHAL.h"
#include "hal/crypto/AVR/drivers/AES/aes.cpp"
#include "hal/crypto/AVR/drivers/SHA256/sha256.cpp"
#include "hal/crypto/AVR/drivers/HMAC_SHA256/hmac_sha256.cpp"
#endif

View File

@@ -0,0 +1,577 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* AES implementation: https://github.com/kokke/tiny-AES-c
*/
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] PROGMEM = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
static const uint8_t rsbox[256] PROGMEM = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] PROGMEM = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
};
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) pgm_read_byte(&sbox[(num)])
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) pgm_read_byte(&rsbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i) {
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i) {
unsigned j, k;
{
k = (i - 1) * 4;
tempa[0] = RoundKey[k + 0];
tempa[1] = RoundKey[k + 1];
tempa[2] = RoundKey[k + 2];
tempa[3] = RoundKey[k + 3];
}
if (i % Nk == 0) {
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ pgm_read_byte(&Rcon[i / Nk]);
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4) {
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4;
k = (i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy(ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
for (i = 0; i < 4; ++i) {
uint8_t Tmp, Tm, t;
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
Tm = (*state)[i][0] ^ (*state)[i][1];
Tm = xtime(Tm);
(*state)[i][0] ^= Tm ^ Tmp;
Tm = (*state)[i][1] ^ (*state)[i][2];
Tm = xtime(Tm);
(*state)[i][1] ^= Tm ^ Tmp;
Tm = (*state)[i][2] ^ (*state)[i][3];
Tm = xtime(Tm);
(*state)[i][2] ^= Tm ^ Tmp;
Tm = (*state)[i][3] ^ t;
Tm = xtime(Tm);
(*state)[i][3] ^= Tm ^ Tmp;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y >> 1 & 1) * xtime(x)) ^
((y >> 2 & 1) * xtime(xtime(x))) ^
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
for (i = 0; i < 4; ++i) {
uint8_t a, b, c, d;
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = 1; round < Nr; ++round) {
SubBytes(state);
ShiftRows(state);
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes(state);
ShiftRows(state);
AddRoundKey(Nr, state, RoundKey);
}
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = (Nr - 1); round > 0; --round) {
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
InvMixColumns(state);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(0, state, RoundKey);
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
#endif // #if defined(ECB) && (ECB == 1)
#if defined(CBC) && (CBC == 1)
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) { // The block in AES is always 128bit no matter the key size
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN) {
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
//printf("Step %d - %d", i/16, i);
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN) {
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];
unsigned i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) {
if (bi == AES_BLOCKLEN) { /* we need to regen xor compliment in buffer */
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer, ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) {
/* inc will overflow */
if (ctx->Iv[bi] == 255) {
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(CTR) && (CTR == 1)

View File

@@ -0,0 +1,111 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* AES implementation: https://github.com/kokke/tiny-AES-c
*/
#ifndef _AES_H_
#define _AES_H_
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif
#ifndef ECB
#define ECB 0
#endif
#ifndef CTR
#define CTR 0
#endif
#define AES128 1
//#define AES192 1
//#define AES256 1
#define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only
#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16 // Key length in bytes
#define AES_keyExpSize 176
#endif
/**
* @brief AES state structure
*/
struct AES_ctx {
uint8_t RoundKey[AES_keyExpSize]; //!< RoundKey
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
uint8_t Iv[AES_BLOCKLEN]; //!< Iv
#endif
};
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
#endif // #if defined(ECB) && (ECB == !)
#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CTR) && (CTR == 1)
#endif //_AES_H_

View File

@@ -0,0 +1,61 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
*/
#include "hmac_sha256.h"
/*
* all lengths in bits!
*/
void hmac_sha256(void *dest, const void *key, uint16_t keylength_b, const void *msg,
uint32_t msglength_b)
{
sha256_ctx_t s;
uint8_t buffer[HMAC_SHA256_BLOCK_BYTES];
(void)memset((void *)buffer, 0x00, HMAC_SHA256_BLOCK_BYTES);
/* if key is larger than a block we have to hash it*/
if (keylength_b > SHA256_BLOCK_BITS) {
sha256((sha256_hash_t *)buffer, key, keylength_b);
} else {
(void)memcpy((void *)buffer, (const void *)key, (keylength_b + 7) / 8);
}
for (uint8_t i = 0; i < SHA256_BLOCK_BYTES; ++i) {
buffer[i] ^= IPAD;
}
sha256_init(&s);
sha256_nextBlock(&s, buffer);
while (msglength_b >= HMAC_SHA256_BLOCK_BITS) {
sha256_nextBlock(&s, msg);
msg = (uint8_t *)msg + HMAC_SHA256_BLOCK_BYTES;
msglength_b -= HMAC_SHA256_BLOCK_BITS;
}
sha256_lastBlock(&s, msg, msglength_b);
/* since buffer still contains key xor ipad we can do ... */
for (uint8_t i = 0; i < HMAC_SHA256_BLOCK_BYTES; ++i) {
buffer[i] ^= IPAD ^ OPAD;
}
sha256_ctx2hash((sha256_hash_t *)dest, &s); /* save inner hash temporary to dest */
sha256_init(&s);
sha256_nextBlock(&s, buffer);
sha256_lastBlock(&s, dest, SHA256_HASH_BITS);
sha256_ctx2hash((sha256_hash_t *)dest, &s);
}

View File

@@ -0,0 +1,78 @@
/*
* 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-2019 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/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.
*
* ======================================================================
*
* HMAC SHA256 implementation for AVR:
*
* This file is part of the AVR-Crypto-Lib.
* Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Daniel Otte
*
* License: GPLv3 or later
*
* ======================================================================
*/
#ifndef _HMAC_SHA256_
#define _HMAC_SHA256_
#define IPAD 0x36 //!< HMAC, inner hash, xor byte
#define OPAD 0x5C //!< HMAC, outer hash, xor byte
#define HMAC_SHA256_BITS SHA256_HASH_BITS //!< Defines the size of a SHA-256 HMAC hash value in bits
#define HMAC_SHA256_BYTES SHA256_HASH_BYTES //!< Defines the size of a SHA-256 HMAC hash value in bytes
#define HMAC_SHA256_BLOCK_BITS SHA256_BLOCK_BITS //!< Defines the size of a SHA-256 HMAC input block in bits
#define HMAC_SHA256_BLOCK_BYTES SHA256_BLOCK_BYTES //!< Defines the size of a SHA-256 HMAC input block in bytes
/**
* @brief hash context structure
*/
typedef struct {
sha256_ctx_t a; //!< a
sha256_ctx_t b; //!< b
} hmac_sha256_ctx_t;
/**
* @brief SHA256 HMAC function
*
* @param dest pointer to the location where the hash value is going to be written to
* @param key pointer to the key that's is needed for the HMAC calculation
* @param keylength_b length of the key
* @param msg pointer to the message that's going to be hashed
* @param msglength_b length of the message
*/
void hmac_sha256(void *dest, const void *key, uint16_t keylength_b, const void *msg,
uint32_t msglength_b);
#endif

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More