Uděláme si program na prohledávání WiFi sítí, který bude skenovat a připojovat se automaticky ke známým sítím. Přihlašovací údaje (ssid, heslo, způsob authentizace a síťové schopnosti) si uložíme na vyhrazenou část vestavěné XIP flash paměti přímo na PicoW. Použijeme znalostí o zápisu na XIP flash Pica, kterou jsem již popisoval a otestoval. Tyto znalosti zkombinujeme je se znalostmi skenování sítí, které jsem popsal zde.

Nejprve si vytvoříme modul pro zápis údajů o známých Wifi sítích, tj. o takových sítích, na které se umíme připojit.

Program pro zápis známých Wifi sítí na XIP flash

Pro zápis přihlašovacích údajů si na Picu vyhradíme paměť 16kByte na konci XIP flash paměti. V CMakeLists.txt nám to zařídí na řádku 18. direktiva pico_set_linker_script. Do target_link_libraries přidáme knihovny hardware_flash a hardware_sync (řádky 24 a 25). Do target_link_options (na řádku 29) si přidáme výpis použití paměti (--print-memory-usage) pro kontrolu, že to máme správně,

nastaveni/CMakeLists.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico_w)
set (CMAKE_C_STANDARD 11)
set (CMAKE_CXX_STANDARD 17)

include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

set(PROJECT_NAME picow_persistent_write)
project(${PROJECT_NAME} C CXX ASM)

pico_sdk_init()

add_executable(${PROJECT_NAME}
    PersistentStorage.c
)
# nastaveni vyhrazeneho mista ve XIP flash pro konfiguracni data
pico_set_linker_script(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/memmap_custom.ld)

target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries( ${PROJECT_NAME}
    pico_stdlib
    pico_cyw43_arch_lwip_threadsafe_background
    hardware_flash
    hardware_sync
)

# volby pro linker
target_link_options( ${PROJECT_NAME} PRIVATE -Xlinker --print-memory-usage)

pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)
pico_add_extra_outputs(${PROJECT_NAME})

Skript pro linker

Na řádku 24 určujeme velikost vyhrazené paměti pro data.

nastaveni/memmap_custom.ld
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/* Based on GCC ARM embedded samples.
   Defines the following symbols for use by code:
    __exidx_start
    __exidx_end
    __etext
    __data_start__
    __preinit_array_start
    __preinit_array_end
    __init_array_start
    __init_array_end
    __fini_array_start
    __fini_array_end
    __data_end__
    __bss_start__
    __bss_end__
    __end__
    end
    __HeapLimit
    __StackLimit
    __StackTop
    __stack (== StackTop)
*/

__PERSISTENT_STORAGE_LEN = 16k ;

MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k - __PERSISTENT_STORAGE_LEN
    FLASH_PERSISTENT(rw) : ORIGIN = 0x10000000 + (2048k - __PERSISTENT_STORAGE_LEN) , LENGTH = __PERSISTENT_STORAGE_LEN
    RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
    SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
    SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}

ENTRY(_entry_point)

