/* * 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-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. */ #include "MyOTAFirmwareUpdate.h" // global variables extern MyMessage _msg; extern MyMessage _msgTmp; // local variables #ifdef MY_OTA_USE_I2C_EEPROM I2CEeprom _flash(MY_OTA_I2C_ADDR); #else SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); #endif LOCAL nodeFirmwareConfig_t _nodeFirmwareConfig; LOCAL bool _firmwareUpdateOngoing = false; LOCAL uint32_t _firmwareLastRequest; LOCAL uint16_t _firmwareBlock; LOCAL uint8_t _firmwareRetry; LOCAL void readFirmwareSettings(void) { hwReadConfigBlock((void*)&_nodeFirmwareConfig, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(nodeFirmwareConfig_t)); } LOCAL void firmwareOTAUpdateRequest(void) { const uint32_t enterMS = hwMillis(); if (_firmwareUpdateOngoing && (enterMS - _firmwareLastRequest > MY_OTA_RETRY_DELAY)) { if (!_firmwareRetry) { setIndication(INDICATION_ERR_FW_TIMEOUT); OTA_DEBUG(PSTR("!OTA:FRQ:FW UPD FAIL\n")); // fw update failed // Give up. We have requested MY_OTA_RETRY times without any packet in return. _firmwareUpdateOngoing = false; return; } _firmwareRetry--; _firmwareLastRequest = enterMS; // Time to (re-)request firmware block from controller requestFirmwareBlock_t firmwareRequest; firmwareRequest.type = _nodeFirmwareConfig.type; firmwareRequest.version = _nodeFirmwareConfig.version; firmwareRequest.block = (_firmwareBlock - 1); OTA_DEBUG(PSTR("OTA:FRQ:FW REQ,T=%04" PRIX16 ",V=%04" PRIX16 ",B=%04" PRIX16 "\n"), _nodeFirmwareConfig.type, _nodeFirmwareConfig.version, _firmwareBlock - 1); // request FW update block (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false).set(&firmwareRequest, sizeof(requestFirmwareBlock_t))); } } LOCAL bool firmwareOTAUpdateProcess(void) { if (_msg.type == ST_FIRMWARE_CONFIG_RESPONSE) { if(_firmwareUpdateOngoing) { OTA_DEBUG(PSTR("!OTA:FWP:UPDO\n")); // FW config response received, FW update already ongoing return true; } nodeFirmwareConfig_t *firmwareConfigResponse = (nodeFirmwareConfig_t *)_msg.data; // compare with current node configuration, if they differ, start FW fetch process if (memcmp(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t))) { setIndication(INDICATION_FW_UPDATE_START); OTA_DEBUG(PSTR("OTA:FWP:UPDATE\n")); // FW update initiated // copy new FW config (void)memcpy(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t)); // Init flash if (!_flash.initialize()) { setIndication(INDICATION_ERR_FW_FLASH_INIT); OTA_DEBUG(PSTR("!OTA:FWP:FLASH INIT FAIL\n")); // failed to initialise flash _firmwareUpdateOngoing = false; } else { // erase lower 32K -> max flash size for ATMEGA328 _flash.blockErase32K(0); // wait until flash erased while ( _flash.busy() ) {} _firmwareBlock = _nodeFirmwareConfig.blocks; _firmwareUpdateOngoing = true; // reset flags _firmwareRetry = MY_OTA_RETRY + 1; _firmwareLastRequest = 0; } return true; } OTA_DEBUG(PSTR("OTA:FWP:UPDATE SKIPPED\n")); // FW update skipped, no newer version available } else if (_msg.type == ST_FIRMWARE_RESPONSE) { if (_firmwareUpdateOngoing) { // extract FW block replyFirmwareBlock_t *firmwareResponse = (replyFirmwareBlock_t *)_msg.data; OTA_DEBUG(PSTR("OTA:FWP:RECV B=%04" PRIX16 "\n"), firmwareResponse->block); // received FW block if (firmwareResponse->block != _firmwareBlock - 1) { OTA_DEBUG(PSTR("!OTA:FWP:WRONG FWB\n")); // received FW block // wrong firmware block received setIndication(INDICATION_FW_UPDATE_RX_ERR); // no further processing required return true; } setIndication(INDICATION_FW_UPDATE_RX); // Save block to flash _flash.writeBytes( ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); // wait until flash written while (_flash.busy()) {} #ifdef OTA_EXTRA_FLASH_DEBUG { char prbuf[8]; uint32_t addr = ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET; OTA_DEBUG(PSTR("OTA:FWP:FL DUMP ")); sprintf_P(prbuf,PSTR("%04" PRIX16 ":"), (uint16_t)addr); MY_SERIALDEVICE.print(prbuf); for(uint8_t i=0; i> 8), (uint8_t)(firmwareSize & 0xff),':'}; _flash.writeBytes(0, OTAbuffer, FIRMWARE_START_OFFSET); // wait until flash ready while (_flash.busy()) {} hwReboot(); } else { setIndication(INDICATION_ERR_FW_CHECKSUM); OTA_DEBUG(PSTR("!OTA:FWP:CRC FAIL\n")); } } // reset flags _firmwareRetry = MY_OTA_RETRY + 1; _firmwareLastRequest = 0; } else { OTA_DEBUG(PSTR("!OTA:FWP:NO UPDATE\n")); } return true; } return false; } LOCAL void presentBootloaderInformation(void) { requestFirmwareConfig_t *requestFirmwareConfig = (requestFirmwareConfig_t *)_msgTmp.data; mSetLength(_msgTmp, sizeof(requestFirmwareConfig_t)); mSetCommand(_msgTmp, C_STREAM); mSetPayloadType(_msgTmp, P_CUSTOM); // copy node settings to reqFWConfig (void)memcpy(requestFirmwareConfig, &_nodeFirmwareConfig, sizeof(nodeFirmwareConfig_t)); // add bootloader information requestFirmwareConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; _firmwareUpdateOngoing = false; (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); } LOCAL bool isFirmwareUpdateOngoing(void) { return _firmwareUpdateOngoing; } // do a crc16 on the whole received firmware LOCAL bool transportIsValidFirmware(void) { // init crc uint16_t crc = ~0; for (uint16_t i = 0; i < _nodeFirmwareConfig.blocks * FIRMWARE_BLOCK_SIZE; ++i) { crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET); for (int8_t j = 0; j < 8; ++j) { if (crc & 1) { crc = (crc >> 1) ^ 0xA001; } else { crc = (crc >> 1); } } } OTA_DEBUG(PSTR("OTA:CRC:B=%04" PRIX16 ",C=%04" PRIX16 ",F=%04" PRIX16 "\n"), _nodeFirmwareConfig.blocks,crc, _nodeFirmwareConfig.crc); return crc == _nodeFirmwareConfig.crc; }