mirror of
https://github.com/grblHAL/core.git
synced 2026-03-23 11:32:44 +08:00
Improved alarm handling, if a critical alarm is active when a non-critical alarm is raised the non-critical alarm will be delayed until after reset for the critical alarm. Improved Modbus exception handling and added high level API call for creating and sending Modbus messages. Added G65P7 inbuilt macro for interacting with Modbus devices from gcode. See the Wiki for details.
467 lines
16 KiB
C
467 lines
16 KiB
C
/*
|
|
nvs_buffer.c - RAM based non-volatile storage buffer/emulation
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2017-2025 Terje Io
|
|
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
|
|
|
grblHAL is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
grblHAL 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License.
|
|
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//
|
|
// Can be used by MCUs with no nonvolatile storage options, be sure to allocate enough heap memory before use
|
|
//
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include "hal.h"
|
|
#include "nvs_buffer.h"
|
|
#include "protocol.h"
|
|
#include "settings.h"
|
|
#include "gcode.h"
|
|
#include "crc.h"
|
|
#include "nvs.h"
|
|
|
|
settings_dirty_t settings_dirty;
|
|
|
|
static nvs_io_t physical_nvs;
|
|
static bool dirty;
|
|
static struct {
|
|
uint8_t *addr;
|
|
uint32_t size;
|
|
} nvsbuffer = { .size = NVS_SIZE };
|
|
|
|
typedef struct {
|
|
uint16_t addr;
|
|
uint8_t type;
|
|
uint8_t offset;
|
|
} emap_t;
|
|
|
|
#define NVS_GROUP_GLOBAL 0
|
|
#define NVS_GROUP_TOOLS 1
|
|
#define NVS_GROUP_PARAMETERS 2
|
|
#define NVS_GROUP_STARTUP 3
|
|
#define NVS_GROUP_BUILD 4
|
|
|
|
#define PARAMETER_ADDR(n) (NVS_ADDR_PARAMETERS + n * (sizeof(coord_system_data_t) + NVS_CRC_BYTES))
|
|
#define STARTLINE_ADDR(n) (NVS_ADDR_STARTUP_BLOCK + n * (sizeof(stored_line_t) + NVS_CRC_BYTES))
|
|
#if N_TOOLS
|
|
#define TOOL_ADDR(n) (NVS_ADDR_TOOL_TABLE + n * (sizeof(tool_data_t) + NVS_CRC_BYTES))
|
|
#endif
|
|
|
|
PROGMEM static const emap_t target[] = {
|
|
{NVS_ADDR_GLOBAL, NVS_GROUP_GLOBAL, 0},
|
|
|
|
{PARAMETER_ADDR(0), NVS_GROUP_PARAMETERS, 0},
|
|
{PARAMETER_ADDR(1), NVS_GROUP_PARAMETERS, 1},
|
|
{PARAMETER_ADDR(2), NVS_GROUP_PARAMETERS, 2},
|
|
{PARAMETER_ADDR(3), NVS_GROUP_PARAMETERS, 3},
|
|
{PARAMETER_ADDR(4), NVS_GROUP_PARAMETERS, 4},
|
|
{PARAMETER_ADDR(5), NVS_GROUP_PARAMETERS, 5},
|
|
{PARAMETER_ADDR(6), NVS_GROUP_PARAMETERS, 6},
|
|
{PARAMETER_ADDR(7), NVS_GROUP_PARAMETERS, 7},
|
|
{PARAMETER_ADDR(8), NVS_GROUP_PARAMETERS, 8},
|
|
{PARAMETER_ADDR(9), NVS_GROUP_PARAMETERS, 9},
|
|
{PARAMETER_ADDR(10), NVS_GROUP_PARAMETERS, 10},
|
|
{PARAMETER_ADDR(11), NVS_GROUP_PARAMETERS, 11},
|
|
{STARTLINE_ADDR(0), NVS_GROUP_STARTUP, 0},
|
|
{STARTLINE_ADDR(1), NVS_GROUP_STARTUP, 1},
|
|
#if N_STARTUP_LINE > 2
|
|
#error Increase number of startup line entries!
|
|
#endif
|
|
{NVS_ADDR_BUILD_INFO, NVS_GROUP_BUILD, 0},
|
|
#if N_TOOLS
|
|
{TOOL_ADDR(0), NVS_GROUP_TOOLS, 0},
|
|
{TOOL_ADDR(1), NVS_GROUP_TOOLS, 1},
|
|
{TOOL_ADDR(2), NVS_GROUP_TOOLS, 2},
|
|
{TOOL_ADDR(3), NVS_GROUP_TOOLS, 3},
|
|
{TOOL_ADDR(4), NVS_GROUP_TOOLS, 4},
|
|
{TOOL_ADDR(5), NVS_GROUP_TOOLS, 5},
|
|
{TOOL_ADDR(6), NVS_GROUP_TOOLS, 6},
|
|
{TOOL_ADDR(7), NVS_GROUP_TOOLS, 7},
|
|
#if N_TOOLS > 8
|
|
{TOOL_ADDR(8), NVS_GROUP_TOOLS, 8},
|
|
{TOOL_ADDR(9), NVS_GROUP_TOOLS, 9},
|
|
{TOOL_ADDR(10), NVS_GROUP_TOOLS, 10},
|
|
{TOOL_ADDR(11), NVS_GROUP_TOOLS, 11},
|
|
{TOOL_ADDR(12), NVS_GROUP_TOOLS, 12},
|
|
{TOOL_ADDR(13), NVS_GROUP_TOOLS, 13},
|
|
{TOOL_ADDR(14), NVS_GROUP_TOOLS, 14},
|
|
{TOOL_ADDR(15), NVS_GROUP_TOOLS, 15},
|
|
#endif
|
|
#if N_TOOLS > 16
|
|
{TOOL_ADDR(16), NVS_GROUP_TOOLS, 16},
|
|
{TOOL_ADDR(17), NVS_GROUP_TOOLS, 17},
|
|
{TOOL_ADDR(18), NVS_GROUP_TOOLS, 18},
|
|
{TOOL_ADDR(19), NVS_GROUP_TOOLS, 19},
|
|
{TOOL_ADDR(20), NVS_GROUP_TOOLS, 20},
|
|
{TOOL_ADDR(21), NVS_GROUP_TOOLS, 21},
|
|
{TOOL_ADDR(22), NVS_GROUP_TOOLS, 22},
|
|
{TOOL_ADDR(23), NVS_GROUP_TOOLS, 23},
|
|
{TOOL_ADDR(24), NVS_GROUP_TOOLS, 24},
|
|
{TOOL_ADDR(25), NVS_GROUP_TOOLS, 25},
|
|
{TOOL_ADDR(26), NVS_GROUP_TOOLS, 26},
|
|
{TOOL_ADDR(27), NVS_GROUP_TOOLS, 27},
|
|
{TOOL_ADDR(28), NVS_GROUP_TOOLS, 28},
|
|
{TOOL_ADDR(29), NVS_GROUP_TOOLS, 29},
|
|
{TOOL_ADDR(30), NVS_GROUP_TOOLS, 30},
|
|
{TOOL_ADDR(31), NVS_GROUP_TOOLS, 31},
|
|
#endif
|
|
#endif
|
|
{0, 0, 0} // list termination - do not remove
|
|
};
|
|
|
|
inline static uint8_t ram_get_byte (uint32_t offset)
|
|
{
|
|
return nvsbuffer.addr[offset];
|
|
}
|
|
|
|
inline static void ram_put_byte (uint32_t offset, uint8_t new_value)
|
|
{
|
|
if(offset == 0)
|
|
settings_dirty.version = true;
|
|
dirty = dirty || nvsbuffer.addr[offset] != new_value || offset == 0;
|
|
nvsbuffer.addr[offset] = new_value;
|
|
}
|
|
|
|
FLASHMEM static nvs_transfer_result_t memcpy_to_ram (uint32_t destination, uint8_t *source, uint32_t size, bool with_checksum)
|
|
{
|
|
if(hal.nvs.driver_area.address && destination > hal.nvs.driver_area.address + hal.nvs.driver_area.size)
|
|
return physical_nvs.memcpy_to_nvs(destination, source, size, with_checksum);
|
|
|
|
uint32_t dest = destination;
|
|
uint16_t checksum = with_checksum ? calc_checksum(source, size) : 0;
|
|
|
|
dirty = false;
|
|
|
|
for(; size > 0; size--)
|
|
ram_put_byte(dest++, *(source++));
|
|
|
|
if(with_checksum) {
|
|
ram_put_byte(dest, checksum & 0xFF);
|
|
#if NVS_CRC_BYTES > 1
|
|
ram_put_byte(++dest, checksum >> 8);
|
|
#endif
|
|
}
|
|
|
|
if(settings_dirty.version || source == hal.nvs.driver_area.mem_address)
|
|
dirty = true;
|
|
|
|
if(dirty && physical_nvs.type != NVS_None) {
|
|
|
|
uint8_t idx = 0;
|
|
|
|
settings_dirty.is_dirty = true;
|
|
|
|
if(hal.nvs.driver_area.address && destination >= hal.nvs.driver_area.address)
|
|
settings_dirty.driver_settings = true;
|
|
|
|
else {
|
|
|
|
do {
|
|
if(target[idx].addr == destination)
|
|
break;
|
|
} while(target[++idx].addr);
|
|
|
|
if(target[idx].addr) switch(target[idx].type) {
|
|
|
|
case NVS_GROUP_GLOBAL:
|
|
settings_dirty.global_settings = true;
|
|
break;
|
|
#if N_TOOLS
|
|
case NVS_GROUP_TOOLS:
|
|
settings_dirty.tool_data |= (1 << target[idx].offset);
|
|
break;
|
|
#endif
|
|
case NVS_GROUP_PARAMETERS:
|
|
settings_dirty.coord_data |= (1 << target[idx].offset);
|
|
break;
|
|
|
|
case NVS_GROUP_STARTUP:
|
|
settings_dirty.startup_lines |= (1 << target[idx].offset);
|
|
break;
|
|
|
|
case NVS_GROUP_BUILD:
|
|
settings_dirty.build_info = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NVS_TransferResult_OK;
|
|
}
|
|
|
|
FLASHMEM static nvs_transfer_result_t memcpy_from_ram (uint8_t *destination, uint32_t source, uint32_t size, bool with_checksum)
|
|
{
|
|
if(hal.nvs.driver_area.address && source > hal.nvs.driver_area.address + hal.nvs.driver_area.size)
|
|
return physical_nvs.memcpy_from_nvs(destination, source, size, with_checksum);
|
|
|
|
uint16_t checksum = with_checksum ? calc_checksum(&nvsbuffer.addr[source], size) : 0;
|
|
|
|
for(; size > 0; size--)
|
|
*(destination++) = ram_get_byte(source++);
|
|
|
|
#if NVS_CRC_BYTES == 1
|
|
return !with_checksum || checksum == ram_get_byte(source);
|
|
#else
|
|
return !with_checksum || checksum == (ram_get_byte(source) | (ram_get_byte(source + 1) << 8));
|
|
#endif
|
|
}
|
|
|
|
// Try to allocate RAM from heap for buffer/emulation.
|
|
FLASHMEM bool nvs_buffer_alloc (void)
|
|
{
|
|
if(nvsbuffer.addr == NULL) {
|
|
|
|
if(hal.nvs.size_max > NVS_SIZE)
|
|
nvsbuffer.size = min(NVS_SIZE_MAX, hal.nvs.size_max);
|
|
|
|
if(nvsbuffer.size >= GRBL_NVS_SIZE && (nvsbuffer.addr = malloc(nvsbuffer.size)))
|
|
memset(nvsbuffer.addr, 0xFF, nvsbuffer.size);
|
|
}
|
|
|
|
return nvsbuffer.addr != NULL;
|
|
}
|
|
|
|
FLASHMEM void nvs_buffer_free (void)
|
|
{
|
|
if(nvsbuffer.addr) {
|
|
nvs_buffer_sync_physical();
|
|
free(nvsbuffer.addr);
|
|
}
|
|
}
|
|
//
|
|
// Switch over to RAM based copy.
|
|
// Changes to RAM based copy will be written to physical storage when grblHAL is in IDLE state.
|
|
FLASHMEM bool nvs_buffer_init (void)
|
|
{
|
|
hal.nvs.size = ((hal.nvs.size - 1) | 0x03) + 1; // Ensure NVS area ends on a word boundary
|
|
|
|
if(nvsbuffer.addr) {
|
|
|
|
memcpy(&physical_nvs, &hal.nvs, sizeof(nvs_io_t)); // save pointers to physical storage handler functions
|
|
|
|
// Copy physical storage content to RAM when available
|
|
if(physical_nvs.type == NVS_Flash)
|
|
physical_nvs.memcpy_from_flash(nvsbuffer.addr);
|
|
else if(physical_nvs.type != NVS_None)
|
|
physical_nvs.memcpy_from_nvs(nvsbuffer.addr, 0, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
|
|
|
|
// Switch hal to use RAM version of non-volatile storage data
|
|
hal.nvs.type = NVS_Emulated;
|
|
hal.nvs.get_byte = &ram_get_byte;
|
|
hal.nvs.put_byte = &ram_put_byte;
|
|
hal.nvs.memcpy_to_nvs = &memcpy_to_ram;
|
|
hal.nvs.memcpy_from_nvs = &memcpy_from_ram;
|
|
hal.nvs.memcpy_from_flash = NULL;
|
|
hal.nvs.memcpy_to_flash = NULL;
|
|
|
|
// If no physical storage available or if NVS import fails copy default settings to RAM
|
|
// and write out to physical storage when available.
|
|
if(physical_nvs.type == NVS_None || ram_get_byte(0) != SETTINGS_VERSION) {
|
|
settings_restore(settings_all);
|
|
if(physical_nvs.type == NVS_Flash)
|
|
physical_nvs.memcpy_to_flash(nvsbuffer.addr);
|
|
else if(physical_nvs.memcpy_to_nvs)
|
|
physical_nvs.memcpy_to_nvs(0, nvsbuffer.addr, GRBL_NVS_SIZE + hal.nvs.driver_area.size, false);
|
|
if(physical_nvs.type != NVS_None)
|
|
grbl.report.status_message(Status_SettingReadFail);
|
|
}
|
|
} else
|
|
task_run_on_startup(report_warning, "Not enough heap for NVS buffer!");
|
|
|
|
// Clear settings dirty flags
|
|
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
|
|
|
|
return nvsbuffer.addr != NULL;
|
|
}
|
|
|
|
// Allocate NVS block for driver settings.
|
|
// NOTE: allocation has to be done before content is copied from physical storage.
|
|
FLASHMEM nvs_address_t nvs_alloc (size_t size)
|
|
{
|
|
static uint8_t *mem_address;
|
|
|
|
uint8_t *start_address;
|
|
nvs_address_t addr = 0;
|
|
|
|
// Check if already switched to emulation or buffer allocation failed, return NULL if so.
|
|
if(hal.nvs.type == NVS_Emulated || (nvsbuffer.addr == NULL && !nvs_buffer_alloc()))
|
|
return 0;
|
|
|
|
if(hal.nvs.driver_area.address == 0) {
|
|
hal.nvs.driver_area.address = GRBL_NVS_SIZE;
|
|
hal.nvs.driver_area.mem_address = mem_address = nvsbuffer.addr + hal.nvs.driver_area.address;
|
|
}
|
|
|
|
size += NVS_CRC_BYTES; // add room for checksum.
|
|
start_address = (uint8_t *)((uintptr_t)(mem_address - 1) | 0x03) + 1; // Align to word boundary
|
|
if(start_address + size < nvsbuffer.addr + nvsbuffer.size) {
|
|
addr = start_address - nvsbuffer.addr;
|
|
mem_address = start_address + size;
|
|
hal.nvs.driver_area.size = mem_address - hal.nvs.driver_area.mem_address;
|
|
hal.nvs.size = GRBL_NVS_SIZE + hal.nvs.driver_area.size + 1;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
// Write RAM changes to physical storage
|
|
FLASHMEM void nvs_buffer_sync_physical (void)
|
|
{
|
|
if(!settings_dirty.is_dirty)
|
|
return;
|
|
|
|
if(physical_nvs.memcpy_to_nvs) {
|
|
|
|
if(settings_dirty.version)
|
|
settings_dirty.version = physical_nvs.memcpy_to_nvs(0, nvsbuffer.addr, 1, false) != NVS_TransferResult_OK;
|
|
|
|
if(settings_dirty.global_settings)
|
|
settings_dirty.global_settings = physical_nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)(nvsbuffer.addr + NVS_ADDR_GLOBAL), sizeof(settings_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
|
|
|
|
if(settings_dirty.build_info)
|
|
settings_dirty.build_info = physical_nvs.memcpy_to_nvs(NVS_ADDR_BUILD_INFO, (uint8_t *)(nvsbuffer.addr + NVS_ADDR_BUILD_INFO), sizeof(stored_line_t) + NVS_CRC_BYTES, false) != NVS_TransferResult_OK;
|
|
|
|
uint_fast8_t idx = N_STARTUP_LINE, offset;
|
|
if(settings_dirty.startup_lines) do {
|
|
idx--;
|
|
if(bit_istrue(settings_dirty.startup_lines, bit(idx))) {
|
|
bit_false(settings_dirty.startup_lines, bit(idx));
|
|
offset = NVS_ADDR_STARTUP_BLOCK + idx * (sizeof(stored_line_t) + NVS_CRC_BYTES);
|
|
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer.addr + offset), sizeof(stored_line_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
|
|
bit_false(settings_dirty.startup_lines, bit(idx));
|
|
}
|
|
} while(idx);
|
|
|
|
idx = N_CoordinateSystems;
|
|
if(settings_dirty.coord_data) do {
|
|
if(bit_istrue(settings_dirty.coord_data, bit(idx))) {
|
|
offset = NVS_ADDR_PARAMETERS + idx * (sizeof(coord_system_data_t) + NVS_CRC_BYTES);
|
|
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer.addr + offset), sizeof(coord_system_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
|
|
bit_false(settings_dirty.coord_data, bit(idx));
|
|
}
|
|
} while(idx--);
|
|
|
|
if(settings_dirty.driver_settings) {
|
|
if(hal.nvs.driver_area.size > 0)
|
|
settings_dirty.driver_settings = physical_nvs.memcpy_to_nvs(hal.nvs.driver_area.address, (uint8_t *)(nvsbuffer.addr + hal.nvs.driver_area.address), hal.nvs.driver_area.size, false) != NVS_TransferResult_OK;
|
|
else
|
|
settings_dirty.driver_settings = false;
|
|
}
|
|
|
|
#if N_TOOLS
|
|
idx = N_TOOLS;
|
|
if(settings_dirty.tool_data) do {
|
|
idx--;
|
|
if(bit_istrue(settings_dirty.tool_data, bit(idx))) {
|
|
offset = NVS_ADDR_TOOL_TABLE + idx * (sizeof(tool_data_t) + NVS_CRC_BYTES);
|
|
if(physical_nvs.memcpy_to_nvs(offset, (uint8_t *)(nvsbuffer.addr + offset), sizeof(tool_data_t) + NVS_CRC_BYTES, false) == NVS_TransferResult_OK)
|
|
bit_false(settings_dirty.tool_data, bit(idx));
|
|
}
|
|
} while(idx);
|
|
#endif
|
|
settings_dirty.is_dirty = settings_dirty.coord_data ||
|
|
settings_dirty.global_settings ||
|
|
settings_dirty.driver_settings ||
|
|
settings_dirty.startup_lines ||
|
|
#if N_TOOLS
|
|
settings_dirty.tool_data ||
|
|
#endif
|
|
settings_dirty.build_info;
|
|
|
|
} else if(physical_nvs.memcpy_to_flash) {
|
|
uint_fast8_t retries = 4;
|
|
do {
|
|
if(physical_nvs.memcpy_to_flash(nvsbuffer.addr))
|
|
retries = 0;
|
|
else if(--retries == 0)
|
|
report_message("Settings write failed!", Message_Warning);
|
|
} while(retries);
|
|
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
|
|
}
|
|
}
|
|
|
|
FLASHMEM nvs_io_t *nvs_buffer_get_physical (void)
|
|
{
|
|
return hal.nvs.type == NVS_Emulated ? &physical_nvs : &hal.nvs;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
#include "report.h"
|
|
|
|
FLASHMEM void nvs_memmap (void)
|
|
{
|
|
char buf[30];
|
|
|
|
report_message("NVS Area: addr size end", Message_Plain);
|
|
|
|
strcpy(buf, "Global: ");
|
|
strcat(buf, uitoa(NVS_ADDR_GLOBAL));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(sizeof(settings_t) + NVS_CRC_BYTES));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_GLOBAL + sizeof(settings_t) + NVS_CRC_BYTES));
|
|
report_message(buf, Message_Plain);
|
|
|
|
strcpy(buf, "Parameters: ");
|
|
strcat(buf, uitoa(NVS_ADDR_PARAMETERS));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_SIZE_PARAMETERS));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_PARAMETERS + N_CoordinateSystems * (sizeof(coord_system_data_t) + NVS_CRC_BYTES)));
|
|
report_message(buf, Message_Plain);
|
|
|
|
strcpy(buf, "Startup block: ");
|
|
strcat(buf, uitoa(NVS_ADDR_STARTUP_BLOCK));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_SIZE_STARTUP_BLOCK));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_STARTUP_BLOCK + N_STARTUP_LINE * (sizeof(stored_line_t) + NVS_CRC_BYTES)));
|
|
report_message(buf, Message_Plain);
|
|
|
|
strcpy(buf, "Build info: ");
|
|
strcat(buf, uitoa(NVS_ADDR_BUILD_INFO));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_SIZE_BUILD_INFO));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_BUILD_INFO + sizeof(stored_line_t) + NVS_CRC_BYTES));
|
|
report_message(buf, Message_Plain);
|
|
|
|
#if N_TOOLS
|
|
strcpy(buf, "Tool table: ");
|
|
strcat(buf, uitoa(NVS_ADDR_TOOL_TABLE));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(N_TOOLS * (sizeof(tool_data_t) + NVS_CRC_BYTES)));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(NVS_ADDR_TOOL_TABLE + N_TOOLS * (sizeof(tool_data_t) + NVS_CRC_BYTES)));
|
|
report_message(buf, Message_Plain);
|
|
#endif
|
|
|
|
strcpy(buf, "Driver: ");
|
|
strcat(buf, uitoa(hal.nvs.driver_area.address));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(hal.nvs.driver_area.size));
|
|
strcat(buf, " ");
|
|
strcat(buf, uitoa(hal.nvs.driver_area.address + hal.nvs.driver_area.size));
|
|
report_message(buf, Message_Plain);
|
|
}
|
|
|
|
#endif
|