2016-10-05 2 views
0

Je travaille sur un petit serveur HTTP. Je construis un routeur et comme il pouvait y avoir pas mal de routes, je voulais les mettre dans la mémoire flash pour ne pas avoir à utiliser la précieuse SRAM. Cependant soit je ne comprends pas quelque chose correctement ou quelque chose d'étrange se passe puisque je n'arrive pas à lire mes données stockées depuis le flash.Le progmem Arduino lit les données brouillées

J'ai une structure qui contient un pointeur de fonction et un pointeur char. Je veux stocker un tableau de ces structures dans Flash et les relire. Cependant, avec une petite impression de débogage, je peux voir que je ne peux pas lire correctement le pointeur de char. Il imprime des déchets sur le port série.

Voici un petit exemple.

#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: 

} 

Répondre

1

Le PROGMEM n'est pas si simple à utiliser. Et il peut être peu simplifié:

#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: 

} 

Je suppose que route1 et route2 pourrait causer tous les problèmes comme il a été utilisé pour la copie dans le routingTable. Si vous initialisez des éléments de routingTable comme je l'ai fait, cela fonctionne beaucoup mieux. Et aussi getRoute a été cassé beaucoup.

Quoi qu'il en soit, si vous avez la chaîne de flash, vous pouvez également utiliser String str {(__FlashStringHelper*)r.URI}; puis utilisez comparer l'opérateur: 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"); 
} 
0
char *f = (char *)pgm_read_word(r->URI); 
Serial.println(f); 

f est un pointeur sur un tableau de caractères dans PROGMEM, mais Serial.println ne sait pas! Il finit par essayer de lire la chaîne de la RAM, où elle ne l'est pas.

La bibliothèque Arduino Serial ne semble pas prendre en charge les chaînes dans PROGMEM. Vous devrez faire une boucle sur la chaîne pour imprimer un caractère à la fois, utiliser une autre bibliothèque ou stocker la chaîne dans la RAM.

0

Comme indiqué par @KIIV, il est préférable de préciser Route directement dans la déclaration de routingTable. En tant que solution alternative, vous pouvez redéfinir la struct Route à

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

De cette façon, la lecture à la fois URI et function adresse de la mémoire flash peut être fait par simple appel à memcpy_P. Les codes complets:

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

Actuellement, mon URI prend en charge jusqu'à 254 caractères, donc ce serait vraiment un retour en arrière. De plus, cela gaspillerait de la mémoire sur n'importe quelle URL de moins de 15 caractères. –

+0

Vous ne l'avez pas mentionné dans la question que votre URI sera aussi longue. Vous avez raison, cette approche gaspillera de la mémoire, mais si l'ordre est de plusieurs octets, je pense que c'est acceptable. Dans votre cas, la réponse @KIIV est meilleure. – putu