firmware: WiFi AP + UDP transport — parallel to BLE, same protocol
This commit is contained in:
parent
7570510491
commit
201d9881ce
|
|
@ -1,3 +1,3 @@
|
|||
idf_component_register(SRCS "eis4.c" "eis.c" "echem.c" "ble.c" "temp.c" "refs.c"
|
||||
idf_component_register(SRCS "eis4.c" "eis.c" "echem.c" "ble.c" "wifi_transport.c" "temp.c" "refs.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES ad5941 ad5941_port bt nvs_flash)
|
||||
REQUIRES ad5941 ad5941_port bt nvs_flash esp_wifi esp_netif esp_event)
|
||||
|
|
|
|||
49
main/ble.c
49
main/ble.c
|
|
@ -1,4 +1,5 @@
|
|||
#include "ble.h"
|
||||
#include "wifi_transport.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
|
@ -100,7 +101,7 @@ static void encode_u16(uint16_t val, uint8_t *out)
|
|||
out[2] = p[1] & 0x7F;
|
||||
}
|
||||
|
||||
static float decode_float(const uint8_t *d)
|
||||
float ble_decode_float(const uint8_t *d)
|
||||
{
|
||||
uint8_t b[4];
|
||||
b[0] = d[1] | ((d[0] & 1) << 7);
|
||||
|
|
@ -112,7 +113,7 @@ static float decode_float(const uint8_t *d)
|
|||
return v;
|
||||
}
|
||||
|
||||
static uint16_t decode_u16(const uint8_t *d)
|
||||
uint16_t ble_decode_u16(const uint8_t *d)
|
||||
{
|
||||
uint8_t b[2];
|
||||
b[0] = d[1] | ((d[0] & 1) << 7);
|
||||
|
|
@ -135,9 +136,9 @@ static void parse_one_sysex(const uint8_t *midi, uint16_t mlen)
|
|||
switch (cmd.type) {
|
||||
case CMD_SET_SWEEP:
|
||||
if (mlen < 16) return;
|
||||
cmd.sweep.freq_start = decode_float(&midi[3]);
|
||||
cmd.sweep.freq_stop = decode_float(&midi[8]);
|
||||
cmd.sweep.ppd = decode_u16(&midi[13]);
|
||||
cmd.sweep.freq_start = ble_decode_float(&midi[3]);
|
||||
cmd.sweep.freq_stop = ble_decode_float(&midi[8]);
|
||||
cmd.sweep.ppd = ble_decode_u16(&midi[13]);
|
||||
break;
|
||||
case CMD_SET_RTIA:
|
||||
if (mlen < 4) return;
|
||||
|
|
@ -153,37 +154,37 @@ static void parse_one_sysex(const uint8_t *midi, uint16_t mlen)
|
|||
break;
|
||||
case CMD_START_LSV:
|
||||
if (mlen < 19) return;
|
||||
cmd.lsv.v_start = decode_float(&midi[3]);
|
||||
cmd.lsv.v_stop = decode_float(&midi[8]);
|
||||
cmd.lsv.scan_rate = decode_float(&midi[13]);
|
||||
cmd.lsv.v_start = ble_decode_float(&midi[3]);
|
||||
cmd.lsv.v_stop = ble_decode_float(&midi[8]);
|
||||
cmd.lsv.scan_rate = ble_decode_float(&midi[13]);
|
||||
cmd.lsv.lp_rtia = midi[18];
|
||||
break;
|
||||
case CMD_START_AMP:
|
||||
if (mlen < 19) return;
|
||||
cmd.amp.v_hold = decode_float(&midi[3]);
|
||||
cmd.amp.interval_ms = decode_float(&midi[8]);
|
||||
cmd.amp.duration_s = decode_float(&midi[13]);
|
||||
cmd.amp.v_hold = ble_decode_float(&midi[3]);
|
||||
cmd.amp.interval_ms = ble_decode_float(&midi[8]);
|
||||
cmd.amp.duration_s = ble_decode_float(&midi[13]);
|
||||
cmd.amp.lp_rtia = midi[18];
|
||||
break;
|
||||
case CMD_START_CL:
|
||||
if (mlen < 34) return;
|
||||
cmd.cl.v_cond = decode_float(&midi[3]);
|
||||
cmd.cl.t_cond_ms = decode_float(&midi[8]);
|
||||
cmd.cl.v_free = decode_float(&midi[13]);
|
||||
cmd.cl.v_total = decode_float(&midi[18]);
|
||||
cmd.cl.t_dep_ms = decode_float(&midi[23]);
|
||||
cmd.cl.t_meas_ms = decode_float(&midi[28]);
|
||||
cmd.cl.v_cond = ble_decode_float(&midi[3]);
|
||||
cmd.cl.t_cond_ms = ble_decode_float(&midi[8]);
|
||||
cmd.cl.v_free = ble_decode_float(&midi[13]);
|
||||
cmd.cl.v_total = ble_decode_float(&midi[18]);
|
||||
cmd.cl.t_dep_ms = ble_decode_float(&midi[23]);
|
||||
cmd.cl.t_meas_ms = ble_decode_float(&midi[28]);
|
||||
cmd.cl.lp_rtia = midi[33];
|
||||
break;
|
||||
|
||||
case CMD_START_PH:
|
||||
if (mlen < 8) return;
|
||||
cmd.ph.stabilize_s = decode_float(&midi[3]);
|
||||
cmd.ph.stabilize_s = ble_decode_float(&midi[3]);
|
||||
break;
|
||||
case CMD_START_CLEAN:
|
||||
if (mlen < 13) return;
|
||||
cmd.clean.v_mv = decode_float(&midi[3]);
|
||||
cmd.clean.duration_s = decode_float(&midi[8]);
|
||||
cmd.clean.v_mv = ble_decode_float(&midi[3]);
|
||||
cmd.clean.duration_s = ble_decode_float(&midi[8]);
|
||||
break;
|
||||
case CMD_START_SWEEP:
|
||||
case CMD_GET_CONFIG:
|
||||
|
|
@ -599,6 +600,9 @@ static void host_task(void *param)
|
|||
|
||||
static int send_sysex(const uint8_t *sysex, uint16_t len)
|
||||
{
|
||||
/* UDP: raw SysEx to all WiFi clients */
|
||||
wifi_send_sysex(sysex, len);
|
||||
|
||||
if (conn_count == 0)
|
||||
return -1;
|
||||
|
||||
|
|
@ -675,6 +679,11 @@ int ble_recv_command(BleCommand *cmd, uint32_t timeout_ms)
|
|||
return xQueueReceive(cmd_queue, cmd, ticks) == pdTRUE ? 0 : -1;
|
||||
}
|
||||
|
||||
void ble_push_command(const BleCommand *cmd)
|
||||
{
|
||||
xQueueSend(cmd_queue, cmd, 0);
|
||||
}
|
||||
|
||||
int ble_send_sweep_start(uint32_t num_points, float freq_start, float freq_stop)
|
||||
{
|
||||
uint8_t sx[20];
|
||||
|
|
|
|||
|
|
@ -68,6 +68,13 @@ void ble_wait_for_connection(void);
|
|||
/* blocking receive from command queue */
|
||||
int ble_recv_command(BleCommand *cmd, uint32_t timeout_ms);
|
||||
|
||||
/* push a parsed command onto the shared queue (used by wifi transport) */
|
||||
void ble_push_command(const BleCommand *cmd);
|
||||
|
||||
/* 7-bit MIDI decode helpers (shared with wifi transport) */
|
||||
float ble_decode_float(const uint8_t *d);
|
||||
uint16_t ble_decode_u16(const uint8_t *d);
|
||||
|
||||
/* outbound: EIS */
|
||||
int ble_send_sweep_start(uint32_t num_points, float freq_start, float freq_stop);
|
||||
int ble_send_eis_point(uint16_t index, const EISPoint *pt);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@
|
|||
#include "eis.h"
|
||||
#include "echem.h"
|
||||
#include "ble.h"
|
||||
#include "wifi_transport.h"
|
||||
#include "temp.h"
|
||||
#include "refs.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define AD5941_EXPECTED_ADIID 0x4144
|
||||
|
|
@ -55,8 +58,12 @@ void app_main(void)
|
|||
eis_load_open_cal();
|
||||
temp_init();
|
||||
|
||||
esp_netif_init();
|
||||
esp_event_loop_create_default();
|
||||
|
||||
esp_log_level_set("NimBLE", ESP_LOG_WARN);
|
||||
ble_init();
|
||||
wifi_transport_init();
|
||||
printf("Waiting for BLE connection...\n");
|
||||
ble_wait_for_connection();
|
||||
ble_send_config(&cfg);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,269 @@
|
|||
#include "wifi_transport.h"
|
||||
#include "ble.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#define WIFI_SSID "EIS4"
|
||||
#define WIFI_PASS "eis4data"
|
||||
#define WIFI_CHANNEL 1
|
||||
#define WIFI_MAX_CONN 2
|
||||
|
||||
#define UDP_PORT 5941
|
||||
#define UDP_BUF_SIZE 128
|
||||
#define MAX_UDP_CLIENTS 4
|
||||
#define CLIENT_TIMEOUT_MS 30000
|
||||
|
||||
static int udp_sock = -1;
|
||||
|
||||
static struct {
|
||||
struct sockaddr_in addr;
|
||||
TickType_t last_seen;
|
||||
bool active;
|
||||
} clients[MAX_UDP_CLIENTS];
|
||||
|
||||
static int client_count;
|
||||
|
||||
static void client_touch(const struct sockaddr_in *addr)
|
||||
{
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
|
||||
for (int i = 0; i < client_count; i++) {
|
||||
if (clients[i].addr.sin_addr.s_addr == addr->sin_addr.s_addr &&
|
||||
clients[i].addr.sin_port == addr->sin_port) {
|
||||
clients[i].last_seen = now;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (client_count < MAX_UDP_CLIENTS) {
|
||||
clients[client_count].addr = *addr;
|
||||
clients[client_count].last_seen = now;
|
||||
clients[client_count].active = true;
|
||||
client_count++;
|
||||
printf("UDP: client added (%d/%d)\n", client_count, MAX_UDP_CLIENTS);
|
||||
}
|
||||
}
|
||||
|
||||
static void clients_expire(void)
|
||||
{
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
TickType_t timeout = pdMS_TO_TICKS(CLIENT_TIMEOUT_MS);
|
||||
|
||||
for (int i = 0; i < client_count; ) {
|
||||
if ((now - clients[i].last_seen) > timeout) {
|
||||
clients[i] = clients[--client_count];
|
||||
printf("UDP: client expired (%d/%d)\n", client_count, MAX_UDP_CLIENTS);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_udp_sysex(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
/* raw SysEx: F0 7D CMD ...payload F7 */
|
||||
if (len < 3 || data[0] != 0xF0 || data[1] != 0x7D)
|
||||
return;
|
||||
|
||||
/* find F7 terminator */
|
||||
uint16_t end = 0;
|
||||
for (uint16_t i = 2; i < len; i++) {
|
||||
if (data[i] == 0xF7) { end = i; break; }
|
||||
}
|
||||
if (!end) return;
|
||||
|
||||
BleCommand cmd;
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.type = data[2];
|
||||
|
||||
switch (cmd.type) {
|
||||
case CMD_SET_SWEEP:
|
||||
if (len < 16) return;
|
||||
cmd.sweep.freq_start = ble_decode_float(&data[3]);
|
||||
cmd.sweep.freq_stop = ble_decode_float(&data[8]);
|
||||
cmd.sweep.ppd = ble_decode_u16(&data[13]);
|
||||
break;
|
||||
case CMD_SET_RTIA:
|
||||
if (len < 4) return;
|
||||
cmd.rtia = data[3];
|
||||
break;
|
||||
case CMD_SET_RCAL:
|
||||
if (len < 4) return;
|
||||
cmd.rcal = data[3];
|
||||
break;
|
||||
case CMD_SET_ELECTRODE:
|
||||
if (len < 4) return;
|
||||
cmd.electrode = data[3];
|
||||
break;
|
||||
case CMD_START_LSV:
|
||||
if (len < 19) return;
|
||||
cmd.lsv.v_start = ble_decode_float(&data[3]);
|
||||
cmd.lsv.v_stop = ble_decode_float(&data[8]);
|
||||
cmd.lsv.scan_rate = ble_decode_float(&data[13]);
|
||||
cmd.lsv.lp_rtia = data[18];
|
||||
break;
|
||||
case CMD_START_AMP:
|
||||
if (len < 19) return;
|
||||
cmd.amp.v_hold = ble_decode_float(&data[3]);
|
||||
cmd.amp.interval_ms = ble_decode_float(&data[8]);
|
||||
cmd.amp.duration_s = ble_decode_float(&data[13]);
|
||||
cmd.amp.lp_rtia = data[18];
|
||||
break;
|
||||
case CMD_START_CL:
|
||||
if (len < 34) return;
|
||||
cmd.cl.v_cond = ble_decode_float(&data[3]);
|
||||
cmd.cl.t_cond_ms = ble_decode_float(&data[8]);
|
||||
cmd.cl.v_free = ble_decode_float(&data[13]);
|
||||
cmd.cl.v_total = ble_decode_float(&data[18]);
|
||||
cmd.cl.t_dep_ms = ble_decode_float(&data[23]);
|
||||
cmd.cl.t_meas_ms = ble_decode_float(&data[28]);
|
||||
cmd.cl.lp_rtia = data[33];
|
||||
break;
|
||||
case CMD_START_PH:
|
||||
if (len < 8) return;
|
||||
cmd.ph.stabilize_s = ble_decode_float(&data[3]);
|
||||
break;
|
||||
case CMD_START_CLEAN:
|
||||
if (len < 13) return;
|
||||
cmd.clean.v_mv = ble_decode_float(&data[3]);
|
||||
cmd.clean.duration_s = ble_decode_float(&data[8]);
|
||||
break;
|
||||
case CMD_START_SWEEP:
|
||||
case CMD_GET_CONFIG:
|
||||
case CMD_STOP_AMP:
|
||||
case CMD_GET_TEMP:
|
||||
case CMD_START_REFS:
|
||||
case CMD_GET_REFS:
|
||||
case CMD_CLEAR_REFS:
|
||||
case CMD_OPEN_CAL:
|
||||
case CMD_CLEAR_OPEN_CAL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ble_push_command(&cmd);
|
||||
}
|
||||
|
||||
static void udp_rx_task(void *param)
|
||||
{
|
||||
(void)param;
|
||||
uint8_t buf[UDP_BUF_SIZE];
|
||||
struct sockaddr_in src;
|
||||
socklen_t slen = sizeof(src);
|
||||
|
||||
for (;;) {
|
||||
int n = recvfrom(udp_sock, buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&src, &slen);
|
||||
if (n <= 0) continue;
|
||||
|
||||
client_touch(&src);
|
||||
clients_expire();
|
||||
parse_udp_sysex(buf, (uint16_t)n);
|
||||
}
|
||||
}
|
||||
|
||||
int wifi_send_sysex(const uint8_t *sysex, uint16_t len)
|
||||
{
|
||||
if (udp_sock < 0 || client_count == 0)
|
||||
return -1;
|
||||
|
||||
int sent = 0;
|
||||
for (int i = 0; i < client_count; i++) {
|
||||
int r = sendto(udp_sock, sysex, len, 0,
|
||||
(struct sockaddr *)&clients[i].addr,
|
||||
sizeof(clients[i].addr));
|
||||
if (r > 0) sent++;
|
||||
}
|
||||
return sent > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
static void wifi_event_handler(void *arg, esp_event_base_t base,
|
||||
int32_t id, void *data)
|
||||
{
|
||||
(void)arg; (void)data;
|
||||
if (base == WIFI_EVENT) {
|
||||
if (id == WIFI_EVENT_AP_STACONNECTED)
|
||||
printf("WiFi: station connected\n");
|
||||
else if (id == WIFI_EVENT_AP_STADISCONNECTED)
|
||||
printf("WiFi: station disconnected\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int wifi_ap_init(void)
|
||||
{
|
||||
esp_netif_create_default_wifi_ap();
|
||||
|
||||
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
esp_err_t err = esp_wifi_init(&wifi_cfg);
|
||||
if (err) return err;
|
||||
|
||||
esp_event_handler_instance_t inst;
|
||||
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
||||
wifi_event_handler, NULL, &inst);
|
||||
|
||||
wifi_config_t ap_cfg = {
|
||||
.ap = {
|
||||
.ssid = WIFI_SSID,
|
||||
.ssid_len = sizeof(WIFI_SSID) - 1,
|
||||
.channel = WIFI_CHANNEL,
|
||||
.password = WIFI_PASS,
|
||||
.max_connection = WIFI_MAX_CONN,
|
||||
.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
},
|
||||
};
|
||||
|
||||
esp_wifi_set_mode(WIFI_MODE_APSTA);
|
||||
esp_wifi_set_config(WIFI_IF_AP, &ap_cfg);
|
||||
err = esp_wifi_start();
|
||||
if (err) return err;
|
||||
|
||||
printf("WiFi: AP \"%s\" on channel %d\n", WIFI_SSID, WIFI_CHANNEL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udp_init(void)
|
||||
{
|
||||
udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (udp_sock < 0) return -1;
|
||||
|
||||
struct sockaddr_in bind_addr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = htons(UDP_PORT),
|
||||
.sin_addr.s_addr = htonl(INADDR_ANY),
|
||||
};
|
||||
|
||||
if (bind(udp_sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) {
|
||||
close(udp_sock);
|
||||
udp_sock = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("UDP: listening on port %d\n", UDP_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_transport_init(void)
|
||||
{
|
||||
int rc = wifi_ap_init();
|
||||
if (rc) {
|
||||
printf("WiFi: AP init failed: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = udp_init();
|
||||
if (rc) {
|
||||
printf("UDP: init failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
xTaskCreate(udp_rx_task, "udp_rx", 4096, NULL, 5, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef WIFI_TRANSPORT_H
|
||||
#define WIFI_TRANSPORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int wifi_transport_init(void);
|
||||
int wifi_send_sysex(const uint8_t *sysex, uint16_t len);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue