diff --git a/Core.h b/Core.h new file mode 100644 index 0000000..a1aab67 --- /dev/null +++ b/Core.h @@ -0,0 +1,57 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1 + Developed and maintained by tekka 2016 +*/ + +#ifndef Core_H +#define Core_H + +// version ************************************************************************************************************* +#define MYSBOOTLOADER_MAJVER 1 +#define MYSBOOTLOADER_MINVER 2 +#define MYSBOOTLOADER_VERSION ((MYSBOOTLOADER_MINVER << 8) + MYSBOOTLOADER_MAJVER) + +// size setting ******************************************************************************************************** +#define BOOTLOADER_SIZE (2048) +#define BOOTLOADER_START_ADDRESS (0x8000 - BOOTLOADER_SIZE) + +// DEBUG led patterns ************************************************************************************************* + +#define DEBUG_INIT _BV(3) +#define DEBUG_READ_CONFIG _BV(4) +#define DEBUG_FIND_PARENTS _BV(5) +#define DEBUG_CHECK_ID _BV(6) +#define DEBUG_CONFIGURATION _BV(7) +#define DEBUG_CONFIG_RECEIVED _BV(8) +#define DEBUG_INIT_UPDATE _BV(6) | _BV(7) +#define DEBUG_DO_UPDATE _BV(4) | _BV(5) | _BV(7) +#define DEBUG_PREPARE_RUN _BV(4) | _BV(5) | _BV(7) +#define DEBUG_RUN _BV(4) | _BV(5) | _BV(6) | _BV(7) + +#include +#include +#include +#include + +#include "Definitions.h" +#include "boot.h" +#include "HW.h" +#include "MyEepromAddresses.h" +#include "MyMessage.h" +#include "RF24.h" +#include "STK500Bootloader.h" +#include "MySensorsBootloader.h" + + +// global variables +MyMessage _outMsg; +MyMessage _inMsg; +nodeConfig_t _eepromNodeConfig; +uint8_t _configuredParentFound; +uint8_t _configuredParentID; +uint8_t _save_MCUSR; + + +#endif // Core_H \ No newline at end of file diff --git a/Definitions.h b/Definitions.h new file mode 100644 index 0000000..91cfba7 --- /dev/null +++ b/Definitions.h @@ -0,0 +1,73 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1 + Developed and maintained by tekka 2016 +*/ + +#ifndef Definitions_H +#define Definitions_H + +#define FIRMWARE_BLOCK_SIZE (16) + +#define false (0) +#define true (1) + +typedef uint8_t bool; + +typedef enum { + BL_INIT, + BL_READ_CONFIG, + BL_FIND_PARENTS, + BL_CHECK_ID, + BL_CONFIGRUATION, + BL_INIT_UPDATE, + BL_DO_UPDATE, + BL_VALIDATE_OTA, + BL_RUN, + BL_EXIT +} SM_BL_STATE; + + +typedef struct { + union { + uint16_t type; + uint16_t bl_command; + } type_command; + union { + uint16_t version; + uint16_t bl_data; + } version_data; + uint16_t blocks; + uint16_t crc; +} __attribute__((packed)) nodeFirmwareConfig_t; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; + uint16_t BLVersion; +} __attribute__((packed)) requestFirmwareConfig_t; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t block; +} __attribute__((packed)) requestFirmwareBlock_t; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t block; + uint8_t data[FIRMWARE_BLOCK_SIZE]; +} __attribute__((packed)) responseFirmwareBlock_t; + +typedef struct { + uint8_t nodeId; // Current node id + uint8_t parentNodeId; // Where this node sends its messages + uint8_t distance; // This nodes distance to sensor net gateway (number of hops) +} __attribute__((packed)) nodeConfig_t; + + +#endif // Definitions_H \ No newline at end of file diff --git a/HW.h b/HW.h new file mode 100644 index 0000000..22967ce --- /dev/null +++ b/HW.h @@ -0,0 +1,255 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1 + Developed and maintained by tekka 2016 +*/ + +#ifndef HW_H +#define HW_H + +// hardware +# define UART_SRA UCSR0A +# define UART_SRB UCSR0B +# define UART_SRC UCSR0C +# define UART_SRL UBRR0L +# define UART_UDR UDR0 + + +#ifndef BAUD_RATE + #if F_CPU >= 16000000L + #define BAUD_RATE 115200 + #elif F_CPU >= 8000000L + #define BAUD_RATE 38400L + #elif F_CPU >= 1000000L + #define BAUD_RATE 9600L + #elif F_CPU >= 128000L + #define BAUD_RATE 4800L + #else + #define BAUD_RATE 1200L + #endif +#endif + +#ifndef UART + #define UART 0 +#endif + +#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 ) +#define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1))) +#if BAUD_ACTUAL <= BAUD_RATE +#define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE) +#if BAUD_ERROR >= 5 +#error BAUD_RATE error greater than -5% +#elif BAUD_ERROR >= 2 +#warning BAUD_RATE error greater than -2% +#endif +#else +#define BAUD_ERROR (( 100*(BAUD_ACTUAL - BAUD_RATE) ) / BAUD_RATE) +#if BAUD_ERROR >= 5 +#error BAUD_RATE error greater than 5% +#elif BAUD_ERROR >= 2 +#warning BAUD_RATE error greater than 2% +#endif +#endif + +#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 > 250 +#error Unachievable baud rate (too slow) BAUD_RATE +#endif // baud rate slow check +#if (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 < 3 +#if BAUD_ERROR != 0 // permit high bitrates (ie 1Mbps@16MHz) if error is zero +#error Unachievable baud rate (too fast) BAUD_RATE +#endif +#endif + +#ifndef UART +#define UART 0 +#endif + + +// Watchdog definitions and functions +#define WATCHDOG_OFF (0) +#define WATCHDOG_16MS (_BV(WDE)) +#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE)) +#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE)) +#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE)) +#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE)) +#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE)) +#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE)) +#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE)) +#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE)) +#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE)) + + +static inline void watchdogReset() { + __asm__ __volatile__ ("wdr\n"); +} + +void watchdogConfig(const uint8_t x) { + WDTCSR = _BV(WDCE) | _BV(WDE); + WDTCSR = x; +} + + +// SPI communication +#define SPI_PORT PORTB // +#define SPI_DDR DDRB // +#define SPI_SCLK PB5 // Arduino Pin 13 <-> Bit 5 of port B +#define SPI_MISO PB4 // Arduino Pin 12 <-> Bit 4 of port B +#define SPI_MOSI PB3 // Arduino Pin 11 <-> Bit 3 of port B + +#if defined(SPI_PINS_CE9_CSN10) + #define CSN_PORT PORTB // port for CSN + #define CSN_DDR DDRB // DDR for CSN + #define CSN_PIN PB2 // Arduino Pin 10 <-> Bit 2 of port B + + #define CE_PORT PORTB // port for CE + #define CE_DDR DDRB // DDR for CE + #define CE_PIN PB1 // Arduino Pin 9 <-> Bit 1 of port B +#elif defined(SPI_PINS_CSN7_CE8) + #define CSN_PORT PORTD // port for CSN + #define CSN_DDR DDRD // DDR for CSN + #define CSN_PIN PD7 // Arduino Pin 7 <-> Bit 7 of port D + + #define CE_PORT PORTB // port for CE + #define CE_DDR DDRB // DDR for CE + #define CE_PIN PB0 // Arduino Pin 8 <-> Bit 0 of port B +#endif + +#define CSN_LOW() CSN_PORT &= ~_BV(CSN_PIN) +#define CSN_HIGH() CSN_PORT |= _BV(CSN_PIN) +#define CE_LOW() CE_PORT &= ~_BV(CE_PIN) +#define CE_HIGH() CE_PORT |= _BV(CE_PIN) + + +static void initSPI(void) { + // Initialize the SPI pins: SCK, MOSI, CE, CSN as outputs, MISO as input + #if defined(SPI_PINS_CE9_CSN10) + // CSN_PIN (=PB2) is SS pin and set as output + SPI_DDR = _BV(SPI_MOSI) | _BV(SPI_SCLK) | _BV(CE_PIN) | _BV(CSN_PIN); + #elif defined(SPI_PINS_CSN7_CE8) + // PB2 is SS pin has to be defined as OUTPUT, else SPI goes to slave mode + SPI_DDR = _BV(SPI_MOSI) | _BV(SPI_SCLK) | _BV(CE_PIN) | _BV(PB2); + CSN_DDR = _BV(CSN_PIN); + #endif + + // SPE = SPI enable + // SPIE = SPI interrupt enable + // DORD = data order (0:MSB first, 1:LSB first) + // MSTR = Master/Slave select + // SPR1 = SPI clock rate bit 1 + // SPR0 = SPI clock rate bit 0; 0,0=osc/4, 0,1=osc/16, 1,0=osc/64, 1,1=osc/128 + // CPOL = clock polarity idle (0:low, 1:high) + // CPHA = clock phase edge sampling (0:leading, 1:trailing) + + // SPI speed setting, nRF24L01P max. 10Mhz + #if (F_CPU >= 16000000) + // DIV 16 = 1 Mhz + SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); + #elif (F_CPU >= 8000000) + // DIV 8 = 1 Mhz + SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); + SPSR = _BV(SPI2X); + #elif (F_CPU >= 4000000) + // DIV 4 = 1Mhz + SPCR = _BV(SPE) | _BV(MSTR); + #elif (F_CPU >= 2000000) + // DIV 2 = 1 Mhz + SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0); + SPSR = _BV(SPI2X); + #else + // DIV 2 <= 0.5 Mhz + SPCR = _BV(SPE) | _BV(MSTR); + SPSR = _BV(SPI2X); + #endif + +} +static uint8_t SPItransfer(const uint8_t value) { + SPDR = value; + while(!(SPSR & _BV(SPIF))); // wait until transmitted + return SPDR; +} +inline void SPIclose(void) { + // disable hardware SPI + SPCR = 0; +} + + +// UART +static void initUART(void) { + UART_SRA = _BV(U2X0); //Double speed mode USART0 + UART_SRB = _BV(RXEN0) | _BV(TXEN0); + UART_SRC = _BV(UCSZ00) | _BV(UCSZ01); + UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); +} + +void putch(const uint8_t ch) { + while (!(UART_SRA & _BV(UDRE0))); + UART_UDR = ch; +} + +uint8_t getch(void) { + // wait until char received + while(!(UART_SRA & _BV(RXC0))); + // 10 bytes + // framing error? + if (!(UART_SRA & _BV(FE0))) { + watchdogReset(); + } + return UART_UDR; +} + +static void writeTemporaryBuffer(const uint16_t address, const uint16_t data) { + // fill temporary page buffer + __boot_page_fill_short(address, data); +} + +static void programPage(const uint16_t page){ + __boot_page_erase_short(page); // erase page + boot_spm_busy_wait(); + __boot_page_write_short(page); // program page + boot_spm_busy_wait(); + __boot_rww_enable_short(); // re-enable RWW +} + +static uint16_t crc16_update(uint16_t crc, const uint8_t data) { + crc ^= data; + for (uint8_t i = 0; i < 8; ++i) { + crc = (crc >> 1) ^ (-(int16_t)(crc & 1) & 0xA001); + } + return crc; +} + +static uint16_t calcCRCrom (const uint16_t len) { + + uint16_t _internal_crc = 0xFFFF; // init + uint16_t address = 0x0000; + uint16_t count = len; + + // prevent overflow + if(count>BOOTLOADER_START_ADDRESS) { + count = BOOTLOADER_START_ADDRESS; + } + + // calc + while (count) { + uint8_t _rom_byte; + // read a flash byte and increment the address + __asm__ ("lpm %0,Z+\n" : "=r" (_rom_byte), "=z" (address): "1" (address)); + _internal_crc = crc16_update(_internal_crc,_rom_byte); + count--; + } + return _internal_crc; +} + +static void blinkLed(void) { + LED_DDR |= _BV(LED_PIN); + //300ms total + uint8_t count = 6; + do { + LED_PORT ^= _BV(LED_PIN); + _delay_ms(50); + } while (--count); +} + + +#endif // HW_H \ No newline at end of file diff --git a/MYSBootloader.c b/MYSBootloader.c deleted file mode 100644 index 1efd459..0000000 --- a/MYSBootloader.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - MYSBootloader: OTA bootloader for Mysensor nodes (www.mysensors.org) - Original OTA bootloader code by ToSa - Optimized and extended by tekka - Version 1.1 / 20150314 - Size: 2002 bytes - - Tested with MYSController 0.1.2.276 (goo.gl/9DCWNo) - - MCU: Atmega328p - bootsz: 1024W - - fuses for ISP: - EX = 0xFE (use 0x06 for Arduino IDE, boards.txt) - HI = 0xDA - LO = 0xF7 - - nRF24L01+ connected to pins: - CE = 9 - CSN = 10 - - Successfully tested with: - - 16Mhz extXTAL, 3.3V & 5V - 8Mhz intRC, 3.3V & 5V - 1Mhz intRC, 3.3 & 5V - 128kHz intRC, 3.3V & 5V - - * Version 1.1 - - use eeprom_update instead of eeprom_write to reduce wear out - - bootloader commands: erase eeprom, set node id - - verify incoming FW blocks for type & address - - communicate over static parent (if set and found) else broadcast to find nearest node - - adjusted timings - - * Version 1.0 - Initial release - - 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 "MYSBootloader.h" - - -#define MYSBOOTLOADER_MAJVER 1 -#define MYSBOOTLOADER_MINVER 1 - -#define MYSBOOTLOADER_VERSION (MYSBOOTLOADER_MINVER * 256 + MYSBOOTLOADER_MAJVER) - -#define MAX_RESEND 5 - - -// procedures and functions - -static void programPage(uint16_t page, uint8_t *buf); -static uint16_t calcCRCrom (const void* ptr, uint16_t len); -static uint8_t IsFirmwareValid(); -static void reboot(); -static void startup(); -static boolean sendWrite(MyMessage message); -static bool sendAndWait(uint8_t reqType, uint8_t resType); - -int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9"))); - - -static void programPage(uint16_t page, uint8_t *buf) { - // these function calls use "out" commands: save some bytes and cycles :) - __boot_page_erase_short(page); - boot_spm_busy_wait(); - for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) { - uint16_t data_word = *buf++; - data_word += (*buf++) << 8; - __boot_page_fill_short(page + i, data_word); - } - __boot_page_write_short(page); - boot_spm_busy_wait(); - __boot_rww_enable_short(); -} - - -static uint16_t calcCRCrom (const void* ptr, uint16_t len) { - // init 0xFFFF - uint16_t crc = ~0; - for (uint16_t i = 0; i < len; i++) { - crc = _crc16_update(crc, pgm_read_byte((uint16_t) ptr + i)); - } - return crc; -} - - -static uint8_t IsFirmwareValid () { - return calcCRCrom(0, fc.blocks * FIRMWARE_BLOCK_SIZE) == fc.crc; -} - - - -static void reboot() { - // wait for pending eeprom activities - eeprom_busy_wait(); - // trigger watchdog ASAP - watchdogConfig(WATCHDOG_16MS); - // endless loop - while (1); -} - -static void startup() { - if (IsFirmwareValid()) { - // WD off - watchdogConfig(WATCHDOG_OFF); - // run sketch - ((void(*)()) 0)(); - - } else { - reboot(); - } -} - - - - -static boolean sendWrite(MyMessage message) { - return write(nc.parentNodeId, message.array, HEADER_SIZE + mGetLength(message), (message.destination == BROADCAST_ADDRESS)); -} - -static bool sendAndWait(uint8_t reqType, uint8_t resType) { - outMsg.type = reqType; - // outer loop, retries - for (uint8_t i = 0; i < MAX_RESEND; i++) { - sendWrite(outMsg); - // loop 20 times, wait time 0.1s if no/wrong data => 2s - for (uint8_t j = 0; j < 20; j++) { - // loop 100 times, wait 1ms if no/wrong data => 0.1s - for (uint8_t k = 0; k < 100; k++) { - watchdogReset(); - // Tx FIFO data available? (we don't care about the pipe here) - if (available(NULL)) { - // read message from FIFO, skip if size = 0 - if (readMessage(inMsg.array) > 0) { - // protocol compatible? if not ignore msg - if ((mGetVersion(inMsg) != PROTOCOL_VERSION)) { - continue; - } - // msg for us? - if (inMsg.destination == nc.nodeId) { - // internal command: find parent - if ((mGetCommand(inMsg) == C_INTERNAL) && (inMsg.type == I_FIND_PARENT_RESPONSE)) { - // static parent found? - if (configuredParentID == inMsg.sender) { - configuredParentFound = true; - } - if ( ((inMsg.bValue < nc.distance - 1) && ( !configuredParentFound) ) || (configuredParentID == inMsg.sender)) { - // got new routing info, update settings - nc.distance = inMsg.bValue + 1; - nc.parentNodeId = inMsg.sender; - } - } - // did we receive expected reply? - if ((mGetCommand(inMsg) == mGetCommand(outMsg)) && (inMsg.type == resType)) { - return true; - } - - } - } - } else { - // wait 1ms if no data available - _delay_ms(1); - } - - } - } - } - return false; -} - - - - -// main start -int main(void) { - - asm volatile ("clr __zero_reg__"); - // reset MCU status register - MCUSR = 0; - - // enable watchdog to avoid deadlock - watchdogConfig(WATCHDOG_8S); - - // initialize SPI - SPIinit(); - - // initialize RF module - RFinit(); - - // Read node config from EEPROM, i.e. nodeId, parent nodeId, distance - eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(struct NodeConfig)); - // Read firmware config from EEPROM, i.e. type, version, CRC, blocks - eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); - - // find nearest node during reboot: invalidate parent node settings, since we have to re-discover them for every single reboot - configuredParentID = nc.parentNodeId; - // nc.parentNodeId = 0xFF; - nc.distance = 0xFF; - - // prepare for I_FIND_PARENTS - outMsg.sender = nc.nodeId; - outMsg.last = nc.nodeId; - outMsg.sensor = 0xFF; - outMsg.destination = BROADCAST_ADDRESS; - - // set header - mSetVersion(outMsg, PROTOCOL_VERSION); - mSetLength(outMsg, 0); - mSetCommand(outMsg, C_INTERNAL); - mSetAck(outMsg,false); - mSetPayloadType(outMsg, P_STRING); - - // set reading & writing pipe address - setAddress(nc.nodeId); - - // network up? get neighbors, else startup - if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) { - startup(); - } - - // all messages to gateway - outMsg.destination = GATEWAY_ADDRESS; - - // if no node id assigned, request new id - if (nc.nodeId == AUTO) { - // listen to broadcast - openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(BROADCAST_ADDRESS)); - if (sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) { - // save id to eeprom - eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data)); - } - // we could go on and set everything right here, but rebooting will take care of that - and saves some bytes :) - reboot(); - } - - // wuff - watchdogReset(); - // prepare for FW config request - RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)outMsg.data; - mSetLength(outMsg, sizeof(RequestFirmwareConfig)); - mSetCommand(outMsg, C_STREAM); - mSetPayloadType(outMsg,P_CUSTOM); - // copy node settings to reqFWConfig - memcpy(reqFWConfig,&fc,sizeof(NodeFirmwareConfig)); - // add bootloader information - reqFWConfig->BLVersion = MYSBOOTLOADER_VERSION; - - // send node config and request FW config from controller - if (!sendAndWait(ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE)) { - startup(); - } - - NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)inMsg.data; - - // bootloader commands - if (firmwareConfigResponse->blocks == 0) { - // verify flag - if (firmwareConfigResponse->crc == 0xDA7A){ - // cmd 0x01 clear eeprom - if(firmwareConfigResponse->bl_command == 0x01) { - for(uint16_t i = 0; i < EEPROM_SIZE; i++) eeprom_update_byte((uint8_t *)i,0xFF); - } else - // cmd 0x02 set id - if(firmwareConfigResponse->bl_command == 0x02) { - eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, (uint8_t)firmwareConfigResponse->bl_data); - } - } - // final step - reboot(); - } - - // compare with current node configuration, if equal startup - if (!memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { - startup(); - } - - // *********** from here on we will fetch new FW - - // invalidate current CRC - fc.crc = 0xFFFF; - // write fetched type and version in case OTA fails (BL will reboot and re-request FW with stored settings) - eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig)); - - // copy new FW config - memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); - RequestFWBlock *firmwareRequest = (RequestFWBlock *)outMsg.data; - mSetLength(outMsg, sizeof(RequestFWBlock)); - - firmwareRequest->type = fc.type; - firmwareRequest->version = fc.version; - - // request FW from controller, load FW counting backwards - uint16_t block = fc.blocks; - do { - firmwareRequest->block = block - 1; - - // request FW block - if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE)) { - reboot(); - } - - ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)inMsg.data; - - // did we receive requested block? - if (!memcmp(firmwareRequest,firmwareResponse,sizeof(RequestFWBlock))) { - // calculate page offset - uint8_t offset = ((block - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE; - // write to buffer - memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); - // program if page full - if (offset == 0) { - programPage(((block - 1) * FIRMWARE_BLOCK_SIZE), progBuf); - } - block--; - } - } while (block); - - // wuff - watchdogReset(); - - // all blocks transmitted, calc CRC and write to eeprom if valid - if (IsFirmwareValid()) { - // if FW is valid, write settings to eeprom - eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); - } - // final step - reboot(); -} - - - - diff --git a/MYSBootloader.h b/MYSBootloader.h deleted file mode 100644 index 1f66faf..0000000 --- a/MYSBootloader.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef MyOtaBootloader_H -#define MyOtaBootloader_H - -#include -#include -#include -#include -#include - -#include "MyMessage.h" -#include "MySensor.h" -#include "boot.h" -#include "MYSBootloaderRF24.h" - - -#define FIRMWARE_BLOCK_SIZE 16 -#define EEPROM_SIZE 1024 // 1024 bytes for ATMEGA328 - -typedef struct { - union { - uint16_t type; - uint16_t bl_command; - }; - union { - uint16_t version; - uint16_t bl_data - }; - uint16_t blocks; - uint16_t crc; -} __attribute__((packed)) NodeFirmwareConfig; - -typedef struct { - uint16_t type; - uint16_t version; - uint16_t blocks; - uint16_t crc; - uint16_t BLVersion; -} __attribute__((packed)) RequestFirmwareConfig; - -typedef struct { - uint16_t type; - uint16_t version; - uint16_t block; -} __attribute__((packed)) RequestFWBlock; - -typedef struct { - uint16_t type; - uint16_t version; - uint16_t block; - uint8_t data[FIRMWARE_BLOCK_SIZE]; -} __attribute__((packed)) ReplyFWBlock; - -static struct NodeConfig nc; -static NodeFirmwareConfig fc; -static MyMessage outMsg; -static MyMessage inMsg; - -static boolean configuredParentFound = false; -static uint8_t configuredParentID = 0xFF; - -//static clock_div_t orgClockDiv = 0; - -static uint8_t progBuf[SPM_PAGESIZE]; - -#endif // MyOtaBootloader_H \ No newline at end of file diff --git a/MYSBootloader.hex b/MYSBootloader.hex index 8095224..80b114f 100644 --- a/MYSBootloader.hex +++ b/MYSBootloader.hex @@ -1,128 +1,131 @@ -:1078000011E0A0E0B1E0E0EDFFE702C005900D92CD -:10781000A230B107D9F721E0A2E0B1E001C01D928A -:10782000A83DB207E1F7CDB7DEB76897DEBFCDBFA1 -:10783000112414BE88E18093600089E28093600087 -:107840008FEE84B963E083E2D9D16FE584E2D6D1CB -:107850006BE381E2D3D16CE485E2D0D167E286E2CA -:10786000CDD12ED263E780E5C9D166E08DE3C6D1E4 -:107870006FE38CE3C3D16EEF80E2C0D18FE19EE471 -:107880000197F1F700C0000043E050E060E070E0D5 -:107890008DEC91E06CD348E050E06BE171E085EC59 -:1078A00091E065D38091CE01809300018FEF8093AA -:1078B000CF012091CD012093A5012093A4018093B5 -:1078C000AA018093A60182E08093A7018091A8017C -:1078D000887083608093A8012F3FD1F130E040E0B1 -:1078E00050E060E070E080E090E034504E41574559 -:1078F00067457F4F8F4F9F4F29013A014B015C0134 -:10790000298B5A8A4B8B7C8A6D8B9E8A8F8BB88E83 -:1079100085E0FE017196A0EDB1E001900D928A958F -:10792000E1F745E0BE016F5E7F4F8AE255D149869F -:107930005A866B867C868D869E86AF86B88A45E0A1 -:10794000BE01675F7F4F8BE247D163E082E256D191 -:1079500068E087E0BAD1811101C09CD11092A601E4 -:107960008091CD018F3FF1F489838CEF8A8381EE82 -:107970008B8388EA8C838D831E821F82188645E064 -:10798000BE016F5F7F4F8BE227D164E083E09DD122 -:10799000882339F08AE891E0B8D2682F80E090E03F -:1079A00005D36FD1A8958091A701877080658093DA -:1079B000A7018091A8018871846C8093A80188E058 -:1079C000E5ECF1E0ABEAB1E001900D928A95E1F7C8 -:1079D00081E091E09093B4018093B30161E080E095 -:1079E00074D1811101C056D180918E0190918F0187 -:1079F000892B09F580919001909191018A379A4DD8 -:107A000081F680918A0190918B018130910569F412 -:107A1000A12CB12C6FEFC501C9D2FFEFAF1ABF0A7D -:107A2000A11424E0B206B1F7BCCF029709F0B9CF98 -:107A300060918C01B3CF48E050E06AE871E085ECDA -:107A400091E080D2892B09F425D18FEF9FEF90939D -:107A5000CC018093CB0148E050E06BE171E085EC14 -:107A600091E095D288E0EAE8F1E0A5ECB1E0019080 -:107A70000D928A95E1F78091A701877080638093CA -:107A8000A7018091C5019091C6019093AC018093AC -:107A9000AB018091C7019091C8019093AE01809392 -:107AA000AD01A090C901B090CA0110E101E0E5E08C -:107AB0007E2EF1E16F2E450181E0881A91089092A7 -:107AC000B0018092AF0163E082E0FFD0882309F427 -:107AD00068CF46E050E06AE871E08BEA91E032D28C -:107AE000892B09F045C091E0A91A90EFB90A8A2DB7 -:107AF00082958077A82FB0E0AE5FBE4FE0E9F1E05D -:107B0000912F01900D929A95E1F7811130C074E0A8 -:107B1000AA0CBB1C7A95E1F783E0F50187BFE895D5 -:107B200007B600FCFDCF4091020180E090E0DC014F -:107B3000AE5FBE4FFC01EA0DFB1D11962C91119713 -:107B400030E0322F2227240F311D090107BFE895AD -:107B5000112412964C9102968038910541F7F50157 -:107B600077BEE89507B600FCFDCF67BEE8955401E7 -:107B7000A114B10409F09FCFA8954FD0882309F430 -:107B800010CF48E050E06BE171E085EC91E0FFD16F -:107B900008CF98E087FF02C02B9A01C02B982D9A3E -:107BA00023B1880F22952170822B2D98915091F747 -:107BB00008951F93CF93DF93EB01142F2A98E9DFE9 -:107BC0001C0F1C1721F08FEFE4DF8993FACF2A9A5C -:107BD000DF91CF911F9108951F93CF93DF93EB0116 -:107BE000142F2A98D6DF1C0F1C1719F08991D1DFAA -:107BF000FBCF2A9ADF91CF911F910895CF93DF9306 -:107C00001F92CDB7DEB7698341E0BE016F5F7F4F42 -:107C1000E3DF0F90DF91CF9108958091C90190919A -:107C2000CA0124E0880F991F2A95E1F7E0E0F0E00F -:107C30002FEF3FEFE817F907D1F044912427422FA7 -:107C400042954227042E469546954025042E46959A -:107C500040254770022E232F469507944795302DD7 -:107C6000242706944795302524273196E3CF81E0D9 -:107C70004091CB015091CC012417350709F080E0E9 -:107C80000895F999FECF88E18093600088E08093A1 -:107C90006000FFCFC2DF882341F088E1809360005D -:107CA00010926000E0E0F0E00994EBDF40E060E07B -:107CB00070E082EE91CF40E060E070E081EE8CCF2A -:107CC000F5DFF9DF60E787E299CF6F927F928F92BD -:107CD0009F92AF92BF92CF92DF92EF92FF920F935B -:107CE0001F93CF93DF93CDB7DEB7AA97DEBFCDBF8B -:107CF000762E8093A90105E051E2652EE4EAF1E0D9 -:107D0000DE011196862D01900D928A95E1F7FB8098 -:107D10001C81169516951695195F2091CE0129980C -:107D20002AA7CEDF6EEF80E269DF2AA530E040E0CF -:107D300050E060E070E080E090E034504E41574504 -:107D400067457F4F8F4F9F4F2AA33BA34CA35DA353 -:107D50006EA37FA388A799A745E0BE016E5D7F4F04 -:107D60008AE23ADF45E0BE016E5D7F4F80E334DF9B -:107D70008FEFF81202C080EB01C080EA412FBE01F4 -:107D80006F5F7F4F29DF299A9AE69A95F1F700C035 -:107D9000299840E060E070E08FEF0BDF8073C9F35B -:107DA00060E787E22BDF84FD86DF6FE080E226DF7D -:107DB00087DF8090D0019090D101A090D201B09047 -:107DC000D301C090D401D090D501E090D601F090BD -:107DD000D7019401A501B601C701A0E08AD029F01E -:107DE00045E060ED71E08AE2F7DE299A44E1E42E95 -:107DF00034E6F32EA89541E060E070E087E1D9DE3B -:107E000080FD5EC041E060E070E080E6D2DE182FC9 -:107E1000813210F04BDF10E0412F63E871E081E622 -:107E2000C8DE60E787E2EADE1123E9F1809186018E -:107E300087708230C1F5909185018091CD019813B2 -:107E400032C050918701852F8770833019F580915A -:107E500088018830F9F47091000140918401741315 -:107E600003C081E08093820160918A01262F30E077 -:107E70008091CF0190E001972817390724F4809171 -:107E80008201882311F0741305C06F5F6093CF01E6 -:107E90004093CE018091A8018527877021F48091BD -:107EA0008801871559F0FA9409F0A4CFEA9409F0F3 -:107EB0009FCF015009F022CF80E009C081E007C0C8 -:107EC0008FE99FE00197F1F700C00000ECCFAA9680 -:107ED000DEBFCDBFDF91CF911F910F91FF90EF904B -:107EE000DF90CF90BF90AF909F908F907F906F90DA -:107EF00008950024A7FD00942A1730054005500579 -:107F000060057005800590050895FC018827992774 -:107F1000E89421912032E9F3293010F02E30C8F393 -:107F20002B3239F02D3231F4689403C018D0820F0F -:107F3000911D219120532A30C8F31EF4909581950C -:107F40009F4F0895FB01DC0104C08D9101908019C1 -:107F500021F441505040C8F7881B990B08957AE0EE -:107F6000979F902D879F802D910D11240895DC01FE -:107F7000CB01FC01F999FECF06C0F2BDE1BDF89A34 -:107F8000319600B40D9241505040B8F70895DC018D -:107F9000A40FB51F4150504040F0CB01840F951FF6 -:107FA0002E9105D041505040D8F70895262FF999C9 -:107FB000FECF92BD81BDF89A019700B4021639F048 -:107FC0001FBA20BD0FB6F894FA9AF99A0FBE089519 -:027FD000FF00B0 +:1078000011E0A0E0B1E0E6EFFFE702C005900D92C5 +:10781000A630B107D9F721E0A6E0B1E001C01D9282 +:10782000AE34B207E1F7CDB7DEB76197DEBFCDBFAB +:10783000112484B78093270114BE88E180936000EF +:1078400089E280936000259A86E020E295B1922734 +:1078500095B93FE748E391E0315040409040E1F76F +:1078600000C00000815091F78091270181FD13C075 +:107870008EE284B981E58CBD81E08DBD812C912C97 +:107880005524539411E0CC24CA94DC2CAA24A3944C +:1078900072E0B72EFDC082E08093C00088E1809343 +:1078A000C10086E08093C20089E18093C400E12C8E +:1078B000F12CC12CD12CAA24AA94BA2C01E0A895B1 +:1078C0000AD3813471F407D3898B11D38989823823 +:1078D00011F482E053C0813811F485E64FC083E093 +:1078E0004DC0823411F484E103C0853419F485E07D +:1078F0000AD363C0853541F4EED2E82EECD2F12CE8 +:10790000F82AEE0CFF1C58C0863521F484E0FBD227 +:1079100080E034C08436E1F4DED2DDD2882EDBD2C2 +:10792000C80CD11C912C981480F4D5D2182FD3D226 +:10793000F701E90DF11D212F30E0382B090107BFB8 +:10794000E895112493949394EECFC7018CD134C061 +:10795000843761F4C0D2BFD2182FBDD2C8D2F7018C +:1079600085917F01B1D21150D1F728C0853741F4FC +:10797000BED28EE1A9D285E9A7D28FE0A5D21DC0E3 +:107980008135D1F4BA82A982BC82AB82C60134E0CF +:10799000969587953A95E1F79E838D83C60175D1BB +:1079A00098878F8348E050E06BE171E0CE0101964B +:1079B00001D39DD211E002C09AD210E080E184D2BE +:1079C000112309F47CCF54CF43E050E060E070E035 +:1079D00089E291E0DFD248E050E06BE171E0CE0156 +:1079E0000196D8D229982A9A63E083E29AD164E575 +:1079F00085E297D167E286E294D163E780E591D191 +:107A000064E08DE38ED161E082E28BD161E081E2BE +:107A100088D161E08CE385D121E041E060E070E055 +:107A200086E058D1873209F008C180912A018093FD +:107A3000280110924D018FEF80932A0180932B0132 +:107A400003E22AE047E06FEF65D260E084E269D1AB +:107A500060E08EEF83D180912A018F3F09F4EDC061 +:107A60008091290181508E3F08F46CC003E222E02E +:107A700043E06FEF80E04ED263E084E06FD1882373 +:107A800079F383E391E054D2682F80E090E0A1D2B3 +:107A9000F999FECF99CF46E050E063E371E08DE0C5 +:107AA00091E063D2892B79F5C3018150904F082F63 +:107AB0000295007740E050E0602F70E0DA01A45DAD +:107AC000BE4FFA01E60FF71F1E962C911E9730E06D +:107AD000322F22271D96AC912A2B090157BEE8951B +:107AE00011244E5F5F4F4031510541F7011106C02F +:107AF00044E0880F991F4A95E1F7B5D0E114F104ED +:107B000009F482C0370189859A8590930E0180938C +:107B10000D018B859C859093100180930F0173015B +:107B200021E0E21AF108F0921201E092110104EC56 +:107B300022E342E06FEF80E0EDD168E083E00ED118 +:107B40008111A9CF6FE584E2ECD010930D01109262 +:107B50000E0103EC22E14FE06FEF80E0DBD160E04B +:107B60008FE0FCD08D819E8124E0880F991F2A959B +:107B7000E1F78BD098878F8388E0FE013196ADE0E6 +:107B8000B1E001900D928A95E1F7B0921601A092B2 +:107B9000150104EC22E540E06FEF80E0BBD163E02B +:107BA00081E0DCD0882309F448C088E0E3E3F1E019 +:107BB000DE01199601900D928A95E1F748E050E0B8 +:107BC000BE01675F7F4FCE010196CFD1892BA9F10E +:107BD00080913701909138018038974070F588E0A6 +:107BE000FE013996DE01119601900D928A95E1F71A +:107BF000D886CF8248E050E06BE171E0CE0101967B +:107C0000D9D16D847E847FCF8D859E8534E0880FA9 +:107C1000991F3A95E1F739D02F8538898217930754 +:107C200021F01E861D86188A1F8648E050E06BE111 +:107C300071E0CE010996BED12BCF8D819E8124E0CB +:107C4000880F991F2A95E1F720D02F813885821758 +:107C5000930709F01DCF1CBCA89580912701282E01 +:107C6000F401099515CF23E0FC0127BFE89507B67D +:107C700000FCFDCF25E0FC0127BFE89507B600FC1E +:107C8000FDCF81E187BFE8950895AC01413088E7D9 +:107C9000580710F040E058E7E0E0F0E08FEF9FEF8A +:107CA00041155105B1F02591822768E0DC01B695B8 +:107CB000A7959C01217033273195219531092170B9 +:107CC000307AC9018A279B27615081F741505109B9 +:107CD000E7CF08952A988EBD0DB407FEFDCF8EB56F +:107CE0009FEF4150D0F0222369F09EBD0DB407FEF6 +:107CF000FDCF8EB561157105A1F3DB018C936F5F2C +:107D00007F4FEFCFFB013196DB018C918EBD0DB41F +:107D100007FEFDCF8EB5BF01E4CF2A9A089520E07B +:107D2000D9CFCF93DF931F92CDB7DEB7698320E021 +:107D300041E0BE016F5F7F4FCDDF0F90DF91CF91AC +:107D4000089540E060E070E082EEE9DF40E060E04E +:107D500070E081EEE4DF60E787E2E3CFEF92FF922D +:107D60000F931F93CF93DF93F82ED62FEE24EA9430 +:107D7000A895C0910901C695C695C695C95F109191 +:107D80002A0129986EE080E2CCDFDBDF109300014E +:107D900045E060E071E08AE2C2DF45E060E071E06A +:107DA00080E3BDDF4C2F66E071E080EAB8DF299AFE +:107DB0002A98EEBC0DB407FEFDCFCEB52A9A8C2FC3 +:107DC0008073B1F3299860E387E2ABDFC4FF05C09D +:107DD00040E060E070E081EEA2DF6FE080E2A1DFD2 +:107DE000609129018AE29DDFACDF299A0FEF1FEF36 +:107DF000A89521E041E060E070E087E16BDF80FF63 +:107E000002C04FEF44C021E041E060E070E080E656 +:107E100061DF813230F040E060E070E082EE7FDFD1 +:107E200080E021E0482F6CE271E081E653DF89DFDA +:107E300090912E01809129019813E3CF4091310157 +:107E4000809130018770833011F5483001F58091C1 +:107E50002D0150912801851303C091E090934D01AD +:107E600090913301692F70E020912B0130E0215077 +:107E700031096217730724F420914D01222311F078 +:107E8000851305C09F5F90932B0180932A0191E099 +:107E90004F1190E088E28A95F1F70150110921F421 +:107EA000892FD11105C009C0992309F4A1CF04C0BD +:107EB000D150992309F45CCF81E0DF91CF911F91DC +:107EC0000F91FF90EF9008959091C00095FFFCCF27 +:107ED0008093C60008958091C00087FFFCCF8091F9 +:107EE000C00084FD01C0A8958091C6000895F3DF0D +:107EF000803239F088E18093600088E080936000F0 +:107F0000FFCF84E1E1CFCF93C82FE5DFC150E9F780 +:107F1000CF91EDCF0F9390912901E6E0F1E09183AD +:107F20009083828366834583048323830F9108951E +:107F3000FC0188279927E89421912032E9F3293020 +:107F400010F02E30C8F32B3239F02D3231F4689412 +:107F500003C018D0820F911D219120532A30C8F3FD +:107F60001EF4909581959F4F0895FB01DC0104C09C +:107F70008D910190801921F441505040C8F7881B21 +:107F8000990B08957AE0979F902D879F802D910DF2 +:107F900011240895DC01CB01FC01F999FECF06C044 +:107FA000F2BDE1BDF89A319600B40D9241505040B7 +:107FB000B8F70895DC01A40FB51F4150504040F0C0 +:107FC000CB01840F951F2E9105D041505040D8F71A +:107FD0000895262FF999FECF92BD81BDF89A019799 +:107FE00000B4021639F01FBA20BD0FB6F894FA9A01 +:067FF000F99A0FBE08958E +:067FF60000FCE1A8A80058 :040000030000780081 :00000001FF diff --git a/MYSBootloaderHW.h b/MYSBootloaderHW.h deleted file mode 100644 index d62b543..0000000 --- a/MYSBootloaderHW.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef MYSBootloaderHW_H -#define MYSBootloaderHW_H - -#include - -// hardware - -#define F_CPU_DIV clock_div_4 -#define F_CPU_REAL F_CPU / (F_CPU_DIV) -#define BAUD_SETTING (( (F_CPU_REAL + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 ) -#define BAUD_ACTUAL (F_CPU_REAL/(8 * ((BAUD_SETTING)+1))) -#define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE) -#define UART_SRA UCSR0A -#define UART_SRB UCSR0B -#define UART_SRC UCSR0C -#define UART_SRL UBRR0L -#define UART_UDR UDR0 - -// boolean definition - -typedef uint8_t bool; -typedef uint8_t boolean; -#define false 0x0 -#define true 0x1 -#define LOW 0x0 -#define HIGH 0x1 - -// Watchdog definitions and functions - -#define WATCHDOG_OFF (0) -#define WATCHDOG_16MS (_BV(WDE)) -#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE)) -#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE)) -#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE)) -#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE)) -#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE)) -#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE)) -#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE)) -#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE)) -#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE)) - -inline void watchdogReset() { - asm volatile ("wdr"); -} - -void watchdogConfig(uint8_t x) { - WDTCSR = _BV(WDCE) | _BV(WDE); - WDTCSR = x; -} - -// SPI communication - -#define SPI_DDR DDRB -#define SPI_PORT PORTB -#define SPI_PIN PINB -#define SPI_SCLK 5 // Arduino Pin 13 <-> Bit 5 of port B -#define SPI_MISO 4 // Arduino Pin 12 <-> Bit 4 of port B -#define SPI_MOSI 3 // Arduino Pin 11 <-> Bit 3 of port B -#define SPI_CSN 2 // Arduino Pin 10 <-> Bit 2 of port B -#define SPI_CE 1 // Arduino Pin 9 <-> Bit 1 of port B -#define CE_PULSE_LENGTH 20 // IMPORTANT: minimum CE pulse width 10us, see nRF24L01 specs. Set 20us to be on the safe side - -#define csnlow() SPI_PORT &= ~_BV(SPI_CSN) -#define csnhigh() SPI_PORT |= _BV(SPI_CSN) -#define celow() SPI_PORT &= ~_BV(SPI_CE) -#define cehigh() SPI_PORT |= _BV(SPI_CE) - -static void SPIinit() { - // set pin mode: MOSI,SCLK,CE = OUTPUT, MISO = INPUT - SPI_DDR = _BV(SPI_MOSI) | _BV(SPI_SCLK) | _BV(SPI_CE) | _BV(SPI_CSN) | ~_BV(SPI_MISO); -} - -static uint8_t SPItransfer(uint8_t value) { - // bit bang SPI - for(uint8_t i = 0; i<8; i++) { - if (value & 0x80) SPI_PORT |= _BV(SPI_MOSI); else SPI_PORT &= ~_BV(SPI_MOSI); - //_delay_us(1); - value <<= 1; - SPI_PORT |= _BV(SPI_SCLK); - //_delay_us(1); - value |= ((SPI_PIN >> SPI_MISO) & 0x01); - SPI_PORT &= ~_BV(SPI_SCLK); - //_delay_us(1); - } - return value; -} - - -// UART debug - -static void uart_init() { - //Double speed mode USART0 - UART_SRA = _BV(U2X0); - UART_SRB = _BV(RXEN0) | _BV(TXEN0); - UART_SRC = _BV(UCSZ00) | _BV(UCSZ01); - UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); -} - -void putch(char ch) { - while (!(UART_SRA & _BV(UDRE0))); - UART_UDR = ch; -} - -uint8_t getch() { - uint8_t ch; - // wait until char received - while(!(UART_SRA & _BV(RXC0))); - // framing error? - if (!(UART_SRA & _BV(FE0))) { - watchdogReset(); - } - ch = UART_UDR; - return ch; -} - -static void put_string(char *s) { - while (*s) - putch(*s++); -} - -static void put_int(uint8_t i) { - char s[5]; - itoa( i, s, 10 ); - put_string( s ); -} - - -#endif // MYSBootloaderHW_H diff --git a/MYSBootloaderRF24.h b/MYSBootloaderRF24.h deleted file mode 100644 index c290527..0000000 --- a/MYSBootloaderRF24.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef MYSBootloaderRF24_H -#define MYSBootloaderRF24_H - -#include "MYSBootloaderHW.h" -#include "nRF24L01.h" - -typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; -typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; - -#define _write_register(reg) ( (reg) | W_REGISTER) -#define _read_register(reg) ( (reg) | R_REGISTER) -#define addr_width 5 - -uint64_t pipe0_reading_address = 0; - -static uint8_t BurstReadAddress(uint8_t addr, uint8_t* buf, uint8_t len) { - csnlow(); - uint8_t status = SPItransfer( addr ); - while ( len-- ) { - status = SPItransfer(NOP); - *buf++ = status; - } - csnhigh(); - return status; -} - -static uint8_t BurstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) { - csnlow(); - uint8_t status = SPItransfer( addr ); - while ( len-- ) { - status = SPItransfer(*buf++); - } - csnhigh(); - return status; -} - -static uint8_t ReadAddress(uint8_t addr) { - return BurstReadAddress(addr,NULL,1); -} - - -static uint8_t WriteAddress(uint8_t addr, uint8_t val) { - return BurstWriteAddress(addr,&val,1); -} - -#define BurstWriteRegister(reg,buf,len) BurstWriteAddress(_write_register(reg), buf, len) -#define WriteRegister(reg,val) WriteAddress(_write_register(reg), val) -#define BurstReadRegister(reg,buf,len) BurstReadAddress(_read_register(reg), buf, len) -#define ReadRegister(reg) ReadAddress(_read_register(reg)) -#define get_status() BurstReadAddress(NOP, NULL, 0) - -void Flush_RX(void) { - BurstWriteAddress(FLUSH_RX, NULL, 0); -} - -void Flush_TX(void) { - BurstWriteAddress(FLUSH_TX, NULL, 0); -} - -static void Flush_RXTX_CLI(void) { - // flush RX and TX buffer - Flush_RX(); - Flush_TX(); - // clear registers - WriteRegister(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); -} - -static void startListening(void) { - // for size reasons, we do not read back the register content, set PRIM_RX - WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | _BV(PRIM_RX)) ; - Flush_RXTX_CLI(); - // write pipe0 address - if (pipe0_reading_address){ - BurstWriteRegister(RX_ADDR_P0, (uint8_t*)&pipe0_reading_address, addr_width); - } - cehigh(); -} - -static void stopListening(void) { - celow(); - Flush_RXTX_CLI(); - // for size reasons, we do not read back the register content, clear PRIM_RX - WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | ~_BV(PRIM_RX)) ; -} - - - -static boolean write_buf(uint8_t* buf, uint8_t len, const bool multicast ) { - // write payload to FIFO - BurstWriteRegister( multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, buf, len) ; - // CE pulse to start transmission - cehigh(); - // IMPORTANT: minimum CE pulse width 10us, see nRF24L01 specs - _delay_us(CE_PULSE_LENGTH); - // start transmitting - celow(); - - - // wait until sent or ACKed, here we potentially have a deadlock when transmitter not connected/not working => wdt recovers - while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } - // read interrupts and clear - uint8_t status = WriteRegister(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - // max retries? clear FIFO - if( status & _BV(MAX_RT)){ - Flush_TX(); - return false; - } - return true; -} - -static uint8_t getDynamicPayloadSize(void) { - uint8_t result = ReadAddress(R_RX_PL_WID); - if(result > 32) { - Flush_RX(); - result = 0; - } - return result; -} - - -static boolean available(uint8_t* pipe_num) { - if (!(ReadAddress(FIFO_STATUS) & _BV(RX_EMPTY) )){ - if (pipe_num) { - uint8_t status = get_status(); - *pipe_num = ( status >> RX_P_NO ) & 0b111; - } - return true; - } - return false; -} - - -static uint8_t readMessage(uint8_t* buf) { - // read payload - uint8_t PL_LEN = getDynamicPayloadSize(); - BurstReadRegister(R_RX_PAYLOAD,buf,PL_LEN); - // reset interrupts - WriteRegister(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); - return PL_LEN; -} - -static void openWritingPipe(uint64_t value) { - BurstWriteRegister(RX_ADDR_P0, (uint8_t*)&value, addr_width); - BurstWriteRegister(TX_ADDR, (uint8_t*)&value, addr_width); -} - -static void openReadingPipe(uint8_t pipe, uint64_t address) { - // If this is pipe 0, cache the address. This is needed because openWritingPipe() will overwrite the pipe 0 address, so - // startListening() will have to restore it - if (pipe == WRITE_PIPE) { - memcpy(&pipe0_reading_address,&address,addr_width); - } - // only write full address for pipe 0 and 1 - // since we do not use the BROADCASTING PIPE in this bootloader, we can remove the check - //if ( pipe < 2 ) - { - BurstWriteRegister(RX_ADDR_P0 + pipe, (uint8_t*)&address, addr_width); - } - - -} - -static boolean write(uint8_t destination, uint8_t* buf, uint8_t len, boolean multicast) { - stopListening(); - openWritingPipe(TO_ADDR(destination)); - boolean result = write_buf(buf, len, multicast); - startListening(); - return result; -} - -static void setAddress(uint8_t addr) -{ - if (addr != BROADCAST_ADDRESS) { - openReadingPipe(WRITE_PIPE, TO_ADDR(addr)); - openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(addr)); - // enable pipe - WriteRegister(EN_RXADDR,_BV(ERX_P0 + WRITE_PIPE) | _BV(ERX_P0 + CURRENT_NODE_PIPE)); - } -} - - - -static void RFinit(void){ - // set address width - WriteRegister(SETUP_AW, (addr_width-2) % 4); - // auto retransmit delay 1500us, auto retransmit count 15 - WriteRegister(SETUP_RETR, 5 << ARD | 15 << ARC); - // enable auto ack on all pipes, except broadcasting pipe - WriteRegister(EN_AA, 0b00111111 & ~_BV(BROADCAST_PIPE)) ; - // set channel - WriteRegister(RF_CH, RF24_CHANNEL); - // set data rate - WriteRegister(RF_SETUP, ((RF24_DATARATE & 0b00000010 ) << 4) | ((RF24_DATARATE & 0b00000001 ) << 3) | ((RF24_PA_LEVEL << 1) + 1)); - // flush RX and TX FIFO, clear interrupts - Flush_RXTX_CLI(); - // activate to unlock features - WriteAddress(ACTIVATE,0x73); - // enable payload with ACK and dynamic payload length - WriteRegister(FEATURE, _BV(EN_ACK_PAY) | _BV(EN_DPL) ); - // Enable dynamic payload length on all pipes - WriteRegister(DYNPD, _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); - //powerUp, enable 16bit CRC, no RX mode - WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | ~_BV(PRIM_RX)); - // stabilize 5ms - _delay_ms(5); -} - -#endif // MYSBootloaderRF24_H diff --git a/Makefile b/Makefile index faabf2a..beb5ca4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ PROJECT = MYSBootloader -INCLUDES = ../libraries/MySensors MCU = atmega328p CLK = 16000000L diff --git a/MyEepromAddresses.h b/MyEepromAddresses.h new file mode 100644 index 0000000..1150233 --- /dev/null +++ b/MyEepromAddresses.h @@ -0,0 +1,89 @@ +/** + * 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 + * Copyright (C) 2013-2016 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. + */ + +/** +* @file MyEepromAddresses.h +* @brief Eeprom addresses for MySensors library data +* +* @defgroup MyEepromAddressesgrp MyEepromAddresses +* @ingroup internals +* @{ +* +*/ + + +#ifndef MyEepromAddresses_h +#define MyEepromAddresses_h + +// EEPROM variable sizes, in bytes +#define SIZE_NODE_ID (1) //!< Size node ID +#define SIZE_PARENT_NODE_ID (1) //!< Size parent node ID +#define SIZE_DISTANCE (1) //!< Size GW distance +#define SIZE_ROUTES (256) //!< Size routing table +#define SIZE_CONTROLLER_CONFIG (24) //!< Size controller config +#define SIZE_FIRMWARE_TYPE (2) //!< Size firmware type +#define SIZE_FIRMWARE_VERSION (2) //!< Size firmware version +#define SIZE_FIRMWARE_BLOCKS (2) //!< Size firmware blocks +#define SIZE_FIRMWARE_CRC (2) //!< Size firmware CRC +#define SIZE_SIGNING_REQUIREMENT_TABLE (32) //!< Size signing requirement table +#define SIZE_WHITELIST_REQUIREMENT_TABLE (32) //!< Size whitelist requirement table +#define SIZE_SIGNING_SOFT_HMAC_KEY (32) //!< Size soft signing HMAC key +#define SIZE_SIGNING_SOFT_SERIAL (9) //!< Size soft signing serial +#define SIZE_RF_ENCRYPTION_AES_KEY (16) //!< Size RF AES encryption key +#define SIZE_NODE_LOCK_COUNTER (1) //!< Size node lock counter + + +/** @brief EEPROM start address */ +#define EEPROM_START 0 +/** @brief Address node ID */ +#define EEPROM_NODE_ID_ADDRESS EEPROM_START +/** @brief Address parent node ID */ +#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_NODE_ID_ADDRESS + SIZE_NODE_ID) +/** @brief Address distance to GW */ +#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS + SIZE_PARENT_NODE_ID) +/** @brief Address routing table */ +#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS + SIZE_DISTANCE) +/** @brief Address configuration bytes sent by controller */ +#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS + SIZE_ROUTES) +/** @brief Address firmware type */ +#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS + SIZE_CONTROLLER_CONFIG) +/** @brief Address firmware version */ +#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS + SIZE_FIRMWARE_TYPE) +/** @brief Address firmware blocks */ +#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS + SIZE_FIRMWARE_VERSION) +/** @brief Address firmware CRC */ +#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS + SIZE_FIRMWARE_BLOCKS) +/** @brief Address signing requirement table */ +#define EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS + SIZE_FIRMWARE_CRC) +/** @brief Address whitelist requirement table */ +#define EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS (EEPROM_SIGNING_REQUIREMENT_TABLE_ADDRESS + SIZE_SIGNING_REQUIREMENT_TABLE) +/** @brief Address soft signing HMAC key. This is set with @ref SecurityPersonalizer.ino */ +#define EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS (EEPROM_WHITELIST_REQUIREMENT_TABLE_ADDRESS + SIZE_WHITELIST_REQUIREMENT_TABLE) +/** @brief Address soft signing serial key. This is set with @ref SecurityPersonalizer.ino */ +#define EEPROM_SIGNING_SOFT_SERIAL_ADDRESS (EEPROM_SIGNING_SOFT_HMAC_KEY_ADDRESS + SIZE_SIGNING_SOFT_HMAC_KEY) +/** @brief Address RF AES encryption key. This is set with @ref SecurityPersonalizer.ino */ +#define EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS (EEPROM_SIGNING_SOFT_SERIAL_ADDRESS + SIZE_SIGNING_SOFT_SERIAL) +/** @brief Address node lock couner. This is set with @ref SecurityPersonalizer.ino */ +#define EEPROM_NODE_LOCK_COUNTER (EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS + SIZE_RF_ENCRYPTION_AES_KEY) +/** @brief First free address for sketch static configuration */ +#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_NODE_LOCK_COUNTER + SIZE_NODE_LOCK_COUNTER) + +#endif // MyEepromAddresses_h + +/** @}*/ diff --git a/MyMessage.h b/MyMessage.h new file mode 100644 index 0000000..59c112a --- /dev/null +++ b/MyMessage.h @@ -0,0 +1,296 @@ +/* + * 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 + * Copyright (C) 2013-2016 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. + */ + +#ifndef MyMessage_h +#define MyMessage_h + +#define PROTOCOL_VERSION (2) //!< The version of the protocol +#define MAX_MESSAGE_LENGTH (32) //!< The maximum size of a message (including header) +#define HEADER_SIZE (7) //!< The size of the header +#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_LENGTH and #HEADER_SIZE + +/// @brief The command field (message-type) defines the overall properties of a message +typedef enum { + C_PRESENTATION = 0, //!< Sent by a node when they present attached sensors. This is usually done in presentation() at startup. + C_SET = 1, //!< This message is sent from or to a sensor when a sensor value should be updated. + C_REQ = 2, //!< Requests a variable value (usually from an actuator destined for controller). + C_INTERNAL = 3, //!< Internal MySensors messages (also include common messages provided/generated by the library). + C_STREAM = 4 //!< For firmware and other larger chunks of data that need to be divided into pieces. +} mysensor_command; + +/// @brief Type of sensor (used when presenting sensors) +typedef enum { + S_DOOR = 0, //!< Door sensor, V_TRIPPED, V_ARMED + S_MOTION = 1, //!< Motion sensor, V_TRIPPED, V_ARMED + S_SMOKE = 2, //!< Smoke sensor, V_TRIPPED, V_ARMED + S_BINARY = 3, //!< Binary light or relay, V_STATUS, V_WATT + S_LIGHT = 3, //!< \deprecated Same as S_BINARY, **** DEPRECATED, DO NOT USE **** + S_DIMMER = 4, //!< Dimmable light or fan device, V_STATUS (on/off), V_PERCENTAGE (dimmer level 0-100), V_WATT + S_COVER = 5, //!< Blinds or window cover, V_UP, V_DOWN, V_STOP, V_PERCENTAGE (open/close to a percentage) + S_TEMP = 6, //!< Temperature sensor, V_TEMP + S_HUM = 7, //!< Humidity sensor, V_HUM + S_BARO = 8, //!< Barometer sensor, V_PRESSURE, V_FORECAST + S_WIND = 9, //!< Wind sensor, V_WIND, V_GUST + S_RAIN = 10, //!< Rain sensor, V_RAIN, V_RAINRATE + S_UV = 11, //!< Uv sensor, V_UV + S_WEIGHT = 12, //!< Personal scale sensor, V_WEIGHT, V_IMPEDANCE + S_POWER = 13, //!< Power meter, V_WATT, V_KWH, V_VAR, V_VA, V_POWER_FACTOR + S_HEATER = 14, //!< Header device, V_HVAC_SETPOINT_HEAT, V_HVAC_FLOW_STATE, V_TEMP + S_DISTANCE = 15, //!< Distance sensor, V_DISTANCE + S_LIGHT_LEVEL = 16, //!< Light level sensor, V_LIGHT_LEVEL (uncalibrated in percentage), V_LEVEL (light level in lux) + S_ARDUINO_NODE = 17, //!< Used (internally) for presenting a non-repeating Arduino node + S_ARDUINO_REPEATER_NODE = 18, //!< Used (internally) for presenting a repeating Arduino node + S_LOCK = 19, //!< Lock device, V_LOCK_STATUS + S_IR = 20, //!< IR device, V_IR_SEND, V_IR_RECEIVE + S_WATER = 21, //!< Water meter, V_FLOW, V_VOLUME + S_AIR_QUALITY = 22, //!< Air quality sensor, V_LEVEL + S_CUSTOM = 23, //!< Custom sensor + S_DUST = 24, //!< Dust sensor, V_LEVEL + S_SCENE_CONTROLLER = 25, //!< Scene controller device, V_SCENE_ON, V_SCENE_OFF. + S_RGB_LIGHT = 26, //!< RGB light. Send color component data using V_RGB. Also supports V_WATT + S_RGBW_LIGHT = 27, //!< RGB light with an additional White component. Send data using V_RGBW. Also supports V_WATT + S_COLOR_SENSOR = 28, //!< Color sensor, send color information using V_RGB + S_HVAC = 29, //!< Thermostat/HVAC device. V_HVAC_SETPOINT_HEAT, V_HVAC_SETPOINT_COLD, V_HVAC_FLOW_STATE, V_HVAC_FLOW_MODE, V_TEMP + S_MULTIMETER = 30, //!< Multimeter device, V_VOLTAGE, V_CURRENT, V_IMPEDANCE + S_SPRINKLER = 31, //!< Sprinkler, V_STATUS (turn on/off), V_TRIPPED (if fire detecting device) + S_WATER_LEAK = 32, //!< Water leak sensor, V_TRIPPED, V_ARMED + S_SOUND = 33, //!< Sound sensor, V_TRIPPED, V_ARMED, V_LEVEL (sound level in dB) + S_VIBRATION = 34, //!< Vibration sensor, V_TRIPPED, V_ARMED, V_LEVEL (vibration in Hz) + S_MOISTURE = 35, //!< Moisture sensor, V_TRIPPED, V_ARMED, V_LEVEL (water content or moisture in percentage?) + S_INFO = 36, //!< LCD text device / Simple information device on controller, V_TEXT + S_GAS = 37, //!< Gas meter, V_FLOW, V_VOLUME + S_GPS = 38, //!< GPS Sensor, V_POSITION + S_WATER_QUALITY = 39 //!< V_TEMP, V_PH, V_ORP, V_EC, V_STATUS +} mysensor_sensor; + +/// @brief Type of sensor data (for set/req/ack messages) +typedef enum { + V_TEMP = 0, //!< S_TEMP. Temperature S_TEMP, S_HEATER, S_HVAC + V_HUM = 1, //!< S_HUM. Humidity + V_STATUS = 2, //!< S_BINARY, S_DIMMER, S_SPRINKLER, S_HVAC, S_HEATER. Used for setting/reporting binary (on/off) status. 1=on, 0=off + V_LIGHT = 2, //!< \deprecated Same as V_STATUS, **** DEPRECATED, DO NOT USE **** + V_PERCENTAGE = 3, //!< S_DIMMER. Used for sending a percentage value 0-100 (%). + V_DIMMER = 3, //!< \deprecated Same as V_PERCENTAGE, **** DEPRECATED, DO NOT USE **** + V_PRESSURE = 4, //!< S_BARO. Atmospheric Pressure + V_FORECAST = 5, //!< S_BARO. Whether forecast. string of "stable", "sunny", "cloudy", "unstable", "thunderstorm" or "unknown" + V_RAIN = 6, //!< S_RAIN. Amount of rain + V_RAINRATE = 7, //!< S_RAIN. Rate of rain + V_WIND = 8, //!< S_WIND. Wind speed + V_GUST = 9, //!< S_WIND. Gust + V_DIRECTION = 10, //!< S_WIND. Wind direction 0-360 (degrees) + V_UV = 11, //!< S_UV. UV light level + V_WEIGHT = 12, //!< S_WEIGHT. Weight(for scales etc) + V_DISTANCE = 13, //!< S_DISTANCE. Distance + V_IMPEDANCE = 14, //!< S_MULTIMETER, S_WEIGHT. Impedance value + V_ARMED = 15, //!< S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER. Armed status of a security sensor. 1 = Armed, 0 = Bypassed + V_TRIPPED = 16, //!< S_DOOR, S_MOTION, S_SMOKE, S_SPRINKLER, S_WATER_LEAK, S_SOUND, S_VIBRATION, S_MOISTURE. Tripped status of a security sensor. 1 = Tripped, 0 + V_WATT = 17, //!< S_POWER, S_BINARY, S_DIMMER, S_RGB_LIGHT, S_RGBW_LIGHT. Watt value for power meters + V_KWH = 18, //!< S_POWER. Accumulated number of KWH for a power meter + V_SCENE_ON = 19, //!< S_SCENE_CONTROLLER. Turn on a scene + V_SCENE_OFF = 20, //!< S_SCENE_CONTROLLER. Turn of a scene + V_HVAC_FLOW_STATE = 21, //!< S_HEATER, S_HVAC. HVAC flow state ("Off", "HeatOn", "CoolOn", or "AutoChangeOver") + V_HEATER = 21, //!< \deprecated Same as V_HVAC_FLOW_STATE, **** DEPRECATED, DO NOT USE **** + V_HVAC_SPEED = 22, //!< S_HVAC, S_HEATER. HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto") + V_LIGHT_LEVEL = 23, //!< S_LIGHT_LEVEL. Uncalibrated light level. 0-100%. Use V_LEVEL for light level in lux + V_VAR1 = 24, //!< VAR1 + V_VAR2 = 25, //!< VAR2 + V_VAR3 = 26, //!< VAR3 + V_VAR4 = 27, //!< VAR4 + V_VAR5 = 28, //!< VAR5 + V_UP = 29, //!< S_COVER. Window covering. Up + V_DOWN = 30, //!< S_COVER. Window covering. Down + V_STOP = 31, //!< S_COVER. Window covering. Stop + V_IR_SEND = 32, //!< S_IR. Send out an IR-command + V_IR_RECEIVE = 33, //!< S_IR. This message contains a received IR-command + V_FLOW = 34, //!< S_WATER. Flow of water (in meter) + V_VOLUME = 35, //!< S_WATER. Water volume + V_LOCK_STATUS = 36, //!< S_LOCK. Set or get lock status. 1=Locked, 0=Unlocked + V_LEVEL = 37, //!< S_DUST, S_AIR_QUALITY, S_SOUND (dB), S_VIBRATION (hz), S_LIGHT_LEVEL (lux) + V_VOLTAGE = 38, //!< S_MULTIMETER + V_CURRENT = 39, //!< S_MULTIMETER + V_RGB = 40, //!< S_RGB_LIGHT, S_COLOR_SENSOR. Sent as ASCII hex: RRGGBB (RR=red, GG=green, BB=blue component) + V_RGBW = 41, //!< S_RGBW_LIGHT. Sent as ASCII hex: RRGGBBWW (WW=white component) + V_ID = 42, //!< Used for sending in sensors hardware ids (i.e. OneWire DS1820b). + V_UNIT_PREFIX = 43, //!< Allows sensors to send in a string representing the unit prefix to be displayed in GUI, not parsed by controller! E.g. cm, m, km, inch. + V_HVAC_SETPOINT_COOL = 44, //!< S_HVAC. HVAC cool setpoint (Integer between 0-100) + V_HVAC_SETPOINT_HEAT = 45, //!< S_HEATER, S_HVAC. HVAC/Heater setpoint (Integer between 0-100) + V_HVAC_FLOW_MODE = 46, //!< S_HVAC. Flow mode for HVAC ("Auto", "ContinuousOn", "PeriodicOn") + V_TEXT = 47, //!< S_INFO. Text message to display on LCD or controller device + V_CUSTOM = 48, //!< Custom messages used for controller/inter node specific commands, preferably using S_CUSTOM device type. + V_POSITION = 49, //!< GPS position and altitude. Payload: latitude;longitude;altitude(m). E.g. "55.722526;13.017972;18" + V_IR_RECORD = 50, //!< Record IR codes S_IR for playback + V_PH = 51, //!< S_WATER_QUALITY, water PH + V_ORP = 52, //!< S_WATER_QUALITY, water ORP : redox potential in mV + V_EC = 53, //!< S_WATER_QUALITY, water electric conductivity μS/cm (microSiemens/cm) + V_VAR = 54, //!< S_POWER, Reactive power: volt-ampere reactive (var) + V_VA = 55, //!< S_POWER, Apparent power: volt-ampere (VA) + V_POWER_FACTOR = 56, //!< S_POWER, Ratio of real power to apparent power: floating point value in the range [-1,..,1] +} mysensor_data; + + +/// @brief Type of internal messages (for internal messages) +typedef enum { + I_BATTERY_LEVEL = 0, //!< Battery level + I_TIME = 1, //!< Time (request/response) + I_VERSION = 2, //!< Version + I_ID_REQUEST = 3, //!< ID request + I_ID_RESPONSE = 4, //!< ID response + I_INCLUSION_MODE = 5, //!< Inclusion mode + I_CONFIG = 6, //!< Config (request/response) + I_FIND_PARENT_REQUEST = 7, //!< Find parent + I_FIND_PARENT_RESPONSE = 8, //!< Find parent response + I_LOG_MESSAGE = 9, //!< Log message + I_CHILDREN = 10, //!< Children + I_SKETCH_NAME = 11, //!< Sketch name + I_SKETCH_VERSION = 12, //!< Sketch version + I_REBOOT = 13, //!< Reboot request + I_GATEWAY_READY = 14, //!< Gateway ready + I_SIGNING_PRESENTATION = 15, //!< Provides signing related preferences (first byte is preference version) + I_NONCE_REQUEST = 16, //!< Request for a nonce + I_NONCE_RESPONSE = 17, //!< Payload is nonce data + I_HEARTBEAT_REQUEST = 18, //!< Heartbeat request + I_PRESENTATION = 19, //!< Presentation message + I_DISCOVER_REQUEST = 20, //!< Discover request + I_DISCOVER_RESPONSE = 21, //!< Discover response + I_HEARTBEAT_RESPONSE = 22, //!< Heartbeat response + I_LOCKED = 23, //!< Node is locked (reason in string-payload) + I_PING = 24, //!< Ping sent to node, payload incremental hop counter + I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter + I_REGISTRATION_REQUEST = 26, //!< Register request to GW + I_REGISTRATION_RESPONSE = 27, //!< Register response from GW + I_DEBUG = 28 //!< Debug message +} mysensor_internal; + + +/// @brief Type of data stream (for streamed message) +typedef enum { + ST_FIRMWARE_CONFIG_REQUEST = 0, //!< Request new FW, payload contains current FW details + ST_FIRMWARE_CONFIG_RESPONSE = 1, //!< New FW details to initiate OTA FW update + ST_FIRMWARE_REQUEST = 2, //!< Request FW block + ST_FIRMWARE_RESPONSE = 3, //!< Response FW block + ST_SOUND = 4, //!< Sound + ST_IMAGE = 5 //!< Image +} mysensor_stream; + +/// @brief Type of payload +typedef enum { + P_STRING = 0, //!< Payload type is string + P_BYTE = 1, //!< Payload type is byte + P_INT16 = 2, //!< Payload type is INT16 + P_UINT16 = 3, //!< Payload type is UINT16 + P_LONG32 = 4, //!< Payload type is INT32 + P_ULONG32 = 5, //!< Payload type is UINT32 + P_CUSTOM = 6, //!< Payload type is binary + P_FLOAT32 = 7 //!< Payload type is float32 +} mysensor_payload; + + + +#ifndef BIT +#define BIT(n) ( 1<<(n) ) //!< Bit indexing macro +#endif +#define BIT_MASK(len) ( BIT(len)-1 ) //!< Create a bitmask of length 'len' +#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) ) //!< Create a bitfield mask of length starting at bit 'start' + +#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) ) //!< Prepare a bitmask for insertion or combining +#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) ) //!< Extract a bitfield of length 'len' starting at bit 'start' from 'y' +#define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) ) //!< Insert a new bitfield value 'x' into 'y' + +// Getters/setters for special bit fields in header +#define mSetVersion(_message,_version) BF_SET(_message.version_length, _version, 0, 2) //!< Set version field +#define mGetVersion(_message) ((uint8_t)BF_GET(_message.version_length, 0, 2)) //!< Get version field + +#define mSetSigned(_message,_signed) BF_SET(_message.version_length, _signed, 2, 1) //!< Set signed field +#define mGetSigned(_message) ((bool)BF_GET(_message.version_length, 2, 1)) //!< Get versignedsion field + +#define mSetLength(_message,_length) BF_SET(_message.version_length, _length, 3, 5) //!< Set length field +#define mGetLength(_message) ((uint8_t)BF_GET(_message.version_length, 3, 5)) //!< Get length field + +#define mSetCommand(_message,_command) BF_SET(_message.command_ack_payload, _command, 0, 3) //!< Set command field +#define mGetCommand(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 0, 3)) //!< Get command field + +#define mSetRequestAck(_message,_rack) BF_SET(_message.command_ack_payload, _rack, 3, 1) //!< Set ack-request field +#define mGetRequestAck(_message) ((bool)BF_GET(_message.command_ack_payload, 3, 1)) //!< Get ack-request field + +#define mSetAck(_message,_ackMsg) BF_SET(_message.command_ack_payload, _ackMsg, 4, 1) //!< Set ack field +#define mGetAck(_message) ((bool)BF_GET(_message.command_ack_payload, 4, 1)) //!< Get ack field + +#define mSetPayloadType(_message, _pt) BF_SET(_message.command_ack_payload, _pt, 5, 3) //!< Set payload type field +#define mGetPayloadType(_message) ((uint8_t)BF_GET(_message.command_ack_payload, 5, 3)) //!< Get payload type field + + +// internal access for special fields +#define miGetCommand() ((uint8_t)BF_GET(command_ack_payload, 0, 3)) //!< Internal getter for command field + +#define miSetLength(_length) BF_SET(version_length, _length, 3, 5) //!< Internal setter for length field +#define miGetLength() ((uint8_t)BF_GET(version_length, 3, 5)) //!< Internal getter for length field + +#define miSetVersion(_version) BF_SET(version_length, _version, 0, 2) //!< Internal setter for version field +#define miGetVersion() ((uint8_t)BF_GET(version_length, 0, 2)) //!< Internal getter for version field + +#define miSetRequestAck(_rack) BF_SET(command_ack_payload, _rack, 3, 1) //!< Internal setter for ack-request field +#define miGetRequestAck() ((bool)BF_GET(command_ack_payload, 3, 1)) //!< Internal getter for ack-request field + +#define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1) //!< Internal setter for ack field +#define miGetAck() ((bool)BF_GET(command_ack_payload, 4, 1)) //!< Internal getter for ack field + +#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 5, 3) //!< Internal setter for payload type field +#define miGetPayloadType() (uint8_t)BF_GET(command_ack_payload, 5, 3) //!< Internal getter for payload type field + + +typedef union { + struct { + uint8_t last; // 8 bit - Id of last node this message passed + uint8_t sender; // 8 bit - Id of sender node (origin) + uint8_t destination; // 8 bit - Id of destination node + + uint8_t version_length; // 2 bit - Protocol version + // 1 bit - Signed flag + // 5 bit - Length of payload + uint8_t command_ack_payload; // 3 bit - Command type + // 1 bit - Request an ack - Indicator that receiver should send an ack back. + // 1 bit - Is ack messsage - Indicator that this is the actual ack message. + // 3 bit - Payload data type + uint8_t type; // 8 bit - Type varies depending on command + uint8_t sensor; // 8 bit - Id of sensor that this message concerns. + + // Each message can transfer a payload. We add one extra byte for string + // terminator \0 to be "printable" this is not transferred OTA + // This union is used to simplify the construction of the binary data types transferred. + union { + uint8_t bValue; + uint16_t uiValue; + int16_t iValue; + uint32_t ulValue; + int32_t lValue; + struct { // Float messages + float fValue; + uint8_t fPrecision; // Number of decimals when serializing + } msg_float; + struct { // Presentation messages + uint8_t version; // Library version + uint8_t sensorType; // Sensor type hint for controller, see table above + } msg_presentation; + char data[MAX_PAYLOAD + 1]; + } __attribute__((packed)) payload; + }; + uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1]; +} __attribute__((packed)) MyMessage; + +#endif \ No newline at end of file diff --git a/MySensorsBootloader.h b/MySensorsBootloader.h new file mode 100644 index 0000000..bd32584 --- /dev/null +++ b/MySensorsBootloader.h @@ -0,0 +1,312 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1 + Developed and maintained by tekka 2016 +*/ + +#ifndef MYSBootloader_H +#define MYSBootloader_H + + +extern MyMessage _outMsg,_inMsg; +extern uint8_t _configuredParentID; +extern bool _configuredParentFound; +extern uint8_t _save_MCUSR; + + +#define C_TIMEOUT (2000) // careful when changing + +#define SIGNING_PRESENTATION_VERSION_1 (1) +#define SIGNING_PRESENTATION_VALUE (0) // no signing + +#define AUTO (0xFF) +#define NODE_SENSOR_ID (0xFF) +#define DISTANCE_INVALID (0xFF) +#define BROADCAST_ADDRESS (0xFF) +#define GATEWAY_ADDRESS (0x00) + +#define MAX_FIRMWARE_REQUEST_RESEND (8) + +void _buildMessageProto(const uint8_t destination, const uint8_t sensor, const uint8_t type, const uint8_t const version_length, const uint8_t command_ack_payload) { + _outMsg.sender = _eepromNodeConfig.nodeId; + _outMsg.last = _eepromNodeConfig.nodeId; + _outMsg.destination = destination; + _outMsg.sensor = sensor; + _outMsg.type = type; + _outMsg.command_ack_payload = command_ack_payload; + _outMsg.version_length = version_length; +} + +#define MSG_SIGN (0) +#define ReqACK (0) + +#define _buildMessage(destination,sensor,command,type,payload_type,length) _buildMessageProto(destination,sensor,type,( (length << 3) | (MSG_SIGN << 2) | (PROTOCOL_VERSION & 3) ),( (payload_type << 5) | (ReqACK << 3) | (command & 7) ) ) + +static bool sendMessage(void) { + watchdogReset(); + return writeMessage(_eepromNodeConfig.parentNodeId, _outMsg.array, HEADER_SIZE + mGetLength(_outMsg) ); +} + +static uint8_t processRX(void) { + uint8_t result = 0xFF; + if ( _dataAvailable() ) { + (void)readMessage(_inMsg.array); + if (_inMsg.destination == _eepromNodeConfig.nodeId) { + result = _inMsg.type; + // internal commands + if (mGetCommand(_inMsg) == C_INTERNAL) { + if (_inMsg.type == I_FIND_PARENT_RESPONSE) { + // static parent found? use it for communication + if (_inMsg.sender == _configuredParentID ) { + _configuredParentFound = true; + } + if ( ((_inMsg.payload.bValue < _eepromNodeConfig.distance - 1) && ( !_configuredParentFound) ) || (_inMsg.sender == _configuredParentID)) { + // got new routing info, update settings + _eepromNodeConfig.distance = _inMsg.payload.bValue + 1; + _eepromNodeConfig.parentNodeId = _inMsg.sender; + } + } + } + + } + } + return result; +} + +// process until specific response received, timeout after C_TIMEOUT ms + +#if F_CPU==1000000L + #define COUNTER_START_VAL 0xFFFE / 4 // empirical value +#else + #define COUNTER_START_VAL 0xFFFE +#endif +#define WAIT_DIVIDER (COUNTER_START_VAL/C_TIMEOUT) +#if F_CPU>=16000000 + #define GRANULARITY_US (1000/WAIT_DIVIDER) +#elif F_CPU>=8000000 + #define GRANULARITY_US (500/WAIT_DIVIDER) +#elif F_CPU>=2000000 + #define GRANULARITY_US (10) +#else + #define GRANULARITY_US (0) +#endif + +static bool send_process_type(const uint8_t response_type, uint8_t retries) { + bool response = false; + do { + sendMessage(); + uint16_t count = COUNTER_START_VAL; + do { + watchdogReset(); + // process incoming messages + response = (processRX() == response_type ) ; + _delay_us(GRANULARITY_US); + } while (count-- && !response); + //if(response) break; + } while (retries-- && !response); + + return response; +} + + +static void MySensorsBootloader(void) { + nodeFirmwareConfig_t newNodeFirmwareConfig; + nodeFirmwareConfig_t _eepromNodeFirmwareConfig; + uint16_t RequestedBlock = 0u; + // mappings + requestFirmwareConfig_t *firmwareConfigRequest = (requestFirmwareConfig_t*)_outMsg.payload.data; + nodeFirmwareConfig_t *firmwareConfigResponse = (nodeFirmwareConfig_t *)_inMsg.payload.data; + requestFirmwareBlock_t *firmwareRequest = (requestFirmwareBlock_t *)_outMsg.payload.data; + responseFirmwareBlock_t *firmwareResponse = (responseFirmwareBlock_t *)_inMsg.payload.data; + SM_BL_STATE BL_STATE = BL_READ_CONFIG; // initial state + initSPI(); + // main loop + while(1) { + // states + if(BL_STATE == BL_READ_CONFIG) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_READ_CONFIG; + #endif + eeprom_busy_wait(); + // Read node config from EEPROM, i.e. nodeId, parent nodeId, distance + eeprom_read_block(&_eepromNodeConfig, (uint8_t*)EEPROM_NODE_ID_ADDRESS, sizeof(nodeConfig_t)); + // Read firmware config from EEPROM, i.e. type, version, CRC, blocks + eeprom_read_block(&_eepromNodeFirmwareConfig, (uint8_t*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(nodeFirmwareConfig_t)); + // initialize radio + if(initRadio()) { + BL_STATE = BL_FIND_PARENTS; + } + else { + // if radio not initialized, proceed to startup check + BL_STATE = BL_RUN; + } + } + else if(BL_STATE == BL_FIND_PARENTS) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_FIND_PARENTS; + #endif + // save parent node id for static parent - if static parent not found, use closest node to gateway for communication + _configuredParentID = _eepromNodeConfig.parentNodeId; + _configuredParentFound = false; + _eepromNodeConfig.parentNodeId = AUTO; + _eepromNodeConfig.distance = DISTANCE_INVALID; + // prepare for I_FIND_PARENTS + _buildMessage(BROADCAST_ADDRESS,NODE_SENSOR_ID,C_INTERNAL,I_FIND_PARENT_REQUEST, P_BYTE, 1); + _writeRegister(SETUP_RETR, 0); + // wait until 0xFE command received => does not exist, therefore process incoming messages until timeout + send_process_type(0xFE,0); + if ( _eepromNodeConfig.parentNodeId!=AUTO ) { + BL_STATE = BL_CHECK_ID; + } + else { + BL_STATE = BL_RUN; // if no reply received (e.g. no parent nodes), proceed to startup + } + } + else if(BL_STATE == BL_CHECK_ID) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_CHECK_ID; + #endif + // check ID + if(_eepromNodeConfig.nodeId==GATEWAY_ADDRESS || _eepromNodeConfig.nodeId==AUTO) { + _buildMessage(GATEWAY_ADDRESS,NODE_SENSOR_ID,C_INTERNAL,I_ID_REQUEST, P_BYTE, 0); + if (send_process_type(I_ID_RESPONSE,3)) { + #if defined(DEBUG) + // atoi uses ~50bytes needed for led debug + uint8_t newID = 254; + #else + uint8_t newID = (uint8_t)atoi(_inMsg.payload.data); + #endif + eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, newID); // save ID in eeprom + BL_STATE = BL_READ_CONFIG; + } + } else { + // ID is valid + BL_STATE = BL_CONFIGRUATION; + } + } + else if(BL_STATE == BL_CONFIGRUATION) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_CONFIGURATION; + #endif + // default state: if no reply received, proceed to startup + BL_STATE = BL_RUN; + // auto retransmit delay (default 1500us), auto retransmit count (default 15) + _writeRegister(SETUP_RETR, RF24_ARD << ARD | RF24_ARC << ARC); + // singing preferences, inform GW that BL does not require signed messages + _outMsg.payload.data[0] = SIGNING_PRESENTATION_VERSION_1; + _outMsg.payload.data[1] = SIGNING_PRESENTATION_VALUE; + _buildMessage(GATEWAY_ADDRESS,NODE_SENSOR_ID,C_INTERNAL,I_SIGNING_PRESENTATION,P_CUSTOM,2); + send_process_type(I_SIGNING_PRESENTATION,0); + // update with current CRC in case of memory corruption or failed transmission + _eepromNodeFirmwareConfig.crc = calcCRCrom(_eepromNodeFirmwareConfig.blocks*FIRMWARE_BLOCK_SIZE); + // copy to outMsg mapping + memcpy(firmwareConfigRequest,&_eepromNodeFirmwareConfig,sizeof(nodeFirmwareConfig_t)); + // add BL information + firmwareConfigRequest->BLVersion = MYSBOOTLOADER_VERSION; + // Send a firmware config request to GW/controller + _buildMessage(GATEWAY_ADDRESS,NODE_SENSOR_ID,C_STREAM,ST_FIRMWARE_CONFIG_REQUEST,P_CUSTOM,sizeof(requestFirmwareConfig_t)); + if(send_process_type(ST_FIRMWARE_CONFIG_RESPONSE, 3)) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_CONFIG_RECEIVED; + #endif + // save received firmware config + memcpy(&newNodeFirmwareConfig,_inMsg.payload.data,sizeof(nodeFirmwareConfig_t)); + if (memcmp(&_eepromNodeFirmwareConfig,&newNodeFirmwareConfig,sizeof(nodeFirmwareConfig_t))) { + // update needed, do not allow writes to bootloader section, i.e. max block < 0x7800 / 0x10 = 0x780 = 1920 + if (firmwareConfigResponse->blocks < (BOOTLOADER_START_ADDRESS/FIRMWARE_BLOCK_SIZE)) { + BL_STATE = BL_INIT_UPDATE; + } + } + } + } + else if(BL_STATE == BL_INIT_UPDATE) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_INIT_UPDATE; + #endif + BL_STATE = BL_DO_UPDATE; // default + // copy new FW details for update + memcpy(&_eepromNodeFirmwareConfig, &newNodeFirmwareConfig, sizeof(nodeFirmwareConfig_t)); + // invalidate CRC + _eepromNodeFirmwareConfig.crc = 0xFFFF; + // update EEPROM, save new FW details in case of faulty transmissions + eeprom_update_block(&_eepromNodeFirmwareConfig, (uint8_t*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(nodeFirmwareConfig_t)); + // set FW block to request + RequestedBlock = newNodeFirmwareConfig.blocks; + } + else if(BL_STATE == BL_DO_UPDATE) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_DO_UPDATE; + #endif + + // maps to outMsg + firmwareRequest->type = newNodeFirmwareConfig.type_command.type; + firmwareRequest->version = newNodeFirmwareConfig.version_data.version; + firmwareRequest->block = RequestedBlock - 1; + + _buildMessage(GATEWAY_ADDRESS,NODE_SENSOR_ID,C_STREAM,ST_FIRMWARE_REQUEST,P_CUSTOM,sizeof(requestFirmwareBlock_t)); + + // request FW from controller, load FW counting backwards + if(send_process_type(ST_FIRMWARE_RESPONSE, MAX_FIRMWARE_REQUEST_RESEND)) { + if (!memcmp(firmwareRequest,firmwareResponse,sizeof(requestFirmwareBlock_t))) { + // calculate page offset + const uint8_t offset = ((RequestedBlock - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE; + // write to page buffer + for(uint8_t i = 0; i < FIRMWARE_BLOCK_SIZE; i += 2) { + writeTemporaryBuffer(offset + i, firmwareResponse->data[i] | firmwareResponse->data[i+1]<<8); + } + // program page if full + if (!offset) programPage( ( (RequestedBlock - 1) * FIRMWARE_BLOCK_SIZE)); + // proceed with previous block + if(!--RequestedBlock) { + // if last block requested, proceed with the validation + // validate FW CRC + if( calcCRCrom(newNodeFirmwareConfig.blocks * FIRMWARE_BLOCK_SIZE) != newNodeFirmwareConfig.crc) { + // received FW is invalid, invalidate CRC + // new FW length 0 blocks + newNodeFirmwareConfig.blocks = 0u; + // CRC will evaluate to 0xFFFF with 0 blocks + newNodeFirmwareConfig.crc = 0u; + } + // write FW settings to EEPROM + eeprom_update_block(&newNodeFirmwareConfig, (uint8_t*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(nodeFirmwareConfig_t)); + BL_STATE = BL_READ_CONFIG; + } + } + } else BL_STATE = BL_CONFIGRUATION; + } + else if(BL_STATE == BL_RUN) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_PREPARE_RUN; + #endif + + if(calcCRCrom(_eepromNodeFirmwareConfig.blocks * FIRMWARE_BLOCK_SIZE) == _eepromNodeFirmwareConfig.crc) { + #ifdef DEBUG + DEBUG_PORT = DEBUG_RUN; + #endif + SPIclose(); + + // watchdog settings + #ifdef WATCHDOG_ON_SKETCH_START + watchdogReset(); + #else + watchdogConfig(WATCHDOG_OFF); + #endif + // save the reset flags in the designated register for application retrieval (using .init0) + __asm__ __volatile__ ("mov r2, %0\n" :: "r" (_save_MCUSR)); + + // run sketch + ((void(*)()) 0)(); + } + + // crc invalid, start from beginning + BL_STATE = BL_READ_CONFIG; + + } + } + +} + + + +#endif \ No newline at end of file diff --git a/README.md b/README.md index 02a9175..db148c8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MySensorsBootloaderRF24 +# MYSBootloader 1.3-beta3 MySensors bootloader supporting over-the-air firmware updates

