OTA Bootloader
- including adjustments to library header files to allow usage across C (bootloader) and C++ (firmware sketches) - including definition of new message types for firmware config and firmware code
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
*.o
|
||||||
|
*.hex
|
||||||
|
*.elf
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
PROJECT = MyOtaBootloader
|
||||||
|
INCLUDES = ../libraries/MySensors
|
||||||
|
|
||||||
|
MCU = atmega328p
|
||||||
|
CLK = 16000000L
|
||||||
|
|
||||||
|
ISP_PORT = com5
|
||||||
|
ISP_SPEED = 115200
|
||||||
|
ISP_PROTOCOL = stk500v2
|
||||||
|
ISP_MCU = m328p
|
||||||
|
# HFUSE = D8 - 4096 byte boot
|
||||||
|
ISP_HFUSE = D8
|
||||||
|
# LFUSE = 4E - Low power resonator (16MHz), fast start, clkdiv/8
|
||||||
|
ISP_LFUSE = FF
|
||||||
|
# EFUSE = 06 - 1.8V brownout
|
||||||
|
ISP_EFUSE = 06
|
||||||
|
ISP_ARGS = -c$(ISP_PROTOCOL) -P$(ISP_PORT) -b$(ISP_SPEED) -p$(ISP_MCU)
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
BINPATH = C:/Program Files (x86)/Arduino/hardware/tools/avr/bin/
|
||||||
|
JAVAPATH = C:/Program Files/Java/jdk1.7.0_40/bin/
|
||||||
|
JAVAC = $(JAVAPATH)javac.exe
|
||||||
|
JAVA = $(JAVAPATH)java.exe
|
||||||
|
else
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
UNAME_P := $(shell uname -p)
|
||||||
|
UNAME_M := $(shell uname -m)
|
||||||
|
ifeq ($(UNAME_S),Linux)
|
||||||
|
ifeq ($(UNAME_P),x86_64)
|
||||||
|
BINPATH = ../arduino/bin-linux64/bin/
|
||||||
|
else ifneq ($(filter %86,$(UNAME_P)),)
|
||||||
|
BINPATH = ../arduino/bin-linux32/bin/
|
||||||
|
else ifneq ($(filter arm%,$(UNAME_M)),)
|
||||||
|
BINPATH = /usr/bin/
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS = -c -g -Os -fno-inline-small-functions -fno-split-wide-types -mshort-calls -Wall -mmcu=$(MCU) -DF_CPU=$(CLK) -std=gnu99
|
||||||
|
LDFLAGS = -Os -Wl,--section-start=.text=0x7000 -nostartfiles -mmcu=$(MCU)
|
||||||
|
|
||||||
|
all: clean out
|
||||||
|
|
||||||
|
clean:
|
||||||
|
- rm *.o
|
||||||
|
- rm *.elf
|
||||||
|
- rm *.hex
|
||||||
|
|
||||||
|
$(PROJECT).o: $(PROJECT).c
|
||||||
|
"$(BINPATH)avr-gcc" $(CFLAGS) -I$(INCLUDES) $< -o $@
|
||||||
|
|
||||||
|
$(PROJECT).elf: $(PROJECT).o
|
||||||
|
"$(BINPATH)avr-gcc" $(LDFLAGS) -o $@ $< -lm
|
||||||
|
|
||||||
|
$(PROJECT).hex: $(PROJECT).elf
|
||||||
|
"$(BINPATH)avr-objcopy" -O ihex -R .eeprom $< $@
|
||||||
|
|
||||||
|
out: $(PROJECT).hex
|
||||||
|
"$(BINPATH)avr-size" $(PROJECT).elf
|
||||||
|
|
||||||
|
load: clean out isp
|
||||||
|
|
||||||
|
isp: $(PROJECT).hex
|
||||||
|
"$(BINPATH)avrdude" $(ISP_ARGS) -e -u -Ulock:w:0x3f:m -qq -Uefuse:w:0x$(ISP_EFUSE):m -Uhfuse:w:0x$(ISP_HFUSE):m -Ulfuse:w:0x$(ISP_LFUSE):m -Ulock:w:0x0f:m
|
||||||
|
"$(BINPATH)avrdude" $(ISP_ARGS) -V -q -s -Uflash:w:$(PROJECT).hex
|
||||||
|
"$(BINPATH)avrdude" $(ISP_ARGS)
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
#include "MyOtaBootloader.h"
|
||||||
|
|
||||||
|
int main(void) __attribute__ ((naked)) __attribute__ ((section (".init9")));
|
||||||
|
|
||||||
|
volatile char dummy;
|
||||||
|
|
||||||
|
EMPTY_INTERRUPT(WDT_vect);
|
||||||
|
|
||||||
|
static uint16_t calcCRCrom (const void* ptr, uint16_t len) {
|
||||||
|
uint16_t crc = ~0;
|
||||||
|
for (uint16_t i = 0; i < len; ++i)
|
||||||
|
crc = _crc16_update(crc, pgm_read_byte((uint16_t) ptr + i));
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t validFirmware () {
|
||||||
|
return calcCRCrom(0, fc.blocks * FIRMWARE_BLOCK_SIZE) == fc.crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reboot() {
|
||||||
|
wdt_enable(WDTO_15MS);
|
||||||
|
for (;;);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void startup() {
|
||||||
|
if (validFirmware()) {
|
||||||
|
wdt_disable();
|
||||||
|
clock_prescale_set(orgClockDiv);
|
||||||
|
((void(*)()) 0)();
|
||||||
|
} else
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void boot_program_page(uint32_t page, uint8_t *buf) {
|
||||||
|
uint8_t sreg = SREG;
|
||||||
|
cli();
|
||||||
|
eeprom_busy_wait();
|
||||||
|
boot_page_erase(page);
|
||||||
|
boot_spm_busy_wait();
|
||||||
|
for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) {
|
||||||
|
uint16_t w = *buf++;
|
||||||
|
w += (*buf++) << 8;
|
||||||
|
boot_page_fill(page + i, w);
|
||||||
|
}
|
||||||
|
boot_page_write(page);
|
||||||
|
boot_spm_busy_wait();
|
||||||
|
boot_rww_enable();
|
||||||
|
SREG = sreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean sendWrite(MyMessage message) {
|
||||||
|
return write(nc.parentNodeId, message.array, HEADER_SIZE + mGetLength(message), (message.destination == BROADCAST_ADDRESS));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t sendAndWait(uint8_t reqType, uint8_t resType) {
|
||||||
|
msg.type = reqType;
|
||||||
|
for (uint8_t i = 0; i < 10; i++) {
|
||||||
|
sendWrite(msg);
|
||||||
|
for (uint16_t j = 0; j < 20; j++) {
|
||||||
|
wdt_reset();
|
||||||
|
for (uint16_t j = 0; j < 100; j++) {
|
||||||
|
wdt_reset();
|
||||||
|
uint8_t pipe;
|
||||||
|
boolean avail = available(&pipe);
|
||||||
|
if (avail && pipe<=6) {
|
||||||
|
read(rmsg.array,pipe);
|
||||||
|
if(!(mGetVersion(rmsg) == PROTOCOL_VERSION))
|
||||||
|
continue;
|
||||||
|
if (rmsg.destination == nc.nodeId) {
|
||||||
|
if (mGetCommand(rmsg) == C_INTERNAL) {
|
||||||
|
if (rmsg.type == I_PING_ACK) {
|
||||||
|
if (rmsg.data[0] < nc.distance - 1) {
|
||||||
|
nc.distance = rmsg.data[0] + 1;
|
||||||
|
nc.parentNodeId = rmsg.sender;
|
||||||
|
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
|
||||||
|
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((mGetCommand(rmsg) == mGetCommand(msg)) && (rmsg.type == resType))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delaym(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main () {
|
||||||
|
asm volatile ("clr __zero_reg__");
|
||||||
|
// switch to 4 MHz
|
||||||
|
orgClockDiv = clock_prescale_get();
|
||||||
|
clock_prescale_set(F_CPU_DIV);
|
||||||
|
MCUSR = 0;
|
||||||
|
// enable watchdog to avoid deadlock
|
||||||
|
wdt_enable(WDTO_8S);
|
||||||
|
|
||||||
|
#ifdef LED_DEBUG
|
||||||
|
// enable LEDs for debugging and blink once as visual "bootloader started" sign
|
||||||
|
led_init();
|
||||||
|
blink(LED_MBOTH, 250);
|
||||||
|
#endif
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
// enable UART for debugging and send start message
|
||||||
|
uart_init();
|
||||||
|
uart_puts("Go\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wdt_reset();
|
||||||
|
// initialize RF module
|
||||||
|
begin();
|
||||||
|
wdt_reset();
|
||||||
|
// Read settings from EEPROM
|
||||||
|
eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(struct NodeConfig));
|
||||||
|
wdt_reset();
|
||||||
|
|
||||||
|
msg.sender = nc.nodeId;
|
||||||
|
msg.last = nc.nodeId;
|
||||||
|
address(nc.nodeId);
|
||||||
|
|
||||||
|
msg.sensor = NODE_SENSOR_ID;
|
||||||
|
mSetLength(msg, 0);
|
||||||
|
mSetPayloadType(msg, P_STRING);
|
||||||
|
mSetAck(msg,false);
|
||||||
|
mSetVersion(msg, PROTOCOL_VERSION);
|
||||||
|
|
||||||
|
nc.distance = 255;
|
||||||
|
mSetCommand(msg, C_INTERNAL);
|
||||||
|
msg.destination = BROADCAST_ADDRESS;
|
||||||
|
if (!sendAndWait(I_PING, I_PING_ACK))
|
||||||
|
// starting existing firmware if no ping response (if firmware is valid)
|
||||||
|
// reboot();
|
||||||
|
startup();
|
||||||
|
|
||||||
|
msg.destination = GATEWAY_ADDRESS;
|
||||||
|
|
||||||
|
if (nc.nodeId == AUTO) {
|
||||||
|
openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
|
||||||
|
if (!sendAndWait(I_ID_REQUEST, I_ID_RESPONSE))
|
||||||
|
reboot();
|
||||||
|
if (mGetPayloadType(rmsg) == P_BYTE)
|
||||||
|
nc.nodeId = rmsg.data[0];
|
||||||
|
else if (mGetPayloadType(rmsg) == P_STRING)
|
||||||
|
nc.nodeId = atoi(rmsg.data);
|
||||||
|
if (nc.nodeId == AUTO)
|
||||||
|
reboot();
|
||||||
|
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId);
|
||||||
|
msg.sender = nc.nodeId;
|
||||||
|
msg.last = nc.nodeId;
|
||||||
|
address(nc.nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read settings from EEPROM
|
||||||
|
eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(struct FirmwareConfig));
|
||||||
|
|
||||||
|
mSetPayloadType(msg,P_CUSTOM);
|
||||||
|
|
||||||
|
FirmwareConfigRequest *firmwareConfigRequest = (FirmwareConfigRequest *)msg.data;
|
||||||
|
mSetLength(msg, sizeof(FirmwareConfigRequest));
|
||||||
|
mSetCommand(msg, C_STREAM);
|
||||||
|
firmwareConfigRequest->type = fc.type;
|
||||||
|
firmwareConfigRequest->version = fc.version;
|
||||||
|
if (!sendAndWait(ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE))
|
||||||
|
// starting existing firmware if no firmware config response (if firmware is valid)
|
||||||
|
// reboot();
|
||||||
|
startup();
|
||||||
|
FirmwareConfigResponse *firmwareConfigResponse = (FirmwareConfigResponse *)rmsg.data;
|
||||||
|
if (firmwareConfigResponse->version == fc.version)
|
||||||
|
if (firmwareConfigResponse->blocks == fc.blocks)
|
||||||
|
if (firmwareConfigResponse->crc == fc.crc)
|
||||||
|
if (validFirmware())
|
||||||
|
startup();
|
||||||
|
fc.type = firmwareConfigResponse->type;
|
||||||
|
fc.version = firmwareConfigResponse->version;
|
||||||
|
fc.blocks = firmwareConfigResponse->blocks;
|
||||||
|
fc.crc = firmwareConfigResponse->crc;
|
||||||
|
|
||||||
|
FirmwareRequest *firmwareRequest = (FirmwareRequest *)msg.data;
|
||||||
|
mSetLength(msg, sizeof(FirmwareRequest));
|
||||||
|
firmwareRequest->type = fc.type;
|
||||||
|
firmwareRequest->version = fc.version;
|
||||||
|
for (uint16_t block = 0; block < fc.blocks; block++) {
|
||||||
|
firmwareRequest->block = block;
|
||||||
|
if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE))
|
||||||
|
reboot();
|
||||||
|
FirmwareResponse *firmwareResponse = (FirmwareResponse *)rmsg.data;
|
||||||
|
uint8_t offset = (block * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE;
|
||||||
|
memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
|
||||||
|
if (offset == SPM_PAGESIZE - FIRMWARE_BLOCK_SIZE)
|
||||||
|
boot_program_page((block * FIRMWARE_BLOCK_SIZE) - offset, progBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
wdt_reset();
|
||||||
|
|
||||||
|
if (validFirmware()) {
|
||||||
|
eeprom_write_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(struct FirmwareConfig));
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_puts("fw ok\n");
|
||||||
|
} else {
|
||||||
|
uart_puts("fw nok\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef MyOtaBootloader_H
|
||||||
|
#define MyOtaBootloader_H
|
||||||
|
|
||||||
|
#include <avr/boot.h>
|
||||||
|
#include <avr/eeprom.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <avr/power.h>
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <util/crc16.h>
|
||||||
|
|
||||||
|
#include "MyMessage.h"
|
||||||
|
#include "MySensor.h"
|
||||||
|
#include "MyOtaBootloaderRF24.h"
|
||||||
|
|
||||||
|
#define FIRMWARE_BLOCK_SIZE 16
|
||||||
|
|
||||||
|
struct FirmwareConfig
|
||||||
|
{
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t blocks;
|
||||||
|
uint16_t crc;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t version;
|
||||||
|
} FirmwareConfigRequest;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t blocks;
|
||||||
|
uint16_t crc;
|
||||||
|
} FirmwareConfigResponse;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t block;
|
||||||
|
} FirmwareRequest;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t block;
|
||||||
|
uint8_t data[FIRMWARE_BLOCK_SIZE];
|
||||||
|
} FirmwareResponse;
|
||||||
|
|
||||||
|
static struct NodeConfig nc;
|
||||||
|
static struct FirmwareConfig fc;
|
||||||
|
static MyMessage msg;
|
||||||
|
static MyMessage rmsg;
|
||||||
|
|
||||||
|
static clock_div_t orgClockDiv = 0;
|
||||||
|
|
||||||
|
static uint8_t progBuf[SPM_PAGESIZE];
|
||||||
|
|
||||||
|
#endif // MyOtaBootloader_H
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#ifndef MyOtaBootloaderHW_H
|
||||||
|
#define MyOtaBootloaderHW_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <avr/power.h>
|
||||||
|
|
||||||
|
//#define LED_DEBUG
|
||||||
|
//#define UART_DEBUG
|
||||||
|
|
||||||
|
// hardware
|
||||||
|
// ********
|
||||||
|
|
||||||
|
#define F_CPU_DIV clock_div_4
|
||||||
|
#define F_CPU_REAL F_CPU / (1 << F_CPU_DIV)
|
||||||
|
#define UART_BAUD 9600UL
|
||||||
|
#define DELAY_M 125 * (1 << F_CPU_DIV)
|
||||||
|
|
||||||
|
// boolean definition
|
||||||
|
// ******************
|
||||||
|
|
||||||
|
typedef uint8_t bool;
|
||||||
|
typedef uint8_t boolean;
|
||||||
|
#define false 0x0
|
||||||
|
#define true 0x1
|
||||||
|
#define LOW 0x0
|
||||||
|
#define HIGH 0x1
|
||||||
|
|
||||||
|
// delay
|
||||||
|
// *****
|
||||||
|
|
||||||
|
static void delayu() {
|
||||||
|
asm("nop");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delaym(uint16_t t) {
|
||||||
|
uint16_t d, a;
|
||||||
|
for (d = 0; d < t; d++)
|
||||||
|
for (a = 0; a < DELAY_M; a++)
|
||||||
|
asm("nop");
|
||||||
|
}
|
||||||
|
|
||||||
|
// LED debug
|
||||||
|
// *********
|
||||||
|
|
||||||
|
#ifdef LED_DEBUG
|
||||||
|
#define LED_DDR DDRD
|
||||||
|
#define LED_PORT PORTD
|
||||||
|
#define LED_RED 6 // Arduino Pin 6 <-> Bit 6 of port D
|
||||||
|
#define LED_GREEN 7 // Arduino Pin 7 <-> Bit 7 of port D
|
||||||
|
#define LED_MRED (1 << LED_RED)
|
||||||
|
#define LED_MGREEN (1 << LED_GREEN)
|
||||||
|
#define LED_MBOTH (LED_MRED | LED_MGREEN)
|
||||||
|
|
||||||
|
static void led_init() {
|
||||||
|
LED_DDR |= LED_MBOTH;
|
||||||
|
LED_PORT &= ~LED_MBOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blink(uint8_t mask, uint16_t time) {
|
||||||
|
LED_PORT |= mask;
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
delaym(time);
|
||||||
|
LED_PORT &= ~mask;
|
||||||
|
delaym(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// UART debug
|
||||||
|
// **********
|
||||||
|
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
static void uart_init()
|
||||||
|
{
|
||||||
|
UCSR0A = _BV(U2X0); //Double speed mode USART0
|
||||||
|
UCSR0B = _BV(RXEN0) | _BV(TXEN0);
|
||||||
|
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
|
||||||
|
UBRR0L = (uint8_t)( (F_CPU_REAL + UART_BAUD * 4L) / (UART_BAUD * 8L) - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_putc(char ch) {
|
||||||
|
while (!(UCSR0A & _BV(UDRE0)));
|
||||||
|
UDR0 = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_puts(char *s)
|
||||||
|
{
|
||||||
|
while (*s)
|
||||||
|
uart_putc(*s++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_puti(uint8_t i)
|
||||||
|
{
|
||||||
|
char s[5];
|
||||||
|
itoa( i, s, 10 );
|
||||||
|
uart_puts( s );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SPI communication
|
||||||
|
// *****************
|
||||||
|
|
||||||
|
#define SPI_DDR DDRB
|
||||||
|
#define SPI_PORT PORTB
|
||||||
|
#define SPI_PIN PINB
|
||||||
|
#define SPI_SCLK 5 // Arduino Pin 13 <-> Bit 5 of port B
|
||||||
|
#define SPI_MISO 4 // Arduino Pin 12 <-> Bit 4 of port B
|
||||||
|
#define SPI_MOSI 3 // Arduino Pin 11 <-> Bit 3 of port B
|
||||||
|
#define SPI_CSN 2 // Arduino Pin 10 <-> Bit 2 of port B
|
||||||
|
#define SPI_CE 1 // Arduino Pin 9 <-> Bit 1 of port B
|
||||||
|
|
||||||
|
#define csnlow() SPI_PORT &= ~(1<<SPI_CSN);delaym(1)
|
||||||
|
#define csnhigh() SPI_PORT |= (1<<SPI_CSN);delaym(1)
|
||||||
|
#define celow() SPI_PORT &= ~(1<<SPI_CE);delaym(1)
|
||||||
|
#define cehigh() SPI_PORT |= (1<<SPI_CE);delaym(1)
|
||||||
|
|
||||||
|
static void SPIinit()
|
||||||
|
{
|
||||||
|
SPI_DDR |= (1 << SPI_MOSI) + (1 << SPI_SCLK) + (1 << SPI_CE) + (1 << SPI_CSN);
|
||||||
|
SPI_DDR &= ~(1 << SPI_MISO);
|
||||||
|
SPI_PORT &= ~((1 << SPI_MOSI) + (1 << SPI_SCLK) + (1 << SPI_CE));
|
||||||
|
SPI_PORT |= (1 << SPI_CSN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t SPItransfer(uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
for(i = 0 ;i < 8;i++) {
|
||||||
|
delayu();
|
||||||
|
if ((value & 0x80) == 0x00)
|
||||||
|
SPI_PORT &= ~(1 << SPI_MOSI);
|
||||||
|
else
|
||||||
|
SPI_PORT |= (1 << SPI_MOSI);
|
||||||
|
value = (value << 1); // shift next bit into MSB..
|
||||||
|
delayu();
|
||||||
|
SPI_PORT |= (1 << SPI_SCLK);
|
||||||
|
value |= ((SPI_PIN >> SPI_MISO) & 0x01); // capture current MISO bit
|
||||||
|
delayu();
|
||||||
|
SPI_PORT &= ~(1 << SPI_SCLK);
|
||||||
|
delayu();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t spiTrans(uint8_t cmd){
|
||||||
|
uint8_t status;
|
||||||
|
csnlow();
|
||||||
|
status = SPItransfer( cmd );
|
||||||
|
csnhigh();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MyOtaBootloaderHW_H
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
#ifndef MyOtaBootloaderRF24_H
|
||||||
|
#define MyOtaBootloaderRF24_H
|
||||||
|
|
||||||
|
#include "MyOtaBootloaderHW.h"
|
||||||
|
#include "../libraries/MySensors/utility/nRF24L01.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// hardware-agnostic interface to transceiver
|
||||||
|
// ******************************************
|
||||||
|
// static void begin()
|
||||||
|
// static void address(uint8_t addr)
|
||||||
|
// static boolean write(uint8_t next, uint8_t* packet, uint8_t length, boolean multicast)
|
||||||
|
// static bool available(uint8_t* pipe_num)
|
||||||
|
// static void read(uint8_t* buf, uint8_t pipe)
|
||||||
|
|
||||||
|
typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ;
|
||||||
|
typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e;
|
||||||
|
|
||||||
|
uint64_t pipe0_reading_address; /**< Last address set on pipe 0 for reading. */
|
||||||
|
|
||||||
|
static uint8_t read_register(uint8_t reg)
|
||||||
|
{
|
||||||
|
csnlow();
|
||||||
|
SPItransfer( R_REGISTER | ( REGISTER_MASK & reg ) );
|
||||||
|
uint8_t result = SPItransfer(0xff);
|
||||||
|
csnhigh();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t write_registers(uint8_t reg, const uint8_t* buf, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t status;
|
||||||
|
csnlow();
|
||||||
|
status = SPItransfer( W_REGISTER | ( REGISTER_MASK & reg ) );
|
||||||
|
while ( len-- )
|
||||||
|
SPItransfer(*buf++);
|
||||||
|
csnhigh();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t write_register(uint8_t reg, uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t status;
|
||||||
|
csnlow();
|
||||||
|
status = SPItransfer( W_REGISTER | ( REGISTER_MASK & reg ) );
|
||||||
|
SPItransfer(value);
|
||||||
|
csnhigh();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t read_payload(uint8_t* buf, uint8_t data_len)
|
||||||
|
{
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_puts("-> ");
|
||||||
|
#endif
|
||||||
|
uint8_t status;
|
||||||
|
uint8_t* current = buf;
|
||||||
|
csnlow();
|
||||||
|
status = SPItransfer( R_RX_PAYLOAD );
|
||||||
|
while ( data_len-- ) {
|
||||||
|
*current = SPItransfer(0xFF);
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_puti(*current);
|
||||||
|
uart_putc(',');
|
||||||
|
#endif
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
csnhigh();
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_putc('\n');
|
||||||
|
#endif
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define get_status() spiTrans(NOP)
|
||||||
|
|
||||||
|
static void powerUp(void)
|
||||||
|
{
|
||||||
|
if (!(read_register(CONFIG) & _BV(PWR_UP))){
|
||||||
|
write_register(CONFIG,read_register(CONFIG) | _BV(PWR_UP));
|
||||||
|
delaym(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define convert_64_bit_to_byte_array(value, data) ((*(uint64_t *)(data)) = (uint64_t)(value))
|
||||||
|
|
||||||
|
static void startListening(void)
|
||||||
|
{
|
||||||
|
powerUp();
|
||||||
|
write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX));
|
||||||
|
write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
|
||||||
|
if (pipe0_reading_address){
|
||||||
|
uint8_t addressa[8];
|
||||||
|
convert_64_bit_to_byte_array(pipe0_reading_address, addressa);
|
||||||
|
write_registers(RX_ADDR_P0, addressa, 5);
|
||||||
|
}
|
||||||
|
spiTrans(FLUSH_RX);
|
||||||
|
spiTrans(FLUSH_TX);
|
||||||
|
cehigh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stopListening(void)
|
||||||
|
{
|
||||||
|
celow();
|
||||||
|
spiTrans(FLUSH_TX);
|
||||||
|
spiTrans(FLUSH_RX);
|
||||||
|
write_register(CONFIG, read_register(CONFIG) & ~_BV(PRIM_RX));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_payload(const uint8_t* buf, uint8_t data_len, const uint8_t writeType)
|
||||||
|
{
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_puts("<- ");
|
||||||
|
#endif
|
||||||
|
const uint8_t* current = buf;
|
||||||
|
csnlow();
|
||||||
|
SPItransfer(writeType);
|
||||||
|
while ( data_len-- ) {
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_puti(*current);
|
||||||
|
uart_putc(',');
|
||||||
|
#endif
|
||||||
|
SPItransfer(*current++);
|
||||||
|
}
|
||||||
|
csnhigh();
|
||||||
|
#ifdef UART_DEBUG
|
||||||
|
uart_putc('\n');
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool writem( const uint8_t* buf, uint8_t len, const bool multicast )
|
||||||
|
{
|
||||||
|
write_payload( buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD) ;
|
||||||
|
cehigh();
|
||||||
|
celow();
|
||||||
|
while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { }
|
||||||
|
uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
|
||||||
|
if( status & _BV(MAX_RT)){
|
||||||
|
spiTrans(FLUSH_TX);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t getDynamicPayloadSize(void)
|
||||||
|
{
|
||||||
|
uint8_t result = 0;
|
||||||
|
csnlow();
|
||||||
|
SPItransfer( R_RX_PL_WID );
|
||||||
|
result = SPItransfer(0xff);
|
||||||
|
csnhigh();
|
||||||
|
if(result > 32) {
|
||||||
|
spiTrans(FLUSH_RX);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool available(uint8_t* pipe_num)
|
||||||
|
{
|
||||||
|
if (!(read_register(FIFO_STATUS) & _BV(RX_EMPTY) )){
|
||||||
|
if (pipe_num) {
|
||||||
|
uint8_t status = get_status();
|
||||||
|
*pipe_num = ( status >> RX_P_NO ) & 0b111;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeAckPayload(uint8_t pipe, const uint8_t* buf, uint8_t len)
|
||||||
|
{
|
||||||
|
const uint8_t* current = buf;
|
||||||
|
uint8_t data_len = len;
|
||||||
|
csnlow();
|
||||||
|
SPItransfer(W_ACK_PAYLOAD | ( pipe & 0b111 ) );
|
||||||
|
while ( data_len-- )
|
||||||
|
SPItransfer(*current++);
|
||||||
|
csnhigh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read( uint8_t* buf, uint8_t pipe ){
|
||||||
|
read_payload(buf, getDynamicPayloadSize());
|
||||||
|
write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS));
|
||||||
|
writeAckPayload(pipe,&pipe, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void openWritingPipe(uint64_t value)
|
||||||
|
{
|
||||||
|
uint8_t valuea[8];
|
||||||
|
convert_64_bit_to_byte_array(value, valuea);
|
||||||
|
write_registers(RX_ADDR_P0, valuea, 5);
|
||||||
|
write_registers(TX_ADDR, valuea, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void openReadingPipe(uint8_t child, uint64_t address)
|
||||||
|
{
|
||||||
|
if (child == 0)
|
||||||
|
pipe0_reading_address = address;
|
||||||
|
uint8_t addressa[8];
|
||||||
|
convert_64_bit_to_byte_array(address, addressa);
|
||||||
|
if ( child < 2 )
|
||||||
|
write_registers(RX_ADDR_P0 + child, addressa, 5);
|
||||||
|
write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(ERX_P0 + child));
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean write(uint8_t next, uint8_t* packet, uint8_t length, boolean multicast) {
|
||||||
|
powerUp();
|
||||||
|
stopListening();
|
||||||
|
openWritingPipe(TO_ADDR(next));
|
||||||
|
bool ok = writem(packet, length, multicast);
|
||||||
|
startListening();
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void address(uint8_t addr)
|
||||||
|
{
|
||||||
|
if (addr != BROADCAST_ADDRESS) {
|
||||||
|
openReadingPipe(WRITE_PIPE, TO_ADDR(addr));
|
||||||
|
openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(addr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void begin(void)
|
||||||
|
{
|
||||||
|
pipe0_reading_address = 0;
|
||||||
|
|
||||||
|
SPIinit();
|
||||||
|
delaym( 10 ) ;
|
||||||
|
|
||||||
|
write_register(SETUP_RETR, 5 << ARD | 15 << ARC);
|
||||||
|
write_register(EN_AA, 0b00111111 & ~_BV(BROADCAST_PIPE)) ;
|
||||||
|
write_register(RF_CH, RF24_CHANNEL);
|
||||||
|
write_register(RF_SETUP, (read_register(RF_SETUP) & 0b11010000) | ((RF24_PA_LEVEL << 1) + 1) | ((RF24_DATARATE & 0b00000010 ) << 4) | ((RF24_DATARATE & 0b00000001 ) << 3));
|
||||||
|
write_register(CONFIG, read_register(CONFIG) | _BV(CRCO) | _BV(EN_CRC)) ;
|
||||||
|
write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
|
||||||
|
|
||||||
|
csnlow();
|
||||||
|
SPItransfer(ACTIVATE);
|
||||||
|
SPItransfer(0x73);
|
||||||
|
csnhigh();
|
||||||
|
write_register(FEATURE, read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL) );
|
||||||
|
write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0));
|
||||||
|
|
||||||
|
spiTrans(FLUSH_RX);
|
||||||
|
spiTrans(FLUSH_TX);
|
||||||
|
|
||||||
|
powerUp(); //Power up by default when begin() is called
|
||||||
|
write_register(CONFIG, read_register(CONFIG) & ~_BV(PRIM_RX));
|
||||||
|
openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
|
||||||
|
}
|
||||||
|
#endif // MyOtaBootloaderRF24_H
|
||||||
@@ -12,9 +12,11 @@
|
|||||||
#ifndef MyMessage_h
|
#ifndef MyMessage_h
|
||||||
#define MyMessage_h
|
#define MyMessage_h
|
||||||
|
|
||||||
#include <SPI.h>
|
#ifdef __cplusplus
|
||||||
|
#include <Arduino.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PROTOCOL_VERSION 2
|
#define PROTOCOL_VERSION 2
|
||||||
#define MAX_MESSAGE_LENGTH 32
|
#define MAX_MESSAGE_LENGTH 32
|
||||||
@@ -56,7 +58,8 @@ typedef enum {
|
|||||||
|
|
||||||
// Type of data stream (for streamed message)
|
// Type of data stream (for streamed message)
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ST_FIRMWARE, ST_SOUND, ST_IMAGE
|
ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE, ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE,
|
||||||
|
ST_SOUND, ST_IMAGE
|
||||||
} stream;
|
} stream;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -101,7 +104,7 @@ typedef enum {
|
|||||||
#define miSetLength(_length) BF_SET(version_length, _length, 3, 5)
|
#define miSetLength(_length) BF_SET(version_length, _length, 3, 5)
|
||||||
#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4)
|
#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 4, 4)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
class MyMessage
|
class MyMessage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -144,6 +147,13 @@ public:
|
|||||||
MyMessage& set(unsigned int value);
|
MyMessage& set(unsigned int value);
|
||||||
MyMessage& set(int value);
|
MyMessage& set(int value);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
uint8_t version_length; // 3 bit - Protocol version
|
uint8_t version_length; // 3 bit - Protocol version
|
||||||
// 5 bit - Length of payload
|
// 5 bit - Length of payload
|
||||||
@@ -167,6 +177,12 @@ public:
|
|||||||
int iValue;
|
int iValue;
|
||||||
char data[MAX_PAYLOAD + 1];
|
char data[MAX_PAYLOAD + 1];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
#ifdef __cplusplus
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
#else
|
||||||
|
};
|
||||||
|
uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1];
|
||||||
|
} __attribute__((packed)) MyMessage;
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,16 +15,19 @@
|
|||||||
#include "Version.h" // Auto generated by bot
|
#include "Version.h" // Auto generated by bot
|
||||||
#include "MyConfig.h"
|
#include "MyConfig.h"
|
||||||
#include "MyMessage.h"
|
#include "MyMessage.h"
|
||||||
#include <Arduino.h>
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <SPI.h>
|
|
||||||
#include <avr/eeprom.h>
|
#include <avr/eeprom.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
#include <avr/wdt.h>
|
#include <avr/wdt.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <SPI.h>
|
||||||
#include "utility/LowPower.h"
|
#include "utility/LowPower.h"
|
||||||
#include "utility/RF24.h"
|
#include "utility/RF24.h"
|
||||||
#include "utility/RF24_config.h"
|
#include "utility/RF24_config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define debug(x,...) debugPrint(x, ##__VA_ARGS__)
|
#define debug(x,...) debugPrint(x, ##__VA_ARGS__)
|
||||||
@@ -47,7 +50,11 @@
|
|||||||
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
|
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
|
||||||
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
|
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
|
||||||
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
|
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
|
||||||
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24) // First free address for sketch static configuration
|
#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
|
||||||
|
#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
|
||||||
|
#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
|
||||||
|
#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
|
||||||
|
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) // First free address for sketch static configuration
|
||||||
|
|
||||||
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
|
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
|
||||||
// This is also act as base value for sensor nodeId
|
// This is also act as base value for sensor nodeId
|
||||||
@@ -74,8 +81,7 @@ struct ControllerConfig {
|
|||||||
uint8_t isMetric;
|
uint8_t isMetric;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
class MySensor : public RF24
|
class MySensor : public RF24
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -267,5 +273,6 @@ class MySensor : public RF24
|
|||||||
void removeChildRoute(uint8_t childId);
|
void removeChildRoute(uint8_t childId);
|
||||||
void internalSleep(int ms);
|
void internalSleep(int ms);
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user