Программа Arduino считывает искаженные данные

Я работаю на небольшом HTTP-сервере. Я строю маршрутизатор и, поскольку маршрутов может быть довольно много, я хотел поместить их во флэш-память, чтобы мне не приходилось использовать ценную SRAM. Однако я либо что-то не правильно понимаю, либо происходит что-то странное, потому что я не могу прочитать свои сохраненные данные из флэш-памяти.

У меня есть структура, которая содержит указатель на функцию и указатель на символ. Я хочу сохранить массив этих структур во флэш-памяти и прочитать их обратно. Однако с небольшой отладочной печатью я вижу, что не могу правильно прочитать указатель на символ. Он печатает мусор в последовательный порт.

Вот небольшой пример.

#include <avr/pgmspace.h>

typedef struct {
void (*func)();
const char *URI;
} Route;

void test1() {
Serial.println("Executed testfunc1");
}

void test2() {
Serial.println("Executed testfunc2");
}

const char route1URI[] PROGMEM = "/route1";
const Route route1 PROGMEM = {
test1,
route1URI
};

const char route2URI[] PROGMEM = "/route2";
const Route route2 PROGMEM = {
test2,
route2URI
};

const Route routingTable[] PROGMEM = {
route1,
route2
};

void (*getRoute(char *URI))() {
Route *r = (Route *)pgm_read_word(routingTable + 0);
char *f = (char *)pgm_read_word(r->URI);

Serial.println(f);

return r->func;
}
void setup() {
Serial.begin(9600);
while (!Serial) { }

Serial.println("started setup");
void (*fn)() = getRoute("sometest");
// will cause errors if called
//fn();
Serial.println("ended setup");
}

void loop() {
// put your main code here, to run repeatedly:

}

0

Решение

PROGMEM не так прост в использовании. И это может быть немного упрощено:

#include <avr/pgmspace.h>

struct Route {
void (*func)();
const char *URI;
};

void test1() {
Serial.println(F("Executed testfunc1")); // if you are using progmem, why not for string literals?
}

void test2() {
Serial.println(F("Executed testfunc2"));
}

const char route1URI[] PROGMEM = "/route1";
const char route2URI[] PROGMEM = "/route2";

const Route routingTable[] PROGMEM = {
{test1,route1URI},
{test2,route2URI}
};

void (*getRoute(char *URI))() {
Route r;
memcpy_P((void*)&r, routingTable, sizeof(r)); // read flash memory into the r space. (can be done by constructor too)

Serial.println((__FlashStringHelper*)r.URI); // it'll use progmem based print
// for comparing use: strcmp_P( URI, r.URI)

return r.func; // r.func is already pointer to the function
}

void setup() {
Serial.begin(57600);
while (!Serial) { }

Serial.println("started setup");
void (*fn)() = getRoute("sometest");
// will cause errors if called
//fn();
Serial.print((uint16_t)test1, HEX); Serial.print(' ');
Serial.print((uint16_t)test2, HEX); Serial.print(' ');
Serial.println((uint16_t)fn, HEX);

Serial.println("ended setup");
}

void loop() {
// put your main code here, to run repeatedly:

}

Я полагаю route1 а также route2 может вызвать все проблемы, так как он был использован для копирования в routingTable, Если вы инициализируете элементы routingTable как и я, это работает намного лучше. А также getRoute был сломан много

В любом случае, если у вас есть флэш-строка, вы также можете использовать String str {(__FlashStringHelper*)r.URI}; и затем используйте оператор сравнения: str == URI:

#include <avr/pgmspace.h>

// get size of array[]
template<typename T, int size> int GetArrLength(T(&)[size]){return size;}

struct Route {
void (*func)();
const char *URI;
};

void test1() {
Serial.println(F("Executed testfunc1")); // if you are using progmem, why not for string literals?
}

void test2() {
Serial.println(F("Executed testfunc2"));
}
void test3() {
Serial.println(F("Executed testfunc3"));
}

const char route1URI[] PROGMEM = "/route1";
const char route2URI[] PROGMEM = "/route2";
const char route3URI[] PROGMEM = "/route3";

const Route routingTable[] PROGMEM = {
{test1,route1URI},
{test2,route2URI},
{test3,route3URI}
};

void (*getRoute(char *URI))() {
for (int8_t i = 0; i < GetArrLength(routingTable); ++i) {
Route r;
memcpy_P((void*)&r, routingTable+i, sizeof(r)); // read flash memory into the r space. (can be done by constructor too)

String uri {(__FlashStringHelper*)r.URI};
if (uri == URI) {
return r.func; // r.func is already pointer to the function
}
}

return nullptr;
}

void setup() {
Serial.begin(57600);
while (!Serial) { }

Serial.println("started setup");
void (*fn)() = getRoute("/route3");
// will cause errors if called
//fn();
Serial.print((uint16_t)test1, HEX); Serial.print(' ');
Serial.print((uint16_t)test2, HEX); Serial.print(' ');
Serial.print((uint16_t)test3, HEX); Serial.print(' ');
Serial.println((uint16_t)fn, HEX);

Serial.println("ended setup");
}
1

Другие решения

char *f = (char *)pgm_read_word(r->URI);
Serial.println(f);

f указатель на массив символов в PROGMEM, но Serial.println не знает этого! В итоге он пытается прочитать строку из оперативной памяти, где это не так.

Ардуино Serial библиотека не поддерживает строки в PROGMEM. Вам нужно будет зациклить строку, печатая по одному символу за раз, использовать другую библиотеку или сохранить строку в ОЗУ.

0

Как указывает @KIIV, лучше указать Route непосредственно внутри декларации routingTable, В качестве альтернативного решения вы можете переопределить структуру Route в

typedef struct {
void (*func)();
char URI[16];  //adjust the size to your need
} Route;

Таким образом, читая оба URI а также function адрес из флеш можно сделать одним звонком memcpy_P, Полные коды:

typedef struct {
void (*func)();
char URI[16];  //adjust the size to your need
} Route;

void test1() {
Serial.println("Executed testfunc1");
}

void test2() {
Serial.println("Executed testfunc2");
}

const Route routingTable[] PROGMEM = {
{test1, "/route1"},
{test2, "/route2"}
};

void (*getRoute(char *URI, int idx))() {
Route r;
memcpy_P(&r, &routingTable[idx], sizeof(Route));

Serial.print(idx); Serial.println(". -----------------------------");
Serial.print("Route: "); Serial.println(r.URI);
Serial.print("fn address: "); Serial.println((uint16_t)r.func, HEX);
Serial.print("test1 address: "); Serial.println((uint16_t)test1, HEX);
Serial.print("test2 address: "); Serial.println((uint16_t)test2, HEX);

return r.func;
}

void setup() {
Serial.begin(9600);
while (!Serial) { }

Serial.println("started setup");
void (*fn)();

const int n = sizeof(routingTable) / sizeof(Route);
for (int i = 0; i < n; i++) {
fn = getRoute("sometest", i);
fn();
}
Serial.println("ended setup");
}

void loop() {
// put your main code here, to run repeatedly:
}
0
По вопросам рекламы [email protected]