WiFi 모듈 응답을 사람이 처리하는 것이 아니기 때문에 코딩을 통해 문자열을 파싱하려고 한다면 WiFi 모듈의 응답에 규칙성이 있어야 한다고 생각합니다.
TCP connection 을 맺는 것까지 몇 개의 명령을 테스트해본 결과
UART_CUR? 이라는 query 문을 제외하고는 과 OK 메시지 사이가 0x0d, 0x0a, 0x0d, 0x0a 로 구분이 됩니다. 그런데 UART_CUR? query 만 0x0d, 0x0a 로 구분이 되네요.
command: [AT+UART_CUR?]
uwizfi response =====> [
AT+UART_CUR?
+UART_CUR:115200,8,1,0,0
OK
]
이것은 의도된 것인가요? 오류인가요?
가끔 OK 메시지 이후에 쓰레기 문자열이 따라붙는 것 같습니다. CWMODE_CUR? 퀘리에 대한 응답입니다.
정상적인 응답
wizfi response: [AT+CWMODE_CUR?
+CWMODE_CUR:1
OK
]
다음은 비정상적인 응답으로 보이는 사례입니다.
[AT+CWMODE_CUR?
+CWMODE_CUR:1
OK
AT+]
모든 응답은 0x0d, 0x0a 로 끝나는 것으로 보이는데 비정상적인 응답에서는 AT+ 로 끝나고 있습니다.
제가 사용하는 코드의 일부입니다.
ret = UART_READ(device, wifiRXbuf, sizeof(wifiRXbuf));
wifiRXbuf[ret] = ‘\0’;
CONSOLE_PRINTF(“wizfi response: [%s]\n”, wifiRXbuf);
위에 질문 중에 2번째 질문은 취소하는 것이 맞을 듯 합니다. 여러 번의 명령/퀘리가 연속적으로 내려질 때 첫번째 명령의 응답 뒤에 그 다음 명령/퀘리에 대한 답변이 따라붙는 것으로 보입니다.
추가 질문(혹은 제안)이 있습니다.
AT 명령을 처리하다보니 line by line 으로 처리하는 것이 제일 효율적인 듯 합니다. 그런데 명령/퀘리의 종류에 따라 응답 포맷에 약간씩 차이가 있어 응답을 처리하기 위한 로직이 복잡해지는 듯 합니다.
처음 커넥션을 맺기 위한 동작까지는 어떻게 해도 큰 지장이 없습니다만 커넥션을 맺은 이후 +IPD 와 +CIPSEND 가 관건인 것으로 생각됩니다.
현재 binary file 을 WizFi 를 통해 보내려고 하고 있습니다. AT 명령을 통해야 하니 바이너리 그래도 보내진 못할 것이고 base64 인코딩을 통해서 보내려고 합니다. base64 인코딩 이전에 데이터 통신 방식은…
after TCP connection established.
단말 → 서버: 파일 전송 요청
서버 → 단말: 일정량의 데이타 전송
서버 → 단말: data integrity 체크 요청
단말: CRC 체크.
단말 → 서버: 파일의 다음 파트 전송 요청.
서버 → 단말: 일정량의 데이타 전송.
…
대체적으로 이런 식으로 통신이 이루어질 것인데요. WizFi 의 +IPD 형식이 불만족스럽습니다. 왜냐하면 기존 명령/퀘리 형식과 달리 <0x0d><0x0a> 가 없어서 line by line 처리가 어렵기 때문입니다. 즉 +IPD 스트링과 명령/퀘리 응답이 버퍼에 섞여 있다고 할 때 그 구분이 쉽게 되지 않습니다. 불가능하다는 얘기는 아닙니다. 되긴 하지만 로직을 더 쉽게 할 수 있는 방법이 있다고 생각하기 때문입니다.
인터넷에서 검색을 해보니 어떤 WiFi 모듈은 아래의 형식을 가지고 있더군요. 다른 명령/퀘리와 동일한 포맷을 가지고 있어서 명령/퀘리 처리 루틴을 가지고 서버에서 보내는 메시지를 쉽게 처리할 수 있습니다.
<0x0d><0x0a>+IPD,0,1:A<0x0d><0x0a>OK<0x0d><0x0a>
그에 반해서 WizFi 의 +IPD 포맷은 라인별로 처리를 할 수 없습니다.
+IPD,0,1:A
만약 아래와 같은 형식을 지원해준다고 한다면 별도로 +IPD 를 처리하기 위한 루틴을 만들지 않아도 됩니다. 왜냐하면 명령/퀘리와 동일하게 처리가 가능하기 때문에 서버 메시지를 매우 쉽게 분리할 수 있습니다.
+IPD,0,512,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…AAAA<0x0d><0x0a>
혹은
+IPD,0,512,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA…AAAA<0x0d><0x0a>OK<0x0d><0x0a>
현재 상황에서 SEND OK 메시지를 파싱하지 않기 위해서 “transparent transmission mode” 를 사용할 수 밖에 없다는 결론을 얻었습니다. 하지만 send 결과를 알 수 없다는 것은 좋은 일이 아니죠.
원래 목표했던 작업을 마무리하고 보니 +IPD 에 <0x0d><0x0a> 존재해야 하는 이유가 더욱 분명합니다. 단순히 고려할 문제가 아니라 그 방향이 맞다고 생각합니다.
왜냐하면… 누군가 line by line 파싱을 하려고 한다고 가정합니다. 다음 pseudo code 를 보시죠.
len = blocked_read(buffer, size)
push_into_ring_buffer(ring, buffer, len);
/* skip garbage 0x0d, 0x0a /
skip_0x0d_0x0a_in_ring(ring);
peek_ring_buffer(ring, &byte, 1);
if(byte == ‘+’) {
len = recv_svrmsg(device, ring, buffer1, size1);
return len;
}
ret = get_a_single_line_from_ring_buffer(ring, buffer1, size1);
if(ret > 0) {
/ a single line provided by the ring buffer. */
parse_a_single_line();
}
recv_svrmsg()가 호출되는 시점에서 ring_buffer 가 아래와 같다고 하면…
+IPD,10:012345
recv_svrmsg() 는 컬렉트해야할 데이타가 부족하기 때문에 결국 리턴을 해서 state 를 관리함으로써 blocked_read()가 호출되도록 하거나… 자신의 내부에서 blocked_read() 를 다시 호출해야 합니다. 개발자는 안 좋은 두 개의 선택에서 더 나은 것을 선택해야합니다. 실제 파싱을 하기 전에 length field 를 파싱할 것이냐 혹은 recv_svrmsg() 내에서 blocked_read() 를 다시 호출 할 것이냐.
아래와 같은 형식을 제공한다고 치죠.
+IPD,10:0123456789<0x0d><0x0a>OK<0x0d><0x0a>
그러면 하나의 라인을 잘라내서 parse_a_single_line() 에 던져주면 됩니다. parse_a_single_line() 명령/퀘리 응답과 함께 IPD 메시지라는 것을 알고 필드를 분석해서 잘라낸 데이타를 리턴해주면 그만입니다.
버퍼에 아래의 데이타만 존재한다면 라인이 만들어지지 않습니다.
+IPD,10:012345
따라서 다시 맨 윗줄의 blocked_read() 를 호출하도록 하면 됩니다.
Str 데이터만을 사용한다면 위와 같은 방법이 사용자에게는 편할 수는 있으나 Hex 데이터를 전송할 때에는 혼란을 야기할 수도 있습니다. 데이터 중간에 <0x0d><0x0a> 자체가 데이터가 되어 버린다면 어떤게 데이터고 어떤 것이 종료 구분자인지 파악할 수 없게 되어 버립니다.
제가 말씀 드린것은 Hex 데이터일때의 0x0d,0x0a 구분을 이야기 드린것입니다.
또한 수신하실때 수신데이터에서 0x0d, 0x0a로 구분자로 데이터를 파싱한다고 하셔서 한 말씀입니다.
length field로 하신다면 0x0d 0x0a를 마지막 구분자로 사용할 필요가 없지 않을 까요?
length 까지만 데이터를 받으면 되지 않을까요?
그리고 구분자를 넣으시고 싶으시면 데이터를 보내실때 구분자를 포함해서 전송하시면 그것으로 데이터를 파싱하시는게 편하실것 같습니다.
제가 <0x0d><0x0a>를 넣었으면 좋겠다는 내용에 대해서는 충분히 설명했다고 생각합니다만.
그런데 전혀 이해를 못하고 계시는 군요. 만약 제가 사용하는 구분자에 맞춰서 IPD 메시지가 딱 끝난다면 여기와서 이런 얘기할 필요가 없습니다. 그렇지 못하기 때문에 length field 를 파싱하기 전에 +IPD 메시지의 끝을 알고 싶은 겁니다. 그래야 전체적인 파싱 루틴이 더 효율적이기 때문입니다.
저는 넣었으면 좋겠다는 주장을 하는 것일 뿐이고요. 넣고 싶지 않다는 말씀이 하고 싶다면 거절하시면 됩니다.
메세지의 딱 끝을 확인 하고 싶으시다면 데이터 프로토콜에 0x0d 0x0a를 넣어서 보내시는 것이 말씀하시는대로 효휼적일것 같아 말씀 드리는 겁니다. 0x0d 0x0a까지 받아 확인 되면 전체 데이터 길이를 확인 하는 것이 확실 할 것 같습니다. 굳이 0x0d 0x0a OK 0x0d 0x0a 이메세지가 따로 없다고 해도 말이죠