Прочитал сегодня пост на 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(" help\r\n statlist statclear\r\n time timeset\r\n 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'; // дописать 0 memcpy( outbuf, mybuf, idx); // скопировать idx = 0; return 1; } if ( idx >=(SBUF_SZ-1) ) { // проверяем на длину внутреннего буфера mybuf[ SBUF_SZ-1 ] = '\0'; // дописать 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.
Автору первоначальной статьи спасибо, хотя бы за то, что заставил меня набрать этот материал.
ссылка на оригинал статьи http://geektimes.ru/post/265966/
Добавить комментарий