Antarmuka Baris Perintah Arduino UNO: Cepat dan Handal
Komponen dan Perlengkapan
Tentang Proyek Ini
Tiba saatnya Anda perlu mencari nilai sensor atau ingin memberi perintah pada robot Anda, misalnya “gerak ke kiri”. Mungkin Anda perlu menanyakan nilai runtime variabel dari program Anda, atau menetapkan nilai potensiometer digital.
Yang Anda butuhkan adalah antarmuka baris perintah (Command Line Interface, CLI). Ya, mudah untuk membuat Arduino merespons perintah teks.
Berikut adalah satu yang dapat Anda kembangkan. Ini adalah CLI sederhana, cepat, dan sangat hemat memori yang dapat Anda salin dan tempel dalam beberapa menit ke dalam kode Anda sendiri dan langsung berjalan. Perintah terdiri dari nama diikuti oleh beberapa argumen. Ini bahkan mendukung tombol backspace saat Anda mengetik perintah.
Ringkasan Cepat
Setiap sketsa memiliki fungsi loop(). Yours bisa sederhana seperti di bawah ini. Ini memanggil dua rutinitas di tab terpisah bernama CommandLine.h. Saya akan membimbing Anda melalui kedua berkas ini dan Anda akan siap digunakan.
Loop
Apa yang dilakukan: Setiap kali loop() berjalan, ia memeriksa apakah ada perintah dari port serial dengan memanggil getCommandLineFromSerialPort(). Variabel CommandLine dideklarasikan dalam CommandLine.h tetapi secara gaya Anda mungkin ingin memindahkannya ke tab Loop utama. Ketika perintah lengkap telah tiba ke port serial dan telah disalin ke buffer perintah:
char commandLine[COMMAND_BUFFER_LENGTH + 1];
Kemudian loop() memanggil DoMyCommand() untuk mengeksekusi perintah yang tepat.
Sekarang mari kita lihat apa yang ada di dalam CommandLine.h. Saya harus menyarankan bahwa saya menempatkan semua kode di dalam CommandLine.h, karena maka Anda hanya perlu menyalin dan menempelkan kode ini ke tab baru di IDE Arduino Anda (pastikan untuk memberi nama tab yang diakhiri dengan “.h”). Kemudian sertakan berkas ini dalam berkas utama Anda, misalnya:
#include "CommandLine.h"
Ini memungkinkan Anda untuk menempatkan semua kode baris perintah di satu tab dan tetap merujuk ke rutinitasnya di bagian lain dari program Anda tanpa kode tambahan.
Melihat di Dalam CommandLine.h
Berkas CommandLine.h disertakan di akhir posting ini. Dalam CommandLine.h, setiap baris yang perlu Anda modifikasi ditandai dengan komentar, //Modify here. Berkas ini mencakup dua perintah contoh add dan sub dan menunjukkan bagaimana mereka dipanggil dari dalam DoMyCommand.
Untuk banyak di antara Anda, itu saja yang Anda butuhkan. Cobalah melalui CommandLine.h. Untuk mereka yang ingin penjelasan yang lebih rinci, teruskan membaca.
Melihat Lebih Dalam
Dalam CommandLine.h kita pertama-tama menyertakan <string.h>. String.h adalah pustaka standar C. Jika Anda belum pernah menemui pustaka C sebelumnya, lakukan pencarian cepat di Internet untuk “The C Programming Language”. Alkitab C ditulis tahun-tahun yang lalu oleh Brian Kernigan dan Dennis Ritchie dan telah diperbarui. Sebagian besar orang memiliki salinan tetapi Anda dapat menemukannya secara gratis online.
Kami hanya akan menggunakan rutinitas strtok() (string ke token) dari <string.h>. Rutinitas ini membaca token, yaitu, kata yang dipisahkan oleh karakter tertentu (parameter kedua dari strtok). Ini bekerja seperti ini.
- Saat Anda memanggilnya pertama kali, Anda meneruskan string
ptr dan ia akan mengembalikan token pertama - Pada panggilan selanjutnya, (bagian hebatnya di sini) teruskan NULL alih-alih string
ptr dan ia akan melanjutkan dari tempat berhenti dengan string awal, sehingga mendapatkan satu token (kira-kira satu kata) pada satu waktu.
Kami juga menyertakan <stdlib.h> dari mana kami hanya menggunakan atoi() untuk konversi ascii-ke-integer. Jangan khawatir, kompiler hanya akan menyertakan rutinitas ini bukan seluruh pustaka tetapi Anda mungkin ingin memeriksa rutinitas lain di pustaka-pustaka ini karena berguna.
Selanjutnya adalah makro opsional kecil yang saya tulis bernama print2:
#define print2(x,y) (Serial.print(x), Serial.println(y)
Saya selalu ingin mencetak label dan string. Anda menggunakannya seperti ini:
print2("myVar = ", myVar);Jika myVar adalah 25 maka ini akan mencetak ke jendela serial:
myVar = 25
CommandLine.h berisi getCommandLineFromSerialPort() yang merakit baris perintah dari port serial. Setiap kali dipanggil ia membaca dari port serial dan menyimpan input ke dalam buffer input global, CommandLine[]. Ketika mencapai karakter return yang menandakan akhir perintah ia mengembalikan true.
Buffer yang telah dirakit lengkap sekarang diteruskan ke DoMyCommand() yang mengetahui fungsi mana yang diminta pengguna dan memanggilnya. Dengan sejumlah besar perintah Anda dapat mendapatkan pernyataan if-the-else yang cukup rumit.
Jika Anda memiliki sejumlah perintah yang sangat besar ada banyak cara untuk mempercepat hal-hal seperti mengadopsi apa yang disebut fungsi hashing. Alternatifnya Anda dapat membuat perintah hanya satu karakter dan kemudian menggunakan karakter itu sebagai label dari pernyataan switch-case. Saya merasa keduanya jarang diperlukan. Kata jauh lebih mudah diingat daripada satu karakter dan mengingat ini adalah Arduino, berapa banyak perintah yang bisa Anda miliki sebelum Anda kehabisan ruang pada chip?
Add dan Sub
Setiap fungsi perintah bertanggung jawab untuk memparsing argumen sendiri. Ini adalah cara sederhana untuk melakukannya dan cukup mudah untuk dimodifikasi. Alternatif untuk ini adalah membaca semua argumen segera di DoMyCommand. Anda dapat memasukkan argumen individual ke dalam array global string argv[].
Untuk tujuan contoh ini saya telah mendefinisikan dua perintah: add dan sub. Keduanya menggunakan argumen numerik tetapi saya telah menyertakan readWord (yang juga dapat disebut readStringToken) untuk mengembalikan kata. Anda juga dapat memodifikasi readStringToken ini untuk mengizinkan string seperti “this is a string”. Pertimbangkan sebagai latihan yang tersisa bagi pembaca.
Cara Tercepat untuk Berjalan
Jika Anda memiliki berkas loop utama seperti yang ditunjukkan di atas, maka buat tab baru menggunakan segitiga yang menunjuk ke bawah di kanan jauh jendela IDE Arduino Anda, dan salin berkas CommandLine.h (di bawah) ke dalamnya, Anda seharusnya dapat mengetik perintah add dan sub.
Sekarang terserah Anda!
Kode
- Penyelarah Baris Perintah Sederhana untuk Arduino
Penyelarah Baris Perintah Sederhana untuk ArduinoC/C++
Lihat komentar dalam artikel ini atau dalam kode untuk menambahkan baris perintah sederhana ke sketsa Arduino Anda.
Anda dapat menambahkan perintah seperti ini:
add 5, 10
subtract 10, 5
atau apa pun yang Anda butuhkan
/***************************************************************************** Cara Menggunakan CommandLine: Buat sketsa. Lihat di bawah untuk contoh penyiapan dan kode loop utama dan salin dan tempel ke dalam sketsa baru. Buat tab baru. (Gunakan menu drop-down (segitiga kecil) di kanan jauh Editor Arduino. Beri nama tab CommandLine.h Tempelkan berkas ini ke dalamnya. Uji: Unduh sketsa yang baru saja Anda buat ke Arduino Anda seperti biasa dan buka Jendela Serial. Ketik perintah ini diikuti oleh return: add 5, 10 subtract 10, 5 Lihat perintah add dan subtract yang disertakan dan kemudian tulis sendiri Anda! ***************************************************************************** Di bawah ini apa yang terjadi di balik layar ***************************************************************************** Penyelarah Baris Perintah Sederhana dan Jelas Berkas ini akan memungkinkan Anda mengetik perintah ke Jendela Serial seperti, add 23,599 blink 5 playSong Yesterday ke sketsa Anda yang berjalan pada Arduino dan mengeksekusinya. Catatan implementasi: Ini akan menggunakan string C alih-alih Objek String berdasarkan asumsi bahwa jika Anda membutuhkan penyelarah baris perintah, Anda mungkin juga sempit pada ruang dan Objek String cenderung tidak efisien ruang. 1) Protokol Sederhana Perintah adalah kata dan angka yang dipisahkan oleh spasi atau koma. Kata pertama adalah perintah, setiap kata tambahan adalah argumen "\n" mengakhiri setiap perintah 2) Menggunakan rutinitas perpustakaan C strtok: Perintah adalah kata yang dipisahkan oleh spasi atau koma. Kata yang dipisahkan oleh karakter tertentu (seperti spasi atau koma) disebut token. Untuk mendapatkan token satu per satu, saya menggunakan rutinitas perpustakaan C strtok (bagian dari C stdlib.h lihat di bawah cara menyertakannya). Ini adalah bagian dari bahasa perpustakaan C <string.h> yang dapat Anda cari online. Dasarnya Anda: 1) serahkan string (dan pemisah yang Anda gunakan, yaitu spasi dan koma) dan ia akan mengembalikan token pertama dari string 2) pada panggilan selanjutnya, serahkan NULL (alih-alih pointer string) dan ia akan melanjutkan dari tempat berhenti dengan string awal. Saya telah menulis beberapa rutinitas pembantu dasar: readNumber: menggunakan strtok dan atoi (atoi: ascii ke int, lagi bagian dari C stdlib.h) untuk mengembalikan integer. Perhatikan bahwa atoi mengembalikan int dan jika Anda menggunakan int 1 byte seperti uint8_t Anda harus mendapatkan lowByte(). readWord: mengembalikan pointer ke kata teks 4) DoMyCommand: Daftar if-then-else untuk setiap perintah. Anda dapat membuatnya pernyataan switch jika semua perintah adalah karakter tunggal. Menggunakan kata lebih mudah dibaca. Untuk tujuan contoh ini kita memiliki: Add Subtract nullCommand */ /******************kode loop utama contoh ************************************ #include "CommandLine.h" void setup() { Serial.begin(115200); } void loop() { bool received = getCommandLineFromSerialPort(CommandLine); //global CommandLine didefinisikan di CommandLine.h if (received) DoMyCommand(CommandLine); } **********************************************************************************/ //Beri nama tab ini: CommandLine.h #include <string.h> #include <stdlib.h> //makro berikut bagus untuk debugging, misalnya print2("myVar= ", myVar); #define print2(x,y) (Serial.print(x), Serial.println(y)) #define CR '\r' #define LF '\n' #define BS '\b' #define NULLCHAR '\0' #define SPACE ' ' #define COMMAND_BUFFER_LENGTH 25 //panjang buffer serial untuk perintah masuk char CommandLine[COMMAND_BUFFER_LENGTH + 1]; //Baca perintah ke dalam buffer ini dari Serial. +1 dalam panjang untuk karakter penghentian const char *delimiters = ", \n"; //perintah dapat dipisahkan oleh return, spasi atau koma /************************************************************************************************************* Nama Perintah Anda di Sini */ const char *addCommandToken = "add"; //Modifikasi di sini const char *subtractCommandToken = "sub"; //Modifikasi di sini /************************************************************************************************************* getCommandLineFromSerialPort() Kembalikan string perintah berikutnya. Perintah dipisahkan oleh return" Tangani karakter BackSpace Buat semua karakter huruf kecil *************************************************************************************************************/ bool getCommandLineFromSerialPort(char * commandLine) { static uint8_t charsRead = 0; //catatan: COMAND_BUFFER_LENGTH harus kurang dari 255 karakter panjang //baca secara asinkron sampai perintah lengkap masuk while (Serial.available()) { char c = Serial.read(); switch (c) { case CR: //kemungkinan telah memiliki perintah lengkap dalam buffer sekarang, perintah diakhiri oleh CR dan/atau LS case LF: commandLine[charsRead] = NULLCHAR; //null terminate array karakter perintah kita if (charsRead > 0) { charsRead = 0; //charsRead adalah statik, jadi harus direset Serial.println(commandLine); return true; } break; case BS: // tangani backspace dalam input: tempatkan spasi di karakter terakhir if (charsRead > 0) { //dan sesuaikan commandLine dan charsRead commandLine[--charsRead] = NULLCHAR; Serial << byte(BS) << byte(SPACE) << byte(BS); //tidak tahu cara ini bekerja, ditemukan di Internet } break; default: // c = tolower(c); if (charsRead < COMMAND_BUFFER_LENGTH) { commandLine[charsRead++] = c; } commandLine[charsRead] = NULLCHAR; //hanya untuk jaga-jaga break; } } return false; } /* **************************** readNumber: mengembalikan integer bertanda 16bit (untuk Arduino Uno) dari baris perintah readWord: mendapatkan kata teks dari baris perintah */ int readNumber () { char * numTextPtr = strtok(NULL, delimiters); //K&R string.h pg. 250 return atoi(numTextPtr); //K&R string.h pg. 251 } char * readWord() { char * word = strtok(NULL, delimiters); //K&R string.h pg. 250 return word; } void nullCommand(char * ptrToCommandName) { print2("Command not found: ", ptrToCommandName); //lihat di atas untuk makro print2 } /**************************************************** Tambahkan perintah Anda di sini */ int addCommand() { //Modifikasi di sini int firstOperand = readNumber(); int secondOperand = readNumber(); return firstOperand + secondOperand; } int subtractCommand() { //Modifikasi di sini int firstOperand = readNumber(); int secondOperand = readNumber(); return firstOperand - secondOperand; } /**************************************************** DoMyCommand */ bool DoMyCommand(char * commandLine) { // print2("\nCommand: ", commandLine); int result; char * ptrToCommandName = strtok(commandLine, delimiters); // print2("commandName= ", ptrToCommandName); if (strcmp(ptrToCommandName, addCommandToken) == 0) { //Modifikasi di sini result = addCommand(); print2("> The sum is = ", result); } else { if (strcmp(ptrToCommandName, subtractCommandToken) == 0) { //Modifikasi di sini result = subtractCommand(); //K&R string.h pg. 251 print2("> The difference is = ", result); } else { nullCommand(ptrToCommandName); } } }