SECTIONS
{
    /* Second stage bootloader is prepended to the image. It must be 256 bytes big
       and checksummed. It is usually built by the boot_stage2 target
       in the Raspberry Pi Pico SDK
    */

    .flash_begin : {
        __flash_binary_start = .;
    } > FLASH

    .boot2 : {
        __boot2_start__ = .;
        KEEP (*(.boot2))
        __boot2_end__ = .;
    } > FLASH

    ASSERT(__boot2_end__ - __boot2_start__ == 256,
        "ERROR: Pico second stage bootloader must be 256 bytes in size")

    /* The second stage will always enter the image at the start of .text.
       The debugger will use the ELF entry point, which is the _entry_point
       symbol if present, otherwise defaults to start of .text.
       This can be used to transfer control back to the bootrom on debugger
       launches only, to perform proper flash setup.
    */

    .text : {
        __logical_binary_start = .;
        KEEP (*(.vectors))
        KEEP (*(.binary_info_header))
        __binary_info_header_end = .;
        KEEP (*(.reset))
        /* TODO revisit this now memset/memcpy/float in ROM */
        /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
         * FLASH ... we will include any thing excluded here in .data below by default */
        *(.init)
        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
        *(.fini)
        /* Pull all c'tors into .text */
        *crtbegin.o(.ctors)
        *crtbegin?.o(.ctors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
        *(SORT(.ctors.*))
        *(.ctors)
        /* Followed by destructors */
        *crtbegin.o(.dtors)
        *crtbegin?.o(.dtors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
        *(SORT(.dtors.*))
        *(.dtors)

        *(.eh_frame*)
        . = ALIGN(4);
    } > FLASH

    .rodata : {
        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
        . = ALIGN(4);
        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
        . = ALIGN(4);
    } > FLASH

    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > FLASH

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > FLASH
    __exidx_end = .;

    /* Machine inspectable binary information */
    . = ALIGN(4);
    __binary_info_start = .;
    .binary_info :
    {
        KEEP(*(.binary_info.keep.*))
        *(.binary_info.*)
    } > FLASH
    __binary_info_end = .;
    . = ALIGN(4);

    /* End of .text-like segments */
    __etext = .;


    .section_persisitent : {
        "ADDR_PERSISTENT" = .;  
    } > FLASH_PERSISTENT
    
   .ram_vector_table (COPY): {
        *(.ram_vector_table)
    } > RAM

    .data : {
        __data_start__ = .;
        *(vtable)

        *(.time_critical*)

        /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
        *(.text*)
        . = ALIGN(4);
        *(.rodata*)
        . = ALIGN(4);

        *(.data*)

        . = ALIGN(4);
        *(.after_data.*)
        . = ALIGN(4);
        /* preinit data */
        PROVIDE_HIDDEN (__mutex_array_start = .);
        KEEP(*(SORT(.mutex_array.*)))
        KEEP(*(.mutex_array))
        PROVIDE_HIDDEN (__mutex_array_end = .);

        . = ALIGN(4);
        /* preinit data */
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP(*(SORT(.preinit_array.*)))
        KEEP(*(.preinit_array))
        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(4);
        /* init data */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);

        . = ALIGN(4);
        /* finit data */
        PROVIDE_HIDDEN (__fini_array_start = .);
        *(SORT(.fini_array.*))
        *(.fini_array)
        PROVIDE_HIDDEN (__fini_array_end = .);

        *(.jcr)
        . = ALIGN(4);
        /* All data end */
        __data_end__ = .;
    } > RAM AT> FLASH

    .uninitialized_data (COPY): {
        . = ALIGN(4);
        *(.uninitialized_data*)
    } > RAM

    /* Start and end symbols must be word-aligned */
    .scratch_x : {
        __scratch_x_start__ = .;
        *(.scratch_x.*)
        . = ALIGN(4);
        __scratch_x_end__ = .;
    } > SCRATCH_X AT > FLASH
    __scratch_x_source__ = LOADADDR(.scratch_x);

    .scratch_y : {
        __scratch_y_start__ = .;
        *(.scratch_y.*)
        . = ALIGN(4);
        __scratch_y_end__ = .;
    } > SCRATCH_Y AT > FLASH
    __scratch_y_source__ = LOADADDR(.scratch_y);

    .bss  : {
        . = ALIGN(4);
        __bss_start__ = .;
        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
        *(COMMON)
        . = ALIGN(4);
        __bss_end__ = .;
    } > RAM

    .heap (COPY):
    {
        __end__ = .;
        end = __end__;
        *(.heap*)
        __HeapLimit = .;
    } > RAM

    /* .stack*_dummy section doesn't contains any symbols. It is only
     * used for linker to calculate size of stack sections, and assign
     * values to stack symbols later
     *
     * stack1 section may be empty/missing if platform_launch_core1 is not used */

    /* by default we put core 0 stack at the end of scratch Y, so that if core 1
     * stack is not used then all of SCRATCH_X is free.
     */
    .stack1_dummy (COPY):
    {
        *(.stack1*)
    } > SCRATCH_X
    .stack_dummy (COPY):
    {
        *(.stack*)
    } > SCRATCH_Y

    .flash_end : {
        __flash_binary_end = .;
    } > FLASH


    /* stack limit is poorly named, but historically is maximum heap ptr */
    __StackLimit = ORIGIN(RAM) + LENGTH(RAM);
    __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
    __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
    __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
    __StackBottom = __StackTop - SIZEOF(.stack_dummy);
    PROVIDE(__stack = __StackTop);

    /* Check if data + heap + stack exceeds RAM limit */
    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")

    ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
    /* todo assert on extra code */
}

Program pro zápis

Program pro zapsání dat na XIP flash se skládá ze dvou souborů flash_utils.h a PersistentStorage.c

Funkce getAddressPersistent() nám bude vracet ukazatel na počátek zapsaných dat.

nastaveni/flash_utils.h
#include <stdint.h>


inline uint32_t *getAddressPersistent() 
{
extern uint32_t ADDR_PERSISTENT[];
    return ADDR_PERSISTENT;
}

Data o známých Wifi sítích budeme ukládat jako záznamy dlouhé 128 bytů (RECORD_SIZE), ve formátu:

#define MOJE_VELIKOST 4*FLASH_SECTOR_SIZE  // FLASH_SECTOR_SIZE je 4096 byte, celkem 16kByte
#define RECORD_SIZE 128
char data[MOJE_VELIKOST] = {0};


char *recordk = &data[k*RECORD_SIZE]; // k je pořadí záznamu od 0 do 127
//
sprintf(recordk,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s",
    "jmenosite", '\0',          // název sítě
    "skutečnéheslo", '\0',      // heslo k síti
    "WPA2_MIXED_PSK",'\0',      // způsob authentizace
    "y",'\0',                   // síť umí IPv6 protokol
    "y"                         // síť umí IPv4 protokol
    );

Takto můžeme uložit údaje až o 128 sítích, v programu jich používám 8, zbylé místo si můžeme vyhradit na něco jiného.

PersistentStorage.c
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/* Ukládání dat Wifi připojení do FLASH paměti Pica
 * (c) Jirka Chráska 2025; <jirka@lixis.cz>
 *
 */

#include <stdio.h>
#include "pico/stdlib.h"
#include "flash_utils.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include <string.h>

const uint8_t *flash_target_content;

#define MOJE_VELIKOST 4*FLASH_SECTOR_SIZE
#define RECORD_SIZE 128

char data[MOJE_VELIKOST] = {0};


// tiskne buffer jako hexadecimální čísla
void print_buf(const uint8_t *buf, size_t len)
{
    for (size_t i = 0; i < len; ++i) {
        printf("%02x", buf[i]);
        if (i % 16 == 15)
            printf("\n");
        else
            printf(" ");
    }
}

// tiskne buffer jako znaky
void print_chars(const uint8_t *buf, size_t len)
{
    for (size_t i = 0; i < len; ++i) {
        printf("%c", buf[i]);
        if (i % 16 == 15)
            printf("\n");
        else
            printf(" ");
    }
}

int main()
{
    flash_target_content = (const uint8_t *) getAddressPersistent();
    uint32_t kam = PICO_FLASH_SIZE_BYTES - MOJE_VELIKOST;
    stdio_init_all();
    sleep_ms(1500);
    printf("Zápis dat dat na XIP flash...\nStiskni klavesu.\n");
    getchar();
    // konfigurace SSID, PASS, AUTH, IPv6, IPv4
    // každý záznam (record) má 128 byte
    char *record1 = &data[0*RECORD_SIZE];
    sprintf(record1,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "chorche", '\0', "xxxxxxxxx", '\0', "WPA2_MIXED_PSK",'\0', "y",'\0', "y");

    char *record2 = &data[1*RECORD_SIZE];
    sprintf(record2,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "chorche5", '\0', "yyyyyyyyy", '\0', "WPA_TKIP_PSK", '\0', "y", '\0', "y");

    char *record3 = &data[2*RECORD_SIZE];
    sprintf(record3,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "chorche6", '\0', "zzzzzzzzzz", '\0', "WPA_TKIP_PSK", '\0', "y", '\0', "y");

    char *record4 = &data[3*RECORD_SIZE];
    sprintf(record4,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "Repeater_663D4C", '\0', "wwwwwwwwwwwwwwwwww", '\0', "WPA2_MIXED_PSK", '\0', "y", '\0', "y");

    char *record5 = &data[4*RECORD_SIZE];
    sprintf(record5,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "sspvc-ucitel", '\0', "tttttttttt", '\0', "WPA_TKIP_PSK", '\0', "y", '\0', "y");

    char *record6 = &data[5*RECORD_SIZE];
    sprintf(record6,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "HEAD", '\0', "aaaaaaaa", '\0', "WPA2_MIXED_PSK", '\0', "n", '\0', "y");

    char *record7 = &data[6*RECORD_SIZE];
    sprintf(record7,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "chorche3", '\0', "mmmmmmmmmmm", '\0', "WPA_TKIP_PSK", '\0', "y", '\0', "y");

    char *record8 = &data[7*RECORD_SIZE];
    sprintf(record8,"SSID:%s%cPASS:%s%cAUTH:%s%cIPv6:%s%cIPv4:%s", "Internet_5C31_EXT", '\0', "Mi514laN", '\0', "WPA2_MIXED_PSK", '\0', "n", '\0', "y");


    printf("\nZapis do flashe.\n");
    printf("FLASH_MEM=%p FLASH_PAGE_SIZE=%d, FLASH_SECTOR_SIZE=%d, XIP_BASE=0x%p, kam=0x%x\n",getAddressPersistent(), FLASH_PAGE_SIZE, FLASH_SECTOR_SIZE, XIP_BASE, kam );
    sleep_ms(5000);

    printf("Erasing %d bytes of flash\n", MOJE_VELIKOST);
    printf("Writing %d bytes to flash\n", FLASH_PAGE_SIZE*4);
    // uložíme a zakážeme přerušení
    uint32_t ints = save_and_disable_interrupts();
    // vymažeme sektory flashe (4*4096 bytů )
    flash_range_erase(kam,MOJE_VELIKOST);
    // zapíšeme data do flashe
    flash_range_program(kam, data, FLASH_PAGE_SIZE*4);
    // obnovíme přerušení
    restore_interrupts(ints);
    // Přečteme, co jsme zapsali
    printf("Uložená síť 1: \"%s\"\n", (char *) flash_target_content+(0*128));
    print_chars(flash_target_content+(0*128), 128);
    printf("Uložená síť 2: \"%s\"\n", (char *) flash_target_content+(1*128));
    print_chars(flash_target_content+(1*128), 128);
    printf("Uložená síť 3: \"%s\"\n", (char *) flash_target_content+(2*128));
    print_chars(flash_target_content+(2*128), 128);
    printf("Uložená síť 4: \"%s\"\n", (char *) flash_target_content+(3*128));
    print_chars(flash_target_content+(3*128), 128);
    printf("Uložená síť 5: \"%s\"\n", (char *) flash_target_content+(4*128));
    print_chars(flash_target_content+(4*128), 128);
    printf("Uložená síť 6: \"%s\"\n", (char *) flash_target_content+(5*128));
    print_chars(flash_target_content+(5*128), 128);
    printf("Uložená síť 7: \"%s\"\n", (char *) flash_target_content+(6*128));
    print_chars(flash_target_content+(6*128), 128);
    printf("Uložená síť 8: \"%s\"\n", (char *) flash_target_content+(7*128));
    print_chars(flash_target_content+(7*128), 128);
    sleep_ms(2000);
    return 0;
}

Do Pica W si tato data nahrajeme jednou, a potom je již můžeme používat v dalších programech.

Sestavení programu
cd nastaveni
mkdir build
cd build
cmake ..
make -j4

Výpis programu vypadá zhruba takto (hesla jsou samozřejmě nepublikována):

Výpis z minicomu ( minicom -b 115200 -D /dev/ttyACM0 )
Zápis dat dat na XIP flash...
Stiskni klavesu.

Zapis do flashe.
FLASH_MEM=101FC000 FLASH_PAGE_SIZE=256, FLASH_SECTOR_SIZE=4096, XIP_BASE=0x10000000, kam=0x1fc000
Erasing 16384 bytes of flash
Writing 1024 bytes to flash
Ulozeny retezec 1: "SSID:chorche"
Ulozeny retezec 2: "SSID:chorche5"
Ulozeny retezec 3: "SSID:chorche6"
Ulozeny retezec 4: "SSID:Repeater_663D4C"
Ulozeny retezec 5: "SSID:sspvc-ucitel"
Ulozeny retezec 6: "SSID:HEAD"
Ulozeny retezec 7: "SSID:chorche3"
Ulozeny retezec 8: "SSID:Internet_5C31_EXT"
S S I D : c h o r c h e  P A S
S : x x x x x x x x x x x x  A
U T H : W P A 2 _ M I X E D _ P
S K  I P v 6 : y  I P v 4 : y




S S I D : c h o r c h e 5  P A
S S : y y y y y y y y y y y y y
y y y y y  A U T H : W P A _ T
K I P _ P S K  I P v 6 : y  I
P v 4 : y



S S I D : c h o r c h e 6  P A
S S : z z z z z z z z z z z z z
z z z z z z  A U T H : W P A _
T K I P _ P S K  I P v 6 : y
I P v 4 : y



S S I D : R e p e a t e r _ 6 6
3 D 4 C  P A S S : w w w w w w
w w w w w w w w w w w w  A U T
H : W P A 2 _ M I X E D _ P S K
 I P v 6 : y  I P v 4 : y



S S I D : s s p v c - u c i t e
l  P A S S : u u u u u u u u u
u u  A U T H : W P A _ T K I P
_ P S K  I P v 6 : y  I P v 4
: y



S S I D : H E A D  P A S S : p
p p p p p p p p p  A U T H : W
P A 2 _ M I X E D _ P S K  I P
v 6 : n  I P v 4 : y




S S I D : c h o r c h e 3  P A
S S : q q q q q q q q q q q q q
q q q q q  A U T H : W P A _ T
K I P _ P S K  I P v 6 : y  I
P v 4 : y



S S I D : I n t e r n e t _ 5 C
3 1 _ E X T  P A S S : c c c c
c c c c  A U T H : W P A 2 _ M
I X E D _ P S K  I P v 6 : n
I P v 4 : y

Program na skenování.

CMakeLists.txt je téměř stejné. Liší se jenom jménem projektu. Ve všech programech, kde hodláme používat již uložené údaje o Wifi připojení, bude CMakeLists.txt podobné.

Pro čtení přihlašovacích údajů máme vyhrazenou paměť 16kByte na konci XIP flash. V CMakeLists.txt nám to zařídí na řádku 18. direktiva pico_set_linker_script. Do target_link_libraries přidáme knihovny hardware_flash a hardware_sync (řádky 24 a 25). Do target_link_options (na řádku 29) si přidáme výpis použití paměti (--print-memory-usage) pro kontrolu, že to máme správně,

CMakeLists.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico_w)
set (CMAKE_C_STANDARD 11)
set (CMAKE_CXX_STANDARD 17)