Technical details to write your own controller

diff --git a/RF24.h b/RF24.h new file mode 100644 index 0000000..56719b6 --- /dev/null +++ b/RF24.h @@ -0,0 +1,187 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1 + Developed and maintained by tekka 2016 +*/ + +#ifndef RF24_H +#define RF24_H + +#include "RF24_registers.h" + +extern nodeConfig_t _eepromNodeConfig; + +#define RF24_SETUP ( ( (RF24_DATARATE & 0x02 ) << 4) | ((RF24_DATARATE & 0x01 ) << 3) | (RF24_PA_LEVEL << 1) ) + 1 + +typedef enum { + RF24_PA_MIN = 0, + RF24_PA_LOW, + RF24_PA_HIGH, + RF24_PA_MAX +} rf24_pa_t; + +typedef enum { + RF24_1MBPS = 0, + RF24_2MBPS, + RF24_250KBPS +} rf24_datarate_t; + +#define RF24_CONFIG _BV(CRCO) | _BV(EN_CRC) + +#define _write_register(reg) ( (reg) | W_REGISTER) +#define _read_register(reg) ( (reg) | R_REGISTER) + +uint8_t base_addr[RF24_ADDR_WIDTH] = { RF24_BASE_RADIO_ID }; + + +static uint8_t SPIBytes(uint8_t addr, uint8_t* buf, uint8_t len, bool aReadMode) { + CSN_LOW(); + uint8_t status = SPItransfer( addr ); + + while ( len-- ) { + if (aReadMode) { + status = SPItransfer(NOP); + if(buf!=NULL) *buf++ = status; + } else status = SPItransfer(*buf++); + } + + CSN_HIGH(); + return status; +} + +static uint8_t burstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) { + return SPIBytes(addr,buf,len,false); +} + + +static uint8_t writeAddress(uint8_t addr, uint8_t buf) { + return SPIBytes(addr,&buf,1,false); +} + + +#define _readAddress(addr) SPIBytes(addr,NULL,1,true) +#define _burstWriteRegister(reg,buf,len) burstWriteAddress(_write_register(reg), buf, len) +#define _burstReadRegister(reg,buf,len) SPIBytes(_read_register(reg), buf, len, true) +#define _writeRegister(reg,val) writeAddress(_write_register(reg), val) +#define _readRegister(reg) _readAddress(_read_register(reg)) +#define _getStatus() SPIBytes(NOP, NULL, 0,true) +#define _dataAvailable() !(_readRegister(FIFO_STATUS) & ( _BV(RX_EMPTY) )) +#define _resetInterrupts() _writeRegister(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ) +#define _flushTX() burstWriteAddress(FLUSH_TX, NULL, 0) +#define _flushRX() burstWriteAddress(FLUSH_RX, NULL, 0) + + +static void Flush_RXTX_CLI(void) { + // flush RX and TX buffer + _flushRX(); + _flushTX(); + // clear interrupts + _resetInterrupts(); +} + +static bool writeBuf(uint8_t* buf, uint8_t len ) { + uint8_t status; + // write payload to FIFO, broadcasts do not require ACK + //_burstWriteRegister( broadcast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, buf, len) ; + _burstWriteRegister( W_TX_PAYLOAD, buf, len) ; + // CE pulse to start transmission + CE_HIGH(); + do { + // get status byte + CSN_LOW(); + status = SPItransfer(NOP); + CSN_HIGH(); + + } while( !( status & ( _BV(TX_DS) | _BV(MAX_RT) )) ); + + CE_LOW(); + + _writeRegister(STATUS, _BV(TX_DS) | _BV(MAX_RT) ); + + // max retries exceeded, flush tx buffer + if(status & _BV(MAX_RT)) { + // flush RX FIFO + _flushTX(); + } + + return (status & _BV(TX_DS) ); +} + +static uint8_t getDynamicPayloadSize(void) { + uint8_t result = _readAddress(R_RX_PL_WID); + // discard payloads with length > 32 + if(result > 32) { + // flush RX FIFO + _flushRX(); + result = 0; + } + return result; +} + + + +static uint8_t readMessage(uint8_t* buf) { + const uint8_t len = getDynamicPayloadSize(); + // read payload + _burstReadRegister(R_RX_PAYLOAD,buf,len); + // reset RX interrupt + Flush_RXTX_CLI(); + //_writeRegister(STATUS, _BV(RX_DR) ); + //#warning maybe clear IRQ only + return len; +} + + +static bool writeMessage(const uint8_t recipient, uint8_t* buf, const uint8_t len) { + //stop listening + CE_LOW(); + // switch to TX mode + _writeRegister(CONFIG, RF24_CONFIG | _BV(PWR_UP) ) ; + // flush FIFO and interrupts + Flush_RXTX_CLI(); + // set pipe 0 RX/TX address + base_addr[0] = recipient; + _burstWriteRegister(RX_ADDR_P0, base_addr, RF24_ADDR_WIDTH); + _burstWriteRegister(TX_ADDR, base_addr, RF24_ADDR_WIDTH); + // write payload + const bool result = writeBuf(buf, len); + // start listening + _writeRegister(CONFIG, RF24_CONFIG | _BV(PWR_UP) | _BV(PRIM_RX) ) ; + // set pipe0 RX address + _writeRegister(RX_ADDR_P0, _eepromNodeConfig.nodeId); + // flush FIFO and interrupts + Flush_RXTX_CLI(); + // go! + CE_HIGH(); + + return result; +} + +static bool initRadio(){ + CE_LOW(); + CSN_HIGH(); + // set address width + _writeRegister(SETUP_AW, RF24_ADDR_WIDTH - 2 ); + // auto retransmit delay 1500us, auto retransmit count 0 (BROADCASTS) + //_writeRegister(SETUP_RETR, 5 << ARD | 15 << ARC); + // set channel + _writeRegister(RF_CH, RF24_CHANNEL); + // set data rate and pa level, +1 for Si24R1 + _writeRegister(RF_SETUP, RF24_SETUP); + // unlock features (certain nRF24 clones and non-P) + writeAddress(ACTIVATE, 0x73); + // enable dynamic payload length + _writeRegister(FEATURE, _BV(EN_DPL) ); + // enable pipe 0 + _writeRegister(EN_RXADDR, _BV(ERX_P0)); + // enable autoACK on pipe 0 + _writeRegister(EN_AA, _BV(ENAA_P0)); + // Enable dynamic payload length on pipe 0 + _writeRegister(DYNPD, _BV(DPL_P0)); + // flush FIFOs and clear IRQs + //Flush_RXTX_CLI(); + return _readRegister(RF_SETUP)==RF24_SETUP; // sanity check +} + +#endif // RF24_H \ No newline at end of file diff --git a/STK500Bootloader.h b/STK500Bootloader.h new file mode 100644 index 0000000..eb2ea88 --- /dev/null +++ b/STK500Bootloader.h @@ -0,0 +1,121 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1 + Developed and maintained by tekka 2016 + + STK500 optiboot code: + - Copyright 2013-2015 by Bill Westfield + - modified from optiboot: https://github.com/Optiboot/optiboot + +*/ + +#ifndef STK500Bootloader_H +#define STK500Bootloader_H + +#include "stk500.h" + + +static void verifySpace(void) { + if (getch() != CRC_EOP) { + // no space (0x20) received, reboot + watchdogConfig(WATCHDOG_16MS); + while(1){}; + } + putch(STK_INSYNC); +} + +static void getNch(uint8_t count) { + do { + getch(); + } while (--count); + verifySpace(); +} + +static void STK500Bootloader(void) { + uint8_t ch; + uint16_t address = 0; + uint8_t length; + uint16_t FW_len = 0; + bool exit_signal = false; + + initUART(); + + // loop + while(!exit_signal) { + watchdogReset(); + // get character from UART + ch = getch(); + + if (ch == STK_GET_PARAMETER) { + ch = getch(); + verifySpace(); + if (ch == 0x82) { + putch(MYSBOOTLOADER_MINVER); + } else if (ch == 0x81) { + putch(MYSBOOTLOADER_MAJVER+100); // use out-of-sequence versioning to prevent collisions, see optiboot source + } else putch(0x03); + } else if (ch == STK_SET_DEVICE) { + getNch(20); + } else if (ch == STK_SET_DEVICE_EXT) { + getNch(5); + } else if (ch == STK_LOAD_ADDRESS) { + // LOAD ADDRESS + address = ( getch() | getch() << 8) << 1; + verifySpace(); + } else if (ch == STK_UNIVERSAL) { + // UNIVERSAL command is ignored + getNch(4); + putch(0x00); + } else if (ch == STK_PROG_PAGE) { + + getch(); + length = getch(); + getch(); + + FW_len += length; + + for (uint8_t i= 0; i < length; i+=2){ + uint16_t data = getch() | ((getch() << 8)); + writeTemporaryBuffer(address + i,data); + } + programPage(address); + // Read command terminator, start reply + verifySpace(); + } else if (ch == STK_READ_PAGE) { + // only flash + getch(); + length = getch(); + getch(); + + verifySpace(); + do { + // read a flash byte and increment the address + __asm__ ("lpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address)); + putch(ch); + } while (--length); + } else if (ch == STK_READ_SIGN) { + // READ SIGN - return what Avrdude wants to hear + verifySpace(); + putch(SIGNATURE_0); + putch(SIGNATURE_1); + putch(SIGNATURE_2); + + } else if (ch == STK_LEAVE_PROGMODE) { + + // version and type = 0xFFFF, adjust CRC and blocks + nodeFirmwareConfig_t _eepromNodeFirmwareConfig; + _eepromNodeFirmwareConfig.type_command.type = 0xFFFF; + _eepromNodeFirmwareConfig.version_data.version = 0xFFFF; + _eepromNodeFirmwareConfig.blocks = FW_len/FIRMWARE_BLOCK_SIZE; + _eepromNodeFirmwareConfig.crc = calcCRCrom(FW_len); + eeprom_update_block((void*)&_eepromNodeFirmwareConfig, (uint8_t*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(nodeFirmwareConfig_t)); + exit_signal = true; + verifySpace(); + } else verifySpace(); + + putch(STK_OK); + } +} + +#endif // STK500Bootloader_H \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..3e4eb29 --- /dev/null +++ b/main.c @@ -0,0 +1,91 @@ +/* + MYSBootloader 1.3pre3 + OTA RF24 bootloader for MySensors: http://www.mysensors.org + Based on MySensors library 2.1.0 + Developed and maintained by tekka 2016 + + Tested with MYSController (goo.gl/9DCWNo) + MCU: ATmega328p + + History: + + * Version 1.3pre + - redesign update process + - implement STK500 protocol / serial upload + - preferred parent with fallback option + - save MCUSR, can be retrieved by application + + * Version 1.2 + - internal version, PoC + + * Version 1.1 + - use eeprom_update instead of eeprom_write to reduce wear out + - bootloader commands: erase eeprom, set node id + - verify incoming FW blocks for type & address + - communicate over static parent (if set and found) else broadcast to find nearest node + - adjusted timings + + * Version 1.0 + - initial release + + 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. + */ + + + +// RF24 communication settings ***************************************************************************************** +#define RF24_CHANNEL (84) // RF channel for the sensor net, 0-127; default 76 +#define RF24_DATARATE RF24_250KBPS // RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps +#define RF24_PA_LEVEL RF24_PA_MAX // PA level, RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm +#define RF24_ADDR_WIDTH (5) +#define RF24_BASE_RADIO_ID 0x00,0xFC,0xE1,0xA8,0xA8 +#define RF24_ARC (15) // Auto Retransmit Counts, see nRF24L01+ data sheet +#define RF24_ARD (5) // Auto Retransmit Delay, 5=1500us, see nRF24L01+ data sheet + +// Options ************************************************************************************************************* +#define WATCHDOG_ON_SKETCH_START // WDT on when application starts +#define WDT_TIMEOUT WATCHDOG_8S // WDT timeout + +// SPI bus setting ***************************************************************************************************** +#define SPI_PINS_CE9_CSN10 +//define SPI_PINS_CSN7_CE8 + +// LED settings ******************************************************************************************************** +#define LED_DDR DDRB +#define LED_PORT PORTB +#define LED_PIN PINB5 + +// DEBUG settings ****************************************************************************************************** +//#define DEBUG +#define DEBUG_PORT PORTD +#define DEBUG_DDR DDRD + + +#include "Core.h" + +// prototype +int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9"))); + +// implementation +int main(void) { + asm volatile ("clr __zero_reg__"); + _save_MCUSR = MCUSR; // save the status register for the reset cause + MCUSR = 0; + watchdogConfig(WDT_TIMEOUT); // enable watchdog + #ifdef DEBUG + DEBUG_DDR = 0xFF; + DEBUG_PORT = DEBUG_INIT; + #endif + + blinkLed(); + + // STK500_bootloader runs only if reset reason was EXTERNAL RESET/POWER ON + if (_save_MCUSR & _BV(EXTRF) ) { + STK500Bootloader(); + } + + MySensorsBootloader(); + +}