Add nrf5 platform (#830)

* Add NVRAM support

This is an EEPROM replacement based on MCU's internal Flash memory.
The NVRAM class is designed to be stateless. Each operation is
written to flash immediately.

The last 16k(nRF51)/32k(nRF52) of Flash memory are managed by the
VirtualPage class.  This class allows writing a single emulated 4k
page up 40,000 times.

The NVRAM class is a log based byte-wise storage allowing up to
40,000,000 writes depending on the number of used addresses.  Each
four allocated addresses from the beginning of the NVRAM reduce the
write log by one.

When first 256 bytes of the emulated storage are used, there is an
average writing capacity of 145,000 cycles per byte.

When the write log is full, the NVRAM is compressed into a new flash
page. After a new page is ready, the old page is released. This
process can take up to 4500 ms and cannot be interrupted.

The NVRAM functionality can easily be ported to ESP8266 or SAMD by
providing a Flash interface.

* Add NRF5 platform support

This is the support of the Nordic Semiconductor NRF51 (nrf51822,
nrf51422) and NRF52 (nrf52832) platform. Based on arduino-nRF5
(https://github.com/sandeepmistry/arduino-nRF5).

The included radio driver is compatible with the NRF24 devices.
Using a SoftDevice (BLE)is not supported and must be removed before
flashing MySensors with a "mass_erase" operation.
This commit is contained in:
d00616
2017-05-14 11:53:01 +02:00
committed by Olivier
parent a4c7b249c1
commit 726a20dad9
26 changed files with 3576 additions and 11 deletions
+436
View File
@@ -0,0 +1,436 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of
* the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include "MyHwNRF5.h"
#include <nrf.h>
#define INVALID_INTERRUPT_NUM (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.
void wakeUp1() // place to send the interrupts
{
_wokeUpByInterrupt = _wakeUp1Interrupt;
}
void wakeUp2() // place to send the second interrupts
{
_wokeUpByInterrupt = _wakeUp2Interrupt;
}
void hwReadConfigBlock(void *buf, void *adr, size_t length)
{
uint8_t *dst = static_cast<uint8_t *>(buf);
const int offs = reinterpret_cast<int>(adr);
(void)NVRAM.read_block(dst, offs, length);
}
void hwWriteConfigBlock(void *buf, void *adr, size_t length)
{
uint8_t *src = static_cast<uint8_t *>(buf);
const int offs = reinterpret_cast<int>(adr);
(void)NVRAM.write_block(src, offs, length);
}
uint8_t hwReadConfig(int adr)
{
return NVRAM.read(adr);
}
void hwWriteConfig(int adr, uint8_t value)
{
(void)NVRAM.write(adr, value);
}
bool hwInit()
{
#ifdef MY_DISABLED_SERIAL
// Disable UART, when not configured
#ifdef NRF51
NRF_UART0->POWER = 0;
#endif
#else
// Confiure UART
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
#if defined(MY_GATEWAY_SERIAL)
while (!MY_SERIALDEVICE) {
}
#endif
#endif
return true;
}
void hwRandomNumberInit()
{
// 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);
// Stop HWRNG
NRF_RNG->TASKS_STOP = 1;
#ifdef NRF51
NRF_RNG->POWER = 0;
#endif
}
void hwWatchdogReset()
{
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
}
void hwReboot()
{
NVIC_SystemReset();
while (true)
;
}
static volatile bool nrf5_rtc_event_triggered;
static volatile bool nrf5_pwr_hfclk;
void hwSleepPrepare(unsigned long ms)
{
// Enable low power sleep mode
NRF_POWER->TASKS_LOWPWR = 1;
if (ms > 0) {
// Configure RTC
#ifdef NRF51
MY_HW_RTC->POWER = 1;
#endif
// Reset RTC
MY_HW_RTC->TASKS_CLEAR = 1;
// Calculate sleep time
// 8 Hz -> max 582.542 hours sleep.
MY_HW_RTC->PRESCALER = 4095;
// Set compare register to 1/125ms + 2 to garantee event triggering
MY_HW_RTC->CC[0] = (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;
nrf5_rtc_event_triggered = false;
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(unsigned long 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()
{
__DSB();
__WFI();
}
// Sleep in System ON mode
inline void hwSleep()
{
__WFE();
__SEV();
__WFE();
}
int8_t hwSleep(unsigned long ms)
{
hwSleepPrepare(ms);
while (nrf5_rtc_event_triggered == false) {
hwSleep();
}
hwSleepEnd(ms);
return MY_WAKE_UP_BY_TIMER;
}
int8_t hwSleep(uint8_t interrupt, uint8_t mode, unsigned long 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, unsigned long 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 {
// Fix cppcheck "(style) Variable '__savePriMask' is assigned a value that is never used."
(void)__savePriMask;
// 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 attrubute
_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()
{
// 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 ressource: 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()
{
return F_CPU / 100000UL;
}
uint16_t hwFreeMem()
{
// TODO: Not supported!
return FUNCTION_NOT_SUPPORTED;
}
void hwDebugPrint(const char *fmt, ...)
{
char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
#ifdef MY_GATEWAY_FEATURE
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
snprintf(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;%lu "),
C_INTERNAL, I_LOG_MESSAGE, hwMillis());
MY_SERIALDEVICE.print(fmtBuffer);
#else
// prepend timestamp
MY_SERIALDEVICE.print(hwMillis());
MY_SERIALDEVICE.print(F(" "));
#endif
va_list args;
va_start (args, fmt );
#ifdef MY_GATEWAY_FEATURE
// Truncate message if this is gateway node
vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
#else
vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
#endif
va_end (args);
MY_SERIALDEVICE.print(fmtBuffer);
MY_SERIALDEVICE.flush();
}
+120
View File
@@ -0,0 +1,120 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of
* the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef MyHwNRF5_h
#define MyHwNRF5_h
#include "MyHw.h"
#ifdef __cplusplus
#include <Arduino.h>
#endif
// Define NRF5_SOFTDEVICE when SoftDevice found
#if defined(S110) || defined(S130) || defined(S132)
#ifndef NRF5_SOFTDEVICE
#define NRF5_SOFTDEVICE
#endif
#endif
#include "drivers/NRF5/nrf5_wiring_digital.c"
#include "drivers/NRF5/wdt.h"
#include "drivers/NVM/NVRAM.h"
#include "drivers/NVM/VirtualPage.h"
#include <avr/dtostrf.h>
#include <nrf.h>
#define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__)
// 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()
bool hwInit();
void hwWatchdogReset();
void hwReboot();
void hwReadConfigBlock(void *buf, void *adr, size_t length);
void hwWriteConfigBlock(void *buf, void *adr, size_t length);
void hwWriteConfig(int adr, uint8_t value);
uint8_t hwReadConfig(int adr);
void hwRandomNumberInit();
#ifndef MY_SERIALDEVICE
#define MY_SERIALDEVICE Serial
#endif
/**
* 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
#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_NRF5
+69
View File
@@ -0,0 +1,69 @@
/**
* 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-2017 Sensnology AB
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
// Initialize library and handle sketch functions like we want to
extern "C" void __libc_init_array(void);
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() {}
int main(void)
{
// Suspend UART
NRF_UART0->TASKS_STOPRX = 1;
NRF_UART0->TASKS_STOPTX = 1;
NRF_UART0->TASKS_SUSPEND = 1;
// 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
init();
initVariant(); // arduino-nRF5 specific
delay(1);
_begin(); // Startup MySensors library
for (;;) {
_process(); // Process incoming data
if (loop) {
loop(); // Call sketch loop
}
if (serialEventRun) {
serialEventRun();
}
}
return 0;
}
+3
View File
@@ -26,6 +26,9 @@
#define INVALID_LEVEL ((int16_t)-256) //!< INVALID_LEVEL
#if defined(MY_RX_MESSAGE_BUFFER_FEATURE)
#if defined(MY_RADIO_NRF5_ESB)
#error Receive message buffering not supported for NRF5 radio! Please define MY_NRF5_RX_BUFFER_SIZE
#endif
#if defined(MY_RADIO_RFM69)
#error Receive message buffering not supported for RFM69!
#endif
+158
View File
@@ -0,0 +1,158 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of
* the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include "MyTransportHAL.h"
#include "drivers/NRF5/Radio.h"
#include "drivers/NRF5/Radio_ESB.h"
#include "drivers/CircularBuffer/CircularBuffer.h"
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
#include "drivers/AES/AES.h"
#endif
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
AES _aes;
uint8_t _dataenc[32] = {0};
uint8_t _psk[16];
#endif
bool transportInit(void)
{
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
hwReadConfigBlock((void *)_psk, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16);
// set up AES-key
_aes.set_key(_psk, 16);
// Make sure it is purged from memory when set
memset(_psk, 0, 16);
#endif
return NRF5_ESB_initialize();
}
void transportSetAddress(const uint8_t address)
{
NRF5_ESB_setNodeAddress(address);
NRF5_ESB_startListening();
}
uint8_t transportGetAddress(void)
{
return NRF5_ESB_getNodeID();
}
bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK)
{
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
// copy input data because it is read-only
(void)memcpy(_dataenc, data, len);
// has to be adjusted, WIP!
_aes.set_IV(0);
const uint8_t finalLength = len > 16 ? 32 : 16;
// encrypt data
_aes.cbc_encrypt(_dataenc, _dataenc, finalLength / 16);
return NRF5_ESB_sendMessage(to, _dataenc, finalLength, noACK);
#else
return NRF5_ESB_sendMessage(to, data, len, noACK);
#endif
}
bool transportAvailable(void)
{
return NRF5_ESB_isDataAvailable();
}
bool transportSanityCheck(void)
{
return NRF5_ESB_sanityCheck();
}
uint8_t transportReceive(void *data)
{
uint8_t len = 0;
len = NRF5_ESB_readMessage(data);
#if defined(MY_NRF5_ESB_ENABLE_ENCRYPTION)
// has to be adjusted, WIP!
_aes.set_IV(0);
// decrypt data
if (_aes.cbc_decrypt((uint8_t *)(data), (uint8_t *)(data),
len > 16 ? 2 : 1) != AES_SUCCESS) {
len = 0;
}
#endif
return len;
}
void transportPowerDown(void)
{
NRF5_ESB_powerDown();
}
void transportPowerUp(void)
{
NRF5_ESB_powerUp();
}
void transportSleep(void)
{
NRF5_ESB_sleep();
}
void transportStandBy(void)
{
NRF5_ESB_standBy();
}
int16_t transportGetSendingRSSI(void)
{
return NRF5_ESB_getSendingRSSI();
}
int16_t transportGetReceivingRSSI(void)
{
return NRF5_ESB_getReceivingRSSI();
}
int16_t transportGetSendingSNR(void)
{
return INVALID_SNR;
}
int16_t transportGetReceivingSNR(void)
{
return INVALID_SNR;
}
int16_t transportGetTxPowerPercent(void)
{
return NRF5_getTxPowerPercent();
}
int16_t transportGetTxPowerLevel(void)
{
return static_cast<int16_t>(NRF5_getTxPowerLevel());
}
bool transportSetTxPowerPercent(const uint8_t powerPercent)
{
return NRF5_setTxPowerPercent(powerPercent);
}