559 lines
12 KiB
C
559 lines
12 KiB
C
#include "bc26.h"
|
|
#include "tos_hal.h"
|
|
#include "sal_module_wrapper.h"
|
|
|
|
#include "stdio.h"
|
|
#include "stdbool.h"
|
|
#include "ctype.h"
|
|
|
|
static int bc26_reset(void)
|
|
{
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, NULL);
|
|
tos_at_cmd_exec(&echo, 3000, "AT+QRST=1\r\n");
|
|
return 0;
|
|
}
|
|
|
|
static int bc26_psm_lock(void)
|
|
{
|
|
int try = 0;
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, "OK");
|
|
while (try++ < 10) {
|
|
tos_at_cmd_exec(&echo, 3000, "AT+SM=LOCK\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_open_cfun(void)
|
|
{
|
|
int try = 0;
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, "OK");
|
|
while (try++ < 10) {
|
|
tos_at_cmd_exec(&echo, 3000, "AT+CFUN=1\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_echo_close(void)
|
|
{
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, "OK");
|
|
tos_at_cmd_exec(&echo, 1000, "ATE0\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_open_err_code(void)
|
|
{
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, "OK");
|
|
tos_at_cmd_exec(&echo, 1000, "AT+CMEE=1\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_check_sim(void)
|
|
{
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, "OK");
|
|
tos_at_cmd_exec(&echo, 1000, "AT+CIMI\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_get_net(void)
|
|
{
|
|
int try = 0;
|
|
at_echo_t echo;
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, "OK");
|
|
while (try++ < 10) {
|
|
tos_at_cmd_exec(&echo, 2000, "AT+CGATT=1\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_OK || echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_signal_quality_check(void)
|
|
{
|
|
int rssi, ber;
|
|
at_echo_t echo;
|
|
char echo_buffer[32], *str;
|
|
|
|
tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
|
|
tos_at_cmd_exec(&echo, 1000, "AT+CSQ\r\n");
|
|
if (echo.status != AT_ECHO_STATUS_OK) {
|
|
return -1;
|
|
}
|
|
|
|
str = strstr(echo.buffer, "+CSQ:");
|
|
sscanf(str, "+CSQ:%d,%d", &rssi, &ber);
|
|
if (rssi == 99) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bc26_set_band(void)
|
|
{
|
|
int try = 0;
|
|
at_echo_t echo;
|
|
char echo_buffer[32];
|
|
|
|
tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), "OK");
|
|
while (try++ < 10) {
|
|
tos_at_cmd_exec(&echo, 1000, "AT+QBAND=1,8\r\n");
|
|
if (echo.status == AT_ECHO_STATUS_EXPECT) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int bc26_band_check(void)
|
|
{
|
|
int band;
|
|
at_echo_t echo;
|
|
char echo_buffer[32], *str;
|
|
|
|
tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
|
|
tos_at_cmd_exec(&echo, 1000, "AT+QBAND?\r\n");
|
|
if (echo.status != AT_ECHO_STATUS_OK) {
|
|
return -1;
|
|
}
|
|
|
|
str = strstr(echo.buffer, "+QBAND:");
|
|
sscanf(str, "+QBAND:%d", &band);
|
|
if (band != 8) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bc26_init(void)
|
|
{
|
|
printf("Init BC26...\n" );
|
|
|
|
if (bc26_reset() != 0) {
|
|
printf("bc26 reset FAILED\n");
|
|
return -1;
|
|
}
|
|
tos_stopwatch_delay_ms(3000);
|
|
|
|
if (bc26_psm_lock() != 0) {
|
|
printf("bc26 psm lock FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_open_cfun() != 0) {
|
|
printf("bc26 open cfun FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_echo_close() != 0) {
|
|
printf("echo close FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_open_err_code() != 0) {
|
|
printf("bc26 open err code FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_check_sim() != 0) {
|
|
printf("bc26 check sim FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_get_net() != 0) {
|
|
printf("bc26 get net FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_set_band() != 0) {
|
|
printf("set band FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_band_check() != 0) {
|
|
printf("check band FAILED\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bc26_signal_quality_check() != 0) {
|
|
printf("check csq FAILED\n");
|
|
return -1;
|
|
}
|
|
printf("Init BC26 done\n");
|
|
}
|
|
|
|
static int bc26_connect(const char *ip, const char *port, sal_proto_t proto)
|
|
{
|
|
int id, send_port = 0,try = 0, is_connected = 0;
|
|
at_echo_t echo;
|
|
char echo_buffer[32],*str;
|
|
|
|
tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
|
|
|
|
tos_at_cmd_exec(&echo, 2000, "AT+QSOC=1,1,1\r\n");
|
|
if (echo.status != AT_ECHO_STATUS_OK) {
|
|
return -1;
|
|
}
|
|
str = strstr(echo.buffer, "+QSOC=");
|
|
sscanf(str, "+QSOC=%d", &id);
|
|
|
|
printf("get socket id is %d\r\n",id);
|
|
id = tos_at_channel_alloc_id(id, ip, port);
|
|
if (id == -1) {
|
|
return -1;
|
|
}
|
|
|
|
sscanf(port, "%d", &send_port);
|
|
while (try++ < 10) {
|
|
tos_at_cmd_exec(&echo, 2000, "AT+QSOCON=%d,%d,\"%s\"\r\n", id, send_port,ip);
|
|
if (echo.status == AT_ECHO_STATUS_OK) {
|
|
is_connected = 1;
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!is_connected) {
|
|
tos_at_channel_free(id);
|
|
return -1;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
static char __num2hex(uint8_t num)
|
|
{
|
|
if (num <= 0x9) {
|
|
return num + '0';
|
|
}
|
|
|
|
if ((0xA <= num) && (num <= 0xF)) {
|
|
return num - 0xA + 'A';
|
|
}
|
|
|
|
return (char)-1;
|
|
}
|
|
|
|
static void __hex2str(uint8_t *in, char *out, int len)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
out[i * 2] = __num2hex(in[i] >> 4);
|
|
out[i * 2 + 1] = __num2hex(in[i] & 0x0F);
|
|
}
|
|
out[2 * len] = '\0';
|
|
}
|
|
|
|
|
|
static int bc26_send(int id, const void *buf, size_t len)
|
|
{
|
|
char *str_buf = NULL;
|
|
at_echo_t echo;
|
|
|
|
if (tos_at_global_lock_pend() != 0) {
|
|
return -1;
|
|
}
|
|
|
|
str_buf = tos_mmheap_calloc(2 * len + 1, sizeof(char));
|
|
if (!str_buf) {
|
|
return -1;
|
|
}
|
|
|
|
__hex2str((uint8_t *)buf, str_buf, len);
|
|
|
|
tos_at_echo_create(&echo, NULL, 0, NULL);
|
|
tos_at_cmd_exec(&echo, 1000,
|
|
"AT+QSOSEND=%d,%d,%s\r\n",
|
|
id, len, str_buf);
|
|
|
|
tos_mmheap_free(str_buf);
|
|
|
|
if (echo.status != AT_ECHO_STATUS_OK) {
|
|
tos_at_global_lock_post();
|
|
return -1;
|
|
}
|
|
|
|
tos_at_global_lock_post();
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
static int bc26_recv_timeout(int id, void *buf, size_t len, uint32_t timeout)
|
|
{
|
|
return tos_at_channel_read_timed(id, buf, len, timeout);
|
|
}
|
|
|
|
static int bc26_recv(int id, void *buf, size_t len)
|
|
{
|
|
return bc26_recv_timeout(id, buf, len, (uint32_t)4000);
|
|
}
|
|
|
|
static int bc26_parse_domain(const char *host_name, char *host_ip, size_t host_ip_len)
|
|
{
|
|
printf("parse_domain-%s,%s.\r\n", host_name, host_ip);
|
|
return 0;
|
|
}
|
|
|
|
static int bc26_close(int id)
|
|
{
|
|
tos_at_cmd_exec(NULL, 1000, "AT+QSODIS=%d",id);
|
|
|
|
tos_at_cmd_exec(NULL, 1000, "AT+QSOCL=%d",id);
|
|
|
|
tos_at_channel_free(id);
|
|
|
|
return 0;
|
|
}
|
|
__STATIC__ uint8_t __ascii2hex(char in) {
|
|
if (('0' <= in) && (in <= '9')) {
|
|
return in - '0';
|
|
}
|
|
if (('A' <= in) && (in <= 'F')) {
|
|
return in - 'A' + 10;
|
|
}
|
|
if (('a' <= in) && (in <= 'f')) {
|
|
return in - 'a' + 10;
|
|
}
|
|
return (uint8_t)-1;
|
|
}
|
|
|
|
__STATIC__ void __asciistr2hex(char *in, uint8_t *out, int len) {
|
|
int i = 0;
|
|
|
|
for (i = 0; i < len; i += 2) {
|
|
out[i / 2] = (__ascii2hex(in[i]) << 4) + __ascii2hex(in[i + 1]);
|
|
}
|
|
}
|
|
|
|
__STATIC__ uint8_t incoming_data_buffer[512];
|
|
__STATIC__ char ascii_stream[512];
|
|
__STATIC__ uint8_t hex_stream[256];
|
|
|
|
__STATIC__ void bc26_incoming_data_process(void)
|
|
{
|
|
uint8_t data;
|
|
int channel_id = 0, data_len = 0, read_len, i = 0, ds_i = 0;
|
|
int socket = 0, ip1 = 0, ip2 = 0, ip3 = 0, ip4 = 0, port = 0, length = 0, remaining_length = 0;
|
|
|
|
/*
|
|
+QSONMI=0,4\r\n
|
|
0: socket
|
|
4: length
|
|
*/
|
|
|
|
while (1) {
|
|
if (tos_at_uart_read(&data, 1) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (data == ',') {
|
|
break;
|
|
}
|
|
channel_id = channel_id * 10 + (data - '0');
|
|
}
|
|
|
|
while (1) {
|
|
if (tos_at_uart_read(&data, 1) != 1) {
|
|
return;
|
|
}
|
|
|
|
if (data == '\r') {
|
|
continue;
|
|
}
|
|
if (data == '\n') {
|
|
break;
|
|
}
|
|
data_len = data_len * 10 + (data - '0');
|
|
}
|
|
|
|
if (data_len > sizeof(incoming_data_buffer)) {
|
|
data_len = sizeof(incoming_data_buffer);
|
|
}
|
|
|
|
// tos_at_echo_create(&echo, NULL, 0, NULL);
|
|
tos_at_cmd_exec(NULL, 0, "AT+QSORF=%d,%d\r\n", channel_id, data_len);
|
|
|
|
// wait for uart buffer to be filled
|
|
/*
|
|
ATTENTION:
|
|
we cannot use tos_at_cmd_exec(NULL, timeout) to delay, because we are in at framework's parser
|
|
task now(current function is a callback called by parser task), delay in tos_at_cmd_exec is
|
|
tos_task_delay, this may cause a task switch, data receiving may be interrupted.
|
|
so we must tos_at_cmd_exec(NULL, 0), and do the delay by tos_stopwatch_delay_ms.
|
|
*/
|
|
tos_stopwatch_delay_ms(1000);
|
|
|
|
/*
|
|
1,xxx.yyy.zzz.www,8000,3,010203,0\r\n
|
|
1: socket
|
|
xxx.yyy.zzz.www: remote ip
|
|
8000: remote port
|
|
3: length
|
|
010203: data
|
|
0: remaining length
|
|
*/
|
|
|
|
// skip the leading \r\n
|
|
if (tos_at_uart_read(&data, 1) != 1) {
|
|
if (data != '\r') {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (tos_at_uart_read(&data, 1) != 1) {
|
|
if (data != '\n') {
|
|
return;
|
|
}
|
|
}
|
|
|
|
read_len = tos_at_uart_readline(incoming_data_buffer, sizeof(incoming_data_buffer));
|
|
|
|
for (i = 0; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == ',') {
|
|
++i;
|
|
break;
|
|
}
|
|
socket = socket * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == '.') {
|
|
++i;
|
|
break;
|
|
}
|
|
ip1 = ip1 * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == '.') {
|
|
++i;
|
|
break;
|
|
}
|
|
ip2 = ip2 * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == '.') {
|
|
++i;
|
|
break;
|
|
}
|
|
ip3 = ip3 * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == ',') {
|
|
++i;
|
|
break;
|
|
}
|
|
ip4 = ip4 * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == ',') {
|
|
++i;
|
|
break;
|
|
}
|
|
port = port * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == ',') {
|
|
++i;
|
|
break;
|
|
}
|
|
length = length * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
if (length > sizeof(ascii_stream)) {
|
|
return;
|
|
}
|
|
|
|
for (ds_i = 0; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == ',') {
|
|
++i;
|
|
break;
|
|
}
|
|
ascii_stream[ds_i++] = incoming_data_buffer[i];
|
|
}
|
|
ascii_stream[ds_i] = '\0';
|
|
|
|
for (; i < read_len; ++i) {
|
|
if (incoming_data_buffer[i] == '\r') {
|
|
break;
|
|
}
|
|
remaining_length = remaining_length * 10 + (incoming_data_buffer[i] - '0');
|
|
}
|
|
|
|
__asciistr2hex(ascii_stream, hex_stream, length * 2);
|
|
tos_at_channel_write(channel_id, hex_stream, length);
|
|
}
|
|
|
|
at_event_t bc26_at_event[] = {
|
|
{ "+QSONMI=", bc26_incoming_data_process },
|
|
};
|
|
|
|
sal_module_t nb_iot_module_bc26 = {
|
|
.init = bc26_init,
|
|
.connect = bc26_connect,
|
|
.send = bc26_send,
|
|
.recv_timeout = bc26_recv_timeout,
|
|
.recv = bc26_recv,
|
|
.close = bc26_close,
|
|
.parse_domain = bc26_parse_domain,
|
|
};
|
|
|
|
|
|
int bc26_sal_init(hal_uart_port_t uart_port)
|
|
{
|
|
if (tos_at_init(uart_port, bc26_at_event,
|
|
sizeof(bc26_at_event) / sizeof(bc26_at_event[0])) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
tos_stopwatch_delay_ms(1000);
|
|
|
|
if (tos_sal_module_register(&nb_iot_module_bc26) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (tos_sal_module_init() != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|