diff --git a/.gitignore b/.gitignore index 619eb92..b747ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ stino.settings +*~ +*bak diff --git a/MYSBootloader/MYSBootloader.c b/MYSBootloader/MYSBootloader.c index 462d99c..1efd459 100644 --- a/MYSBootloader/MYSBootloader.c +++ b/MYSBootloader/MYSBootloader.c @@ -1,17 +1,15 @@ /* MYSBootloader: OTA bootloader for Mysensor nodes (www.mysensors.org) - Code based on OTA bootloader by ToSa + Original OTA bootloader code by ToSa Optimized and extended by tekka - Version 1.0 / 20150206 - Size: 1834 bytes + Version 1.1 / 20150314 + Size: 2002 bytes - Tested with Atmega328P, 16Mhz 3.3V and 5V and MYSController 0.1.2.266 (goo.gl/9DCWNo) + Tested with MYSController 0.1.2.276 (goo.gl/9DCWNo) - MCU: atmega328 - clock: 16Mhz (prescaler: off/1) + MCU: Atmega328p bootsz: 1024W - fuses for ISP: EX = 0xFE (use 0x06 for Arduino IDE, boards.txt) HI = 0xDA @@ -23,9 +21,20 @@ Successfully tested with: - 16Mhz extXTAL, 3.3V - 1Mhz intRC (8Mhz + DIV8), 3.3V - 128kHz intRC, 3.3V + 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 @@ -36,45 +45,63 @@ #define MYSBOOTLOADER_MAJVER 1 -#define MYSBOOTLOADER_MINVER 0 +#define MYSBOOTLOADER_MINVER 1 #define MYSBOOTLOADER_VERSION (MYSBOOTLOADER_MINVER * 256 + MYSBOOTLOADER_MAJVER) -//#define USE_PRESCALER #define MAX_RESEND 5 // procedures and functions -int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9"))); +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 void programPage(uint32_t page, uint8_t *buf); 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) { + 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() { - // any pending eeprom activities? + // wait for pending eeprom activities eeprom_busy_wait(); // trigger watchdog ASAP watchdogConfig(WATCHDOG_16MS); - // wait until WD triggers + // endless loop while (1); } @@ -82,13 +109,7 @@ static void startup() { if (IsFirmwareValid()) { // WD off watchdogConfig(WATCHDOG_OFF); - - #ifdef USE_PRESCALER - // reset prescaler - clock_prescale_set(orgClockDiv); - #endif - - // start sketch + // run sketch ((void(*)()) 0)(); } else { @@ -96,20 +117,7 @@ static void startup() { } } -static void programPage(uint32_t page, uint8_t *buf) { - // using out commands, saves some bytes :) - __boot_page_erase_short(page); - boot_spm_busy_wait(); - for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) { - uint16_t w = *buf++; - // generate word - w += (*buf++) << 8; - __boot_page_fill_short(page + i, w); - } - __boot_page_write_short(page); - boot_spm_busy_wait(); - __boot_rww_enable_short(); -} + static boolean sendWrite(MyMessage message) { @@ -121,38 +129,45 @@ static bool sendAndWait(uint8_t reqType, uint8_t resType) { // outer loop, retries for (uint8_t i = 0; i < MAX_RESEND; i++) { sendWrite(outMsg); - // loop 20 times, wait time 0.1ms if no/wrong data => 2s + // 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 - readMessage(inMsg.array); - // protocol compatible? if not ignore msg - if ((mGetVersion(inMsg) != PROTOCOL_VERSION)) { - continue; - } - if (inMsg.destination == nc.nodeId) { - // msg for us - if ((mGetCommand(inMsg) == C_INTERNAL) && (inMsg.type == I_FIND_PARENT_RESPONSE)) { - if (inMsg.bValue < nc.distance - 1) { - // got new routing info, update settings - nc.distance = inMsg.bValue + 1; - nc.parentNodeId = inMsg.sender; + // 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; } - } - // did we receive expected reply? - if ((mGetCommand(inMsg) == mGetCommand(outMsg)) && (inMsg.type == resType)) { - return true; - } + } } - } else { + } else { // wait 1ms if no data available - delaym(10); + _delay_ms(1); } + } } } @@ -169,13 +184,6 @@ int main(void) { // reset MCU status register MCUSR = 0; - #ifdef USE_PRESCALER - // get current prescale - orgClockDiv = clock_prescale_get(); - //switch to 4 MHz on 16Mhz crystal - clock_prescale_set(F_CPU_DIV); - #endif - // enable watchdog to avoid deadlock watchdogConfig(WATCHDOG_8S); @@ -190,11 +198,10 @@ int main(void) { // Read firmware config from EEPROM, i.e. type, version, CRC, blocks eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); - // bootloader should find closest node during each reboot - - // invalidate parent node settings, since we have to re-discover them for every single reboot + // 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; - nc.parentNodeId = 0xFF; // prepare for I_FIND_PARENTS outMsg.sender = nc.nodeId; @@ -202,6 +209,7 @@ int main(void) { outMsg.sensor = 0xFF; outMsg.destination = BROADCAST_ADDRESS; + // set header mSetVersion(outMsg, PROTOCOL_VERSION); mSetLength(outMsg, 0); mSetCommand(outMsg, C_INTERNAL); @@ -211,7 +219,7 @@ int main(void) { // set reading & writing pipe address setAddress(nc.nodeId); - // network up?, get neighbors, else startup + // network up? get neighbors, else startup if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) { startup(); } @@ -225,9 +233,9 @@ int main(void) { openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(BROADCAST_ADDRESS)); if (sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) { // save id to eeprom - eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data)); + 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 - saves bytes :) + // we could go on and set everything right here, but rebooting will take care of that - and saves some bytes :) reboot(); } @@ -250,6 +258,23 @@ int main(void) { 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(); @@ -260,7 +285,7 @@ int main(void) { // 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_write_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig)); + eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig)); // copy new FW config memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); @@ -270,33 +295,39 @@ int main(void) { firmwareRequest->type = fc.type; firmwareRequest->version = fc.version; - // request FW from controller - for (uint16_t block = fc.blocks; block > 0; block--) { - firmwareRequest->block = (block - 1); + // 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; - // 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); - } - } - + + // 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_write_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); - // we could restart directly here, but eeprom may still be busy, thus reboot (we check for idle eeprom before rebooting) and save some bytes :) + eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); } // final step reboot(); diff --git a/MYSBootloader/MYSBootloader.h b/MYSBootloader/MYSBootloader.h index 10c48d8..1f66faf 100644 --- a/MYSBootloader/MYSBootloader.h +++ b/MYSBootloader/MYSBootloader.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "MyMessage.h" #include "MySensor.h" @@ -13,11 +14,17 @@ #define FIRMWARE_BLOCK_SIZE 16 +#define EEPROM_SIZE 1024 // 1024 bytes for ATMEGA328 -// FW config structure, stored in eeprom typedef struct { - uint16_t type; - uint16_t version; + 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; @@ -48,9 +55,10 @@ static NodeFirmwareConfig fc; static MyMessage outMsg; static MyMessage inMsg; -#ifdef PRESCALE -static clock_div_t orgClockDiv = 0; -#endif +static boolean configuredParentFound = false; +static uint8_t configuredParentID = 0xFF; + +//static clock_div_t orgClockDiv = 0; static uint8_t progBuf[SPM_PAGESIZE]; diff --git a/MYSBootloader/MYSBootloader.hex b/MYSBootloader/MYSBootloader.hex index 7039c3b..8095224 100644 --- a/MYSBootloader/MYSBootloader.hex +++ b/MYSBootloader/MYSBootloader.hex @@ -1,117 +1,128 @@ -:1078000021E0A0E0B1E001C01D92A53DB207E1F783 -:10781000CDB7DEB76897DEBFCDBF112414BE88E1B7 -:107820008093600089E2809360008FEE84B963E00A -:1078300083E201D26FE584E2FED16BE381E2FBD10A -:107840006CE485E2F8D167E286E2F5D10DD263E718 -:1078500080E5F1D166E08DE3EED16FE38CE3EBD10F -:107860006EEF80E2E8D185E053D143E050E060E084 -:1078700070E08AEC91E032D348E050E06BE171E0D7 -:1078800082EC91E02BD38FEF8093CC018093CB01DE -:107890002091CA012093A2012093A1018093A70106 -:1078A0008093A30182E08093A4018091A501887058 -:1078B00083608093A5012F3FD1F130E040E050E09C -:1078C00060E070E080E090E034504E4157456745FD -:1078D0007F4F8F4F9F4F29013A014B015C01298B4C -:1078E0005A8A4B8B7C8A6D8B9E8A8F8BB88E85E0F3 -:1078F000FE017196ADECB1E001900D928A95E1F731 -:1079000045E0BE016F5E7F4F8AE27FD149865A868D -:107910006B867C868D869E86AF86B88A45E0BE01E2 -:10792000675F7F4F8BE271D163E082E284D168E0D0 -:1079300087E09FD1811101C049D11092A3018091AC -:10794000CA018F3FF1F489838CEF8A8381EE8B83A8 -:1079500088EA8C838D831E821F82188645E0BE01D3 -:107960006F5F7F4F8BE251D164E083E082D1882347 -:1079700039F087E891E080D2682F80E090E0C7D2AC -:107980001CD1A8958091A401877080658093A40183 -:107990008091A5018871846C8093A50188E0E2EC58 -:1079A000F1E0A8EAB1E001900D928A95E1F781E05B -:1079B00090E09093B1018093B00161E080E059D1F3 -:1079C000811101C003D148E050E067E871E082EC2A -:1079D00091E06FD2892B09F4F9D08FEF9FEF90934C -:1079E000C9018093C80148E050E06BE171E082EC8E -:1079F00091E084D288E0E7E8F1E0A2ECB1E0019008 -:107A00000D928A95E1F78091A4018770806380933D -:107A1000A4018091C2019091C3019093A901809328 -:107A2000A8018091C4019091C5019093AB0180930E -:107A3000AA01A090C601B090C70185010150104F66 -:107A400064E0000F111F6A95E1F770E1972E88241A -:107A50008394E5E07E2EF1E16F2EA114B10409F4C8 -:107A60004AC081E0A81AB108B092AD01A092AC0161 -:107A700063E082E0FED0882309F482CF802F8077F4 -:107A8000A82FB0E0A050BF4FEDE8F1E0992D019094 -:107A90000D929A95E1F781112BC083E0F80187BF21 -:107AA000E89507B600FCFDCF4091000180E090E032 -:107AB000DC01A050BF4FFC01E00FF11F11962C918B -:107AC000119730E0322F2227240F311D090187BE84 -:107AD000E895112412964C9102968038910541F751 -:107AE000F80177BEE89507B600FCFDCF67BEE895C4 -:107AF00000511109B2CFA8952CD0882309F440CFAA -:107B000048E050E06BE171E082EC91E0F7D138CFD2 -:107B100090E0981751F02DE730E000002150310936 -:107B200021153105D1F79F5FF4CF089598E087FFC5 -:107B300002C02B9A01C02B9800002D9A000023B19F -:107B4000880F22952170822B2D980000915079F793 -:107B500008958091C6019091C70124E0880F991F74 -:107B60002A95E1F7E0E0F0E02FEF3FEFE817F907A3 -:107B7000D1F044912427422F42954227042E469566 -:107B800046954025042E469540254770022E232F0A -:107B9000469507944795302D242706944795302520 -:107BA00024273196E3CF81E04091C8015091C9016B -:107BB0002417350709F080E00895F999FECF88E190 -:107BC0008093600088E080936000FFCFC2DF88234D -:107BD00041F088E18093600010926000E0E0F0E006 -:107BE0000994EBDF1F93CF93DF93EB01142F2A98B7 -:107BF0009DDF1C0F1C1721F08FEF98DF8993FACFC0 -:107C00002A9ADF91CF911F9108951F93CF93DF930D -:107C1000EB01142F2A988ADF1C0F1C1719F0899189 -:107C200085DFFBCF2A9ADF91CF911F91089541E024 -:107C300060E070E0D7CFCF93DF931F92CDB7DEB770 -:107C4000698341E0BE016F5F7F4FDFDF0F90DF91FF -:107C5000CF91089540E060E070E082EED6CF40E042 -:107C600060E070E081EED1CFF5DFF9DF60E787E219 -:107C7000E2CF6F927F928F929F92AF92BF92CF92FC -:107C8000DF92EF92FF920F931F93CF93DF93CDB7C5 -:107C9000DEB7AA97DEBFCDBF762E8093A60105E0A2 -:107CA00041E2642EE1EAF1E0DE011196862D0190B9 -:107CB0000D928A95E1F7FB801C8116951695169515 -:107CC000195F2091CB0129982AA7CEDF6EEF80E2C1 -:107CD000B2DF2AA530E040E050E060E070E080E0F4 -:107CE00090E034504E41574567457F4F8F4F9F4F2F -:107CF0002AA33BA34CA35DA36EA37FA388A799A748 -:107D000045E0BE016E5D7F4F8AE27FDF45E0BE0148 -:107D10006E5D7F4F80E379DF8FEFF81202C080EB5A -:107D200001C080EA412FBE016F5F7F4F6EDF299A4D -:107D300081E0EEDE299840E060E070E08FEF52DFF6 -:107D40008073C9F360E787E276DF84FD88DF6FE048 -:107D500080E271DF89DF8090CD019090CE01A0900C -:107D6000CF01B090D001C090D101D090D201E0906D -:107D7000D301F090D4019401A501B601C701A0E0A0 -:107D80006FD029F045E06DEC71E08AE23EDF299A80 -:107D900014E134E6F32EA89587E149DF80FD4BC05E -:107DA00080E645DF813210F055DF80E0482F60E843 -:107DB00071E081E617DF60E787E23DDF80918301B4 -:107DC0008770823059F5909182018091CA01981391 -:107DD00025C040918401842F87708330B1F4809155 -:107DE0008501883091F450918701252F30E08091F2 -:107DF000CC0190E00197281739073CF45F5F50935E -:107E0000CC01809181018093CB018091A5018427D1 -:107E1000877021F480918501871559F0FA9409F053 -:107E2000BACF115009F0B5CF015009F03BCF80E037 -:107E300005C081E003C08AE06BDEF0CFAA96DEBF0A -:107E4000CDBFDF91CF911F910F91FF90EF90DF9009 -:107E5000CF90BF90AF909F908F907F906F9008953C -:107E60000024A7FD00942A17300540055005600541 -:107E70007005800590050895FC0188279927E894EE -:107E800021912032E9F3293010F02E30C8F32B3243 -:107E900039F02D3231F4689403C018D0820F911D4F -:107EA000219120532A30C8F31EF4909581959F4F5D -:107EB0000895FB01DC0104C08D910190801921F42B -:107EC00041505040C8F7881B990B08957AE0979F5E -:107ED000902D879F802D910D11240895DC01CB01F9 -:107EE000FC01F999FECF06C0F2BDE1BDF89A3196CA -:107EF00000B40D9241505040B8F70895DC01CB0119 -:107F000002C02D9105D041505040D8F70895262F3A -:107F1000F999FECF1FBA92BD81BD20BD0FB6F8946E -:0A7F2000FA9AF99A0FBE019608952F +: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 :040000030000780081 :00000001FF diff --git a/MYSBootloader/MYSBootloaderHW.h b/MYSBootloader/MYSBootloaderHW.h index 8769ce1..d62b543 100644 --- a/MYSBootloader/MYSBootloaderHW.h +++ b/MYSBootloader/MYSBootloaderHW.h @@ -5,18 +5,10 @@ // hardware - - -#ifdef USE_PRESCALER #define F_CPU_DIV clock_div_4 -#else -#define F_CPU_DIV clock_div_1 -#endif - -#define F_CPU_REAL F_CPU / (1 << F_CPU_DIV) -#define DELAY_M 125 * (1 << F_CPU_DIV) -#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 ) -#define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1))) +#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 @@ -56,20 +48,6 @@ void watchdogConfig(uint8_t x) { WDTCSR = x; } -// delay procedures - -inline static void delayu() { - asm volatile ("nop"); -} - -static void delaym(uint8_t value) { - for (uint8_t outer = 0; outer < value; outer++) { - for (uint16_t inner = 0; inner < DELAY_M; inner++) { - delayu(); - } - } -} - // SPI communication #define SPI_DDR DDRB @@ -80,10 +58,12 @@ static void delaym(uint8_t value) { #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 csnlow() SPI_PORT &= ~_BV(SPI_CSN)//;delaym(1) // not necessary -#define csnhigh() SPI_PORT |= _BV(SPI_CSN)//;delaym(1) // not necessary -#define celow() SPI_PORT &= ~_BV(SPI_CE)//;delaym(1) // not necessary -#define cehigh() SPI_PORT |= _BV(SPI_CE)//;delaym(1) // minimum puls width 10us +#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 @@ -91,15 +71,16 @@ static void SPIinit() { } static uint8_t SPItransfer(uint8_t value) { - for(uint8_t i = 0 ;i < 8;i++) { + // 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); - delayu(); + //_delay_us(1); value <<= 1; SPI_PORT |= _BV(SPI_SCLK); - delayu(); + //_delay_us(1); value |= ((SPI_PIN >> SPI_MISO) & 0x01); SPI_PORT &= ~_BV(SPI_SCLK); - delayu(); + //_delay_us(1); } return value; } diff --git a/MYSBootloader/MYSBootloaderRF24.h b/MYSBootloader/MYSBootloaderRF24.h index c91f902..c290527 100644 --- a/MYSBootloader/MYSBootloaderRF24.h +++ b/MYSBootloader/MYSBootloaderRF24.h @@ -9,12 +9,11 @@ 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; - -uint8_t BurstReadAddress(uint8_t addr, uint8_t* buf, uint8_t len) { +static uint8_t BurstReadAddress(uint8_t addr, uint8_t* buf, uint8_t len) { csnlow(); uint8_t status = SPItransfer( addr ); while ( len-- ) { @@ -25,7 +24,7 @@ uint8_t BurstReadAddress(uint8_t addr, uint8_t* buf, uint8_t len) { return status; } -uint8_t BurstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) { +static uint8_t BurstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) { csnlow(); uint8_t status = SPItransfer( addr ); while ( len-- ) { @@ -35,30 +34,30 @@ uint8_t BurstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) { return status; } -uint8_t ReadAddress(uint8_t addr) { +static uint8_t ReadAddress(uint8_t addr) { return BurstReadAddress(addr,NULL,1); } -uint8_t WriteAddress(uint8_t addr, uint8_t val) { +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 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) +#define get_status() BurstReadAddress(NOP, NULL, 0) void Flush_RX(void) { - BurstWriteAddress(FLUSH_RX,NULL,0); + BurstWriteAddress(FLUSH_RX, NULL, 0); } void Flush_TX(void) { - BurstWriteAddress(FLUSH_TX,NULL,0); + BurstWriteAddress(FLUSH_TX, NULL, 0); } -void Flush_RXTX_CLI(void) { +static void Flush_RXTX_CLI(void) { // flush RX and TX buffer Flush_RX(); Flush_TX(); @@ -86,18 +85,18 @@ static void stopListening(void) { -static bool write_buf(uint8_t* buf, uint8_t len, const bool multicast ) { +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 - delaym(1); + _delay_us(CE_PULSE_LENGTH); // start transmitting celow(); - // wait until sent, here we potentially have a deadlock when transmitter not connected/not working + // 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) ); @@ -119,7 +118,7 @@ static uint8_t getDynamicPayloadSize(void) { } -static bool available(uint8_t* pipe_num) { +static boolean available(uint8_t* pipe_num) { if (!(ReadAddress(FIFO_STATUS) & _BV(RX_EMPTY) )){ if (pipe_num) { uint8_t status = get_status(); @@ -131,11 +130,13 @@ static bool available(uint8_t* pipe_num) { } -static void readMessage(uint8_t* buf) { +static uint8_t readMessage(uint8_t* buf) { // read payload - BurstReadRegister(R_RX_PAYLOAD,buf,getDynamicPayloadSize()); + uint8_t PL_LEN = getDynamicPayloadSize(); + BurstReadRegister(R_RX_PAYLOAD,buf,PL_LEN); // reset interrupts - WriteRegister(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS)); + WriteRegister(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + return PL_LEN; } static void openWritingPipe(uint64_t value) { @@ -162,7 +163,7 @@ static void openReadingPipe(uint8_t pipe, uint64_t address) { static boolean write(uint8_t destination, uint8_t* buf, uint8_t len, boolean multicast) { stopListening(); openWritingPipe(TO_ADDR(destination)); - bool result = write_buf(buf, len, multicast); + boolean result = write_buf(buf, len, multicast); startListening(); return result; } @@ -177,6 +178,8 @@ static void setAddress(uint8_t addr) } } + + static void RFinit(void){ // set address width WriteRegister(SETUP_AW, (addr_width-2) % 4); @@ -190,7 +193,7 @@ static void RFinit(void){ 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 dynamic payload feature + // activate to unlock features WriteAddress(ACTIVATE,0x73); // enable payload with ACK and dynamic payload length WriteRegister(FEATURE, _BV(EN_ACK_PAY) | _BV(EN_DPL) ); @@ -198,13 +201,8 @@ static void RFinit(void){ 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 - delaym(5); - - // We are not listening to broadcast pipe since we do not reply to broadcasting messages (e.g. FIND_PARENT_RESPONSE messages) - // openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); - - + // stabilize 5ms + _delay_ms(5); } #endif // MYSBootloaderRF24_H diff --git a/MYSBootloader/boot.h b/MYSBootloader/boot.h new file mode 100644 index 0000000..2639cd8 --- /dev/null +++ b/MYSBootloader/boot.h @@ -0,0 +1,848 @@ +/* Modified to use out for SPM access +** Peter Knight, Optiboot project http://optiboot.googlecode.com +** +** Todo: Tidy up +** +** "_short" routines execute 1 cycle faster and use 1 less word of flash +** by using "out" instruction instead of "sts". +** +** Additional elpm variants that trust the value of RAMPZ +*/ + +/* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 Eric B. Weddington + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ + +/* $Id: boot.h,v 1.27.2.3 2008/09/30 13:58:48 arcanum Exp $ */ + +#ifndef _AVR_BOOT_H_ +#define _AVR_BOOT_H_ 1 + +/** \file */ +/** \defgroup avr_boot : Bootloader Support Utilities + \code + #include + #include + \endcode + + The macros in this module provide a C language interface to the + bootloader support functionality of certain AVR processors. These + macros are designed to work with all sizes of flash memory. + + Global interrupts are not automatically disabled for these macros. It + is left up to the programmer to do this. See the code example below. + Also see the processor datasheet for caveats on having global interrupts + enabled during writing of the Flash. + + \note Not all AVR processors provide bootloader support. See your + processor datasheet to see if it provides bootloader support. + + \todo From email with Marek: On smaller devices (all except ATmega64/128), + __SPM_REG is in the I/O space, accessible with the shorter "in" and "out" + instructions - since the boot loader has a limited size, this could be an + important optimization. + + \par API Usage Example + The following code shows typical usage of the boot API. + + \code + #include + #include + #include + + void boot_program_page (uint32_t page, uint8_t *buf) + { + uint16_t i; + uint8_t sreg; + + // Disable interrupts. + + sreg = SREG; + cli(); + + eeprom_busy_wait (); + + boot_page_erase (page); + boot_spm_busy_wait (); // Wait until the memory is erased. + + for (i=0; i +#include +#include +#include + +/* Check for SPM Control Register in processor. */ +#if defined (SPMCSR) +# define __SPM_REG SPMCSR +#elif defined (SPMCR) +# define __SPM_REG SPMCR +#else +# error AVR processor does not provide bootloader support! +#endif + + +/* Check for SPM Enable bit. */ +#if defined(SPMEN) +# define __SPM_ENABLE SPMEN +#elif defined(SELFPRGEN) +# define __SPM_ENABLE SELFPRGEN +#else +# error Cannot find SPM Enable bit definition! +#endif + +/** \ingroup avr_boot + \def BOOTLOADER_SECTION + + Used to declare a function or variable to be placed into a + new section called .bootloader. This section and its contents + can then be relocated to any address (such as the bootloader + NRWW area) at link-time. */ + +#define BOOTLOADER_SECTION __attribute__ ((section (".bootloader"))) + +/* Create common bit definitions. */ +#ifdef ASB +#define __COMMON_ASB ASB +#else +#define __COMMON_ASB RWWSB +#endif + +#ifdef ASRE +#define __COMMON_ASRE ASRE +#else +#define __COMMON_ASRE RWWSRE +#endif + +/* Define the bit positions of the Boot Lock Bits. */ + +#define BLB12 5 +#define BLB11 4 +#define BLB02 3 +#define BLB01 2 + +/** \ingroup avr_boot + \def boot_spm_interrupt_enable() + Enable the SPM interrupt. */ + +#define boot_spm_interrupt_enable() (__SPM_REG |= (uint8_t)_BV(SPMIE)) + +/** \ingroup avr_boot + \def boot_spm_interrupt_disable() + Disable the SPM interrupt. */ + +#define boot_spm_interrupt_disable() (__SPM_REG &= (uint8_t)~_BV(SPMIE)) + +/** \ingroup avr_boot + \def boot_is_spm_interrupt() + Check if the SPM interrupt is enabled. */ + +#define boot_is_spm_interrupt() (__SPM_REG & (uint8_t)_BV(SPMIE)) + +/** \ingroup avr_boot + \def boot_rww_busy() + Check if the RWW section is busy. */ + +#define boot_rww_busy() (__SPM_REG & (uint8_t)_BV(__COMMON_ASB)) + +/** \ingroup avr_boot + \def boot_spm_busy() + Check if the SPM instruction is busy. */ + +#define boot_spm_busy() (__SPM_REG & (uint8_t)_BV(__SPM_ENABLE)) + +/** \ingroup avr_boot + \def boot_spm_busy_wait() + Wait while the SPM instruction is busy. */ + +#define boot_spm_busy_wait() do{}while(boot_spm_busy()) + +#define __BOOT_PAGE_ERASE (_BV(__SPM_ENABLE) | _BV(PGERS)) +#define __BOOT_PAGE_WRITE (_BV(__SPM_ENABLE) | _BV(PGWRT)) +#define __BOOT_PAGE_FILL _BV(__SPM_ENABLE) +#define __BOOT_RWW_ENABLE (_BV(__SPM_ENABLE) | _BV(__COMMON_ASRE)) +#define __BOOT_LOCK_BITS_SET (_BV(__SPM_ENABLE) | _BV(BLBSET)) + +#define __boot_page_fill_short(address, data) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r0, %3\n\t" \ + "out %0, %1\n\t" \ + "spm\n\t" \ + "clr r1\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_FILL), \ + "z" ((uint16_t)address), \ + "r" ((uint16_t)data) \ + : "r0" \ + ); \ +})) + +#define __boot_page_fill_normal(address, data) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r0, %3\n\t" \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + "clr r1\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_FILL), \ + "z" ((uint16_t)address), \ + "r" ((uint16_t)data) \ + : "r0" \ + ); \ +})) + +#define __boot_page_fill_alternate(address, data)\ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r0, %3\n\t" \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + ".word 0xffff\n\t" \ + "nop\n\t" \ + "clr r1\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_FILL), \ + "z" ((uint16_t)address), \ + "r" ((uint16_t)data) \ + : "r0" \ + ); \ +})) + +#define __boot_page_fill_extended(address, data) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r0, %4\n\t" \ + "movw r30, %A3\n\t" \ + "sts %1, %C3\n\t" \ + "sts %0, %2\n\t" \ + "spm\n\t" \ + "clr r1\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "i" (_SFR_MEM_ADDR(RAMPZ)), \ + "r" ((uint8_t)__BOOT_PAGE_FILL), \ + "r" ((uint32_t)address), \ + "r" ((uint16_t)data) \ + : "r0", "r30", "r31" \ + ); \ +})) + +#define __boot_page_fill_extended_short(address, data) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r0, %4\n\t" \ + "movw r30, %A3\n\t" \ + "out %1, %C3\n\t" \ + "out %0, %2\n\t" \ + "spm\n\t" \ + "clr r1\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "i" (_SFR_IO_ADDR(RAMPZ)), \ + "r" ((uint8_t)__BOOT_PAGE_FILL), \ + "r" ((uint32_t)address), \ + "r" ((uint16_t)data) \ + : "r0", "r30", "r31" \ + ); \ +})) + +#define __boot_page_erase_short(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "out %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_ERASE), \ + "z" ((uint16_t)address) \ + ); \ +})) + + +#define __boot_page_erase_normal(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_ERASE), \ + "z" ((uint16_t)address) \ + ); \ +})) + +#define __boot_page_erase_alternate(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + ".word 0xffff\n\t" \ + "nop\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_ERASE), \ + "z" ((uint16_t)address) \ + ); \ +})) + +#define __boot_page_erase_extended(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r30, %A3\n\t" \ + "sts %1, %C3\n\t" \ + "sts %0, %2\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "i" (_SFR_MEM_ADDR(RAMPZ)), \ + "r" ((uint8_t)__BOOT_PAGE_ERASE), \ + "r" ((uint32_t)address) \ + : "r30", "r31" \ + ); \ +})) +#define __boot_page_erase_extended_short(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r30, %A3\n\t" \ + "out %1, %C3\n\t" \ + "out %0, %2\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "i" (_SFR_IO_ADDR(RAMPZ)), \ + "r" ((uint8_t)__BOOT_PAGE_ERASE), \ + "r" ((uint32_t)address) \ + : "r30", "r31" \ + ); \ +})) + +#define __boot_page_write_short(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "out %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_WRITE), \ + "z" ((uint16_t)address) \ + ); \ +})) + +#define __boot_page_write_normal(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_WRITE), \ + "z" ((uint16_t)address) \ + ); \ +})) + +#define __boot_page_write_alternate(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + ".word 0xffff\n\t" \ + "nop\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_PAGE_WRITE), \ + "z" ((uint16_t)address) \ + ); \ +})) + +#define __boot_page_write_extended(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r30, %A3\n\t" \ + "sts %1, %C3\n\t" \ + "sts %0, %2\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "i" (_SFR_MEM_ADDR(RAMPZ)), \ + "r" ((uint8_t)__BOOT_PAGE_WRITE), \ + "r" ((uint32_t)address) \ + : "r30", "r31" \ + ); \ +})) +#define __boot_page_write_extended_short(address) \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "movw r30, %A3\n\t" \ + "out %1, %C3\n\t" \ + "out %0, %2\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "i" (_SFR_IO_ADDR(RAMPZ)), \ + "r" ((uint8_t)__BOOT_PAGE_WRITE), \ + "r" ((uint32_t)address) \ + : "r30", "r31" \ + ); \ +})) + +#define __boot_rww_enable_short() \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "out %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_RWW_ENABLE) \ + ); \ +})) + +#define __boot_rww_enable() \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_RWW_ENABLE) \ + ); \ +})) + +#define __boot_rww_enable_alternate() \ +(__extension__({ \ + __asm__ __volatile__ \ + ( \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + ".word 0xffff\n\t" \ + "nop\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_RWW_ENABLE) \ + ); \ +})) + +/* From the mega16/mega128 data sheets (maybe others): + + Bits by SPM To set the Boot Loader Lock bits, write the desired data to + R0, write "X0001001" to SPMCR and execute SPM within four clock cycles + after writing SPMCR. The only accessible Lock bits are the Boot Lock bits + that may prevent the Application and Boot Loader section from any + software update by the MCU. + + If bits 5..2 in R0 are cleared (zero), the corresponding Boot Lock bit + will be programmed if an SPM instruction is executed within four cycles + after BLBSET and SPMEN (or SELFPRGEN) are set in SPMCR. The Z-pointer is + don't care during this operation, but for future compatibility it is + recommended to load the Z-pointer with $0001 (same as used for reading the + Lock bits). For future compatibility It is also recommended to set bits 7, + 6, 1, and 0 in R0 to 1 when writing the Lock bits. When programming the + Lock bits the entire Flash can be read during the operation. */ + +#define __boot_lock_bits_set_short(lock_bits) \ +(__extension__({ \ + uint8_t value = (uint8_t)(~(lock_bits)); \ + __asm__ __volatile__ \ + ( \ + "ldi r30, 1\n\t" \ + "ldi r31, 0\n\t" \ + "mov r0, %2\n\t" \ + "out %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ + "r" (value) \ + : "r0", "r30", "r31" \ + ); \ +})) + +#define __boot_lock_bits_set(lock_bits) \ +(__extension__({ \ + uint8_t value = (uint8_t)(~(lock_bits)); \ + __asm__ __volatile__ \ + ( \ + "ldi r30, 1\n\t" \ + "ldi r31, 0\n\t" \ + "mov r0, %2\n\t" \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ + "r" (value) \ + : "r0", "r30", "r31" \ + ); \ +})) + +#define __boot_lock_bits_set_alternate(lock_bits) \ +(__extension__({ \ + uint8_t value = (uint8_t)(~(lock_bits)); \ + __asm__ __volatile__ \ + ( \ + "ldi r30, 1\n\t" \ + "ldi r31, 0\n\t" \ + "mov r0, %2\n\t" \ + "sts %0, %1\n\t" \ + "spm\n\t" \ + ".word 0xffff\n\t" \ + "nop\n\t" \ + : \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ + "r" (value) \ + : "r0", "r30", "r31" \ + ); \ +})) + +/* + Reading lock and fuse bits: + + Similarly to writing the lock bits above, set BLBSET and SPMEN (or + SELFPRGEN) bits in __SPMREG, and then (within four clock cycles) issue an + LPM instruction. + + Z address: contents: + 0x0000 low fuse bits + 0x0001 lock bits + 0x0002 extended fuse bits + 0x0003 high fuse bits + + Sounds confusing, doesn't it? + + Unlike the macros in pgmspace.h, no need to care for non-enhanced + cores here as these old cores do not provide SPM support anyway. + */ + +/** \ingroup avr_boot + \def GET_LOW_FUSE_BITS + address to read the low fuse bits, using boot_lock_fuse_bits_get + */ +#define GET_LOW_FUSE_BITS (0x0000) +/** \ingroup avr_boot + \def GET_LOCK_BITS + address to read the lock bits, using boot_lock_fuse_bits_get + */ +#define GET_LOCK_BITS (0x0001) +/** \ingroup avr_boot + \def GET_EXTENDED_FUSE_BITS + address to read the extended fuse bits, using boot_lock_fuse_bits_get + */ +#define GET_EXTENDED_FUSE_BITS (0x0002) +/** \ingroup avr_boot + \def GET_HIGH_FUSE_BITS + address to read the high fuse bits, using boot_lock_fuse_bits_get + */ +#define GET_HIGH_FUSE_BITS (0x0003) + +/** \ingroup avr_boot + \def boot_lock_fuse_bits_get(address) + + Read the lock or fuse bits at \c address. + + Parameter \c address can be any of GET_LOW_FUSE_BITS, + GET_LOCK_BITS, GET_EXTENDED_FUSE_BITS, or GET_HIGH_FUSE_BITS. + + \note The lock and fuse bits returned are the physical values, + i.e. a bit returned as 0 means the corresponding fuse or lock bit + is programmed. + */ +#define boot_lock_fuse_bits_get_short(address) \ +(__extension__({ \ + uint8_t __result; \ + __asm__ __volatile__ \ + ( \ + "ldi r30, %3\n\t" \ + "ldi r31, 0\n\t" \ + "out %1, %2\n\t" \ + "lpm %0, Z\n\t" \ + : "=r" (__result) \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ + "M" (address) \ + : "r0", "r30", "r31" \ + ); \ + __result; \ +})) + +#define boot_lock_fuse_bits_get(address) \ +(__extension__({ \ + uint8_t __result; \ + __asm__ __volatile__ \ + ( \ + "ldi r30, %3\n\t" \ + "ldi r31, 0\n\t" \ + "sts %1, %2\n\t" \ + "lpm %0, Z\n\t" \ + : "=r" (__result) \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \ + "M" (address) \ + : "r0", "r30", "r31" \ + ); \ + __result; \ +})) + +/** \ingroup avr_boot + \def boot_signature_byte_get(address) + + Read the Signature Row byte at \c address. For some MCU types, + this function can also retrieve the factory-stored oscillator + calibration bytes. + + Parameter \c address can be 0-0x1f as documented by the datasheet. + \note The values are MCU type dependent. +*/ + +#define __BOOT_SIGROW_READ (_BV(__SPM_ENABLE) | _BV(SIGRD)) + +#define boot_signature_byte_get_short(addr) \ +(__extension__({ \ + uint16_t __addr16 = (uint16_t)(addr); \ + uint8_t __result; \ + __asm__ __volatile__ \ + ( \ + "out %1, %2\n\t" \ + "lpm %0, Z" "\n\t" \ + : "=r" (__result) \ + : "i" (_SFR_IO_ADDR(__SPM_REG)), \ + "r" ((uint8_t) __BOOT_SIGROW_READ), \ + "z" (__addr16) \ + ); \ + __result; \ +})) + +#define boot_signature_byte_get(addr) \ +(__extension__({ \ + uint16_t __addr16 = (uint16_t)(addr); \ + uint8_t __result; \ + __asm__ __volatile__ \ + ( \ + "sts %1, %2\n\t" \ + "lpm %0, Z" "\n\t" \ + : "=r" (__result) \ + : "i" (_SFR_MEM_ADDR(__SPM_REG)), \ + "r" ((uint8_t) __BOOT_SIGROW_READ), \ + "z" (__addr16) \ + ); \ + __result; \ +})) + +/** \ingroup avr_boot + \def boot_page_fill(address, data) + + Fill the bootloader temporary page buffer for flash + address with data word. + + \note The address is a byte address. The data is a word. The AVR + writes data to the buffer a word at a time, but addresses the buffer + per byte! So, increment your address by 2 between calls, and send 2 + data bytes in a word format! The LSB of the data is written to the lower + address; the MSB of the data is written to the higher address.*/ + +/** \ingroup avr_boot + \def boot_page_erase(address) + + Erase the flash page that contains address. + + \note address is a byte address in flash, not a word address. */ + +/** \ingroup avr_boot + \def boot_page_write(address) + + Write the bootloader temporary page buffer + to flash page that contains address. + + \note address is a byte address in flash, not a word address. */ + +/** \ingroup avr_boot + \def boot_rww_enable() + + Enable the Read-While-Write memory section. */ + +/** \ingroup avr_boot + \def boot_lock_bits_set(lock_bits) + + Set the bootloader lock bits. + + \param lock_bits A mask of which Boot Loader Lock Bits to set. + + \note In this context, a 'set bit' will be written to a zero value. + Note also that only BLBxx bits can be programmed by this command. + + For example, to disallow the SPM instruction from writing to the Boot + Loader memory section of flash, you would use this macro as such: + + \code + boot_lock_bits_set (_BV (BLB11)); + \endcode + + \note Like any lock bits, the Boot Loader Lock Bits, once set, + cannot be cleared again except by a chip erase which will in turn + also erase the boot loader itself. */ + +/* Normal versions of the macros use 16-bit addresses. + Extended versions of the macros use 32-bit addresses. + Alternate versions of the macros use 16-bit addresses and require special + instruction sequences after LPM. + + FLASHEND is defined in the ioXXXX.h file. + USHRT_MAX is defined in . */ + +#if defined(__AVR_ATmega161__) || defined(__AVR_ATmega163__) \ + || defined(__AVR_ATmega323__) + +/* Alternate: ATmega161/163/323 and 16 bit address */ +#define boot_page_fill(address, data) __boot_page_fill_alternate(address, data) +#define boot_page_erase(address) __boot_page_erase_alternate(address) +#define boot_page_write(address) __boot_page_write_alternate(address) +#define boot_rww_enable() __boot_rww_enable_alternate() +#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_alternate(lock_bits) + +#elif (FLASHEND > USHRT_MAX) + +/* Extended: >16 bit address */ +#define boot_page_fill(address, data) __boot_page_fill_extended_short(address, data) +#define boot_page_erase(address) __boot_page_erase_extended_short(address) +#define boot_page_write(address) __boot_page_write_extended_short(address) +#define boot_rww_enable() __boot_rww_enable_short() +#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits) + +#else + +/* Normal: 16 bit address */ +#define boot_page_fill(address, data) __boot_page_fill_short(address, data) +#define boot_page_erase(address) __boot_page_erase_short(address) +#define boot_page_write(address) __boot_page_write_short(address) +#define boot_rww_enable() __boot_rww_enable_short() +#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_short(lock_bits) + +#endif + +/** \ingroup avr_boot + + Same as boot_page_fill() except it waits for eeprom and spm operations to + complete before filling the page. */ + +#define boot_page_fill_safe(address, data) \ +do { \ + boot_spm_busy_wait(); \ + eeprom_busy_wait(); \ + boot_page_fill(address, data); \ +} while (0) + +/** \ingroup avr_boot + + Same as boot_page_erase() except it waits for eeprom and spm operations to + complete before erasing the page. */ + +#define boot_page_erase_safe(address) \ +do { \ + boot_spm_busy_wait(); \ + eeprom_busy_wait(); \ + boot_page_erase (address); \ +} while (0) + +/** \ingroup avr_boot + + Same as boot_page_write() except it waits for eeprom and spm operations to + complete before writing the page. */ + +#define boot_page_write_safe(address) \ +do { \ + boot_spm_busy_wait(); \ + eeprom_busy_wait(); \ + boot_page_write (address); \ +} while (0) + +/** \ingroup avr_boot + + Same as boot_rww_enable() except waits for eeprom and spm operations to + complete before enabling the RWW mameory. */ + +#define boot_rww_enable_safe() \ +do { \ + boot_spm_busy_wait(); \ + eeprom_busy_wait(); \ + boot_rww_enable(); \ +} while (0) + +/** \ingroup avr_boot + + Same as boot_lock_bits_set() except waits for eeprom and spm operations to + complete before setting the lock bits. */ + +#define boot_lock_bits_set_safe(lock_bits) \ +do { \ + boot_spm_busy_wait(); \ + eeprom_busy_wait(); \ + boot_lock_bits_set (lock_bits); \ +} while (0) + +#endif /* _AVR_BOOT_H_ */ diff --git a/hardware/MySensors/avr/boards.txt b/hardware/MySensors/avr/boards.txt new file mode 100644 index 0000000..a603989 --- /dev/null +++ b/hardware/MySensors/avr/boards.txt @@ -0,0 +1,22 @@ + +MysensorsMicro.name=Mysensors Micro + +MysensorsMicro.upload.tool=arduino:avrdude +MysensorsMicro.upload.protocol=arduino +MysensorsMicro.upload.maximum_size=30720 +MysensorsMicro.upload.maximum_data_size=2048 +MysensorsMicro.upload.speed=57600 + +MysensorsMicro.bootloader.tool=arduino:avrdude +MysensorsMicro.bootloader.unlock_bits=0x3F +MysensorsMicro.bootloader.lock_bits=0x0F +MysensorsMicro.bootloader.low_fuses=0xE2 +MysensorsMicro.bootloader.high_fuses=0xD2 +MysensorsMicro.bootloader.extended_fuses=0xFE +MysensorsMicro.bootloader.file=DualOptiboot/optiboot_atmega328p-pro8.hex + +MysensorsMicro.build.board=AVR_MICRO8 +MysensorsMicro.build.core=arduino:arduino +MysensorsMicro.build.variant=micro +MysensorsMicro.build.mcu=atmega328p +MysensorsMicro.build.f_cpu=8000000L diff --git a/hardware/MySensors/avr/platform.txt b/hardware/MySensors/avr/platform.txt new file mode 100644 index 0000000..332dfce --- /dev/null +++ b/hardware/MySensors/avr/platform.txt @@ -0,0 +1,104 @@ + +# Arduino AVR Core and platform. +# ------------------------------ + +# For more info: +# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification + +name=MySensors Boards +version=1.6.0 + +# AVR compile variables +# --------------------- + +# Default "compiler.path" is correct, change only if you want to overidde the initial value +compiler.path={runtime.tools.avr-gcc.path}/bin/ +compiler.c.cmd=avr-gcc +compiler.c.flags=-c -g -Os -w -ffunction-sections -fdata-sections -MMD +# -w flag added to avoid printing a wrong warning http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59396 +# This is fixed in gcc 4.8.3 and will be removed as soon as we update the toolchain +compiler.c.elf.flags=-w -Os -Wl,--gc-sections +compiler.c.elf.cmd=avr-gcc +compiler.S.flags=-c -g -x assembler-with-cpp +compiler.cpp.cmd=avr-g++ +compiler.cpp.flags=-c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD +compiler.ar.cmd=avr-ar +compiler.ar.flags=rcs +compiler.objcopy.cmd=avr-objcopy +compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 +compiler.elf2hex.flags=-O ihex -R .eeprom +compiler.elf2hex.cmd=avr-objcopy +compiler.ldflags= +compiler.size.cmd=avr-size + +# This can be overriden in boards.txt +build.extra_flags= + +# These can be overridden in platform.local.txt +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= + +# AVR compile patterns +# -------------------- + +## Compile c files +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" + +## Compile c++ files +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" + +## Compile S files +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.S.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" + +## Create archives +recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/{archive_file}" "{object_file}" + +## Combine gc-sections, archives, and objects +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mmcu={build.mcu} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" {object_files} "{build.path}/{archive_file}" "-L{build.path}" -lm + +## Create eeprom +recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.objcopy.eep.flags} {compiler.objcopy.eep.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.eep" + +## Create hex +recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex" + +## Compute size +recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" +recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).* +recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).* +recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).* + + +# AVR Uploader/Programmers tools +# ------------------------------ + +tools.avrdude.cmd.path={runtime.ide.path}/hardware/tools/avr/bin/avrdude +tools.avrdude.config.path={runtime.ide.path}/hardware/tools/avr/etc/avrdude.conf + +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} -p{build.mcu} -c{upload.protocol} -P{serial.port} -b{upload.speed} -D "-Uflash:w:{build.path}/{build.project_name}.hex:i" + +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i" + +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern="{cmd.path}" "-C{config.path}" {erase.verbose} -p{build.mcu} -c{protocol} {program.extra_params} -e -Ulock:w:{bootloader.unlock_bits}:m -Uefuse:w:{bootloader.extended_fuses}:m -Uhfuse:w:{bootloader.high_fuses}:m -Ulfuse:w:{bootloader.low_fuses}:m + +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern="{cmd.path}" "-C{config.path}" {bootloader.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{runtime.platform.path}/bootloaders/{bootloader.file}:i" -Ulock:w:{bootloader.lock_bits}:m + + +# USB Default Flags +# Default blank usb manufacturer will be filled it at compile time +# - from numeric vendor ID, set to Unknown otherwise +build.usb_manufacturer= +build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}' diff --git a/hardware/MySensors/avr/variants/micro/pins_arduino.h b/hardware/MySensors/avr/variants/micro/pins_arduino.h new file mode 100644 index 0000000..3d4a9f6 --- /dev/null +++ b/hardware/MySensors/avr/variants/micro/pins_arduino.h @@ -0,0 +1,238 @@ +/* + pins_arduino.h - Pin definition functions for Arduino + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2007 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define NUM_DIGITAL_PINS 20 +#define NUM_ANALOG_INPUTS 6 +#define analogInputToDigitalPin(p) ((p < 6) ? (p) + 14 : -1) + +#if defined(__AVR_ATmega8__) +#define digitalPinHasPWM(p) ((p) == 9 || (p) == 10 || (p) == 11) +#else +#define digitalPinHasPWM(p) ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11) +#endif + +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 12; +static const uint8_t SCK = 13; + +static const uint8_t SDA = 18; +static const uint8_t SCL = 19; +#define LED_BUILTIN 13 + +static const uint8_t A0 = 14; +static const uint8_t A1 = 15; +static const uint8_t A2 = 16; +static const uint8_t A3 = 17; +static const uint8_t A4 = 18; +static const uint8_t A5 = 19; +static const uint8_t A6 = 20; +static const uint8_t A7 = 21; + +#define digitalPinToPCICR(p) (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0)) +#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1)) +#define digitalPinToPCMSK(p) (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)0)))) +#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14))) + +#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : NOT_AN_INTERRUPT)) + +#ifdef ARDUINO_MAIN + +// On the Arduino board, digital pins are also used +// for the analog output (software PWM). Analog input +// pins are a separate set. + +// ATMEL ATMEGA8 & 168 / ARDUINO +// +// +-\/-+ +// PC6 1| |28 PC5 (AI 5) +// (D 0) PD0 2| |27 PC4 (AI 4) +// (D 1) PD1 3| |26 PC3 (AI 3) +// (D 2) PD2 4| |25 PC2 (AI 2) +// PWM+ (D 3) PD3 5| |24 PC1 (AI 1) +// (D 4) PD4 6| |23 PC0 (AI 0) +// VCC 7| |22 GND +// GND 8| |21 AREF +// PB6 9| |20 AVCC +// PB7 10| |19 PB5 (D 13) +// PWM+ (D 5) PD5 11| |18 PB4 (D 12) +// PWM+ (D 6) PD6 12| |17 PB3 (D 11) PWM +// (D 7) PD7 13| |16 PB2 (D 10) PWM +// (D 8) PB0 14| |15 PB1 (D 9) PWM +// +----+ +// +// (PWM+ indicates the additional PWM pins on the ATmega168.) + +// ATMEL ATMEGA1280 / ARDUINO +// +// 0-7 PE0-PE7 works +// 8-13 PB0-PB5 works +// 14-21 PA0-PA7 works +// 22-29 PH0-PH7 works +// 30-35 PG5-PG0 works +// 36-43 PC7-PC0 works +// 44-51 PJ7-PJ0 works +// 52-59 PL7-PL0 works +// 60-67 PD7-PD0 works +// A0-A7 PF0-PF7 +// A8-A15 PK0-PK7 + + +// these arrays map port names (e.g. port B) to the +// appropriate addresses for various functions (e.g. reading +// and writing) +const uint16_t PROGMEM port_to_mode_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &DDRB, + (uint16_t) &DDRC, + (uint16_t) &DDRD, +}; + +const uint16_t PROGMEM port_to_output_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PORTB, + (uint16_t) &PORTC, + (uint16_t) &PORTD, +}; + +const uint16_t PROGMEM port_to_input_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PINB, + (uint16_t) &PINC, + (uint16_t) &PIND, +}; + +const uint8_t PROGMEM digital_pin_to_port_PGM[] = { + PD, /* 0 */ + PD, + PD, + PD, + PD, + PD, + PD, + PD, + PB, /* 8 */ + PB, + PB, + PB, + PB, + PB, + PC, /* 14 */ + PC, + PC, + PC, + PC, + PC, +}; + +const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = { + _BV(0), /* 0, port D */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), + _BV(6), + _BV(7), + _BV(0), /* 8, port B */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), + _BV(0), /* 14, port C */ + _BV(1), + _BV(2), + _BV(3), + _BV(4), + _BV(5), +}; + +const uint8_t PROGMEM digital_pin_to_timer_PGM[] = { + NOT_ON_TIMER, /* 0 - port D */ + NOT_ON_TIMER, + NOT_ON_TIMER, + // on the ATmega168, digital pin 3 has hardware pwm +#if defined(__AVR_ATmega8__) + NOT_ON_TIMER, +#else + TIMER2B, +#endif + NOT_ON_TIMER, + // on the ATmega168, digital pins 5 and 6 have hardware pwm +#if defined(__AVR_ATmega8__) + NOT_ON_TIMER, + NOT_ON_TIMER, +#else + TIMER0B, + TIMER0A, +#endif + NOT_ON_TIMER, + NOT_ON_TIMER, /* 8 - port B */ + TIMER1A, + TIMER1B, +#if defined(__AVR_ATmega8__) + TIMER2, +#else + TIMER2A, +#endif + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, /* 14 - port C */ + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, +}; + +#endif + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_MONITOR Serial +#define SERIAL_PORT_HARDWARE Serial + +#endif diff --git a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino index 006a785..24e25eb 100644 --- a/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino +++ b/libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino @@ -81,7 +81,8 @@ http://forum.mysensors.org/topic/303/mqtt-broker-gateway #define RADIO_SPI_SS_PIN 8 // radio SPI serial select #define RADIO_ERROR_LED_PIN A2 // Error led pin #define RADIO_RX_LED_PIN A1 // Receive led pin -#define RADIO_TX_LED_PIN A0 // the PCB, on board LED*/ +#define RADIO_TX_LED_PIN A0 // the PCB, on board LED +*/ // * Use this for default configured pro mini / nano etc : ///* @@ -94,7 +95,7 @@ http://forum.mysensors.org/topic/303/mqtt-broker-gateway #define TCP_PORT 1883 // Set your MQTT Broker Listening port. IPAddress TCP_IP ( 192, 168, 0, 234 ); // Configure your static ip-address here -uint8_t TCP_MAC[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - You should change this! see note *2 above! +byte TCP_MAC[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - You should change this! see note *2 above! ////////////////////////////////////////////////////////////////// @@ -115,6 +116,7 @@ MySensor gw(transport, hw /*, signer*/); EthernetServer server = EthernetServer(TCP_PORT); +EthernetClient *currentClient = NULL; MyMessage msg; char convBuf[MAX_PAYLOAD*2+1]; char broker[] PROGMEM = MQTT_BROKER_PREFIX; @@ -137,26 +139,44 @@ void writeEthernet(const char *writeBuffer, uint8_t *writeSize) { void processEthernetMessages() { - char inputString[MQTT_MAX_PACKET_SIZE] = ""; - uint8_t inputSize = 0; - EthernetClient client = server.available(); - if (client) { - while (client.available()) { - char inChar = client.read(); - inputString[inputSize] = inChar; - inputSize++; - } + char inputString[MQTT_MAX_PACKET_SIZE] = ""; + byte inputSize = 0; + byte readCnt = 0; + byte length = 0; + + EthernetClient client = server.available(); + if (client) { + while (client.available()) { + // Save the current client we are talking with + currentClient = &client; + byte inByte = client.read(); + readCnt++; + + if (inputSize < MQTT_MAX_PACKET_SIZE) { + inputString[inputSize] = (char)inByte; + inputSize++; + } + + if (readCnt == 2) { + length = (inByte & 127) * 1; + } + if (readCnt == (length+2)) { + break; + } + } #ifdef TCPDUMP - Serial.print("<<"); - char buf[4]; - for (uint8_t a=0; a #include #include +#include #include +#include +#include // Define a static node address, remove if you want auto address assignment //#define NODE_ADDRESS 3 @@ -20,13 +23,26 @@ #define CHILD_ID_HUM 2 #define CHILD_ID_BATT 199 -//Pin definitions -#define RESET_CFG_PIN A1 -#define LED_PIN A2 - +// How many milli seconds between each measurement #define MEASURE_INTERVAL 60000 +// FORCE_TRANSMIT_INTERVAL, this number of times of wakeup, the sensor is forced to report all values to the controller +#define FORCE_TRANSMIT_INTERVAL 30 + +// When MEASURE_INTERVAL is 60000 and FORCE_TRANSMIT_INTERVAL is 30, we force a transmission every 30 minutes. +// Between the forced transmissions a tranmission will only occur if the measured value differs from the previous measurement + +//Pin definitions +#define TEST_PIN A0 +#define LED_PIN A2 +#define ATSHA204_PIN 17 // A3 + +const int sha204Pin = ATSHA204_PIN; +atsha204Class sha204(sha204Pin); + SI7021 humiditySensor; +SPIFlash flash(8, 0x1F65); + MySensor gw; // Sensor messages @@ -45,16 +61,17 @@ long lastBattery = -100; void setup() { - Serial.begin(115200); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); - // First check if we should boot into clear eeprom mode - pinMode(RESET_CFG_PIN, INPUT); - digitalWrite(RESET_CFG_PIN, HIGH); // Enable pullup - if (!digitalRead(RESET_CFG_PIN)) resetEEP(); - digitalWrite(RESET_CFG_PIN, LOW); + Serial.begin(115200); + // First check if we should boot into test mode + + pinMode(TEST_PIN,INPUT); + digitalWrite(TEST_PIN, HIGH); // Enable pullup + if (!digitalRead(TEST_PIN)) testMode(); + digitalWrite(TEST_PIN,LOW); digitalWrite(LED_PIN, HIGH); #ifdef NODE_ADDRESS @@ -67,12 +84,13 @@ void setup() { humiditySensor.begin(); - gw.sendSketchInfo("EnvironmentSensor", "1.0"); + gw.sendSketchInfo("MysensorMicro", "1.0"); gw.present(CHILD_ID_TEMP,S_TEMP); gw.present(CHILD_ID_HUM,S_HUM); gw.present(CHILD_ID_BATT, S_POWER); + switchClock(1< 30) {// Every 60th time we wake up, force a transmission on all sensors. + if (measureCount > FORCE_TRANSMIT_INTERVAL + ) { // force a transmission forceTransmit = true; measureCount = 0; } @@ -106,23 +125,19 @@ void sendTempHumidityMeasurements(bool force) lastTemperature = -100; } - float temperature = humiditySensor.getCelsiusHundredths()/10; + si7021_env data = humiditySensor.getHumidityAndTemperature(); - temperature = temperature / 10; - - int humidity = humiditySensor.getHumidityPercent(); + float temperature = data.celsiusHundredths/100; + + int humidity = data.humidityPercent; if (lastTemperature != temperature) { gw.send(msgTemp.set(temperature,1)); lastTemperature = temperature; - Serial.print("temperature "); - Serial.println(temperature); } if (lastHumidity != humidity) { gw.send(msgHum.set(humidity)); lastHumidity = humidity; - Serial.print("Humidity "); - Serial.println(humidity); } } @@ -155,7 +170,7 @@ long readVcc() { #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ADMUX = _BV(MUX5) | _BV(MUX0); #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) - ADMUX = _BV(MUX3) | _BV(MUX2); + ADcdMUX = _BV(MUX3) | _BV(MUX2); #else ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #endif @@ -173,19 +188,110 @@ long readVcc() { return result; // Vcc in millivolts } - -/* - * Resets eeprom to default value (while blinking LED_PIN) - */ -void resetEEP() +void switchClock(unsigned char clk) { - for (int i=0;i<512;i++) { - digitalWrite(LED_PIN, HIGH); - delay(100); - digitalWrite(LED_PIN, LOW); - delay(100); - EEPROM.write(i, 0xff); - } + cli(); + + CLKPR = 1< SI7021 : ")); + Serial.flush(); + + if (humiditySensor.begin()) + { + Serial.println(F("ok!")); + tests ++; + } + else + { + Serial.println(F("failed!")); + } + Serial.flush(); + + Serial.print(F("-> Flash : ")); + Serial.flush(); + if (flash.initialize()) + { + Serial.println(F("ok!")); + tests ++; + } + else + { + Serial.println(F("failed!")); + } + Serial.flush(); + + + Serial.print(F("-> SHA204 : ")); + ret_code = sha204.sha204c_wakeup(rx_buffer); + Serial.flush(); + if (ret_code != SHA204_SUCCESS) + { + Serial.print(F("Failed to wake device. Response: ")); Serial.println(ret_code, HEX); + } + Serial.flush(); + if (ret_code == SHA204_SUCCESS) + { + ret_code = sha204.getSerialNumber(rx_buffer); + if (ret_code != SHA204_SUCCESS) + { + Serial.print(F("Failed to obtain device serial number. Response: ")); Serial.println(ret_code, HEX); + } + else + { + Serial.print(F("Ok (serial : ")); + for (int i=0; i<9; i++) + { + if (rx_buffer[i] < 0x10) + { + Serial.print('0'); // Because Serial.print does not 0-pad HEX + } + Serial.print(rx_buffer[i], HEX); + } + Serial.println(")"); + tests ++; + } + + } + Serial.flush(); + + Serial.println(F("Test finished")); + + if (tests == 3) + { + Serial.println(F("Selftest ok!")); + while (1) // Blink OK pattern! + { + digitalWrite(LED_PIN, HIGH); + delay(800); + digitalWrite(LED_PIN, LOW); + delay(200); + } + } + else + { + Serial.println(F("----> Selftest failed!")); + while (1) // Blink FAILED pattern! Rappidly blinking.. + { + digitalWrite(LED_PIN, HIGH); + delay(100); + digitalWrite(LED_PIN, LOW); + delay(100); + } + } +} diff --git a/libraries/MySensors/examples/UVSensor/UVSensor.ino b/libraries/MySensors/examples/UVSensor/UVSensor.ino index 838cdbd..417ae43 100644 --- a/libraries/MySensors/examples/UVSensor/UVSensor.ino +++ b/libraries/MySensors/examples/UVSensor/UVSensor.ino @@ -1,5 +1,7 @@ +#include +#include /* - Vera Arduino UVM-30A + Arduino UVM-30A connect the sensor as follows : @@ -7,44 +9,55 @@ - >>> GND out >>> A0 - Contribution: epierre, bulldoglowell - Converted to 1.4 by Henrik Ekblad + Contribution: epierre, bulldoglowell, gizmocuz + + Index table taken from: http://www.elecrow.com/sensors-c-111/environment-c-111_112/uv-sensor-moduleuvm30a-p-716.html + Because this table is pretty lineair, we can calculate a UVI with one decimal License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) - */ #include #include -#define CHILD_ID_UV 0 #define UV_SENSOR_ANALOG_PIN 0 + +#define CHILD_ID_UV 0 + unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) MySensor gw; MyMessage uvMsg(CHILD_ID_UV, V_UV); -int lastUV = -1; -int uvIndexValue [13] = { 50, 227, 318, 408, 503, 606, 696, 795, 881, 976, 1079, 1170, 3000}; -int uvIndex; + +unsigned long lastSend =0; +float uvIndex; +float lastUV = -1; +int uvIndexValue [12] = { 50, 227, 318, 408, 503, 606, 696, 795, 881, 976, 1079, 1170}; void setup() { gw.begin(); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("UV Sensor", "1.1"); + gw.sendSketchInfo("UV Sensor", "1.2"); // Register all sensors to gateway (they will be created as child devices) gw.present(CHILD_ID_UV, S_UV); - } void loop() -{ - uint16_t uv = analogRead(0);// Get UV value - Serial.print("Uv reading: "); - Serial.println(uv); - for (int i = 0; i < 13; i++) +{ + unsigned long currentTime = millis(); + + uint16_t uv = analogRead(UV_SENSOR_ANALOG_PIN);// Get UV value + if (uv>1170) + uv=1170; + + //Serial.print("UV Analog reading: "); + //Serial.println(uv); + + int i; + for (i = 0; i < 12; i++) { if (uv <= uvIndexValue[i]) { @@ -52,11 +65,21 @@ void loop() break; } } - Serial.print("Uv index: "); - Serial.println(uvIndex); + + //calculate 1 decimal if possible + if (i>0) { + float vRange=uvIndexValue[i]-uvIndexValue[i-1]; + float vCalc=uv-uvIndexValue[i-1]; + uvIndex+=(1.0/vRange)*vCalc-1.0; + } - if (uvIndex != lastUV) { - gw.send(uvMsg.set(uvIndex)); + //Serial.print("UVI: "); + //Serial.println(uvIndex,2); + + //Send value to gateway if changed, or at least every 5 minutes + if ((uvIndex != lastUV)||(currentTime-lastSend >= 5*60*1000)) { + lastSend=currentTime; + gw.send(uvMsg.set(uvIndex,2)); lastUV = uvIndex; } diff --git a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index 1add41d..6e9c4ee 100644 --- a/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/libraries/MySensors/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -13,15 +13,23 @@ #include #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!) +#define SENSOR_INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) + #define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter) + #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false. -#define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers. -#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) -#define CHILD_ID 5 // Id of the sensor child + +#define MAX_FLOW 40 // Max flow (l/min) value to report. This filters outliers. + +#define CHILD_ID 1 // Id of the sensor child + unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't want to spam the gateway. MySensor gw; - +MyMessage flowMsg(CHILD_ID,V_FLOW); +MyMessage volumeMsg(CHILD_ID,V_VOLUME); +MyMessage lastCounterMsg(CHILD_ID,V_VAR1); + double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter volatile unsigned long pulseCount = 0; @@ -31,108 +39,119 @@ boolean pcReceived = false; unsigned long oldPulseCount = 0; unsigned long newBlink = 0; double oldflow = 0; -double volume; -double oldvolume; -unsigned long lastSend; -unsigned long lastPulse; -unsigned long currentTime; -boolean metric; -MyMessage flowMsg(CHILD_ID,V_FLOW); -MyMessage volumeMsg(CHILD_ID,V_VOLUME); -MyMessage pcMsg(CHILD_ID,V_VAR1); - +double volume =0; +double oldvolume =0; +unsigned long lastSend =0; +unsigned long lastPulse =0; void setup() { gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller - gw.sendSketchInfo("Water Meter", "1.0"); + gw.sendSketchInfo("Water Meter", "1.2"); // Register this device as Waterflow sensor gw.present(CHILD_ID, S_WATER); + pulseCount = oldPulseCount = 0; + // Fetch last known pulse count value from gw gw.request(CHILD_ID, V_VAR1); - attachInterrupt(INTERRUPT, onPulse, RISING); - lastSend = millis(); + lastSend = lastPulse = millis(); + + attachInterrupt(SENSOR_INTERRUPT, onPulse, RISING); } void loop() { gw.process(); - currentTime = millis(); + unsigned long currentTime = millis(); // Only send values at a maximum frequency or woken up from sleep - bool sendTime = currentTime - lastSend > SEND_FREQUENCY; - if (pcReceived && (SLEEP_MODE || sendTime)) { - // New flow value has been calculated + if (SLEEP_MODE || (currentTime - lastSend > SEND_FREQUENCY)) + { + lastSend=currentTime; + + if (!pcReceived) { + //Last Pulsecount not yet received from controller, request it again + gw.request(CHILD_ID, V_VAR1); + return; + } + if (!SLEEP_MODE && flow != oldflow) { + oldflow = flow; + + Serial.print("l/min:"); + Serial.println(flow); + // Check that we dont get unresonable large flow value. // could hapen when long wraps or false interrupt triggered if (flow<((unsigned long)MAX_FLOW)) { gw.send(flowMsg.set(flow, 2)); // Send flow value to gw } - //Serial.print("l/min:"); - //Serial.println(flow); - oldflow = flow; } - // No Pulse count in 2min + // No Pulse count received in 2min if(currentTime - lastPulse > 120000){ flow = 0; } - - + // Pulse count has changed if (pulseCount != oldPulseCount) { - gw.send(pcMsg.set(pulseCount)); // Send volumevalue to gw VAR1 - double volume = ((double)pulseCount/((double)PULSE_FACTOR)); oldPulseCount = pulseCount; + + Serial.print("pulsecount:"); + Serial.println(pulseCount); + + gw.send(lastCounterMsg.set(pulseCount)); // Send pulsecount value to gw in VAR1 + + double volume = ((double)pulseCount/((double)PULSE_FACTOR)); if (volume != oldvolume) { - gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw oldvolume = volume; + + Serial.print("volume:"); + Serial.println(volume, 3); + + gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw } } - lastSend = currentTime; - } else if (sendTime && !pcReceived) { - // No count received. Try requesting it again - gw.request(CHILD_ID, V_VAR1); - lastSend=currentTime; } - if (SLEEP_MODE) { gw.sleep(SEND_FREQUENCY); } } void incomingMessage(const MyMessage &message) { - if (message.type==V_VAR1) { - pulseCount = oldPulseCount = message.getLong(); + if (message.type==V_VAR1) { + unsigned long gwPulseCount=message.getULong(); + pulseCount += gwPulseCount; Serial.print("Received last pulse count from gw:"); Serial.println(pulseCount); pcReceived = true; } } - void onPulse() -{ - if (!SLEEP_MODE) { +{ + if (!SLEEP_MODE) + { unsigned long newBlink = micros(); unsigned long interval = newBlink-lastBlink; - lastPulse = millis(); - if (interval<500000L) { - // Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min) - return; + + if (interval!=0) + { + lastPulse = millis(); + if (interval<500000L) { + // Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min) + return; + } + flow = (60000000.0 /interval) / ppl; } - flow = (60000000.0 /interval) / ppl; lastBlink = newBlink; } pulseCount++; } - - diff --git a/libraries/SI7021/SI7021.cpp b/libraries/SI7021/SI7021.cpp index 2bde080..c51ab6b 100644 --- a/libraries/SI7021/SI7021.cpp +++ b/libraries/SI7021/SI7021.cpp @@ -128,7 +128,7 @@ void SI7021::setHeater(bool on) { // get humidity, then get temperature reading from humidity measurement struct si7021_env SI7021::getHumidityAndTemperature() { si7021_env ret = {0, 0, 0}; - ret.humidityBasisPoints = getHumidityBasisPoints(); + ret.humidityPercent = getHumidityPercent(); ret.celsiusHundredths = _getCelsiusPostHumidity(); ret.fahrenheitHundredths = (1.8 * ret.celsiusHundredths) + 3200; return ret; diff --git a/libraries/SI7021/SI7021.h b/libraries/SI7021/SI7021.h index 5aa9f4b..d577900 100644 --- a/libraries/SI7021/SI7021.h +++ b/libraries/SI7021/SI7021.h @@ -23,7 +23,7 @@ This program is licensed under the GNU GPL v2 typedef struct si7021_env { int celsiusHundredths; int fahrenheitHundredths; - unsigned int humidityBasisPoints; + unsigned int humidityPercent; } si7021_env; class SI7021 diff --git a/libraries/SPIFlash/SPIFlash.cpp b/libraries/SPIFlash/SPIFlash.cpp index 03d6a1a..bad4cdf 100644 --- a/libraries/SPIFlash/SPIFlash.cpp +++ b/libraries/SPIFlash/SPIFlash.cpp @@ -6,6 +6,9 @@ * Minimal modifications should allow chips that have different page size but modifications * DEPENDS ON: Arduino SPI library * + * Updated Jan. 5, 2015, TomWS1, modified writeBytes to allow blocks > 256 bytes and handle page misalignment. + * Updated Feb. 26, 2015 TomWS1, added support for SPI Transactions (Arduino 1.5.8 and above) + * * This file is free software; you can redistribute it and/or modify * it under the terms of either the GNU General Public License version 2 * or the GNU Lesser General Public License version 2.1, both as @@ -32,15 +35,22 @@ SPIFlash::SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID) { /// Select the flash chip void SPIFlash::select() { - noInterrupts(); //save current SPI settings +#ifndef SPI_HAS_TRANSACTION + _SREG = SREG; + noInterrupts(); +#endif _SPCR = SPCR; _SPSR = SPSR; - //set FLASH chip SPI settings + +#ifdef SPI_HAS_TRANSACTION + SPI.beginTransaction(_settings); +#else + // set FLASH SPI settings SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); - SPI.setClockDivider(SPI_CLOCK_DIV4); //decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present - SPI.begin(); + SPI.setClockDivider(SPI_CLOCK_DIV4); // decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present +#endif digitalWrite(_slaveSelectPin, LOW); } @@ -48,9 +58,13 @@ void SPIFlash::select() { void SPIFlash::unselect() { digitalWrite(_slaveSelectPin, HIGH); //restore SPI settings to what they were before talking to the FLASH chip +#ifdef SPI_HAS_TRANSACTION + SPI.endTransaction(); +#else + SREG = _SREG; // restore interrupts IFF they were enabled +#endif SPCR = _SPCR; SPSR = _SPSR; - interrupts(); } /// setup SPI, read device ID etc... @@ -58,7 +72,10 @@ boolean SPIFlash::initialize() { _SPCR = SPCR; _SPSR = SPSR; + digitalWrite(_slaveSelectPin, HIGH); pinMode(_slaveSelectPin, OUTPUT); + SPI.begin(); + _settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); unselect(); wakeup(); @@ -185,22 +202,33 @@ void SPIFlash::writeByte(long addr, uint8_t byt) { unselect(); } -/// write 1-256 bytes to flash memory +/// write multiple bytes to flash memory (up to 64K) /// WARNING: you can only write to previously erased memory locations (see datasheet) /// use the block erase commands to first clear memory (write 0xFFs) -/// WARNING: if you write beyond a page boundary (or more than 256bytes), -/// the bytes will wrap around and start overwriting at the beginning of that same page -/// see datasheet for more details +/// This version handles both page alignment and data blocks larger than 256 bytes. +/// void SPIFlash::writeBytes(long addr, const void* buf, uint16_t len) { - command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program - SPI.transfer(addr >> 16); - SPI.transfer(addr >> 8); - SPI.transfer(addr); - for (uint16_t i = 0; i < len; i++) - SPI.transfer(((byte*) buf)[i]); - unselect(); + uint16_t n; + uint16_t maxBytes = 256-(addr%256); // force the first set of bytes to stay within the first page + uint16_t offset = 0; + while (len>0) + { + n = (len<=maxBytes) ? len : maxBytes; + command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + for (uint16_t i = 0; i < n; i++) + SPI.transfer(((byte*) buf)[offset + i]); + unselect(); + addr+=n; // adjust the addresses and remaining bytes by what we've just transferred. + offset +=n; + len -= n; + maxBytes = 256; // now we can do up to 256 bytes per loop + } } + /// erase entire flash memory array /// may take several seconds depending on size, but is non blocking /// so you may wait for this to complete using busy() or continue doing diff --git a/libraries/SPIFlash/SPIFlash.h b/libraries/SPIFlash/SPIFlash.h index f0c071a..8f530b7 100644 --- a/libraries/SPIFlash/SPIFlash.h +++ b/libraries/SPIFlash/SPIFlash.h @@ -1,11 +1,13 @@ /* * Copyright (c) 2013 by Felix Rusu * SPI Flash memory library for arduino/moteino. - * This works with 256byte/page SPI flash memory - * For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes) + * This works with 256uint8_t/page SPI flash memory + * For instance a 4MBit (512Kuint8_t) flash chip will have 2048 pages: 256*2048 = 524288 uint8_ts (512Kuint8_ts) * Minimal modifications should allow chips that have different page size but modifications * DEPENDS ON: Arduino SPI library * + * Updated Feb. 26, 2015 TomWS1, added support for SPI Transactions (Arduino 1.5.8 and above) + * * This file is free software; you can redistribute it and/or modify * it under the terms of either the GNU General Public License version 2 * or the GNU Lesser General Public License version 2.1, both as @@ -56,34 +58,34 @@ // but no actual need to wait for completion (instead need to check the status register BUSY bit) #define SPIFLASH_STATUSREAD 0x05 // read status register #define SPIFLASH_STATUSWRITE 0x01 // write status register -#define SPIFLASH_ARRAYREAD 0x0B // read array (fast, need to add 1 dummy byte after 3 address bytes) +#define SPIFLASH_ARRAYREAD 0x0B // read array (fast, need to add 1 dummy uint8_t after 3 address uint8_ts) #define SPIFLASH_ARRAYREADLOWFREQ 0x03 // read array (low frequency) #define SPIFLASH_SLEEP 0xB9 // deep power down #define SPIFLASH_WAKE 0xAB // deep power wake up -#define SPIFLASH_BYTEPAGEPROGRAM 0x02 // write (1 to 256bytes) -#define SPIFLASH_IDREAD 0x9F // read JEDEC manufacturer and device ID (2 bytes, specific bytes for each manufacturer and device) +#define SPIFLASH_BYTEPAGEPROGRAM 0x02 // write (1 to 256uint8_ts) +#define SPIFLASH_IDREAD 0x9F // read JEDEC manufacturer and device ID (2 uint8_ts, specific uint8_ts for each manufacturer and device) // Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf) // Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf) #define SPIFLASH_MACREAD 0x4B // read unique ID number (MAC) class SPIFlash { public: - static byte UNIQUEID[8]; - SPIFlash(byte slaveSelectPin, uint16_t jedecID=0); + static uint8_t UNIQUEID[8]; + SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID=0); boolean initialize(); - void command(byte cmd, boolean isWrite=false); - byte readStatus(); - byte readByte(long addr); + void command(uint8_t cmd, boolean isWrite=false); + uint8_t readStatus(); + uint8_t readByte(long addr); void readBytes(long addr, void* buf, word len); - void writeByte(long addr, byte byt); + void writeByte(long addr, uint8_t byt); void writeBytes(long addr, const void* buf, uint16_t len); boolean busy(); void chipErase(); void blockErase4K(long address); void blockErase32K(long address); word readDeviceId(); - byte* readUniqueId(); + uint8_t* readUniqueId(); void sleep(); void wakeup(); @@ -91,10 +93,16 @@ public: protected: void select(); void unselect(); - byte _slaveSelectPin; + uint8_t _slaveSelectPin; uint16_t _jedecID; - byte _SPCR; - byte _SPSR; + uint8_t _SPCR; + uint8_t _SPSR; + uint8_t _SREG; + +#ifdef SPI_HAS_TRANSACTION + SPISettings _settings; +#endif + }; #endif \ No newline at end of file