Прочитал сегодня пост на Geektimes, и хочу поделиться своим опытом. Не хочу обсуждать плюсы и минусы Arduino, но условия проекта, в котором применил нижеописанное — должно быть сделано под неё. Суть — нужно предоставить пользователю терминал для управления устройством. Соотвественно, не малая часть работы является работой со строками. Применять или нет предложенное решение — пусть каждый решает сам.
От класса String решил отказаться: ошибка линкера. Она у меня появлялась исключительно при попытке использовать класс String.
Что требовалось? Вывод информации и обработка введённых пользователем строк. Например:
Ethernet controller — ok
STATIC mode
>time
2015-11-16 22:35:27
Собственно, надо сравнить стоки. Нет, сначала надо разбить текст на фрагменты разделителем ( например пробел), но потом всё равно сравнить строки. Поскольку команд было «раз, два — и обчёлся», то разбивку текста на фрагменты убрал. Из-за указанной выше ошибки класс String использовать не получалось, то как можно по другому? Arduino использует библиотеку AVR-libc, то резонно в первую очередь обратиться к ней.
Что имеем?
- stdlib.h — функции взаимного преобразования чисел и строк (в обе стороны).
- string.h — функции работы со строками. Основной наш интерес.
- stdio.h — функции стандартного ввода-вывода.
Этим не ограничивается функционал. Упомянуто то, что связано с задачей.
Итак, №1 рекомендую для ознакомления — вдруг пригодится отдельно где-то. Сам по себе используется только для работы string.h.
№2 — используем функции memset для заполнения или очистки буфера, memcmp — для сравнения. strcmp не использую, так как нужно явно ограничивать длину сравниваемого фрагмента. №3 — для форматного чтения и вывода: sprintf, sprint_P, sscanf, sscanf_P. Функции с суффиксом _P отличаются тем, что строку форматирования берут из памяти программ PROGMEM, он же макрос F() в библиотеках Arduino.
У меня сравнение строк выглядит так:
if ( memcmp(str ,"statlist" ,8)==0 ) {
// your code here
}
Пожалуй, стоит оговориться, что сравниваются начала строк. Для поиска фрагментов можно использовать memmem.
То есть поиск команды может выглядеть так:
if ( memcmp(str ,"statclear", 9)==0 ) {
memset(journal, 0, sizeof(jrn_rec_t)*JRN_REC_NUM );
Serial.println( F("ok") );
}else if ( memcmp(str ,"statlist" ,8)==0 ) {
funcStatlist();
}else if ( memcmp(str ,"cfgshow", 7)==0 ) {
funcCfgShow();
}else if ( memcmp(str ,"timeset", 7)==0 ) {
funcTimeSet( str); // setup date and time YYYY-MM-DD hh:mm:ss
}else if ( memcmp(str ,"cfgset", 6)==0 ) {
funcCfgSet( str); //funcPingdel( str);
}else if ( memcmp(str ,"time", 4)==0 ) {
funcTime(); // print date and time from RTC
}else if ( memcmp(str ,"help", 4)==0 ) {
// print short help
Serial.println( F(" helprn statlist statclearrn time timesetrn cfgshow cfgset") );
}else{
Serial.print( F("unknow cmd> "));
Serial.println( str);
}
Строки «собираю» следующим образом: читаю байты с порта, пока не превышена допустимая длинна строки или пока не встречен один из символов перевода строки r или n.
bool readln( HardwareSerial &uart, char *outbuf)
// return true when find CR, LF or both and if size limit
{
static char mybuf[SBUF_SZ] = { 0 };
static char idx = 0;
while (uart.available()) {
if ( uart.peek()!= 'r' && uart.peek()!= 'n' ) {
mybuf[ idx++ ] = uart.read();
} else {// если CR
uart.read();
if ( uart.peek()=='n' || uart.peek()=='r' ) uart.read();
if ( idx == 0 ) {
return 0;
}
mybuf[ idx++ ] = ''; // дописать 0
memcpy( outbuf, mybuf, idx); // скопировать
idx = 0;
return 1;
}
if ( idx >=(SBUF_SZ-1) ) { // проверяем на длину внутреннего буфера
mybuf[ SBUF_SZ-1 ] = ''; // дописать 0
memcpy( outbuf, mybuf, 32); // скопировать
idx = 0;
return 1;
}
}
return 0;
}
Ещё очень полезен форматный ввод-вывод. Например, разбор строки с ведённой датой и временем выглядит так:
sscanf_P(str, (const char *)F("%*s %d-%d-%d %d:%d:%d"), &y, &m, &d, &hh, &mm, &ss)
Получение строки для вывода IP:
sprintf_P(buff, (const char *)F("Your IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
Подробней о строке формата можно почитать, например, здесь scanf и здесь (printf).
Вот собственно и всё. Надеюсь, кому-то данный материал поможет «отвязаться» от Arduino или просто лучше и за меньшее время писать свои программы. Но более типичная ситуация — обойти ограничения Wiring.
Автору первоначальной статьи спасибо, хотя бы за то, что заставил меня набрать этот материал.
Автор: Hoksmur