include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

set(PROJECT_NAME picow_persistent)
project(${PROJECT_NAME} C CXX ASM)

pico_sdk_init()

add_executable(${PROJECT_NAME}
    wifi_scan.c
)
# nastaveni vyhrazeneho mista ve XIP flash pro konfiguracni data
pico_set_linker_script(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/memmap_custom.ld)

target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries( ${PROJECT_NAME}
    pico_stdlib
    pico_cyw43_arch_lwip_threadsafe_background
    hardware_flash
    hardware_sync
)

# volby pro linker
target_link_options( ${PROJECT_NAME} PRIVATE -Xlinker --print-memory-usage)


pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)
pico_add_extra_outputs(${PROJECT_NAME})

flash_utils.h je stejné.

flash_utils.h
#include <stdint.h>


inline uint32_t *getAddressPersistent() 
{
extern uint32_t ADDR_PERSISTENT[];
    return ADDR_PERSISTENT;
}

Konfigurace LWIP knihovny

lwipopts.h
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
#define _LWIPOPTS_EXAMPLE_COMMONH_H

// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)

// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#define MEM_SIZE 4000
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 1
#define LWIP_IPV4 1
#define LWIP_TCP 1
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 1
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0

#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif

#define ETHARP_DEBUG        LWIP_DBG_OFF
#define NETIF_DEBUG         LWIP_DBG_OFF
#define PBUF_DEBUG          LWIP_DBG_OFF
#define API_LIB_DEBUG       LWIP_DBG_OFF
#define API_MSG_DEBUG       LWIP_DBG_OFF
#define SOCKETS_DEBUG       LWIP_DBG_OFF
#define ICMP_DEBUG          LWIP_DBG_OFF
#define INET_DEBUG          LWIP_DBG_OFF
#define IP_DEBUG            LWIP_DBG_OFF
#define IP_REASS_DEBUG      LWIP_DBG_OFF
#define RAW_DEBUG           LWIP_DBG_OFF
#define MEM_DEBUG           LWIP_DBG_OFF
#define MEMP_DEBUG          LWIP_DBG_OFF
#define SYS_DEBUG           LWIP_DBG_OFF
#define TCP_DEBUG           LWIP_DBG_OFF
#define TCP_INPUT_DEBUG     LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG    LWIP_DBG_OFF
#define TCP_RTO_DEBUG       LWIP_DBG_OFF
#define TCP_CWND_DEBUG      LWIP_DBG_OFF
#define TCP_WND_DEBUG       LWIP_DBG_OFF
#define TCP_FR_DEBUG        LWIP_DBG_OFF
#define TCP_QLEN_DEBUG      LWIP_DBG_OFF
#define TCP_RST_DEBUG       LWIP_DBG_OFF
#define UDP_DEBUG           LWIP_DBG_OFF
#define TCPIP_DEBUG         LWIP_DBG_OFF
#define PPP_DEBUG           LWIP_DBG_OFF
#define SLIP_DEBUG          LWIP_DBG_OFF
#define DHCP_DEBUG          LWIP_DBG_OFF

