Merge branch 'master' into development
Conflicts: .gitignore libraries/MySensors/MyGateway.cpp libraries/MySensors/MyMQTT.cpp libraries/MySensors/MyMQTT.h libraries/MySensors/examples/MQTTGateway/MQTTGateway.ino
This commit is contained in:
@@ -1 +1,3 @@
|
||||
stino.settings
|
||||
*~
|
||||
*bak
|
||||
|
||||
+120
-89
@@ -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();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <util/crc16.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
#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];
|
||||
|
||||
|
||||
+126
-115
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <avr/boot.h>: Bootloader Support Utilities
|
||||
\code
|
||||
#include <avr/io.h>
|
||||
#include <avr/boot.h>
|
||||
\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 <inttypes.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
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<SPM_PAGESIZE; i+=2)
|
||||
{
|
||||
// Set up little-endian word.
|
||||
|
||||
uint16_t w = *buf++;
|
||||
w += (*buf++) << 8;
|
||||
|
||||
boot_page_fill (page + i, w);
|
||||
}
|
||||
|
||||
boot_page_write (page); // Store buffer in flash page.
|
||||
boot_spm_busy_wait(); // Wait until the memory is written.
|
||||
|
||||
// Reenable RWW-section again. We need this if we want to jump back
|
||||
// to the application after bootloading.
|
||||
|
||||
boot_rww_enable ();
|
||||
|
||||
// Re-enable interrupts (if they were ever enabled).
|
||||
|
||||
SREG = sreg;
|
||||
}\endcode */
|
||||
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/io.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* 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 <limits.h>. */
|
||||
|
||||
#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_ */
|
||||
@@ -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
|
||||
@@ -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}'
|
||||
@@ -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 <avr/pgmspace.h>
|
||||
|
||||
#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
|
||||
@@ -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<inputSize; a++) { sprintf(buf,"%02X ", (uint8_t)inputString[a]); Serial.print(buf); } Serial.println("");
|
||||
Serial.print("<<");
|
||||
char buf[4];
|
||||
for (byte a=0; a<inputSize; a++) { sprintf(buf, "%02X ", (byte)inputString[a]); Serial.print(buf); } Serial.println();
|
||||
#endif
|
||||
processMQTTMessage(inputString, inputSize);
|
||||
}
|
||||
processMQTTMessage(inputString, inputSize);
|
||||
currentClient = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void incomingMessage(const MyMessage &message) {
|
||||
rxBlink(1);
|
||||
sendMQTT(message);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Default sensor sketch for MySensor Micro module
|
||||
// Act as a temperature / humidity sensor by default.
|
||||
//
|
||||
// If A1 is held low while powering on, it will clear eeprom (and thereby the stored nodeid)
|
||||
// If A0 is held low while powering on, it will enter testmode, which verifies all on-board peripherals
|
||||
//
|
||||
// Battery voltage is repported as child sensorId 199, as well as battery percentage
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
#include <Wire.h>
|
||||
#include <SI7021.h>
|
||||
#include <SPI.h>
|
||||
#include <SPIFlash.h>
|
||||
#include <EEPROM.h>
|
||||
#include <sha204_lib_return_codes.h>
|
||||
#include <sha204_library.h>
|
||||
|
||||
// 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<<CLKPS2); // Switch to 1Mhz for the reminder of the sketch, save power.
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +99,8 @@ void loop() {
|
||||
measureCount ++;
|
||||
bool forceTransmit = false;
|
||||
|
||||
if (measureCount > 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<<CLKPCE; // Set CLKPCE to enable clk switching
|
||||
CLKPR = clk;
|
||||
sei();
|
||||
}
|
||||
|
||||
|
||||
// Verify all peripherals, and signal via the LED if any problems.
|
||||
void testMode()
|
||||
{
|
||||
uint8_t rx_buffer[SHA204_RSP_SIZE_MAX];
|
||||
uint8_t ret_code;
|
||||
byte tests = 0;
|
||||
|
||||
digitalWrite(LED_PIN, HIGH); // Turn on LED.
|
||||
|
||||
Serial.println(F("Testing peripherals!"));
|
||||
Serial.flush();
|
||||
Serial.print(F("-> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <SPI.h>
|
||||
#include <MySensor.h>
|
||||
/*
|
||||
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 <MySensor.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,23 @@
|
||||
#include <MySensor.h>
|
||||
|
||||
#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++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2013 by Felix Rusu <felix@lowpowerlab.com>
|
||||
* 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
|
||||
Reference in New Issue
Block a user