Write flash in a sketch?

I am trying to write to flash in a sketch so I can save some data that is being collected (I'm not worried about accidentally deleting the bootloader because I am writing the sketch via ICSP). I found an example of using avr/boot.h and wrote the test sketch included below (it just writes a few pages and then looks for the written data).

But the flash is not being written. When run only a single matching string, which I assume is the string in the compiled program. Are there fuse bits or something that need to be set to enable writing of flash from sketches?

I am seeing the "boot lock protection bits" in the 328p spec but I am having trouble figuring out the ramifications of the various settings (other then to change the size reserved for the bootloader). I have no bootloader since I am writing the sketch via ICSP... (note, please ignore the USB.print stuff in the sketch below; it is similar to Serial.print but goes to a usb chip over SPI instead of serial).

Cheers!
Andrew

#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/boot.h>
#include <lightuino5.h>

// Pulled from boot.h example code:   
void programFlash (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;
    }

// Printf-style to the USB
void p(const char *fmt, ... )
{
  char tmp[128]; // resulting string limited to 128 chars
  va_list args;
  va_start (args, fmt );
  vsnprintf(tmp, 128, fmt, args);
  va_end (args);
  Usb.print(tmp);
}

void setup(void)
{
  Usb.begin();
}

char c[128];

#define FLASH_PAGES 32768/SPM_PAGESIZE

void loop(void)
{
  strcpy (c,"!ATHIS IS A TEST OF THE FLASH WRITE CAPABILITY");
  int lastpage = FLASH_PAGES-1;
  programFlash(32768UL-SPM_PAGESIZE,(uint8_t*)c);
  c[1] = 'B';
  programFlash(32768UL-(SPM_PAGESIZE*2),(uint8_t*)c);
  c[1] = 'C';
  programFlash(32768UL-2048,(uint8_t*)c);
  
  delay(5000);
  Usb.print("Flash write test\n");
  p("Page size: %d\n", SPM_PAGESIZE);

  // Now search for my written string:
  for (unsigned long int i=0;i<32768;i++)
  {
    char c = pgm_read_byte(i);
    if ((c>='A') && (c<='Z')) Usb.write(c);  // Dump everything readable and caps
    if (c=='!')  // This may be my string's beginning
    {
      p("\nFound ! at: %lu\n", i);
    }
  }

  Usb.print("Done!\n");
  delay(20000);
  while(1);
}

You can only write program memory from the boot sector.
Is your sketch in the boot sector?

Nope! And its way too big. So that begs the question... do you know how to force C code (or assembly I guess) into certain sectors or sections?

There is a "--section-start=.sectioname=0x3e00" switch for the link step.
The usual idea is to put some subroutines for writing flash up in the "boot section" and then call them from the main program (effectively, adding a "bootloader" that does something other than bootloading.)

You might want to look at "amforth"; it uses this technique to write the user programs to flash...

Thanks! That points me in the right direction. The code is currently in a "sketch", not a normal C project so I've got a bit of effort and learning ahead of me before I can get to the point where I can jam a linker flag into the link process and a section identifier into the code somewhere... so I may go silent for a few days :-). I'm thinking it would be Really CoolTM, to add this facility to the bootloader (well I am not using the bootloader for this sketch so it would not help me ...but regardless...) and then adding an Arduino library that calls these functions... it could turn the standard Arduino into a datalogger with a much bigger capacity (depends on program size) than the EEPROM.

Well, the bootloader currently has 10 bytes of free space...

The problem with flash memory is that it is only writable a "page" at a time, has a relatively limited specification for the number of allowed writes, and is actually pretty small compared to external EEPROMS, "Dataflash" chips, and consumer flash memory cards that are relatively easy to write to and REALLY cheap.

The main point here is that the processor flash memory is in two partitions. You can't write in the partition you are running in only the other one.

The problem with flash memory is that it is only writable a "page" at a time,

I think you can write in smaller blocks than this, possibly bytes, but you must erase in pages. It is the number of erase cycles that determines the memory ware out rate. Blank memory look like all 1s and you write 0s to it. You can write a 0 to any bit in a byte at any time (providing it is in the other partition) but you need an erase to make a 1.