#endif /* __LWIPOPTS_H__ */

Hlavní program funguje tak, že si nejprve přečteme uložené přístupové údaje o Wifi připojení na uložíme si je do jednoduchého seznamu (funkce napln_zname_wifi()).

Při skenování sítí (ve funkci scan_result() si budeme ke každé známé síti zaznamenávat sílu signálu (rssi) a po skončení skenu se připojíme se k síti s nejlepším signálem.

wifi_scan.c
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* Pico W cvičení 12: skenování sítí a nalezení sítí, ke ktrým se můžeme připojit
 * (c) Jirka Chráska 2025; <jirka@lixis.cz
 * BSD licence 3 clause
 */

#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "flash_utils.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include <string.h>

const uint8_t *flash_target_content; 

// struktura údajů o známé síti
typedef struct wifi_zname {
        char* ssid;     // jméno sítě
        char* pass;     // heslo v otevřeném textu
        char* auth;     // způsob autorizace
        char* ipv6;     // umí síť IPv6 (y/n)
        char* ipv4;     // umí síť IPv4 (y/n)
        int   rssi;     // aktuální síla signálu
        struct wifi_zname * next; // ukazatel na další prvek seznamu
} ZNAME_WIFI;

// ukazatel na počátek seznamu známých sítí
ZNAME_WIFI* zwifi = NULL;

// vytvoří seznam známých Wifi, ke kterým se můžeme připojit 
void napln_zname_wifi(void)
{
char *wifidata; // pomocný ukazatel do XIP flash
ZNAME_WIFI *zw;

    flash_target_content = (const uint8_t *) getAddressPersistent();
    for( int i=0; i<8; i++ ){
        wifidata = (char*) flash_target_content+(i*128);
        zw = malloc(sizeof(ZNAME_WIFI));
        if(zw != NULL) {
            zw->ssid = wifidata+5;
            // printf("%p ssid='%s' ", wifidata, zw->ssid);
            int len = strlen(zw->ssid)+6;
            zw->pass = wifidata+len+5;
            // printf("%p pass='%s' ", wifidata, zw->pass);
            len += strlen(zw->pass)+6;
            zw->auth = wifidata+len+5;
            // printf("%p auth='%s' ", wifidata, zw->auth);
            len += strlen(zw->auth)+6;
            zw->ipv6 = wifidata+len+5;
            //printf("%p ipv6='%s' ", wifidata, zw->ipv6);
            len += strlen(zw->ipv6)+6;
            zw->ipv4 = wifidata+len+5;
            // printf("%p ipv4='%s'\n", wifidata, zw->ipv4);
            // počáteční signál -120 dBm 
            zw->rssi = -120;
            if( zwifi != NULL ) { // v seznamu už něco máme
                zw->next = zwifi;
            } else {              // vkládáme první prvek (v seznamu bude poslední) 
                zw->next = NULL;
            }
            zwifi = zw;
        } else {
            printf("Nedostatek paměti.\n");
            break;
        }
    }
}

// najde v seznamu známou WiFi podle ssid
ZNAME_WIFI * najdi_wifi(const char *ssid) 
{
    for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
        if(strcmp(zw->ssid, ssid) == 0 ) {
            return zw;
        }
    }
return NULL;
}

