Skip to content

Commit

Permalink
prompt for bootloader trigger keys
Browse files Browse the repository at this point in the history
Prompt the user to press the bootloader trigger keys, both in
the serial monitor and by lighting up the right LEDs (assuming
there's already firmware that understands that.)

Signed-off-by: Taylor Yu <[email protected]>
  • Loading branch information
tlyu authored and obra committed May 31, 2022
1 parent f840e91 commit 4f88e80
Showing 1 changed file with 138 additions and 60 deletions.
198 changes: 138 additions & 60 deletions etc/flash_firmware.ino
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,27 @@ extern "C" {
#define ENDTRANS_DATA_NACK 3
#define ENDTRANS_ERROR 4

#define write_cmd(addr, data, sendStop) \
twi_writeTo((addr), (data), ELEMENTS(data), true, (sendStop))

// Reset delay, in milliseconds. Also retry interval for initial
// commands to bootloader.
//
// The ATtinys in the Model 01 ship with fuses programmed for a
// 64 ms reset delay (actually 8192 watchdog timer cycles, so
// maybe 65ms to 70ms in practice, given temperature and voltage
// variations). Also, the original Model 01 bootloader times out
// in that same period. So delay once and retry with an interval
// a little less than that, to make sure we hit the bootloader
// before it times out.
//
// (We could delay slightly longer for the initial delay, but
// we would miss the bootloader timeout if someone changed
// the fuses to a shorter startup delay.)
#define RESET_DELAY 60

#define debug_print(...) Serial.print(__VA_ARGS__)
#define debug_println(...) Serial.println(__VA_ARGS__)

void debug_print_result(uint8_t result) {
switch(result) {
Expand All @@ -36,9 +56,31 @@ void debug_print_result(uint8_t result) {
}
}

void debug_println_result(uint8_t result) {
debug_print_result(result);
debug_println();
}

#define LEFT_LEDS 0x58
#define RIGHT_LEDS (LEFT_LEDS | 0x03)

void turn_leds_off(uint8_t addr) {
uint8_t leds_off[] = { 0x03, 0x00, 0x00, 0x00 };
(void)write_cmd(addr, leds_off, true);
}

void setup() {
delay(2000);
// Turn on LED (and right hand) power, in case bootloader didn't
// do that for us (e.g., not coming straight off an ATmega flash.)
// That lets us explicitly turn them off, in case they're lit up
// from power cycling or bootloader status updates.
DDRC |= _BV(7);
PORTC |= _BV(7);
delay(3 * RESET_DELAY);
twi_init();

turn_leds_off(LEFT_LEDS);
turn_leds_off(RIGHT_LEDS);
}

// The LEFT ATTiny has a reset pin directly connected to the ATMega
Expand All @@ -48,6 +90,7 @@ void reset_left_attiny() {
PORTC &= ~_BV(6);
delay(30);
DDRC &= ~_BV(6); // Turn the ATTiny back on
delay(RESET_DELAY);
}

// The RIGHT ATTiny is on the other side of the wired i2c bus.
Expand All @@ -59,21 +102,53 @@ void reset_right_attiny() {
PORTC &= ~_BV(7);
delay(1000);
PORTC |= _BV(7); // Turn the ATTiny back on
delay(RESET_DELAY);
// In case power cycling caused any to turn on
turn_leds_off(LEFT_LEDS);
}

#define PROMPT_COLOR 0x00, 0x7f, 0x7f
void prompt_trigger(uint8_t addr) {
uint8_t left_prompt0[] = { 0x04, 0, PROMPT_COLOR };
uint8_t left_prompt1[] = { 0x04, 30, PROMPT_COLOR };
uint8_t right_prompt0[] = { 0x04, 1, PROMPT_COLOR };
uint8_t right_prompt1[] = { 0x04, 31, PROMPT_COLOR };
debug_println(F("Exceeded retry count"));
debug_print(F("Try holding down the bootloader keys on the "));
if (addr == LEFT_ADDRESS) {
debug_println(F("left side"));
(void)write_cmd(LEFT_LEDS, left_prompt0, true);
delay(1);
(void)write_cmd(LEFT_LEDS, left_prompt1, true);
delay(1000);
reset_left_attiny();
} else {
debug_println(F("right side"));
(void)write_cmd(RIGHT_LEDS, right_prompt0, true);
delay(1);
(void)write_cmd(RIGHT_LEDS, right_prompt1, true);
delay(1000);
reset_right_attiny();
}
}



void leds_success(uint8_t addr) {
uint8_t data[] = { 0x03, 0x00, 0x3f, 0x00 };
(void)write_cmd(addr, data, true);
}

uint8_t read_crc16(byte addr, byte *version, uint16_t *crc16, uint16_t offset, uint16_t length) {
uint8_t result = ENDTRANS_ADDR_NACK;


// get version and CRC16 // addr (lo) // addr (hi) // len (lo) // len (hi)
uint8_t data[] = { (0x06), (uint8_t)(offset & 0xff), (uint8_t)(offset >> 8), (uint8_t)(length & 0xff), (uint8_t)(length >> 8) };
result = twi_writeTo(addr, data, ELEMENTS(data), true, true);

// get version and CRC16
uint8_t data[] = {
(0x06),
(uint8_t)(offset & 0xff), (uint8_t)(offset >> 8), // addr
(uint8_t)(length & 0xff), (uint8_t)(length >> 8) // len
};

result = write_cmd(addr, data, true);
if (result != ENDTRANS_SUCCESS) {
return result;
}
Expand All @@ -97,43 +172,45 @@ uint8_t read_crc16(byte addr, byte *version, uint16_t *crc16, uint16_t offset, u


void get_version (byte addr) {

uint8_t retries = 0;
byte result = ENDTRANS_ADDR_NACK;

while (result != ENDTRANS_SUCCESS) {
debug_print(F("Reading CRC16: "));

byte version;
uint16_t crc16;
result = read_crc16(addr, &version, &crc16, 0, firmware_length);
debug_print_result(result);
debug_print(F("\r\n"));
debug_println_result(result);

if (result != ENDTRANS_SUCCESS) {
_delay_ms(100);
delay(RESET_DELAY);
if (++retries < 4) {
continue;
}
retries = 0;
prompt_trigger(addr);
continue;
}
debug_print(F("Version: "));
debug_print(version);
debug_print(F("\r\nExisting CRC16 of 0000-1FFF: "));
debug_print(crc16, HEX);
debug_print(F("\r\n"));
debug_println(version);
debug_print(F("Existing CRC16 of 0000-1FFF: "));
debug_println(crc16, HEX);
}

}



int erase_program(uint8_t addr) {
// erase user space
uint8_t data[] = { 0x04 };
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
uint8_t result = write_cmd(addr, data, true);

debug_print(F("Erasing: "));
debug_print_result(result);
debug_print(F("\r\n"));
debug_println_result(result);
if (result != ENDTRANS_SUCCESS) {
_delay_ms(1000);
debug_print(F("failed.\r\n"));
debug_println(F("failed."));
return -1;
}
return 0;
Expand All @@ -153,14 +230,15 @@ int write_firmware(uint8_t addr ) {

// write page addr
uint8_t data[] = { 0x01, (uint8_t)(offsets[o] & 0xff), (uint8_t)(offsets[o] >> 8)};
result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
result = write_cmd(addr, data, true);
debug_print_result(result);
debug_print(F(" - "));

_delay_ms(DELAY);
// got something other than ACK. Start over.
if (result != ENDTRANS_SUCCESS) {
debug_print(F("\r\nFailed\r\n"));
debug_println();
debug_println(F("Failed"));
return -1;
}

Expand All @@ -187,19 +265,20 @@ int write_firmware(uint8_t addr ) {
data[data_counter++] = (uint8_t)(crc16 >> 8);
data[data_counter++] = (0x00); // dummy end uint8_t

result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
result = write_cmd(addr, data, true);
debug_print(F(" "));
debug_print(frame);
debug_print(F(" of 4: "));
// got something other than NACK. Start over.
if (result != ENDTRANS_DATA_NACK) {
debug_print(F("\nERROR: Got something other than NACK\n") );
debug_println();
debug_println(F("ERROR: Got something other than NACK"));
return -1;
}
debug_print(F("OK;"));
delay(DELAY);
}
debug_print(F("\r\n"));
debug_println();
o++;
}
return 0;
Expand All @@ -209,7 +288,7 @@ int write_firmware(uint8_t addr ) {
int verify_firmware(byte addr) {
byte result = ENDTRANS_DATA_NACK;
// verify firmware
debug_print(F("Verifying install\r\n"));
debug_println(F("Verifying install"));
while (result != ENDTRANS_SUCCESS) {
debug_print(F("CRC16 "));

Expand All @@ -218,22 +297,20 @@ int verify_firmware(byte addr) {
// skip the first 4 bytes, are they were probably overwritten by the reset vector preservation
result = read_crc16(addr, &version, &crc16, offsets[0] + 4, firmware_length - 4);

debug_print_result(result);
debug_print(F("\r\n"));
debug_println_result(result);

if (result != ENDTRANS_SUCCESS) {
_delay_ms(100);
continue;
}
debug_print(F("Version: "));
debug_print(version);
debug_print(F("\r\nCRC16 of "));
debug_println(version);
debug_print(F("CRC16 of "));
debug_print(offsets[0] + 4, HEX);
debug_print(F("-"));
debug_print(offsets[0] + firmware_length, HEX);
debug_print(F(": "));
debug_print(crc16, HEX);
debug_print(F("\r\n"));
debug_println(crc16, HEX);
// calculate our own CRC16
uint16_t check_crc16 = 0xffff;
for (uint16_t i = 4; i < firmware_length; i++) {
Expand All @@ -244,43 +321,40 @@ int verify_firmware(byte addr) {
debug_print(check_crc16, HEX);
return -1;
}
debug_print(F("OK\r\n"));
debug_println(F("OK"));
}
return 0;
}

byte update_attiny(byte addr) {
debug_print(F("Communicating\r\n"));
debug_println(F("Communicating"));

get_version(addr);

int erased = erase_program(addr);

if (erased == -1) {

return 0;
}

int firmware_written = write_firmware(addr);
if(firmware_written == -1) {
debug_print(F("Write failed.\r\n"));
if (firmware_written == -1) {
debug_println(F("Write failed."));
return 0;
}

int firmware_verified = verify_firmware(addr);
if(firmware_verified == -1) {
debug_print(F("Verify failed.\r\n"));
if (firmware_verified == -1) {
debug_println(F("Verify failed."));
return 0;
}

debug_print(F("Resetting ATTiny "));

// execute app
uint8_t data[] = {0x03, 0x00};
uint8_t result = twi_writeTo(addr, data, ELEMENTS(data), true, true);
debug_print_result(result);
debug_print(F("\r\n"));
debug_print(F("Done!\r\n"));
uint8_t result = write_cmd(addr, data, true);
debug_println_result(result);
debug_println(F("Done!"));

return 1;
}
Expand All @@ -291,32 +365,36 @@ int right_written = 0;
void loop() {
delay(2000);

if (left_written > 0) {
debug_print(F("Done with left side.\r\n"));
// Update right side first, because resetting it will power-cycle
// LEDs on both sides.
if (right_written > 0) {
debug_println(F("Done with right side."));
leds_success(RIGHT_LEDS);
// we're done
} else {
debug_print(F("Updating left side\r\n"));
reset_left_attiny();
left_written = update_attiny(LEFT_ADDRESS);

debug_println(F("Updating right side"));
reset_right_attiny();
right_written = update_attiny(RIGHT_ADDRESS);
delay(3 * RESET_DELAY);
leds_success(RIGHT_LEDS);
}

if (right_written > 0) {
debug_print(F("Done with right side.\r\n"));
if (left_written > 0) {
debug_println(F("Done with left side."));
leds_success(LEFT_LEDS);
// we're done
} else {
debug_print(F("Updating right side\r\n"));
reset_right_attiny();
right_written = update_attiny(RIGHT_ADDRESS);
debug_println(F("Updating left side"));
reset_left_attiny();
left_written = update_attiny(LEFT_ADDRESS);
delay(3 * RESET_DELAY);
leds_success(LEFT_LEDS);
}

if (left_written && right_written ) {
debug_print (F("Both ATTiny MCUs have been flashed\r\nIt is now safe to reload the regular firmware\r\n"));
debug_println(F("Both ATTiny MCUs have been flashed"));
debug_println(F("It is now safe to reload the regular firmware"));
delay(5000);
return;
}


}


0 comments on commit 4f88e80

Please sign in to comment.