
ST67W611M1 T01 實作
開發環境與程式分工
| 元件 | 設定 |
|---|---|
| Host MCU | STM32H7R7 |
| Wi-Fi Module | ST67W611M1 |
| 網路架構 | T01(LwIP / TLS in module) |
| Middleware | X-CUBE-ST67W61 v1.3.0 |
| RTOS | FreeRTOS |
| Host – NCP 介面 | SPI,搭配必要控制 / 中斷 GPIO |
| Wi-Fi 帳密 | 範例由 app_wifi_config.h 提供 WIFI_SSID、WIFI_PASSWORD |
Appli/
├── Appli/App/
│ ├── main_app.c // Module Init、Scan、整體測試順序
│ └── wifi_connection.c // 指定 SSID / BSSID,連線並確認 IP
├── Core/Inc/
│ └── app_wifi_config.h // 測試設定(不公開原始帳密)
└── ST67W6X/Target/
└── spi_port.c // Host 與 Wi-Fi 模組的 SPI/IRQ 板級介面
Note:範例程式使用 LoggingInit()、LogInfo() 等專案 logging wrapper;複製到官方範例時,要將 logger 替換成自己的 UART logger 或原專案 logging
T01 模組初始化流程
FreeRTOS Task 啟動
↓
註冊 Wi-Fi / Network / Error callback
↓
確認 NCP Reset / CHIP_EN / SPI / IRQ 接線與啟動時序
↓
W6X_Init()
↓
W6X_SetPowerMode(0) [教學先關閉低功耗]
↓
W6X_WiFi_Init()
↓
W6X_Net_Init() [T01 使用]
↓
掃描並連線
初始化關鍵段落
#include "w6x_api.h"
#include "FreeRTOS.h"
#include "task.h"
#include "logging.h"
static void WifiEvent(W6X_event_id_t event, void *args)
{
if (event == W6X_WIFI_EVT_DISCONNECTED_ID)
LogWarn("[WIFI] disconnected\n");
if (event == W6X_WIFI_EVT_REASON_ID && args)
LogWarn("[WIFI] reason=%s\n", W6X_WiFi_ReasonToStr(args));
}
static void NetEvent(W6X_event_id_t event, void *args)
{
(void)event;
(void)args;
}
static void DriverError(W6X_Status_t status, const char *function)
{
LogError("[NCP] %s status=%ld\n", function, (long)status);
}
static int T01_ModuleInit(void)
{
W6X_App_Cb_t cb = {0};
cb.APP_wifi_cb = WifiEvent;
cb.APP_net_cb = NetEvent;
cb.APP_error_cb = DriverError;
if (W6X_RegisterAppCb(&cb) != W6X_STATUS_OK) return -1;
/* 板級 NCP reset / SPI / IRQ 必須已完成初始化。 */
if (W6X_Init() != W6X_STATUS_OK) return -1;
if (W6X_SetPowerMode(0U) != W6X_STATUS_OK) return -1;
if (W6X_WiFi_Init() != W6X_STATUS_OK) return -1;
if (W6X_Net_Init() != W6X_STATUS_OK) return -1;
LogInfo("[WIFI] T01 module init completed\n");
return 0;
}
Note:T01 專案必須配合 mission_t01 firmware,否則 W6X_Init() 會檢查到架構不相符
掃描附近 Wi-Fi
使用 W6X_WiFi_Scan();掃描結果由 callback 非同步送回,所以不能呼叫完成後就假設列表立即可用
static volatile uint32_t scan_done;
static volatile int32_t scan_status;
static W6X_WiFi_Ap_t scan_aps[10];
static uint32_t scan_count;
static void CopyScanSsid(char *ssid, const W6X_WiFi_Ap_t *ap)
{
memcpy(ssid, ap->SSID, sizeof(ap->SSID));
ssid[sizeof(ap->SSID)-1U] = '\0';
for (char *p = ssid; *p; ++p) if ((unsigned char)*p < 0x20U || *p == 0x7f) *p = '?';
}
static void ScanResult(int32_t status, W6X_WiFi_Scan_Result_t *result)
{
/* The driver owns result/AP. Copy before returning; print in the app task. */
scan_count = 0U;
scan_status = status;
if (status == 0 && result && result->AP) {
scan_count = result->Count > 10U ? 10U : result->Count;
memcpy(scan_aps, result->AP, scan_count * sizeof(scan_aps[0]));
}
__DMB();
scan_done = 1U;
}
static int ScanOnce(void)
{
W6X_WiFi_Scan_Opts_t options = {0};
options.MaxCnt = 10U;
scan_done = 0U;
scan_status = -1;
LogInfo("[STEP1] scan start (max 10)\n");
if (W6X_WiFi_Scan(&options, ScanResult) != W6X_STATUS_OK) return 0;
uint32_t start = HAL_GetTick();
while (!scan_done) {
if ((uint32_t)(HAL_GetTick() - start) >= 60000U) return 0;
vTaskDelay(pdMS_TO_TICKS(20U));
}
__DMB();
if (scan_status != 0) return 0;
LogInfo("\n========== Wi-Fi SSID list (%lu found) ==========\n", (unsigned long)scan_count);
for (uint32_t i = 0; i < scan_count; ++i) {
char ssid[sizeof(scan_aps[i].SSID)];
CopyScanSsid(ssid, &scan_aps[i]);
LogInfo("%lu. %s\n", (unsigned long)(i+1U), ssid[0] ? ssid : "<hidden SSID>");
}
if (scan_count == 0U) LogInfo("No Wi-Fi networks found.\n");
LogInfo("========== End Wi-Fi SSID list ==========\n\n");
for (uint32_t i = 0; i < scan_count; ++i) {
const W6X_WiFi_Ap_t *ap = &scan_aps[i];
char ssid[sizeof(ap->SSID)];
CopyScanSsid(ssid, ap);
LogInfo("[AP %lu] SSID=%s RSSI=%d ch=%u sec=%u BSSID=%02X:%02X:%02X:%02X:%02X:%02X\n",
(unsigned long)(i+1U), ssid, ap->RSSI, ap->Channel, ap->Security,
ap->MAC[0],ap->MAC[1],ap->MAC[2],ap->MAC[3],ap->MAC[4],ap->MAC[5]);
}
LogInfo("[STEP1] scan done count=%lu\n", (unsigned long)scan_count);
return 1;
}
Note: MaxCnt = 10 是這份教學的應用掃描數量設定,不是模組硬體最多只能掃描 10 個 AP
Wi-Fi SSID 連線
掃描後,先比對 SSID;若同名 AP 多台,範例會選擇掃描結果中 RSSI 較高、MAC 有效的 BSSID,再把 MAC 寫進 W6X_WiFi_Connect_Opts_t。掃描結果找不到目標時,則保留只用 SSID 嘗試連線的備援路徑wifi_connection.c,其中 WIFI_SSID / WIFI_PASSWORD 應由專案自己的設定檔提供
static const W6X_WiFi_Ap_t *FindTarget(const W6X_WiFi_Ap_t *aps, uint32_t count)
{
const W6X_WiFi_Ap_t *best = NULL;
if (!aps) return NULL;
for (uint32_t i = 0U; i < count; ++i) {
const W6X_WiFi_Ap_t *ap = &aps[i];
uint8_t any = 0U;
for (unsigned j = 0U; j < sizeof(ap->MAC); ++j) any |= ap->MAC[j];
if (!any || (ap->MAC[0] & 1U) ||
strncmp((const char *)ap->SSID, WIFI_SSID, sizeof(ap->SSID))) continue;
if (!best || ap->RSSI > best->RSSI) best = ap;
}
return best;
}
bool APP_HotspotIsReady(void)
{
W6X_WiFi_StaStateType_e state;
W6X_WiFi_Connect_t info = {0};
uint8_t ip[4], gw[4], mask[4];
return W6X_WiFi_Station_GetState(&state, &info) == W6X_STATUS_OK &&
state == W6X_WIFI_STATE_STA_GOT_IP && !strncmp((const char *)info.SSID, WIFI_SSID, sizeof(info.SSID)) &&
W6X_Net_Station_GetIPAddress(ip, gw, mask) == W6X_STATUS_OK &&
ip[0] != 0U && ip[0] != 127U && ip[0] < 224U;
}
bool APP_ConnectHotspotOnce(const W6X_WiFi_Ap_t *aps, uint32_t count)
{
W6X_WiFi_Connect_Opts_t options = {0};
_Static_assert(sizeof(WIFI_SSID) <= sizeof(options.SSID), "Fixed SSID exceeds SDK capacity");
_Static_assert(sizeof(WIFI_PASSWORD) <= sizeof(options.Password), "Fixed password exceeds SDK capacity");
strncpy((char *)options.SSID, WIFI_SSID, sizeof(options.SSID)-1U);
strncpy((char *)options.Password, WIFI_PASSWORD, sizeof(options.Password)-1U);
options.Reconnection_interval = 0U;
/* Zero means unlimited attempts in this SDK. Match BWIFI's bounded setting. */
options.Reconnection_nb_attempts = 1U;
const W6X_WiFi_Ap_t *target = FindTarget(aps, count);
LogInfo("[STEP2] connecting SSID=%s\n", WIFI_SSID);
if (target) {
memcpy(options.MAC, target->MAC, sizeof(options.MAC));
LogInfo("[STEP2] scanned target BSSID=%02X:%02X:%02X:%02X:%02X:%02X ch=%u RSSI=%d\n",
target->MAC[0], target->MAC[1], target->MAC[2], target->MAC[3], target->MAC[4], target->MAC[5],
target->Channel, target->RSSI);
} else {
LogInfo("[STEP2] target not in retained scan results; SSID-only discovery\n");
}
W6X_Status_t status = W6X_WiFi_Connect(&options);
volatile uint8_t *wipe = (volatile uint8_t *)&options;
for (unsigned i=0; i<sizeof(options); ++i) wipe[i]=0;
if (status != W6X_STATUS_OK) {
LogError("[STEP2] connect command failed status=%ld\n", (long)status);
return false;
}
uint32_t start = HAL_GetTick();
while ((uint32_t)(HAL_GetTick()-start) < APP_WIFI_CONNECT_TIMEOUT_MS) {
if (APP_HotspotIsReady()) {
uint8_t ip[4], gw[4], mask[4];
if (W6X_Net_Station_GetIPAddress(ip, gw, mask) != W6X_STATUS_OK) return false;
LogInfo("[STEP2] connected IP=%u.%u.%u.%u\n", ip[0],ip[1],ip[2],ip[3]);
return true;
}
vTaskDelay(pdMS_TO_TICKS(250U));
}
LogError("[STEP2] no matching SSID/GOT_IP within %lu ms\n", (unsigned long)APP_WIFI_CONNECT_TIMEOUT_MS);
return false;
}
判斷真的連上網路
收到 AP 連線成功事件不等於能上傳到網際網路。 至少需要確認三件事:
- Station state 是
W6X_WIFI_STATE_STA_GOT_IP。 - 連上的 SSID 符合預期;若需強制指定基地台,另外檢查 BSSID。
W6X_Net_Station_GetIPAddress()能讀出有效的 IP;再用 DNS / socket 連線來確認 Internet 可用
