Käytössä on Arduino Duemilanove (ATMega328 pohjanen) mikrokontrolleri.
Käytännössä mikrokontrollerin ohjelmoiminen on minulle peruskauraa, jota on tullut jo suhteellisen kauan harrastettua.
Ongelmakseni kuitenkin tuli kyseisen kielen "laajuus".
Varsinaiseen kysymykseen. Miten pystyn lukemaan Serial-portista kokonaisen merkkijonon ja tallentaa sen string-tyyppiseen muuttujaan.
LCD-näytölle tulostushan serialista onnistuu näin
while (Serial.available() > 0) { lcd.write(Serial.read()); }
Eli käytännössä luetaan kaikki merkit, jota ollaan saatu Serial-portista.
Tarkoituksena olisi kuitenkin saada esim seuraavaan enteriin asti luettua ja tallennettua tämä "komento" muuttujaan.
http://arduino.cc/en/Tutorial/HomePage
Sattuuko kenenkään silmään ratkaisua?
Jos käytössä on String luokka, niin lisäilet siihen merkki kerrallaan. Jos ei ole (vaan joutuu käyttämään tavutaulukkoja) niin sitten varaat riittävän ison taulukon, teet indeksimuuttujan jonka alustat nollaksi ja laitat niitä merkkejä taulukkoon aina indeksiä kasvatten. Samalla kannattaa katsoa ettei indeksi pääse kasvamaan taulukkoa suuremmaksi. Kun vastaan tulee "enteri" niin käsittelet sen.
Eihän se asia ole yhtään mutkikkaampi kuin tavallinen silmukka:
const int MAX_PITUUS = 15; char rivi[MAX_PITUUS + 1]; int pituus; // Luetaan teksti, kuitenkin enintään MAX_PITUUS merkkiä. for (pituus = 0; pituus < MAX_PITUUS; ++pituus) { rivi[pituus] = Serial.read(); // Jos luettiin lopetusmerkki, poistutaan silmukasta. if (rivi[pituus] == '\r' || rivi[pituus] == '\n') { break; } } // Asetetaan tekstin loppuun nollamerkki, kuten C:ssä yleensä on tapana. rivi[pituus] = 0; // Tulostus: käydään läpi alusta nollamerkkiin asti. for (int i = 0; rivi[i] != 0; ++i) { Serial.write(rivi[i]); } Serial.write('\n');
Metabolix kirjoitti:
(koodia)
Juu hieman tämäntyyppistä ratkaisua haettiin, mutta tarkoitus olis tämän jälkeen pystyä vertaamaan, onko komento esim "readset" tai "writeset" esim...
tesmu kirjoitti:
tarkoitus olis tämän jälkeen pystyä vertaamaan, onko komento esim "readset" tai "writeset" esim...
No kai nyt tuon verran osaat itsekin ohjelmoida, vai kannattaisiko sittenkin aloittaa vaikka lukemalla jokin ohjelmoinnin perusopas?
Noniin, sain ratkaistua ongelmani muuntelemalla koodia hieman. Nyt kyseinen koodi kokonaisuudessaan näyttää tältä.
int pituus = 0 ; void setup() { Serial.begin(9600); } void loop() { const int MAX_PITUUS=15; char rivi[MAX_PITUUS +1]; int finished = 0; int i = 0; int inByte; while (Serial.available() >0) { inByte = Serial.read(); if (inByte == '\n' || inByte == '\r') { finished = 1; rivi[pituus+1] = '\0'; pituus = 0; break; } else { rivi[pituus] = inByte; pituus++; } } if (finished) { if (strcmp(rivi, "readset") == 0) { Serial.println("Reading settings from EEPROM"); } if (strcmp(rivi, "writeset") == 0) { Serial.println("Writing settings to EEPROM"); } } }
Ainoa vaan, että välillä tuonne tulee jotain ihan outoja merkkejä väliin...
Tossa tapahtuu myös jotain tosi hassua, jos sarjaportista tulee enemmän kuin 15 merkkiä ennen \r tai \n merkkejä.
Grez kirjoitti:
Samalla kannattaa katsoa ettei indeksi pääse kasvamaan taulukkoa suuremmaksi.
Metabolix kirjoitti:
for (pituus = 0; pituus < MAX_PITUUS; ++pituus) {
Helpottaa sun elämää kummasti jatkossa, jos teet ihan kunnollisen getline-funktion lukemaan rivejä, missä otetaan erikoismerkkejäkin huomioon ja jonkinlaisen köyhän miehen komentoriviparserin. Niin ja tietysti noi yli-indeksoinnin tarkistukset getlineen ja parseriin mukaan kans.
Eli teet vaikka promptin joka kutsuu tuota sun getline -funktiota. Sit teet handlerin, joka käsittelee getlinella saatua riviä.
Varsinaisen komentojen suorittamisen voit tehdä callbackeilla. Tällä tavoin tuo sun handleri on siirrettävissä suoraan aina uuteen projektiin, eikä sun tarvi tehdä mitään uusiksi. Ainoastaan tarvii tehdä lista hyväksytyistä komennoista ja proseduurit niiden suorittamiseen.
Komennon prototyyppi voisi olla esim:
typedef int (*serial_command_handler_t)(int argn, char** argc);
Sitten vaan taulukko komennoille ja niiden handlereille:
#define MAX_COMMAND_LENGTH 9 // esimerkki komennon maksimipituudeksi 8 + null typedef struct serial_command_list_t { char command[MAX_COMMAND_LENGTH]; serial_command_handler_t handler; } serial_command_list_t; // Asenna hyväksytyt komennot ja assosioi ne oikeisiin käsittelijöihin. static serial_command_list_t serial_command_list[] = { {"command1", &command1_handler}, {"command2", &command2_handler}, };
Sit vaan teet tolle handlerin, missä sun omalla getline -funktiolla luet rivin, pilkot sen komentoon ja sen argumentteihin (esim. strtok). Sen jälkeen etsit vain listasta sopivaa komentoa. Jos löytyi niin kutsut komentoon assosioutua käsittelijää juuri pilkkomillasi argumenteilla. Kertaalleen kun saat tuon tehtyä, niin voit käyttää sitä missä vaan vastaavassa tilanteessa. Handlerin paluuarvon voit muuttaa halutessasi voidiksi, jos "komentotulkkisi" ei tarvitse tutkia funktioiden paluuarvoja.
tesmu kirjoitti:
Ainoa vaan, että välillä tuonne tulee jotain ihan outoja merkkejä väliin...
Yritätkö lähettää sinne tavaraa pc:ltä terminaaliohjelman kautta? Jos näin, niin luultavasti kyse on terminaaliohjelmastasi tai sen asetuksista ja se lähettää kontrollimerkkejä sinne väliin.
Grez kirjoitti:
Tossa tapahtuu myös jotain tosi hassua, jos sarjaportista tulee enemmän kuin 15 merkkiä ennen \r tai \n merkkejä.
Juu tapahtuu hassua, mutta ohjelma joka kommunikoi kyseisen laitteen kanssa ei laita yli 10-merkkisiä komentoja sinne...
Ja sarjaliikenteessähän on tietenkin mahdotonta, että \n jäisi välistä tai korruptoituisi joksikin muuksi merkiksi, jolloin olisi jo 21 merkkiä ennen rivinvaihtoa ja ohjelma täysin sekaisin.
Aihe on jo aika vanha, joten et voi enää vastata siihen.