// najde v seznamu Wifi s nejlepším signálem
ZNAME_WIFI *najdi_nejvetsi_rssi( void )
{
int rssi = -120;
ZNAME_WIFI *res = NULL;
    for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
        if(zw->rssi > rssi) {
            res = zw;
            rssi = zw->rssi;
        }
    }
return res;
}

// uvolní seznam známých wifi
void zrus_zname_wifi( void )
{
    for( ZNAME_WIFI *ptr=zwifi; ptr!=NULL;  ) {
        ZNAME_WIFI *ptr2 = ptr;
        ptr = ptr->next;
        free(ptr2);
    }
}

static int scan_result(void *env, const cyw43_ev_scan_result_t *result)
{
    if (result)
    {
        ZNAME_WIFI *znam = najdi_wifi( result->ssid );
        if( znam != NULL) {
            znam->rssi = result->rssi;
            printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
               result->ssid, result->rssi, result->channel,
               result->bssid[0], result->bssid[1], result->bssid[2],
               result->bssid[3], result->bssid[4], result->bssid[5],
               (result->auth_mode == 5) ? "WPA2_MIXED_PSK" : 
               (result->auth_mode == 7) ? "WPA_TKIP_PSK" : 
               (result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
               "yes" );
        } else {
            printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
               result->ssid, result->rssi, result->channel,
               result->bssid[0], result->bssid[1], result->bssid[2],
               result->bssid[3], result->bssid[4], result->bssid[5],
               (result->auth_mode == 5) ? "WPA2_MIXED_PSK" : 
               (result->auth_mode == 7) ? "WPA_TKIP_PSK" : 
               (result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
               "no" );
        }
    }
    return 0;
}



int main()
{
    stdio_init_all();
    sleep_ms(2000);
    printf("AP scan...\n");
    
    napln_zname_wifi();
    
    // inicializace cyw43 chipu
    if (cyw43_arch_init())
    {
        printf("failed to initialise\n");
        return 1;
    }

    // Picow nastavíme jako stanici
    cyw43_arch_enable_sta_mode();
    cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, CYW43_COUNTRY_CZECH_REPUBLIC);
    cyw43_wifi_scan_options_t scan_options = {0};
    // začínáme skenovat
    int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);

    while (true)
    {
        if (!cyw43_wifi_scan_active(&cyw43_state))
            break;
        sleep_ms(1000);
        printf("Skenujeme ... \n");
    }
    printf("Scan je hotov.\n");
    // najdeme nejlepší signál mezi známými WiFi
    ZNAME_WIFI *zw = najdi_nejvetsi_rssi();
    if( zw != NULL ) {
        printf("Nejlepší signál %d má ssid %s sem se připojíme.\n", zw->rssi, zw->ssid);
    } else {
        printf("Nemohu se připojit, žádná známá Wifi není v dosahu.\n");
    }
    // úklid seznamu (uvolnění paměti)
    zrus_zname_wifi();
    cyw43_arch_deinit();
    return 0;
}
Výpis z minicomu (minicom -b 115200 -D /dev/ttyACM0
ssid: chorche5        rssi:  -43 chan:   3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5        rssi:  -42 chan:   3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5        rssi:  -44 chan:   3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5        rssi:  -44 chan:   3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -59 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -60 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -59 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -59 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -59 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -58 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -58 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -59 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -60 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -60 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -59 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -61 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -55 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche         rssi:  -76 chan:  11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche6        rssi:  -58 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6        rssi:  -56 chan:   8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche         rssi:  -72 chan:  11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche         rssi:  -73 chan:  11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche         rssi:  -75 chan:  11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: Turbotrabant    rssi:  -92 chan:  12 mac: 0c:9d:92:af:05:a0 sec: WPA2_MIXED_PSK known: no
ssid: chorche         rssi:  -73 chan:  11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche         rssi:  -75 chan:  11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
Skenujeme ...
Scan je hotov.
Nejlepší signál -44 má ssid chorche5 sem se připojíme.