麦克风

ADC 指模/数转换器或者模数转换器。是指将连续变化的模拟信号转换为离散的数字信号的器件。
我们这里需要使用 ADC 功能采集麦克风数据(原始 PCM 数据),发送给服务端。

LN882H对ADC的支持

支持 6 通道轮询采样
12bit 分辨率
单次和连续转换模式
支持 DMA
支持转换完成中断

注:如果要使用 ADC 的 DMA,除了要使能 ADC_DMA_EN,还要配置 ADC 的转换中断使能(不用配置 NVIC),这样才能触发 DMA采集数据

引脚和ADC通道对应关系
GPIOA0 -> ADC2
GPIOA1 -> ADC3
GPIOA4 -> ADC4
GPIOB3 -> ADC5
GPIOB4 -> ADC6
GPIOB5 -> ADC7

电路设计

实体样式
咪头式麦克风

mtmkf

规格说明

mkfgzsm

参数说明:

指向性:全指向(说明麦克风能均匀接收各个方向的声音,适用于语音采集)
灵敏度:-31±3(麦克风将声压转换为电信号的能力)

  • 0 dB = 1V/Pa,即 1 帕斯卡(Pa)的声压下,麦克风输出 1V 电压

  • 规格中参数是 -31 ±3 dB,意味着灵敏度范围在 -34 dB 到 -28 dB 之间。

  • 那么根据公式:dbjsgs

  • 灵敏度范围:-34 dB 到 -28 dB。

    最差情况(-34 dB):$ V_{\text{out}} = 10^{(-34/20)} \times 1V \approx 0.01995V = 19.95 , \text{mV} $。

    最佳情况(-28 dB):$ V_{\text{out}} = 10^{(-28/20)} \times 1V \approx 0.03981V = 39.81 , \text{mV} $。

  • 我们可以将参数 -31dB 带入:dairufsycs可以得出有效值为 28.2mv,峰值约为 39.9mV

标准工作电压:4.5V(范围是 2v-10V,4.5V 最佳)
输出阻抗:2.2KΩ(麦克风输出信号的内阻,影响与后续电路的匹配)
频率:100Hz-20000Hz(麦克风能有效响应的音频频率范围)
最大耗电流:0.5mA(麦克风在工作时的最大电流)

最小测试电路

zxcsdl

为什么不可以直接将咪头麦克风接入 ADC 引脚而需要外置电路?

根据规格说明中的解释我们可以得出如下麦克风特性:
咪头麦克风(驻极体麦克风)输出的是交流音频信号,以直流偏置电压为中心;而ADC只能采集正电压(0V到3.3V,假设 ln882H 的参考电压是3.3V)。
直流偏置:根据公式,假设负载电阻 RL = 2.2kΩ,电流 0.5 mA,咪头两端压降约为 0.5 x 10-3 x 2200 = 1.1 V。若供电3.3V,输出引脚(Term 1)的直流电平约为 3.3 - 1.1 = 2.2。(驻极体麦克风内部有一个FET(场效应管),它需要一个偏置电流来工作。)
交流部分:±39.9 mV(1Pa声压),总输出范围约为 2.2V ± 39.9 mV(2.1601V 到 2.2399V)。

LN882H ADC 引脚特性:
输入范围:0V 至 3.3V(单极输入,常见值)。

分辨率:LN882H ADC 12 位(212 = 4096),电压分辨率约为$$ \frac{3.3}{4096} \approx 0.805\text{mV} $$

这两个特性说明了两点:

第一、引脚输入电压不能超过 3.3V,因为它最大只支持 3.3V
第二、咪头麦克风的电压变化大概在 0.805mV 左右太微弱了,ADC 可能无法精确识别
第三、麦克风需要的电压为 4.5V,ADC 引脚最大也就 3.3V

为了解决这些问题:我们就需要外置电路,对麦克风提供 3.3V 的电压,并将电压变大让 ADC 可以正常采集这些变化;

实现思路

使用 ADC 采集音频数据(原始 PCM 数据)。
注意因为直流偏置范围只有(2.1601V 到 2.2399V),所以我们可以计算出 ADC 步长为:
bzjs
那么信号范围 $ 2.1601V $ 到 $ 2.2399V $ 对应的ADC值
bzfw
信号变化范围为 2683 到 2783,总共约100个量化级别。所以我们需要线性映射对应的数值。
将采集到的数据转为 16 位的 PCM,存储到 PCMBuffer(环形缓冲区)

使用 G.726 编码(将 PCM 数据压缩,降低带宽需求)。这个后续实现
通过 Socket 发送 G.726 编码后的数据给服务端。
服务端解码 G.726,还原成 PCM,再进行后续处理(播放/存储/语音识别等)。

FFT

通过 ADC 采样麦克风数据,但采样结果受到干扰。测试发现 ADC 引脚本身存在大概 0.0几V 的噪声波动,我当前使用的是咪头麦克风输出信号幅度较低,导致采样信号淹没在噪声中,无法正确获取麦克风数据。这里就可使用 FFT 算法

软件实现

思路:通过 ADC 读取音频数据,将其转为 PCM 数据流,通过 socket 发送给大模型进行处理

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
#include "hal/hal_clock.h"
#include "hal/hal_common.h"
#include "hal/hal_gpio.h"
#include "hal/hal_adc.h"
#include "hal/hal_timer.h"
#include "ln_show_reg.h"
#include "ln_test_common.h"
#include "utils/debug/log.h"
#include <string.h>

#include "ln_drv_timer.h"
#include "ln_drv_adc.h"

#define ADC_CHANNEL ADC_CH5 // 使用GPIOB3作为ADC输入
#define SAMPLE_RATE 40000 // 目标采样率 40kHz
#define BUFFER_SIZE 1024 // PCM缓冲区大小

// PCM缓冲区
int16_t pcmBuffer[BUFFER_SIZE];
volatile uint16_t bufferIndex = 0;

// 定时器中断回调
void timer_callback(void){
// 直接读取最新ADC值 (0-4095,非阻塞)
uint16_t adcValue = adc_get_data(ADC_CHANNEL);
LOG(LOG_LVL_INFO, "PCM ...\n");
// 转换为16位PCM (-32768至32767)
int16_t pcmValue = (int16_t)((adcValue - 2733) * (29490.0 / 50.0)); // 映射到90%满量程
if (pcmValue > 32767) pcmValue = 32767; // 防止溢出
if (pcmValue < -32768) pcmValue = -32768;

// 存储到缓冲区
if (bufferIndex < BUFFER_SIZE){
pcmBuffer[bufferIndex++] = pcmValue;
}
else{
// 缓冲区满,处理数据(例如发送或重置)
bufferIndex = 0;
LOG(LOG_LVL_INFO, "PCM Buffer Full, Processing...\n");
// 可添加数据传输逻辑(如串口发送)
}
}

int main(int argc, char *argv[]){
/****************** 1. 系统初始化 ***********************/
SetSysClock();
log_init();
LOG(LOG_LVL_INFO, "LN882H init for audio capture!\n");
ln_show_reg_init();

/****************** 2. 外设配置 ***********************/
// 初始化ADC
adc_init(ADC_CHANNEL); // 配置ADC_CH5 (GPIOB3)为连续模式
adc_start(); // 开始ADC转换

// 初始化定时器 (使用Timer0,40kHz中断)
uint32_t timer_us = 1000000 / SAMPLE_RATE; // 40kHz -> 25 us
timer_init(TIMER_CH_0, timer_us, timer_callback); // 初始化Timer0,25us中断

/****************** 3. 采集循环 ***********************/
while (1){
ln_delay_ms(100); // 避免CPU过载
}

return 0;
}

123

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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
// CRC16
static int calculate_crc16(const uint8_t *data, uint16_t length) {
uint16_t crc = 0x0000;
for (uint16_t i = 0; i < length; i++) {
crc ^= (uint16_t)data[i] << 8;
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x8000) crc = (crc << 1) ^ 0x8005; else crc <<= 1;
}
}
return crc;
}

// 格式校验
static int validate_format(uint8_t *buffer, uint32_t pos) {
if (pos < 7) return 1;
uint16_t data_length = (buffer[2] << 8) | buffer[3];
if (data_length > TEMP_BUFFER_SIZE - 7) {
LOG(LOG_LVL_ERROR, "Data length too big: %u\n", data_length);
return -1;
}
uint16_t expected_pos = 4 + data_length + 2 + 1;
if (pos < expected_pos) return 1;
if (buffer[0] != 0xAA || buffer[expected_pos - 1] != 0xFF || pos != expected_pos) {
LOG(LOG_LVL_ERROR, "Data format error: head=0x%02X, tail=0x%02X, pos=%u, expected=%u\n",
buffer[0], buffer[pos - 1], pos, expected_pos);
return -1;
}
return 2;
}

static void send_ACK(int sockfd, uint8_t response_type) {
uint8_t response[3] = {0xAA, response_type, 0xFF};
int attempts = 5;
int sent;
while (attempts-- > 0) {
sent = lwip_send(sockfd, response, sizeof(response), 0);
if (sent < 0) {
LOG(LOG_LVL_ERROR, "send_ACK error, errno=%d, attempts left=%d\n", errno, attempts);
if (errno == EAGAIN || errno == ETIMEDOUT) {
OS_MsDelay(50);
continue;
}
break;
} else if (sent != sizeof(response)) {
LOG(LOG_LVL_ERROR, "Partial ACK send: expected 3, sent %d\n", sent);
break;
} else {
switch(response_type){
case 0xAA: LOG(LOG_LVL_INFO, "Sent %s\n", "ACK_0xAA");break;
case 0xEE: LOG(LOG_LVL_INFO, "Sent %s\n", "ACK_0xEE");break;
case 0x02: LOG(LOG_LVL_INFO, "Sent %s\n", "ACK_0x02");break;
case 0x01: LOG(LOG_LVL_INFO, "Sent %s\n", "ACK_0x01");break;
}
return;
}
}
LOG(LOG_LVL_ERROR, "Failed to send ACK after retries\n");
}

static int get_free_space(uint32_t write_pos, uint32_t read_pos, uint32_t buffer_size) {
if ((write_pos + 1) % buffer_size == read_pos) {
return 0;
}
if (write_pos >= read_pos) {
return buffer_size - (write_pos - read_pos) - 1;
} else {
return read_pos - write_pos - 1;
}
}

static int read_circular_buffer(void *buffer, uint32_t *read_pos, uint32_t *write_pos, uint32_t buffer_size, void *data, uint16_t data_length, size_t element_size) {
uint32_t available = (*write_pos >= *read_pos) ? (*write_pos - *read_pos) : (buffer_size - *read_pos + *write_pos);
if (available < data_length) {
return -1;
}
uint32_t space_to_end = buffer_size - *read_pos;
if (space_to_end >= data_length) {
memcpy(data, (uint8_t *)buffer + (*read_pos * element_size), data_length * element_size);
} else {
memcpy(data, (uint8_t *)buffer + (*read_pos * element_size), space_to_end * element_size);
memcpy((uint8_t *)data + (space_to_end * element_size), buffer, (data_length - space_to_end) * element_size);
}
*read_pos = (*read_pos + data_length) % buffer_size;
return 0;
}

// 向环形缓冲区写入数据
// buffer:环形缓冲区的起始地址(通用指针)。
// write_pos:指向写指针的指针,表示当前写入位置。
// read_pos:指向读指针的指针,表示当前读取位置。
// buffer_size:缓冲区总大小(元素个数)。
// data:要写入的数据的起始地址(通用指针)。
// data_length:要写入的元素个数。
// element_size:每个元素的大小(字节数,例如 sizeof(int16_t) 为 2)。
// 0:写入成功。
// -1:因缓冲区空间不足超时失败
static int write_circular_buffer(void *buffer, uint32_t *write_pos, uint32_t *read_pos,
uint32_t buffer_size, void *data, uint16_t data_length,
size_t element_size) {
// 计算可用空间
uint32_t free_space = get_free_space(*write_pos, *read_pos, buffer_size);
uint32_t wait_start = OS_GetTicks(); // 获取当前系统时间(tick 计数),用于超时检测

// 空间不够,等待缓冲区空间
while (free_space < data_length) {
LOG(LOG_LVL_INFO, "Buffer full, data_length=%u, free_space=%u\n", data_length, free_space);
vTaskDelay(pdMS_TO_TICKS(10));
free_space = get_free_space(*write_pos, *read_pos, buffer_size);
LOG(LOG_LVL_INFO, "write_circular_buffer write=%u, read=%u\n", *write_pos, *read_pos);
if (OS_GetTicks() - wait_start > pdMS_TO_TICKS(5000)) {
LOG(LOG_LVL_ERROR, "Timeout waiting for buffer space after 5s\n");
return -1; // 返回 -1 表示因空间不足超时
}
}

// 写入数据
uint32_t space_to_end = buffer_size - *write_pos;
if (space_to_end >= data_length) {
memcpy((uint8_t *)buffer + (*write_pos * element_size), data, data_length * element_size);
} else {
memcpy((uint8_t *)buffer + (*write_pos * element_size), data, space_to_end * element_size);
memcpy(buffer, (uint8_t *)data + (space_to_end * element_size),
(data_length - space_to_end) * element_size);
}
*write_pos = (*write_pos + data_length) % buffer_size;
return 0; // 写入成功
}

static int receive_sum_size(int sockfd, uint32_t *sum_size) {
uint8_t size_buffer[4];
int size_pos = 0;
char buffer[4];
int len = lwip_recv(sockfd, buffer, sizeof(buffer), 0);
if (len < 0) {
// 处理非阻塞临时错误
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return 0;
}
return -1;
} else if (len > 0) {
LOG(LOG_LVL_INFO, "receive_sum_size len: %d\n", len);
int i = 0;
while (i < len && size_pos < 4) {
size_buffer[size_pos] = buffer[i];
size_pos++;
i++;
}
if (size_pos == 4) {
*sum_size = ((uint32_t)size_buffer[0] << 24) | ((uint32_t)size_buffer[1] << 16) |
((uint32_t)size_buffer[2] << 8) | (uint32_t)size_buffer[3];
LOG(LOG_LVL_INFO, "Received total size: %u bytes\n", *sum_size);
send_ACK(sockfd, 0xAA);
return 1;
}
}
return 0;
}

// 检查结束信号
static int check_end_signal(uint8_t *buffer, uint32_t *pos, int sockfd) {
if (*pos < 3) {
return 0; // 数据不足以包含结束信号,直接返回
}

for (uint32_t i = 0; i <= *pos - 3; i++) {
if (buffer[i] == 0xAA && buffer[i + 1] == 0x02 && buffer[i + 2] == 0xFF) {
LOG(LOG_LVL_INFO, "recv end pos=%u\n", i);
uint32_t remaining = *pos - (i + 3);

// 检查结束信号后是否紧跟开始信号
if (remaining >= 3 && buffer[i + 3] == 0xAA && buffer[i + 4] == 0x01 && buffer[i + 5] == 0xFF) {
LOG(LOG_LVL_INFO, "Start signal follows end signal, remaining=%u\n", remaining);
memmove(buffer, &buffer[i + 3], remaining);
*pos = remaining;
send_ACK(sockfd, 0xAA);
return 1; // 表示结束信号后紧跟开始信号
}

// 处理剩余数据
if (remaining > 0) {
LOG(LOG_LVL_INFO, "Moving %u bytes after end signal\n", remaining);
memmove(buffer, &buffer[i + 3], remaining);
*pos = remaining;
} else {
*pos = 0;
}

send_ACK(sockfd, 0xAA);
return -3; // 仅检测到结束信号
}
}
return 0; // 未检测到结束信号
}

// 处理数据包
// buffer:包含完整数据包的缓冲区。
// pos:缓冲区中数据包的长度(字节数)。
// sockfd:socket 文件描述符,用于发送 ACK。
// data_type:指向数据类型变量的指针,存储解析出的类型(0x11 或 0x12)。
// total_written:指向已写入数据总量的指针(样本数或字节数)。
// 0:成功处理数据包或仅接收类型信息。
// -1:CRC 校验失败。
static int process_packet(uint8_t *buffer, uint32_t pos, int sockfd, uint8_t *data_type, uint32_t *total_written) {
uint16_t data_length = (buffer[2] << 8) | buffer[3]; // 数据长度
*data_type = buffer[1]; // 数据包类型
uint8_t *data = &buffer[4]; // 指向数据开头
uint16_t recv_crc = (buffer[4 + data_length] << 8) | buffer[5 + data_length];
uint16_t calculated_crc = calculate_crc16(data, data_length);

if (calculated_crc != recv_crc) {
LOG(LOG_LVL_ERROR, "CRC16 error: received=0x%04X, calculated=0x%04X\n", recv_crc, calculated_crc);
send_ACK(sockfd, 0xEE);
return -1;
}
// 引导包
if (data_length == 0 && (*data_type == 0x11 || *data_type == 0x12)) {
LOG(LOG_LVL_INFO, "recv type : 0x%02X\n", *data_type);
send_ACK(sockfd, 0xAA);
return 0;
}

taskENTER_CRITICAL();
if (*data_type == 0x12) { // 音频数据
if (data_length % 2 != 0) {
LOG(LOG_LVL_ERROR, "Audio data length not two: %u\n", data_length);
send_ACK(sockfd, 0xEE);
} else {
// 转为16位样本
uint16_t sample_count = data_length / 2;
int16_t samples[sample_count];
for (uint16_t i = 0; i < sample_count; i++) {
samples[i] = (int16_t)((data[i * 2] << 8) | data[i * 2 + 1]);
}
int write_result = write_circular_buffer(PCMBuffer_rx, (uint32_t *)&rx_write_pos,
(uint32_t *)&rx_read_pos, RING_BUFFER_SIZE,
(uint8_t *)samples, sample_count, sizeof(int16_t));
if (write_result == 0) {
*total_written += sample_count;
send_ACK(sockfd, 0xAA);
LOG(LOG_LVL_INFO, "CRC match, total_written=%u (0x12)\n", *total_written);
xSemaphoreGive(rx_data_ready_sem); // 写入成功,增加计数
} else if (write_result == -1) {
xSemaphoreGive(rx_data_ready_sem); // 空间不足,通知消费者
send_ACK(sockfd, 0xEE);
LOG(LOG_LVL_INFO, "Buffer full, signalled consumer for audio data\n");
}
}
} else if (*data_type == 0x11) { // 打印数据
int write_result = write_circular_buffer(print_buffer, (uint32_t *)&print_write_pos,
(uint32_t *)&print_read_pos, SUM_BUFFER_SIZE,
data, data_length, sizeof(uint8_t));
if (write_result == 0) {
*total_written += data_length;
send_ACK(sockfd, 0xAA);
LOG(LOG_LVL_INFO, "CRC match, total_written=%u (bytes)\n", *total_written);
xSemaphoreGive(print_data_ready_sem); // 写入成功,增加计数
} else if (write_result == -1) {
xSemaphoreGive(print_data_ready_sem); // 空间不足,通知消费者
send_ACK(sockfd, 0xEE);
LOG(LOG_LVL_INFO, "Buffer full, signalled consumer for print data\n");
}
}
taskEXIT_CRITICAL();
return 0;
}

static int receive_data(int sockfd, int *has_partial, uint8_t *temp_buffer, uint32_t *temp_pos, uint8_t *data_type, uint32_t *total_written) {
int len;
static uint8_t buffer[4096];

if (*has_partial && *temp_pos > 0) {
int end_result = check_end_signal(temp_buffer, temp_pos, sockfd);
if (end_result == -3) {
*has_partial = 0;
return -3; // 仅结束信号
} else if (end_result == 1) {
*has_partial = 1; // 保留开始信号数据
return 1; // 结束信号后紧跟开始信号
}

int validation_result = validate_format(temp_buffer, *temp_pos);
if (validation_result == 2) {
process_packet(temp_buffer, *temp_pos, sockfd, data_type, total_written);
*temp_pos = 0;
*has_partial = 0;
} else if (validation_result == -1) {
send_ACK(sockfd, 0xEE);
*temp_pos = 0;
*has_partial = 0;
}
}

len = lwip_recv(sockfd, buffer, sizeof(buffer), 0);
if (len < 0) {
if (errno == EAGAIN || errno == ETIMEDOUT) {
OS_MsDelay(100);
return 0;
}
LOG(LOG_LVL_ERROR, "recv_data len < 0: %d\n", errno);
return -1;
} else if (len == 0) {
LOG(LOG_LVL_INFO, "Server closed connection\n");
return -2;
}

int i = 0;
while (i < len) {
if (*temp_pos >= TEMP_BUFFER_SIZE) {
send_ACK(sockfd, 0xEE);
*temp_pos = 0;
*has_partial = 0;
LOG(LOG_LVL_INFO, "temp_pos >= sizeof(temp_buffer)...\n");
break;
}

temp_buffer[*temp_pos] = buffer[i];
(*temp_pos)++;

int end_result = check_end_signal(temp_buffer, temp_pos, sockfd);
if (end_result == -3) {
*has_partial = 0;
return -3; // 仅结束信号
} else if (end_result == 1) {
*has_partial = 1; // 保留开始信号数据
return 1; // 结束信号后紧跟开始信号
}

int validation_result = validate_format(temp_buffer, *temp_pos);
if (validation_result == -1) {
send_ACK(sockfd, 0xEE);
*temp_pos = 0;
*has_partial = 0;
continue;
} else if (validation_result == 2) {
process_packet(temp_buffer, *temp_pos, sockfd, data_type, total_written);
*temp_pos = 0;
*has_partial = 0;
if (i + 1 < len && buffer[i + 1] != 0xAA) {
LOG(LOG_LVL_INFO, "Discarding invalid data after packet: %02X\n", buffer[i + 1]);
break;
}
}
i++;
}

if (*temp_pos > 0 && temp_buffer[*temp_pos - 1] == 0xAA && validate_format(temp_buffer, *temp_pos) != 2) {
*has_partial = 1;
LOG(LOG_LVL_INFO, "package not wanzheng\n");
}

return 0;
}

static void socket_task_entry(void *params) {

LN_UNUSED(params); // 抑制编译器对未使用参数的警告。
struct sockaddr_in server_addr; // 存储 IP 和端口。
int timeout_ms = 5000; // 接收超时时间,单位毫秒 5s
int sockfd = -1; // socket 句柄 -1 未连接
int has_partial = 0; // 标志位,表示是否有未处理完的部分数据。
// 临时缓冲区,用于接收数据,初始化为全 0。
uint8_t temp_buffer[TEMP_BUFFER_SIZE] = {0};
uint32_t temp_pos = 0; // 临时缓冲区的当前写入位置
uint8_t data_type = 0; // 接收数据的类型
uint32_t total_written = 0; // 已写入的总数据量(样本数或字节数)
State_t state = STATE_IDLE; // 状态机变量,初始状态为空闲
uint32_t data_sum_size = 0; // 打印数据的总大小
uint8_t is_get_sum_size = 0; // 标志位,表示是否已获取打印数据的总大小
// 记录上次接收数据的时间,用于超时检测,静态变量保持任务间的状态。
static uint32_t last_data_time = 0;

while (1) {
// 检查 MUC 是否获取到 IP 地址(WiFi 是否连接)
if (!netdev_got_ip()) {
LOG(LOG_LVL_INFO, "No IP, waiting for WiFi...\n");
OS_MsDelay(1000);
continue;
}

// 获取网络接口(STA 模式)的 IP 信息,包括 IP 地址、子网掩码和网关
tcpip_ip_info_t ip_info;
netdev_get_ip_info(NETIF_IDX_STA, &ip_info);
LOG(LOG_LVL_INFO, "IP: %s, Mask: %s, Gateway: %s\n",
ip4addr_ntoa(&ip_info.ip), ip4addr_ntoa(&ip_info.netmask), ip4addr_ntoa(&ip_info.gw));

// 获取 WiFi 信号强度(RSSI,单位 dBm),并打印。
int8_t rssi;
wifi_sta_get_rssi(&rssi);
LOG(LOG_LVL_INFO, "WiFi RSSI: %d dBm\n", rssi);

// 当网络连接正常时,进入内层循环处理 socket 连接和数据通信。
while (netdev_got_ip()) {
if (sockfd < 0) { // 未创建,则创建 socket 句柄
sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) { // 失败延迟 2s 重试
LOG(LOG_LVL_ERROR, "Socket create error: %d\n", errno);
OS_MsDelay(2000);
continue;
}
LOG(LOG_LVL_INFO, "Socket create ID=%d\n", sockfd);

// 设置 socket 为非阻塞模式(FIONBIO)失败则关闭重试
int flags = 1;
if (lwip_ioctl(sockfd, FIONBIO, &flags) < 0) {
LOG(LOG_LVL_ERROR, "socket ioctl error: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// 配置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = lwip_htons(8080);
server_addr.sin_addr.s_addr = inet_addr("192.168.2.27");
LOG(LOG_LVL_INFO, "Connecting to 192.168.2.27:8080...\n");

// 连接到指定 IP 和 端口,非阻塞失败就关闭 socket 重试
int connect_result = lwip_connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (connect_result < 0 && errno != EINPROGRESS) {
LOG(LOG_LVL_ERROR, "lwip_connect failed: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// 检查连接结果
fd_set writefds, exceptfds;
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET(sockfd, &writefds);
FD_SET(sockfd, &exceptfds);
int select_result = lwip_select(sockfd + 1, NULL, &writefds, &exceptfds, &tv);
if (select_result <= 0 || FD_ISSET(sockfd, &exceptfds)) {
LOG(LOG_LVL_ERROR, "Connect timed out or failed: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// 检查错误状态
int so_error = 0;
socklen_t len = sizeof(so_error);
if (lwip_getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0 || so_error != 0) {
LOG(LOG_LVL_ERROR, "Connect failed with error: %d\n", so_error);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// 设置接收超时
LOG(LOG_LVL_INFO, "Connected to 192.168.2.27:8080\n");
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
if (lwip_setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
LOG(LOG_LVL_ERROR, "setsockopt SO_RCVTIMEO failed: %d\n", errno);
}

// 重置状态
state = STATE_IDLE;
has_partial = 0;
temp_pos = 0;
data_type = 0;
total_written = 0;
data_sum_size = 0;
is_get_sum_size = 0;
last_data_time = 0;
}

// socket 和 WiFi 都正常时进入循环
while (sockfd >= 0 && netdev_got_ip()) {
switch (state) {
case STATE_IDLE: { // 接收 3 字节的开始信号。
uint8_t response[3];
int len = lwip_recv(sockfd, response, sizeof(response), 0);
if (len < 0) {
// 无数据时候就 break
if (errno == EAGAIN || errno == ETIMEDOUT) {
OS_MsDelay(100);
break;
}
// 说明错误了
LOG(LOG_LVL_ERROR, "STATE_IDLE len < 0: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
break;
} else if (len == 0) { // 0 表示对方关闭了连接
LOG(LOG_LVL_INFO, "Server closed connection\n");
lwip_close(sockfd);
sockfd = -1;
break;
}

// 检查接收到的开始信号是否为 0xAA 0x01 0xFF
if (len == 3 && response[0] == 0xAA && response[1] == 0x01 && response[2] == 0xFF) {
LOG(LOG_LVL_INFO, "recv start...\n");
send_ACK(sockfd, 0xAA);
state = STATE_TYPE;
} else {
LOG(LOG_LVL_ERROR, "recv start error: %02X %02X %02X\n", response[0], response[1], response[2]);
send_ACK(sockfd, 0xEE);
}
break;
}

case STATE_TYPE: { // 调用 receive_data 接收数据类型。
int ret = receive_data(sockfd, &has_partial, temp_buffer, &temp_pos, &data_type, &total_written);
// -1 或 -2:socket 错误或连接关闭,关闭 socket。
if (ret == -1 || ret == -2) {
LOG(LOG_LVL_INFO, "STATE_TYPE(sockfd)...\n");
lwip_close(sockfd);
sockfd = -1;
break;
} else if (ret == -3) { // -3:收到结束信号,切换回 STATE_IDLE
LOG(LOG_LVL_ERROR, "Unexpected end signal in STATE_TYPE\n");
state = STATE_IDLE;
break;
}

// 如果数据类型为 0x11(打印)或 0x12(音频),切换到对应状态并清空 data_type
if (data_type == 0x11 || data_type == 0x12) {
state = (data_type == 0x11) ? STATE_PRINT : STATE_AUDIO;
data_type = 0;// 清空数据类型
}
break;
}

// 打印逻辑
case STATE_PRINT: {
if (!is_get_sum_size) { // 获取打印数据总大小
int ret = receive_sum_size(sockfd, &data_sum_size);
if (ret == -1) {
LOG(LOG_LVL_ERROR, "receive_sum_size error\n");
lwip_close(sockfd);
sockfd = -1;
break;
}
if (ret == 1) {
is_get_sum_size = 1;
total_written = 0;
last_data_time = OS_GetTicks();
LOG(LOG_LVL_INFO, "STATE_PRINT sum size: %u bytes\n", data_sum_size);
}
} else {
int ret = receive_data(sockfd, &has_partial, temp_buffer, &temp_pos, &data_type, &total_written);
if (ret == -1) { // socket 错误,关闭 socket。
LOG(LOG_LVL_INFO, "STATE_PRINT(sockfd)...\n");
lwip_close(sockfd);
sockfd = -1;
break;
} else if (ret == -2) { // 服务器关闭连接,检查数据完整性后关闭 socket
LOG(LOG_LVL_INFO, "Server closed connection\n");
if (total_written < data_sum_size) {
LOG(LOG_LVL_ERROR, "Incomplete print data: received %u of %u bytes\n", total_written, data_sum_size);
}
lwip_close(sockfd);
sockfd = -1;
is_get_sum_size = 0;
total_written = 0;
print_write_pos = 0;
print_read_pos = 0;
state = STATE_IDLE;
break;
} else if (ret == -3) { // 收到结束信号,检查完整性后切换回 STATE_IDLE
if (total_written < data_sum_size) {
LOG(LOG_LVL_ERROR, "print data not wz: received %u of %u bytes\n", total_written, data_sum_size);
}
is_get_sum_size = 0;
total_written = 0;
print_write_pos = 0;
print_read_pos = 0;
state = STATE_IDLE;
break;
}else if (ret == 1) {
// 结束信号后紧跟开始信号
LOG(LOG_LVL_INFO, "New session started after end signal\n");
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_TYPE; // 切换到类型状态处理新数据
break;
}
// 设置超时接收
last_data_time = OS_GetTicks(); // 获取当前时间戳
// 超时 5s,重置接收状态和缓冲区
if (OS_GetTicks() - last_data_time > pdMS_TO_TICKS(5000)) {
LOG(LOG_LVL_ERROR, "Timeout waiting for print data\n");
lwip_close(sockfd);
sockfd = -1;
is_get_sum_size = 0;
total_written = 0;
print_write_pos = 0;
print_read_pos = 0;
state = STATE_IDLE;
break;
}
// 打印接收进度。
LOG(LOG_LVL_INFO, "Received %u of %u bytes\n", total_written, data_sum_size);
}
break;
}

case STATE_AUDIO: {
int ret = receive_data(sockfd, &has_partial, temp_buffer, &temp_pos, &data_type, &total_written);
if (ret == -1) {
LOG(LOG_LVL_INFO, "lwip_close(sockfd)...\n");
lwip_close(sockfd);
sockfd = -1;
break;
} else if (ret == -2) {
LOG(LOG_LVL_INFO, "Server closed connection\n");
lwip_close(sockfd);
sockfd = -1;
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_IDLE;
break;
} else if (ret == -3) {
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_IDLE;
break;
}else if (ret == 1) {
// 结束信号后紧跟开始信号
LOG(LOG_LVL_INFO, "New session started after end signal\n");
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_TYPE; // 切换到类型状态处理新数据
break;
}

last_data_time = OS_GetTicks();
if (OS_GetTicks() - last_data_time > pdMS_TO_TICKS(5000)) {
LOG(LOG_LVL_ERROR, "Timeout waiting for audio data\n");
lwip_close(sockfd);
sockfd = -1;
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_IDLE;
break;
}

// 从音频发送缓冲区(PCMBuffer_tx)读取数据,构造数据包,并通过 TCP socket 发送给服务器。
// 获取信号量
if (xSemaphoreTake(tx_data_ready_sem, pdMS_TO_TICKS(100)) == pdTRUE) {
uint16_t data_length = 0; // 发送的数据长度
int16_t data[MAX_DATA_PER_PACKET / 2]; // 存储从缓冲区读取的音频样本。
taskENTER_CRITICAL();
// 计算 PCMBuffer_tx 中可用的样本数
uint32_t available = (tx_write_pos >= tx_read_pos) ? (tx_write_pos - tx_read_pos) : (RING_BUFFER_SIZE - tx_read_pos + tx_write_pos);
// SEND_THRESHOLD 预定义的阈值,避免频繁发送
if (available >= SEND_THRESHOLD / 2) {
data_length = (available > MAX_DATA_PER_PACKET / 2) ? (MAX_DATA_PER_PACKET / 2) : available;
// 从 PCMBuffer_tx 中读取 data_length 个样本
read_circular_buffer(PCMBuffer_tx, (uint32_t *)&tx_read_pos, (uint32_t *)&tx_write_pos, RING_BUFFER_SIZE, data, data_length, sizeof(int16_t));
}
taskEXIT_CRITICAL();

// 构造并发送数据包
if (data_length > 0) {
uint8_t send_buffer[7 + MAX_DATA_PER_PACKET];
send_buffer[0] = 0xAA;
send_buffer[1] = 0x12;
send_buffer[2] = (data_length * 2 >> 8) & 0xFF;
send_buffer[3] = (data_length * 2) & 0xFF;
memcpy(&send_buffer[4], data, data_length * 2);
uint16_t crc = calculate_crc16((uint8_t *)data, data_length * 2);
send_buffer[4 + data_length * 2] = (crc >> 8) & 0xFF;
send_buffer[5 + data_length * 2] = crc & 0xFF;
send_buffer[6 + data_length * 2] = 0xFF;

int sent = lwip_send(sockfd, send_buffer, 7 + data_length * 2, 0);
if (sent != 7 + data_length * 2) {
LOG(LOG_LVL_ERROR, "Send data failed: expected %d, sent %d\n", 7 + data_length * 2, sent);
lwip_close(sockfd);
sockfd = -1;
break;
} else {
LOG(LOG_LVL_INFO, "Sent %d bytes (data length: %u)\n", sent, data_length * 2);
}
}
}
break;
}
}
}

if (sockfd < 0) {
LOG(LOG_LVL_ERROR, "Socket disconnect, retrying...\n");
OS_MsDelay(2000);
}
}
LOG(LOG_LVL_ERROR, "WiFi disconnected, waiting...\n");
OS_MsDelay(2000);
}
}

void audio_task(void *param) {
pwm_init(40000, 0, PWM_CHA_0, GPIO_B, GPIO_PIN_5);
pwm_init(40000, 0, PWM_CHA_1, GPIO_B, GPIO_PIN_6);
pwm_start(PWM_CHA_0);
pwm_start(PWM_CHA_1);

bool speaker_on = false;
int16_t samples[192]; // 每次处理 192 个样本,与 receive_data 一致
const uint32_t samples_per_batch = 192;

while (1) {
LOG(LOG_LVL_INFO, "Audio task sem, count=%lu\n", uxSemaphoreGetCount(rx_data_ready_sem));
if (xSemaphoreTake(rx_data_ready_sem, pdMS_TO_TICKS(10)) == pdTRUE) {
taskENTER_CRITICAL();
uint32_t available = (rx_write_pos >= rx_read_pos) ?
(rx_write_pos - rx_read_pos) :
(RING_BUFFER_SIZE - rx_read_pos + rx_write_pos);
LOG(LOG_LVL_INFO, "Audio task: available=%u, write=%u, read=%u\n",
available, rx_write_pos, rx_read_pos);
if (available >= samples_per_batch) {
if (!speaker_on) {
pwm_start(PWM_CHA_0);
pwm_start(PWM_CHA_1);
speaker_on = true;
}
if (read_circular_buffer(PCMBuffer_rx, (uint32_t *)&rx_read_pos,
(uint32_t *)&rx_write_pos, RING_BUFFER_SIZE,
samples, samples_per_batch, sizeof(int16_t)) == 0) {
LOG(LOG_LVL_INFO, "Audio task read %u samples, new read=%u\n",
samples_per_batch, rx_read_pos);
taskEXIT_CRITICAL();
// // 播放音频样本
for (uint32_t i = 0; i < samples_per_batch; i++) {
uint16_t pwm_value = (samples[i] + 32768) >> 4;
float duty = (pwm_value / 4095.0f) * 100.0f;
pwm_set_duty(PWM_CHA_0, duty);
vTaskDelay(pdMS_TO_TICKS(1000 / 16000));
}
} else {
LOG(LOG_LVL_ERROR, "Read from PCMBuffer_rx failed\n");
taskEXIT_CRITICAL();
}
} else {
taskEXIT_CRITICAL();
}
} else {
LOG(LOG_LVL_INFO, "Audio task timeout, no data ready\n");
if (speaker_on) {
pwm_stop(PWM_CHA_0);
pwm_stop(PWM_CHA_1);
speaker_on = false;
LOG(LOG_LVL_INFO, "Speaker turned off due to no data\n");
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}

// 打印任务
void print_task(void *param) {
while (1) {
if (xSemaphoreTake(print_data_ready_sem, portMAX_DELAY) == pdTRUE) {
uint8_t data[SUM_BUFFER_SIZE];
taskENTER_CRITICAL();
uint32_t available = (print_write_pos >= print_read_pos) ?
(print_write_pos - print_read_pos) :
(SUM_BUFFER_SIZE - print_read_pos + print_write_pos);
if (available > 0) {
read_circular_buffer(print_buffer, (uint32_t *)&print_read_pos,
(uint32_t *)&print_write_pos, SUM_BUFFER_SIZE,
data, available, sizeof(uint8_t));
LOG(LOG_LVL_INFO, "Print task processed %u bytes\n", available);
xSemaphoreGive(print_data_ready_sem); // ?????
}
taskEXIT_CRITICAL();
}
}
}

void usr_app_task_entry(void *params){
rx_data_ready_sem = xSemaphoreCreateCounting(65535, 0);;
tx_data_ready_sem = xSemaphoreCreateCounting(35535, 0);;
print_data_ready_sem = xSemaphoreCreateCounting(35535, 0);;
if (rx_data_ready_sem == NULL || tx_data_ready_sem == NULL ||
print_data_ready_sem == NULL) {
LOG(LOG_LVL_ERROR, "Semaphore creation failed!\n");
//return -1;
}

// 创建 socket Task
if (OS_OK != OS_ThreadCreate(&g_socket_thread, "SocketTask", socket_task_entry, NULL, OS_PRIORITY_BELOW_NORMAL, USR_APP_TASK_STACK_SIZE)) {
LOG(LOG_LVL_ERROR, "SocketTask failed!\n");
LN_ASSERT(1);
}
// audio task
if (OS_OK != OS_ThreadCreate(&g_audio_thread, "AudioTask", audio_task, NULL, OS_PRIORITY_BELOW_NORMAL, USR_APP_TASK_STACK_SIZE)) {
LOG(LOG_LVL_ERROR, "AudioTask failed!\n");
LN_ASSERT(1);
}

// // print task
// if (OS_OK != OS_ThreadCreate(&g_print_thread;, "PrintTask", print_task, NULL, OS_PRIORITY_BELOW_NORMAL, USR_APP_TASK_STACK_SIZE)) {
// LN_ASSERT(1);
// }

}

456

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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
static void socket_task(void *params) {

LN_UNUSED(params); // ???????????????
struct sockaddr_in server_addr; // ?? IP ????
int timeout_ms = 5000; // ??????,???? 5s
int sockfd = -1; // socket ?? -1 ???
int has_partial = 0; // ???,???????????????
// ?????,??????,????? 0?
uint8_t temp_buffer[TEMP_BUFFER_SIZE] = {0};
uint32_t temp_pos = 0; // ????????????
uint8_t data_type = 0; // ???????
uint32_t total_written = 0; // ????????(???????)
State_t state = STATE_IDLE; // ?????,???????
uint32_t data_sum_size = 0; // ????????
uint8_t is_get_sum_size = 0; // ???,???????????????
// ???????????,??????,?????????????
static uint32_t last_data_time = 0;

while (1) {
// ?? MUC ????? IP ??(WiFi ????)
if (!netdev_got_ip()) {
LOG(LOG_LVL_INFO, "No IP, waiting for WiFi...\n");
OS_MsDelay(1000);
continue;
}

// ??????(STA ??)? IP ??,?? IP ??????????
tcpip_ip_info_t ip_info;
netdev_get_ip_info(NETIF_IDX_STA, &ip_info);
LOG(LOG_LVL_INFO, "IP: %s, Mask: %s, Gateway: %s\n",
ip4addr_ntoa(&ip_info.ip), ip4addr_ntoa(&ip_info.netmask), ip4addr_ntoa(&ip_info.gw));

// ?? WiFi ????(RSSI,?? dBm),????
int8_t rssi;
wifi_sta_get_rssi(&rssi);
LOG(LOG_LVL_INFO, "WiFi RSSI: %d dBm\n", rssi);

// ????????,???????? socket ????????
while (netdev_got_ip()) {
if (sockfd < 0) { // ???,??? socket ??
sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) { // ???? 2s ??
LOG(LOG_LVL_ERROR, "Socket create error: %d\n", errno);
OS_MsDelay(2000);
continue;
}
LOG(LOG_LVL_INFO, "Socket create ID=%d\n", sockfd);

// ?? socket ??????(FIONBIO)???????
int flags = 1;
if (lwip_ioctl(sockfd, FIONBIO, &flags) < 0) {
LOG(LOG_LVL_ERROR, "socket ioctl error: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// ???????
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = lwip_htons(8080);
server_addr.sin_addr.s_addr = inet_addr("192.168.2.27");
LOG(LOG_LVL_INFO, "Connecting to 192.168.2.27:8080...\n");

// ????? IP ? ??,???????? socket ??
int connect_result = lwip_connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (connect_result < 0 && errno != EINPROGRESS) {
LOG(LOG_LVL_ERROR, "lwip_connect failed: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// ??????
fd_set writefds, exceptfds;
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET(sockfd, &writefds);
FD_SET(sockfd, &exceptfds);
int select_result = lwip_select(sockfd + 1, NULL, &writefds, &exceptfds, &tv);
if (select_result <= 0 || FD_ISSET(sockfd, &exceptfds)) {
LOG(LOG_LVL_ERROR, "Connect timed out or failed: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// ??????
int so_error = 0;
socklen_t len = sizeof(so_error);
if (lwip_getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0 || so_error != 0) {
LOG(LOG_LVL_ERROR, "Connect failed with error: %d\n", so_error);
lwip_close(sockfd);
sockfd = -1;
OS_MsDelay(2000);
continue;
}

// ??????
LOG(LOG_LVL_INFO, "Connected to 192.168.2.27:8080\n");
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
if (lwip_setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
LOG(LOG_LVL_ERROR, "setsockopt SO_RCVTIMEO failed: %d\n", errno);
}

// ????
state = STATE_IDLE;
has_partial = 0;
temp_pos = 0;
data_type = 0;
total_written = 0;
data_sum_size = 0;
is_get_sum_size = 0;
last_data_time = 0;
}

// socket ? WiFi ????????
while (sockfd >= 0 && netdev_got_ip()) {
switch (state) {
case STATE_IDLE: { // ?? 3 ????????
uint8_t response[3];
int len = lwip_recv(sockfd, response, sizeof(response), 0);
if (len < 0) {
// ?????? break
if (errno == EAGAIN || errno == ETIMEDOUT) {
OS_MsDelay(100);
break;
}
// ?????
LOG(LOG_LVL_ERROR, "STATE_IDLE len < 0: %d\n", errno);
lwip_close(sockfd);
sockfd = -1;
break;
} else if (len == 0) { // 0 ?????????
LOG(LOG_LVL_INFO, "Server closed connection\n");
lwip_close(sockfd);
sockfd = -1;
break;
}

// ????????????? 0xAA 0x01 0xFF
if (len == 3 && response[0] == 0xAA && response[1] == 0x01 && response[2] == 0xFF) {
LOG(LOG_LVL_INFO, "recv start...\n");
send_ACK(sockfd, 0xAA);
state = STATE_TYPE;
} else {
LOG(LOG_LVL_ERROR, "recv start error: %02X %02X %02X\n", response[0], response[1], response[2]);
send_ACK(sockfd, 0xEE);
}
break;
}

case STATE_TYPE: { // ?? receive_data ???????
int ret = receive_data(sockfd, &has_partial, temp_buffer, &temp_pos, &data_type, &total_written);
// -1 ? -2:socket ???????,?? socket?
if (ret == -1 || ret == -2) {
LOG(LOG_LVL_INFO, "STATE_TYPE(sockfd)...\n");
lwip_close(sockfd);
sockfd = -1;
break;
} else if (ret == -3) { // -3:??????,??? STATE_IDLE
LOG(LOG_LVL_ERROR, "Unexpected end signal in STATE_TYPE\n");
state = STATE_IDLE;
break;
}

// ??????? 0x11(??)? 0x12(??),?????????? data_type
if (data_type == 0x11 || data_type == 0x12) {
state = (data_type == 0x11) ? STATE_PRINT : STATE_AUDIO;
data_type = 0;// ??????
}
break;
}

// ????
case STATE_PRINT: {
if (!is_get_sum_size) { // ?????????
int ret = receive_sum_size(sockfd, &data_sum_size);
if (ret == -1) {
LOG(LOG_LVL_ERROR, "receive_sum_size error\n");
lwip_close(sockfd);
sockfd = -1;
break;
}
if (ret == 1) {
is_get_sum_size = 1;
total_written = 0;
last_data_time = OS_GetTicks();
LOG(LOG_LVL_INFO, "STATE_PRINT sum size: %u bytes\n", data_sum_size);
}
} else {
int ret = receive_data(sockfd, &has_partial, temp_buffer, &temp_pos, &data_type, &total_written);
if (ret == -1) { // socket ??,?? socket?
LOG(LOG_LVL_INFO, "STATE_PRINT(sockfd)...\n");
lwip_close(sockfd);
sockfd = -1;
break;
} else if (ret == -2) { // ???????,?????????? socket
LOG(LOG_LVL_INFO, "Server closed connection\n");
if (total_written < data_sum_size) {
LOG(LOG_LVL_ERROR, "Incomplete print data: received %u of %u bytes\n", total_written, data_sum_size);
}
lwip_close(sockfd);
sockfd = -1;
is_get_sum_size = 0;
total_written = 0;
print_write_pos = 0;
print_read_pos = 0;
state = STATE_IDLE;
break;
} else if (ret == -3) { // ??????,????????? STATE_IDLE
if (total_written < data_sum_size) {
LOG(LOG_LVL_ERROR, "print data not wz: received %u of %u bytes\n", total_written, data_sum_size);
}
is_get_sum_size = 0;
total_written = 0;
print_write_pos = 0;
print_read_pos = 0;
state = STATE_IDLE;
break;
}else if (ret == 1) {
// ???????????
LOG(LOG_LVL_INFO, "New session started after end signal\n");
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_TYPE; // ????????????
break;
}
// ??????
last_data_time = OS_GetTicks(); // ???????
// ?? 5s,??????????
if (OS_GetTicks() - last_data_time > pdMS_TO_TICKS(5000)) {
LOG(LOG_LVL_ERROR, "Timeout waiting for print data\n");
lwip_close(sockfd);
sockfd = -1;
is_get_sum_size = 0;
total_written = 0;
print_write_pos = 0;
print_read_pos = 0;
state = STATE_IDLE;
break;
}
// ???????
LOG(LOG_LVL_INFO, "Received %u of %u bytes\n", total_written, data_sum_size);
}
break;
}

case STATE_AUDIO: {
int ret = receive_data(sockfd, &has_partial, temp_buffer, &temp_pos, &data_type, &total_written);
if (ret == -1) {
LOG(LOG_LVL_INFO, "lwip_close(sockfd)...\n");
lwip_close(sockfd);
sockfd = -1;
break;
} else if (ret == -2) {
LOG(LOG_LVL_INFO, "Server closed connection\n");
lwip_close(sockfd);
sockfd = -1;
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_IDLE;
break;
} else if (ret == -3) {
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_IDLE;
break;
}else if (ret == 1) {
// ???????????
LOG(LOG_LVL_INFO, "New session started after end signal\n");
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_TYPE; // ????????????
break;
}

last_data_time = OS_GetTicks();
if (OS_GetTicks() - last_data_time > pdMS_TO_TICKS(5000)) {
LOG(LOG_LVL_ERROR, "Timeout waiting for audio data\n");
lwip_close(sockfd);
sockfd = -1;
total_written = 0;
rx_write_pos = 0;
rx_read_pos = 0;
state = STATE_IDLE;
break;
}

// ????????(PCMBuffer_tx)????,?????,??? TCP socket ???????
// ?????
if (xSemaphoreTake(tx_data_ready_sem, pdMS_TO_TICKS(100)) == pdTRUE) {
uint16_t data_length = 0; // ???????
int16_t data[MAX_DATA_PER_PACKET / 2]; // ??????????????
taskENTER_CRITICAL();
// ?? PCMBuffer_tx ???????
uint32_t available = (tx_write_pos >= tx_read_pos) ? (tx_write_pos - tx_read_pos) : (RING_BUFFER_SIZE - tx_read_pos + tx_write_pos);
// SEND_THRESHOLD ??????,??????
if (available >= SEND_THRESHOLD / 2) {
data_length = (available > MAX_DATA_PER_PACKET / 2) ? (MAX_DATA_PER_PACKET / 2) : available;
// ? PCMBuffer_tx ??? data_length ???
read_circular_buffer(PCMBuffer_tx, (uint32_t *)&tx_read_pos, (uint32_t *)&tx_write_pos, RING_BUFFER_SIZE, data, data_length, sizeof(int16_t));
}
taskEXIT_CRITICAL();

// ????????
if (data_length > 0) {
uint8_t send_buffer[7 + MAX_DATA_PER_PACKET];
send_buffer[0] = 0xAA;
send_buffer[1] = 0x12;
send_buffer[2] = (data_length * 2 >> 8) & 0xFF;
send_buffer[3] = (data_length * 2) & 0xFF;
memcpy(&send_buffer[4], data, data_length * 2);
uint16_t crc = calculate_crc16((uint8_t *)data, data_length * 2);
send_buffer[4 + data_length * 2] = (crc >> 8) & 0xFF;
send_buffer[5 + data_length * 2] = crc & 0xFF;
send_buffer[6 + data_length * 2] = 0xFF;

int sent = lwip_send(sockfd, send_buffer, 7 + data_length * 2, 0);
if (sent != 7 + data_length * 2) {
LOG(LOG_LVL_ERROR, "Send data failed: expected %d, sent %d\n", 7 + data_length * 2, sent);
lwip_close(sockfd);
sockfd = -1;
break;
} else {
LOG(LOG_LVL_INFO, "Sent %d bytes (data length: %u)\n", sent, data_length * 2);
}
}
}
break;
}
}
}

if (sockfd < 0) {
LOG(LOG_LVL_ERROR, "Socket disconnect, retrying...\n");
OS_MsDelay(2000);
}
}
LOG(LOG_LVL_ERROR, "WiFi disconnected, waiting...\n");
OS_MsDelay(2000);
}
}