|
发表于 2013-5-27 00:06:54
|
显示全部楼层
好久没上论坛看帖子,看到客户对我们的直接评价,很欣慰。
基于单片机的开源代码,我们这方面确实是弱项,原因:1、我们没有玩STC单片机;2、我们的开源方面已经有OPEN AT的DEMO代码,里面带一个AT指令处理框架,我们相信这个框架一些编程思想可以应用到其它平台。AT指令的基本格式是请求-应答,除了极少数是unsolicited,比如,AT+CREG=2 后,模块主动上报基站信息;有来电时,会上报“RING”提示等。AT指令的标准格式是行处理,指令返回都是通过"\r\n“来分行(编码时,"\r"也足够了),有时候串口并非一次返回,可能返回的结果是分段返回。
一般购买我们的GU900D模块,我们提供合适的原理图、封装库等方便客户开发,缩短客户开发周期是我们的希望所在。
#include "gu_sdk.h"
#include "custom_app.h"
#include "at_handler.h"
static gu_char g_cmd_buf[512];
static gu_uint16 g_cmd_idx = 0;
static at_command_type_enum g_at_cmd_type;
static const char *g_response_prefix = NULL;
static at_response_struct *g_sp_response = NULL;
static const char * s_unsolicited_msg[] = {
"+IPD:", // TCP IP data coming
"+CMT:", // SMS message coming
"+CMTI:", // SMS message coming
"+CREG:", /* sometimes! */
"RING", // Somebody call me
};
static const char * s_finalResponsesError[] = {
"ERROR",
"+CMS ERROR:",
"+CME ERROR:",
"NO CARRIER", /* sometimes! */
};
static const char * s_finalResponsesSuccess[] = {
"OK",
"CONNECT OK" /* some stacks start up data on another channel */
};
/** returns 1 if line starts with prefix, 0 if it does not */
int strStartsWith(const char *line, const char *prefix)
{
for ( ; (*line != '\0' && *line != '\r') && *prefix != '\0' ; line++, prefix++)
{
if (*line != *prefix)
{
return 0;
}
}
return (*prefix == '\0');
}
int at_is_final_response_error(const char *line)
{
gu_uint8 i;
for (i = 0 ; i < NUM_ELEMS(s_finalResponsesError) ; i++)
{
if (strStartsWith(line, s_finalResponsesError[i]))
{
return 1;
}
}
return 0;
}/** * returns 1 if line is a final response indicating success * See 27.007 annex B * WARNING: NO CARRIER and others are sometimes unsolicited */
int at_is_final_response_success(const char *line)
{
gu_uint8 i;
for (i = 0 ; i < NUM_ELEMS(s_finalResponsesSuccess) ; i++)
{
if (strStartsWith(line, s_finalResponsesSuccess[i]))
{
return 1;
}
}
return 0;
}
at_unsolicited_msg_enum at_check_unsolicited_msg(const char *line)
{
gu_uint8 i;
for (i = 0 ; i < NUM_ELEMS(s_unsolicited_msg) ; i++)
{
if (strStartsWith(line, s_unsolicited_msg[i]))
{
return i;
}
}
return UNSOL_END;
}
static gu_char *at_strdup(const gu_char *str)
{
gu_uint16 len = strlen(str);
gu_char *strtmp = gu_app_malloc(len + 1);
strcpy(strtmp, str);
return strtmp;
}
/** add an intermediate response to g_sp_response*/
static void add_intermediate(at_response_struct *p_response, const gu_char *line)
{
at_line_struct *p_new;
p_new = (at_line_struct*)gu_app_malloc(sizeof(at_line_struct));
p_new->line = at_strdup(line);
/* note: this adds to the head of the list, so the list
will be in reverse order of lines received. the order is flipped
again before passing on to the command issuer */
p_new->p_next = p_response->p_intermediates;
p_response->p_intermediates = p_new;
}
at_response_struct *at_response_new()
{
at_response_struct *resp = (at_response_struct *)gu_app_malloc(sizeof(at_response_struct));
if (resp == NULL)
{
return NULL;
}
memset(resp, 0, sizeof(at_response_struct));
return resp;
}
void at_response_free(at_response_struct *p_response)
{
at_line_struct *p_line;
if (p_response == NULL) return;
p_line = p_response->p_intermediates;
while (p_line != NULL)
{
at_line_struct *p_toFree;
p_toFree = p_line;
p_line = p_line->p_next;
gu_app_free(p_toFree->line);
gu_app_free(p_toFree);
}
if (p_response->final_response)
gu_app_free(p_response->final_response);
gu_app_free(p_response);
}
static void at_handle_unsolicited(const char *line)
{
at_unsolicited_msg_enum unsol_msg;
unsol_msg = at_check_unsolicited_msg(line);
switch (unsol_msg)
{
case UNSOL_IPD:
break;
case UNSOL_CMT:
{
sms_cmt_msg_handler(unsol_msg, line, GU_TRUE);
break;
}
case UNSOL_CMTI:
break;
case UNSOL_CREG:
break;
case UNSOL_RING:
break;
default:
{
sms_cmt_msg_handler(unsol_msg, line, GU_TRUE);
UNIT_TEST_DBG(GU_AT_PROC, "Unsupported response type %d\n", unsol_msg);
break;
}
}
g_cmd_idx = 0;
}
/*****************************************************************************
* FUNCTION
* at_process_line()
* DESCRIPTION
* When AT result is comming, it should be processed by this function. It shall parse the AT result one line by one line.
* All AT command can be seperated by '\r\n'.
*
* PARAMETERS
* line[in], one line content in the AT result.
*
* RETURN
* NONE
* ...:
*****************************************************************************/
static void at_process_line(const gu_char *line)
{
UNIT_TEST_DBG(GU_AT_PROC, "at_process_line=%s", line);
if (g_sp_response == NULL) {
/* no command pending */
at_handle_unsolicited(line);
}
// Check final response
else if (at_is_final_response_success(line) && (!g_response_prefix ||
g_sp_response->p_intermediates ))
{
g_sp_response->success = AT_RET_SUCCESS;
}
else if (at_is_final_response_error(line))
{
g_sp_response->success = AT_RET_ERROR;
}
else if (g_response_prefix && strcmp(line, "> ") == 0 && strcmp(line, g_response_prefix) == 0)
{
g_sp_response->success = AT_RET_SUCCESS;
}
else
{
switch (g_at_cmd_type)
{
case NO_RESULT:
at_handle_unsolicited(line);
break;
case NUMERIC:
if (g_sp_response->p_intermediates == NULL
&& ((line[0] >= '0') && (line[0] <= '9')))
{
add_intermediate(g_sp_response, line);
}
else
{
/* either we already have an intermediate response or
the line doesn't begin with a digit */
at_handle_unsolicited(line);
}
break;
case SINGLELINE:
if (g_sp_response->p_intermediates == NULL && strStartsWith (line, g_response_prefix))
{
g_sp_response->success = AT_RET_SUCCESS;
add_intermediate(g_sp_response, line);
}
else if (!at_is_final_response_success(line))
{
/* we already have an intermediate response */
at_handle_unsolicited(line);
}
break;
case MULTILINE:
if (strStartsWith (line, g_response_prefix))
{
add_intermediate(g_sp_response, line);
}
else
{
at_handle_unsolicited(line);
}
break;
case WAIT_EXPECT:
if (strStartsWith (line, g_response_prefix))
{
g_sp_response->success = AT_RET_SUCCESS;
}
break;
default: /* this should never be reached */
UNIT_TEST_DBG(GU_AT_PROC, "Unsupported AT command type %d\n", g_at_cmd_type);
at_handle_unsolicited(line);
break;
}
}
}
/*****************************************************************************
* FUNCTION
* at_event_handler()
* DESCRIPTION
* When AT result is comming, it should be processed by this function. It shall parse the AT result one line by one line.
* All AT command can be seperated by '\r\n'.
*
* PARAMETERS
* msg_req[in], the message of AT result from core.
*
* RETURN
* NONE
* ...:
*****************************************************************************/
void at_event_handler(gu_message_req_struct *msg_req)
{
gu_char result_str[512 + 1];
gu_uint16 result_idx = 0;
gu_uint16 n = 0, i = 0, len ;
gu_bool is_line;
memset(result_str, 0, sizeof(result_str));
// Store the unfinished data for last time
for (n = 0; n < g_cmd_idx; )
{
result_str[n] = g_cmd_buf[n];
n++;
}
is_line = GU_FALSE;
while (i < msg_req->data_length)
{
while (msg_req->data[i] == '\r' || msg_req->data[i] == '\n')
{
i++;
if (n > 0)
is_line = GU_TRUE;
}
for (; i < msg_req->data_length && n < sizeof(result_str); i++)
{
if (msg_req->data[i] == '\r' &&
msg_req->data[i+1] == '\n') // for sms,tcp,http,ftp prompt
{
if (n > 0)
is_line = GU_TRUE;
break;
}
g_cmd_buf[g_cmd_idx++] = msg_req->data[i];
result_str[n++] = msg_req->data[i];
}
len = strlen(result_str);
if (!is_line && result_str[0] == '>') // For SMS, TCP, HTTP, FTP etc
{
is_line = GU_TRUE;
}
if (len == 0 || !is_line)
{
break;
}
//UNIT_TEST_DBG(GU_AT_PROC, "i=%d, n=%d, dlen=%d, idx=%d:\"%s\"", i, n,
// msg_req->data_length, g_cmd_idx, result_str);
g_cmd_idx = 0;
at_process_line(result_str);
memset(result_str, 0, sizeof(result_str));
n = 0;
}
while (msg_req->data[i] == '\r' || msg_req->data[i] == '\n')
i++;
//// write the rest data to g_cmd_buf for next line
if (is_line)
{
for (g_cmd_idx = 0; i < msg_req->data_length && g_cmd_idx < sizeof(g_cmd_buf); i++)
{
g_cmd_buf[g_cmd_idx++] = msg_req->data[i];
}
}
//UNIT_TEST_DBG(GU_AT_PROC, "at_event_handler(len=%d) l=%d, n=%d, idx=%d", msg_req->data_length,
// is_line, n, g_cmd_idx);
}
/*****************************************************************************
* FUNCTION
* at_exe_cmd_wait_result()
* DESCRIPTION
* User can use this function to send AT command to the SDK core, then wait for the result , this function just provides
* a good method to avoid ugly program in the source code. The user also can send AT command to the SDK core with
* the gu_send_at_command function, but you need to get the result from the main message in GU_MSG_AT_RESULT .
* after releasing the current thread.
*
* PARAMETERS
* at_cmd_data[in], AT command or data sent to core.
* data_len[in], the length of AT command or data
* cmd_type[in], refer to at_command_type_enum in gu_sdk_def.h
* response_prefix[in],the prefix of the AT command result, for example, "AT+CREG?" returns "+CREG: 0,x", the prefix is "+CREG"
* timeout_ms[in], command executation timeout
* p_response_out[in,out], result shall be stored in this param, caller should allocate a new at_response_struct and release it by himself
*
* RETURN
* NONE
* ...:
*****************************************************************************/
at_ret_code_enum at_exe_cmd_wait_result(gu_char *at_cmd_data,
gu_uint16 data_len,
at_command_type_enum cmd_type,
const gu_char *response_prefix,
gu_uint32 timeout_ms,
at_response_struct *p_response_out)
{
gu_message_req_struct *msg_req = NULL;
at_ret_code_enum ret = AT_RET_SUCCESS;
if (gu_send_at_command(at_cmd_data, data_len) == GU_FALSE)
{
UNIT_TEST_DBG(GU_AT_PROC, "AT data queue is full %d", data_len);
return GU_FALSE;
}
if (p_response_out)
{
g_sp_response = p_response_out;
}
else
{
g_sp_response = at_response_new();
}
g_at_cmd_type = cmd_type;
g_response_prefix = response_prefix;
g_sp_response->success = AT_RET_INIT;
gu_app_start_timer(timeout_ms);
if (data_len < 50)
UNIT_TEST_DBG(GU_AT_PROC, "at_exe_cmd_wait_result(\"%s\",%d)", at_cmd_data, timeout_ms);
else
UNIT_TEST_DBG(GU_AT_PROC, "at_exe_cmd_wait_result(%d)", timeout_ms);
while (g_sp_response->success == AT_RET_INIT)
{
if ((msg_req = gu_get_message()))
{
if (msg_req->msg_type == GU_MSG_AT_RESULT)
{
at_event_handler(msg_req);
}
else if (msg_req->msg_type == GU_MSG_TIMER_EXPIRE) // main timer is expire
{
g_sp_response->success = AT_RET_TIMEOUT;
UNIT_TEST_DBG(GU_AT_PROC, "main timer is expire", timeout_ms);
}
else // process other message from CORE
{
cm_message_handler(msg_req);
}
gu_free_message(msg_req);
}
}
gu_app_stop_timer();
ret = g_sp_response->success;
// Release response memory if app user don't care it
if (p_response_out == NULL)
{
at_response_free(g_sp_response);
}
g_sp_response = NULL;
g_response_prefix = NULL;
return ret;
}
/*****************************************************************************
* FUNCTION
* sms_cmd_handler()
* DESCRIPTION
* Whatever the sms_text is come from SMS or uart, we can handler it as a command and do what we would like to.
*
* PARAMETERS
* sms_text[in], the receiving SMS message
* send_sms[in], do we need to send SMS message.
* RETURN
* GU_TRUE or GU_FALSE;
* ...:
*****************************************************************************/
gu_bool sms_cmd_handler(const gu_char *sms_text, gu_bool send_sms)
{
gu_char phone_num[32];
gu_bool success = GU_FALSE;
if (!strncmp(sms_text, "zjyh:", 5))
{
if (!sms_get_phone_num(sms_text, "zjyh:", phone_num))
{
sms_adm_send_response("zjyh:the phone number is incorrect", GU_FALSE, send_sms);
return GU_TRUE;
}
success = sms_update_admin_role(phone_num, ROLE_USER, ACTION_ADD);
sms_adm_send_response((success ? "zjyh:OK" : "zjyh:the user is existed"), GU_FALSE, send_sms);
return GU_TRUE;
}
else if (!strncmp(sms_text, "scyh:", 5))
{
if (!sms_get_phone_num(sms_text, "scyh:", phone_num))
{
sms_adm_send_response("scyh:phone number is incorrect", GU_FALSE, send_sms);
return GU_TRUE;
}
if (sms_get_user_role(phone_num) == ROLE_ADMIN)
{
sms_adm_send_response("scyh:you can't delete admin", GU_FALSE, send_sms);
return GU_TRUE;
}
success = sms_update_admin_role(phone_num, ROLE_USER, ACTION_DEL);
sms_adm_send_response((success ? "scyh:OK" : "scyh:there is no user to be deleted"), GU_FALSE, send_sms);
return GU_TRUE;
}
else if (!strncmp(sms_text, "cxyh", 4))
{
gu_char buffer[256];
gu_int8 i;
buffer[0] = '\0';
for (i = 0; i < MAX_ADMIN_NUM; i++)
{
if (g_admin_role[i].role == ROLE_NONE)
{
break;
}
sprintf(buffer + strlen(buffer), "cxyh:%s:%d\r\n", g_admin_role[i].admin_phone, g_admin_role[i].role);
}
if (strlen(buffer) > 0)
sms_adm_send_response(buffer, GU_TRUE, send_sms);
else
sms_adm_send_response("cxyh:no user", GU_FALSE, send_sms);
return GU_TRUE;
}
else if (!strncmp(sms_text, "szgly:", 6))
{
if (!sms_get_phone_num(sms_text, "szgly:", phone_num))
{
sms_adm_send_response("szgly:you didn't input phone number", GU_FALSE, send_sms);
return GU_TRUE;
}
// Need to delete the user who exists in phone book
if (sms_get_user_role(phone_num) == ROLE_USER)
{
if (!sms_update_admin_role(phone_num, ROLE_USER, ACTION_DEL))
{
sms_adm_send_response("szgly:failed to delete the user", GU_FALSE, send_sms);
return GU_TRUE;
}
}
success = sms_update_admin_role(phone_num, ROLE_ADMIN, ACTION_ADM_UPT);
sms_adm_send_response((success ? "szgly:admin had been changed." : "szgly:error"), GU_FALSE, send_sms);
return GU_TRUE;
}
else if (!strncmp(sms_text, "setbaud:", 8))
{
gu_char buffer[64];
if (!sms_get_phone_num(sms_text, "setbaud:", phone_num))
{
sms_adm_send_response("setbaud:baudrate set error", GU_FALSE, send_sms);
return GU_TRUE;
}
sprintf(buffer, "AT+CIPR=%s\r\n", phone_num);
success = (at_exe_cmd_wait_result(buffer, strlen(buffer), SINGLELINE, NULL, 3000, NULL) == AT_RET_SUCCESS);
sms_adm_send_response((success ? "setbaud:OK" : "setbaud:ERROR"), GU_FALSE, send_sms);
return GU_TRUE;
}
return GU_FALSE;
}
gu_bool sms_get_phone_num(const gu_char *sms_text,
const gu_char *prefix, gu_char *phone_num)
{
gu_bool ret = GU_FALSE;
const gu_char *tmp = NULL;
if ((tmp = (gu_char*)strstr(sms_text, prefix)))
{
const gu_char *tmp2 = NULL;
tmp = tmp + strlen(prefix);
if ((tmp2 = (gu_char*)strstr(tmp, "+86"))) // Chinese area code
{
tmp = tmp2 + 3;
}
while (*tmp != ',' && *tmp != '\0' && *tmp != '\r' && *tmp != '\n')
{
if ((*tmp >= '0') && (*tmp <= '9'))
{
*phone_num = *tmp;
phone_num++;
ret = GU_TRUE;
}
tmp++;
}
}
*phone_num = '\0';
UNIT_TEST_DBG(GU_SMS_UART, "sms_get_phone_num(%s,%s)=%s", sms_text, prefix, phone_num);
return ret;
}
gu_bool sms_get_quote_str(const gu_char *sms_text,
const gu_char *prefix, const gu_char start, const gu_char end, gu_char *text_out)
{
gu_bool ret = GU_FALSE;
gu_char *tmp = NULL;
if ((tmp = (gu_char*)strstr(sms_text, prefix)))
{
tmp = tmp + strlen(prefix);
while (*tmp != start)
tmp++;
tmp++;
while (*tmp != end)
{
*text_out = *tmp;
text_out++;
tmp++;
}
ret = GU_TRUE;
}
*text_out = '\0';
UNIT_TEST_DBG(GU_SMS_UART, "sms_get_quote_str=(%s)", text_out);
return ret;
}
/*****************************************************************************
* FUNCTION
* sms_cmt_msg_handler()
* DESCRIPTION
* Check the comming SMS message to see if the message needs to be handlered.
*
* PARAMETERS
* unsol_msg[in], SMS message type.
* sms_text[in], the receiving SMS message
* RETURN
* NONE
* ...:
*****************************************************************************/
void sms_cmt_msg_handler(at_unsolicited_msg_enum unsol_msg, const gu_char *sms_text,
gu_bool send_sms)
{
UNIT_TEST_DBG(GU_SMS_UART, "sms_cmt_msg_handler(%d,%s,%d)role=%d, %d", unsol_msg, sms_text, send_sms,
g_sms_user_role, g_unsol_msg);
if (unsol_msg == UNSOL_CMT)
{
g_sms_user_role = ROLE_NONE;
// 1. get mobile number
if (sms_get_phone_num(sms_text, "+CMT: ", gu_phone_num))
{
// 2. Check if the mobile number is administrator or normal user
if (g_first_bootup == GU_TRUE)
{
g_sms_user_role = ROLE_ADMIN;
//sms_update_admin_role(phone_num, g_sms_user_role, ACTION_ADM_UPT);
}
else
{
g_sms_user_role = sms_get_user_role(gu_phone_num);
}
}
gu_buzzer_sound(3200);
}
else if(g_unsol_msg == UNSOL_CMT && unsol_msg == UNSOL_END)
{
g_gui_menu = MENU_SMS;
g_sms_curr_page = 0;
g_sms_pages = strlen(sms_text) / MAX_LCD_BYTES + 1;
memset(g_sms_buff, 0, sizeof(g_sms_buff));
gu_lcd_put_string(gu_phone_num, GU_TRUE);
gu_app_sleep_ms(3000);
gu_lcd_put_string(sms_text, GU_TRUE);
strcpy(g_sms_buff, sms_text);
}
g_unsol_msg = unsol_msg;
}
/*****************************************************************************
* FUNCTION
* sms_send_message()
* DESCRIPTION
* send the text to the phone_num, it uses AT command to send it out.
*
* PARAMETERS
* phone_num[in], phone number likes as '1398478944'.
* sms_text[in], the sending SMS message
* sms_len[in], SMS text len
* RETURN
* GU_TRUE, OK; GU_FALSE, fails
* ...:
*****************************************************************************/
gu_bool sms_send_message(gu_char *phone_num, gu_char *sms_text, gu_uint16 sms_len, gu_bool english)
{
gu_uint8 counter = 1;
gu_char buffer[256];
gu_char *codec[] = {
"GB2312", // Chinese language
"GSM",
};
UNIT_TEST_DBG(GU_SMS_UART, "sms_send_message(%s,,%d,%d)", phone_num, sms_len, english);
sprintf(buffer, "AT+CSCS=\"%s\"\r\n", codec[english]);
if (at_exe_cmd_wait_result(buffer, strlen(buffer), SINGLELINE, NULL, 3000, NULL) != AT_RET_SUCCESS)
{
return GU_FALSE;
}
while (counter-- > 0)
{
sprintf(buffer, "AT+CMGS=\"%s\"\r\n", phone_num);
if (at_exe_cmd_wait_result(buffer, strlen(buffer), SINGLELINE, "> ", 3000, NULL) != AT_RET_SUCCESS)
{
app_power_down();
}
gu_app_sleep_ms(50);
if (!gu_send_at_command(sms_text, sms_len))
{
app_power_down();
}
*buffer = 0x1A;
if (at_exe_cmd_wait_result(buffer, 1, SINGLELINE, NULL, 120000, NULL) == AT_RET_SUCCESS)
{
break;
}
gu_app_sleep_ms(3000);
UNIT_TEST_DBG(GU_SMS_UART, "Try send SMS again counter=%d", counter);
}
return GU_TRUE;
}
/*****************************************************************************
* FUNCTION
* sms_adm_send_response()
* DESCRIPTION
* Only send SMS message to admin.
*
* PARAMETERS
* sms_text[in], the sending SMS message
* english[in], Is english
* send_sms[in], send SMS or print to uart
* RETURN
* GU_TRUE, OK; GU_FALSE, fails
* ...:
*****************************************************************************/
gu_bool sms_adm_send_response(gu_char *sms_text, gu_bool english, gu_bool send_sms)
{
if (send_sms)
{
return sms_send_message(g_admin_role[0].admin_phone, sms_text, strlen(sms_text), english);
}
else
{
Uart_Printf("%s\r\n",sms_text);
return GU_TRUE;
}
}
/*****************************************************************************
* FUNCTION
* sms_nor_send_response()
* DESCRIPTION
* Send SMS message to all uses.
*
* PARAMETERS
* sms_text[in], the sending SMS message
* english[in], Is english
* send_sms[in], send SMS or print to uart
*
* RETURN
* GU_TRUE, OK; GU_FALSE, fails
* ...:
*****************************************************************************/
gu_bool sms_nor_send_response(gu_char *sms_text, gu_uint16 sms_len, gu_bool english)
{
gu_int8 i;
UNIT_TEST_DBG(GU_SMS_UART, "sms_nor_send_response(,%d,%d)", sms_len, english);
for (i = 0; i < MAX_ADMIN_NUM; i++)
{
if (g_admin_role[i].role == ROLE_NONE)
{
break;
}
sms_send_message(g_admin_role[i].admin_phone, sms_text, sms_len, english);
}
return GU_TRUE;
}
|
|