本人第九篇中,已经提及项目需要从SD卡加载配置参数,主要是对settings.txt文件的解析。参数主要分为三个方面:
1)port是TCP端口号,在整个项目中,Kaluga板的身份为网关的“上位机”,通过TCP连接向网关发送命令,控制网关启动对终端的唤醒和采集工作(采用LoRa自组网)。
port=网关的TCP Server端口号
2)接着三行就是联通网关的时间参数,period表示间隔多少分钟连接一次网关,timeout是网关唤醒一个终端的超时秒钟数也就是过了多少秒确定唤醒终端失败了,repeat就是唤醒终端失败而重新尝试唤醒的次数。
period=两次轮询全部终端之间的时间间隔(单位分)
timeout=请求一个终端无响应超时(单位秒)
repeat=请求一个终端失败最大重复次数
3)接着四行就是MQTT接入OneNET云平台的参数,分别是设备ID,产品ID,鉴权信息和APIKey。
deviceid=OneNET平台设备ID
productid=OneNET平台产品ID
authkey=OneNET平台鉴权信息(MQTT旧版协议)
OneNET平台APIKey
本人上篇帖子已经实现了OneNET接入,不过接入参数是写死的,所以本篇将读取SD卡中settings.txt文件的配置参数,构建OneNET的接入参数。从文件读取的参数都保存在系统全局变量里。
#define MAX_EID 16 // Max endian number
/* Global variables ----------------------------------------------- */
int port = 2346; //Gateway tcp server port
int period = 10; //polling period (minutes)
int timeout = 8; //timeout of once request (second)
int repeat = 3; //repeat times when once fail
char deviceid[16]; //OneNET device id
char productid[16]; //OneNET product id
char authkey[16]; //OneNET authentication key (MQTT)
char apikey[32]; //OneNET APIKey
再看接入OneNET的函数aita_StartMqtt(),代码略作修改:
图12-1 OneNET接入参数的代码修改
不过,这样修改后,遇到了连接平台错误,提示为:username和password不正确。
图12-2 OneNET接入错误
经过分析本人发现,settings.txt文件记录的参数都是以“参数名=参数值”的形式(当然格式也是自定义的)。而解析时,调用了C库函数strtok()来做以“=”为参照的字符串分割,结果分割出来的“参数值”保存为字符串格式时,最后都包含了一个“空格符”,这样等于如果设备ID是“12345”,解析出来的就是“12345 ”,这样自然在连接OneNET时就会报错了。
图12-3 解析字符串参数尾部包含空格符
解决方法就是把最后多的“空格符”改为“\0”就可以了,比如ID是“12345”,长度为5,加上空格长度就是6,字符串数组索引都是0开始,那么空格的位置在5,开始尝试代码如下方式,用strlen()得到字符串长度,然后减1就是要修改的索引。
deviceid[strlen(deviceid)-1] = '\0';
但是这样改动还是有错,代码中就是一行的控制台输出,串口输出显示也变得换行了。
图12-4 长度-1位置改0的效果
因为还没有搞明白Kaluga板ESP32S2怎么做Debug,所以也没法查看内存单元的实际值,这里本人大胆揣测:“利用函数strtok()进行解析后,字符串结尾可能插入了utf-8字符,也可能是别的会被显示为空格的字符,反正是占用两个字节”。所以再次修改代码,也就是字符串长度-2的位置改为0,没想到竟然成功了!(当然,具体原因并不明朗,除非能做Debug)。
deviceid[strlen(deviceid)-2] = '\0';
于是乎全面修改读取并解析settings.txt函数如下,其实就是解析后四行字符串类型参数时,加入一行末尾(长度-2)位置赋值为0的语句。
void readSettings(void) {
//open settings.txt for reading
ESP_LOGI(TAG, "Reading GateWay Settings file %s", file_settings);
FILE *f = fopen(file_settings, "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for reading");
return;
}
//read lines
char line[32];
char *pos;
//line 1. tcp server port
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
port = atoi(pos);
//line 2. polling period(minute)
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
period = atoi(pos);
//line 3. timeout of once request(second)
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
timeout = atoi(pos);
//line 4. repeat times when once fail
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
repeat = atoi(pos);
//line 5. OneNET device id
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
memset(deviceid, 0, 16);
strcpy(deviceid, pos);
deviceid[strlen(deviceid)-2] = '\0';
//line 6. OneNET product id
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
memset(productid, 0, 16);
strcpy(productid, pos);
productid[strlen(productid)-2] = '\0';
//line 7. OneNET authentication key (MQTT)
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
pos = strtok(line, "=");
pos = strtok(NULL, "=");
memset(authkey, 0, 16);
strcpy(authkey, pos);
authkey[strlen(authkey)-2] = '\0';
//line 8. OneNET APIKey
fgets(line, sizeof(line), f);
ESP_LOGI(TAG, "%s", line);
memset(apikey, 0, 32);
strcpy(apikey, line);
apikey[strlen(apikey)-2] = '\0';
ESP_LOGI(TAG, "port:%d, period:%d, timeout:%d, repeat:%d",
port, period, timeout, repeat);
ESP_LOGI(TAG, "deviceid:%s, productid:%s", deviceid, productid);
ESP_LOGI(TAG, "authkey:%s, apikey:%s", authkey, apikey);
//close file
fclose(f);
}