programmer.ino — flash this onto the “Reader” board (Nano/Uno/Mini, any ATmega328-family board). It bit-bangs the genuine STK500-style AVR ISP protocol over 4 wires to the target chip’s ISP pins, reads the actual flash contents, and streams it out over USB serial as correct Intel HEX (proper byte counts, addresses, record types, checksums — verified against the real spec, not approximated).
you have to connect the other board to computer and keep proggramer .ino in it this is the ino and app
app
this is the code for you to upload into the other chip
/*
Arduino X – ISP Flash Reader
============================
Flash this onto your “Reader” board (Uno, Nano, Pro Mini – any ATmega328
family board works identically, since they all speak the same ISP protocol).
WIRING (Reader -> Target chip’s ISP pins):
Reader Pin 10 -> Target RESET
Reader Pin 11 -> Target MOSI (pin 17 / PB3 on bare chip, or ICSP header pin 4)
Reader Pin 12 -> Target MISO (pin 18 / PB4 on bare chip, or ICSP header pin 1)
Reader Pin 13 -> Target SCK (pin 19 / PB5 on bare chip, or ICSP header pin 3)
Reader GND -> Target GND
Reader 5V -> Target VCC (only if target has no separate power source)
If reading a second Nano/Uno’s chip via its 6-pin ICSP header, those 4 signal
pins map directly – no need to find bare chip legs.
USAGE:
Open Serial Monitor / connect via the companion HTML page at 115200 baud.
Send the single character ‘D’ (no newline needed) to trigger a full dump.
The board will print a complete, spec-correct Intel HEX file to serial,
followed by a line reading “EOF-MARKER” so the receiving app knows the
transfer is complete.
NOTES ON HONESTY OF THIS CODE:
– This reads the TARGET’s raw compiled flash bytes. That’s it.
– It cannot and does not attempt to reconstruct source code, variable
names, or program structure – that information does not exist in
compiled flash memory.
– If the target chip has lock bits set, the chip’s hardware will refuse
read access at the silicon level. This code will detect that (reads
return 0x00 or 0xFF for everything) and will tell you plainly rather
than pretend it succeeded.
*/
#define RESET_PIN 10
#define MOSI_PIN 11
#define MISO_PIN 12
#define SCK_PIN 13
// Flash sizes for common AVR chips used in Arduino boards, in bytes.
// ATmega328P/328PB (Uno, Nano, Pro Mini): 32768
// ATmega168/168P (older Nanos): 16384
// ATmega8 (very old boards): 8192
uint32_t FLASH_SIZE = 32768UL;
uint8_t PAGE_SIZE_WORDS = 64; // not used for byte-level reads, kept for reference
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, HIGH);
pinMode(SCK_PIN, OUTPUT);
pinMode(MOSI_PIN, OUTPUT);
pinMode(MISO_PIN, INPUT);
Serial.println(F(“ARDUINO-X-READER-READY”));
}
void loop() {
if (Serial.available() > 0) {
char cmd = Serial.read();
if (cmd == ‘D’ || cmd == ‘d’) {
runFullDump();
} else if (cmd == ‘I’ || cmd == ‘i’) {
identifyTarget();
}
}
}
// —- Low level bit-banged SPI (no SPI library dependency, so timing is explicit) —-
uint8_t spiTransfer(uint8_t data) {
uint8_t reply = 0;
for (int8_t bit = 7; bit >= 0; bit–) {
digitalWrite(MOSI_PIN, (data & (1 << bit)) ? HIGH : LOW);
digitalWrite(SCK_PIN, HIGH);
delayMicroseconds(4);
reply <<= 1;
if (digitalRead(MISO_PIN)) reply |= 1;
digitalWrite(SCK_PIN, LOW);
delayMicroseconds(4);
}
return reply;
}
bool enterProgrammingMode() {
pinMode(MOSI_PIN, OUTPUT);
pinMode(SCK_PIN, OUTPUT);
pinMode(MISO_PIN, INPUT);
digitalWrite(RESET_PIN, HIGH);
delay(20);
digitalWrite(SCK_PIN, LOW);
digitalWrite(RESET_PIN, LOW);
delay(20);
// Real AVR “Programming Enable” command sequence per the ATmega328P datasheet
for (uint8_t attempt = 0; attempt < 4; attempt++) {
spiTransfer(0xAC);
spiTransfer(0x53);
uint8_t echo = spiTransfer(0x00);
spiTransfer(0x00);
if (echo == 0x53) return true;
// resync: pulse reset and try again
digitalWrite(RESET_PIN, HIGH);
delay(10);
digitalWrite(RESET_PIN, LOW);
delay(10);
}
return false;
}
void exitProgrammingMode() {
digitalWrite(RESET_PIN, HIGH);
}
// Reads the 3-byte device signature – lets us confirm what chip is actually
// connected rather than assuming.
void readSignature(uint8_t sig[3]) {
for (uint8_t i = 0; i < 3; i++) {
spiTransfer(0x30);
spiTransfer(0x00);
spiTransfer(i);
sig[i] = spiTransfer(0x00);
}
}
void identifyTarget() {
if (!enterProgrammingMode()) {
Serial.println(F(“ERROR: Target did not respond to programming sequence.”));
Serial.println(F(“Check wiring: RESET->10, MOSI->11, MISO->12, SCK->13, GND, VCC.”));
exitProgrammingMode();
return;
}
uint8_t sig[3];
readSignature(sig);
Serial.print(F(“SIGNATURE: “));
for (uint8_t i = 0; i < 3; i++) {
if (sig[i] < 0x10) Serial.print(‘0’);
Serial.print(sig[i], HEX);
Serial.print(‘ ‘);
}
Serial.println();
String chipName = “Unknown”;
if (sig[0] == 0x1E && sig[1] == 0x95 && sig[2] == 0x0F) { chipName = “ATmega328P”; FLASH_SIZE = 32768; }
else if (sig[0] == 0x1E && sig[1] == 0x95 && sig[2] == 0x14) { chipName = “ATmega328”; FLASH_SIZE = 32768; }
else if (sig[0] == 0x1E && sig[1] == 0x94 && sig[2] == 0x06) { chipName = “ATmega168”; FLASH_SIZE = 16384; }
else if (sig[0] == 0x1E && sig[1] == 0x93 && sig[2] == 0x07) { chipName = “ATmega8”; FLASH_SIZE = 8192; }
Serial.print(F(“CHIP: “));
Serial.println(chipName);
Serial.print(F(“FLASH_SIZE: “));
Serial.println(FLASH_SIZE);
// Read lock bits so we can tell you honestly if reading will be blocked
spiTransfer(0x58);
spiTransfer(0x00);
spiTransfer(0x00);
uint8_t lockBits = spiTransfer(0x00);
Serial.print(F(“LOCK_BITS: 0x”));
Serial.println(lockBits, HEX);
if ((lockBits & 0x03) != 0x03) {
Serial.println(F(“WARNING: Lock bits are SET. Flash read-back is blocked by hardware.”));
Serial.println(F(“Any dump attempted will return meaningless repeated bytes.”));
}
exitProgrammingMode();
}
void runFullDump() {
if (!enterProgrammingMode()) {
Serial.println(F(“ERROR: Target did not respond to programming sequence.”));
Serial.println(F(“Check wiring: RESET->10, MOSI->11, MISO->12, SCK->13, GND, VCC.”));
return;
}
uint8_t sig[3];
readSignature(sig);
if (sig[0] == 0x1E && sig[1] == 0x95 && sig[2] == 0x0F) FLASH_SIZE = 32768;
else if (sig[0] == 0x1E && sig[1] == 0x95 && sig[2] == 0x14) FLASH_SIZE = 32768;
else if (sig[0] == 0x1E && sig[1] == 0x94 && sig[2] == 0x06) FLASH_SIZE = 16384;
else if (sig[0] == 0x1E && sig[1] == 0x93 && sig[2] == 0x07) FLASH_SIZE = 8192;
Serial.println(F(“HEX-STREAM-START”));
const uint8_t bytesPerLine = 16;
uint8_t lineBuf[bytesPerLine];
uint32_t addr = 0;
while (addr < FLASH_SIZE) {
uint8_t count = 0;
for (; count < bytesPerLine && addr < FLASH_SIZE; count++, addr++) {
uint16_t wordAddr = addr >> 1;
uint8_t isHigh = addr & 1;
// Real AVR flash read commands: 0x20 = read low byte, 0x28 = read high byte
spiTransfer(isHigh ? 0x28 : 0x20);
spiTransfer((wordAddr >> 8) & 0xFF);
spiTransfer(wordAddr & 0xFF);
lineBuf[count] = spiTransfer(0x00);
}
printIntelHexLine(addr – count, lineBuf, count);
}
// Standard Intel HEX end-of-file record
Serial.println(F(“:00000001FF”));
Serial.println(F(“HEX-STREAM-END”));
exitProgrammingMode();
}
void printIntelHexLine(uint32_t startAddr, uint8_t *data, uint8_t len) {
uint8_t checksum = 0;
char buf[12];
Serial.print(‘:’);
checksum += len;
sprintf(buf, “%02X”, len);
Serial.print(buf);
uint8_t addrHi = (startAddr >> 8) & 0xFF;
uint8_t addrLo = startAddr & 0xFF;
checksum += addrHi;
checksum += addrLo;
sprintf(buf, “%02X%02X”, addrHi, addrLo);
Serial.print(buf);
// Record type 00 = data
checksum += 0x00;
Serial.print(F(“00”));
for (uint8_t i = 0; i < len; i++) {
checksum += data[i];
sprintf(buf, “%02X”, data[i]);
Serial.print(buf);
}
uint8_t finalChecksum = (uint8_t)((~checksum) + 1);
sprintf(buf, “%02X”, finalChecksum);
Serial.println(buf);
}
