/***********************************************************************/ /* */ /* Merici program pro zarizeni firmy http://www.PaPouch.com */ /* Napsal Marek Drapal (c) 2007. Volne siritelne pod licenci GNU GPL. */ /* verze 0.1 */ /***********************************************************************/ #include /* Definice standardnich fci - npr. atoi */ #include /* Definice standardnich vstupne/vyst. fci */ #include /* Definice stringovych fci */ #include /* Definice standardnich UNIXovych fci */ #include /* Definice souborovych operaci */ #include /* Definice chybovych hlasek a fci */ #include /* Definice POSIXovych terminal. nastaveni */ #include /* Definice isdigit */ #define BAUD_RATE B9600 /* Rychlost komunikace, pouze pro systemy, */ /* ktere nemaji funkci cfsetspeed */ //#define DEBUG /* Vypis komunikace */ #define DELKA_DOTAZU 255 /* Maximalni delka dotazu */ #define DELKA_DAT 255 /* Maximalni delka dat odpovedi */ /* 16 bitovy signed integer pro ucely prevodu dat */ struct int16 { signed int data:16; }; /* Pro data je treba vytvorit specialni datovou strukturu, protoze */ /* se v datech bezne vyskytuje \0 znak, ktery soucasne ukoncuje string */ struct data_spinel { unsigned char *data; int delka_dat; }; /* Prazdna data - globalni promenna pro instrukce, ktere neobsahuji zadna */ /* data */ struct data_spinel no_data = { (unsigned char *) "", 0 }; /* Definice nekterych instrukci, dalsi mozno jednoduse doplnit */ unsigned const char nastaveni_komunikacnich_parametru = '\xe0'; unsigned const char jednorazovy_odmer = '\x51'; unsigned const char kontinualni_mereni = '\x52'; unsigned const char cteni_vstupu = '\x31'; unsigned const char cteni_vystupu = '\x30'; unsigned const char cteni_komunikacnich_parametru = '\xf0'; unsigned const char cteni_jmena_a_verze = '\xf3'; unsigned const char reset = '\xe3'; unsigned const char cteni_surove_hodnoty = '\x55'; unsigned const char povoleni_konfigurace = '\xe4'; unsigned const char kalibrace = '\x12'; /* Funkce otevre port, v Linuxu napr. /dev/ttyS0, vystupem filedesc. portu */ /* (souboru) ci -1 pri chybe */ int open_port (char *port, int dtr_delay) { int fd; /* File deskriptor souboru (tj. portu) */ /* Otevre PORT pro cteni a zapis, vyradi terminal. preruseni a DCD */ fd = open (port, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { perror ("open_port: Nelze otevrit:"); /* Nepodarilo se otevrit */ perror (port); } else { fcntl (fd, F_SETFL, 0); /* Nast. read do blok. rezimu */ usleep (dtr_delay); /* Pockej az najede DTR */ } return (fd); } /* Funkce nastavi seriovy port. Rychlost podle baud_rate a opusteni */ /* blokovaciho rezimu, kdyz v bufferu nejsou data dele nez 1 sekundu */ int nastav_port (int fd, int baud_rate) /* Vstup fd, vraci 0 pri OK */ { struct termios options; /* Systemova struktura terminalu */ tcgetattr (fd, &options); /* Nacteni aktualniho stavu */ #ifdef NO_cfsetspeed /* Definuj, pokud neni fce cfsetspeed */ cfsetispeed (&options, BAUD_RATE); /* Vstupni rychlost portu */ cfsetospeed (&options, BAUD_RATE); /* Vystupni rychlost portu */ #else cfsetspeed (&options, baud_rate); /* BSD4.4 rozsireni nastaveni BRATE */ #endif cfmakeraw (&options); /* Nastav vstup na ciste binarni */ options.c_cc[VMIN] = 0; /* Pro libovolny znak... */ options.c_cc[VTIME] = 10; /* ...je maximalni timeout 1 sekunda */ tcsetattr (fd, TCSAFLUSH, &options); /* Zapsani struktury */ return (0); } /* Funkce z portu port, ze zarizeni s adresou adr, precte do pointru data */ /* zpravu se signaturou sig */ int prijmi_odpoved (int port, unsigned char adr, unsigned char sig, unsigned char *data) { int i, j; /* Pomocne promenne cyklu */ int tmp = 0; /* Promenna na kontrolni soucet */ int delka_zbytku = 0; /* Delka SDATA */ int delka_dat; /* Delka vlastnich dat, tj. DATA */ unsigned char odpoved[DELKA_DAT + 6]; /* Pole pro celou odpoved */ /************************************************************************/ /* Odpoved zacina hvezdickou, pokud jsou na vstupu nejaka zbloudila */ /* data, tak je timto vyprazdnime, sice je to pomalejsi nez buffrovane */ /* cteni, ale je to bezpecne */ /************************************************************************/ while (1) { if (read (port, odpoved, 1) == 1 && odpoved[0] == '\x2a') /* Je tam'*'? */ { break; } else if (i == 1) { printf ("# Na vstupu je smeti: %02x (%c)\n", odpoved[0], odpoved[0]); continue; } else { perror ("# Timeout cteni odpovedi! Konec!\n"); /* 1sec zadna data */ return (-1); } } /* Jedna se o protokol 97 (61 hexa)? */ if (read (port, odpoved + 1, 1) != 1 || odpoved[1] != '\x61') { #ifdef DEBUG printf ("# Chybna odpoved - %02x (%c) \n", odpoved[1], odpoved[1]); #endif perror ("Chybny protokol, nebo chyba cteni - timeout"); return (-2); } /* Cyklus pro buffrovane cteni je zde prakticky zbytecny */ if (read (port, odpoved + 2, 2) != 2) { perror ("Chyba cteni - timeout"); return (-3); } /* Nyni zname delku zbyle odpovedi, cili ji celou precteme */ delka_zbytku = (odpoved[2] << 8) + odpoved[3]; /* Pocet prectenych dat v cyklu */ i = 0; while ((i < delka_zbytku) && (i < DELKA_DAT)) { /* Cteme vzdy maximum z bufferu, vzdy na aktualni pozici */ j = read (port, odpoved + 4 + i, delka_zbytku - i); //printf("Nactenych dat %d\n",j); if (j < 1) { perror ("Chyba cteni - timeout"); return (-4); } i += j; } /* Kontroly */ /* Kontrola adresy - vyrazena pokud je pouzita univ. adresa */ if (odpoved[4] != adr && adr != 254 && adr != 255) { { printf ("Adresa: %02x ziskana %02x\n", adr, odpoved[4]); perror ("Chyba, odpovida spatne zarizeni"); return (-5); } } if (odpoved[5] != sig) { perror ("Chyba, spatny podpis zpravy"); printf ("Signatura ocekavana: %02x, ziskana: %02x\n", sig, odpoved[5]); return (-6); } if (odpoved[6] != '\x00' && odpoved[6] != '\x0d' && odpoved[6] != '\x0e') { perror ("Chyba, spatna instrukce (neplatny kod/data/nepovolen zapis"); return (-7); } if (odpoved[3 + delka_zbytku] != '\x0d') { perror ("Chyba, cteni, data nekompletni, nebo udana spatna delka"); return (-8); } #ifdef DEBUG printf ("# Odpoved: "); #endif for (i = 0; i < 2 + delka_zbytku; i++) { #ifdef DEBUG printf ("%02x ", odpoved[i]); #endif tmp += odpoved[i]; } #ifdef DEBUG printf ("%02x %02x \n", odpoved[2 + delka_zbytku], odpoved[3 + delka_zbytku]); #endif if (odpoved[2 + delka_zbytku] != (unsigned char) ~tmp) { perror ("Chyba kontrolniho souctu"); return (-9); } delka_dat = delka_zbytku - 5; if (delka_dat == 0) { return (0); } else { for (i = 0; i < delka_dat; i++) { data[i] = odpoved[7 + i]; } return (delka_dat); } } int posli_instrukci (int port, unsigned char adr, unsigned char sig, unsigned const char inst, struct data_spinel data) { int i; int tmp = 0; unsigned char dotaz[DELKA_DOTAZU]; /************************************************************************/ /* Postupne zformujeme dotaz */ /* Dotaz ma obecne nasledujici format: */ /* PRE FRM NUM NUM ADR SIG INST DATA ... SUMA CR */ /* Blizsi popis v dokumentaci SPINELu na http://www.papouch.com */ /************************************************************************/ /* PRE - prefixovy znak '*' */ dotaz[0] = '\x2a'; /* FRM - cislo formatu SPINELu - 97 */ dotaz[1] = '\x61'; /* NUM - horni byte delky zpravy, kvuli big/small indian pouzit vypocet */ dotaz[2] = (unsigned char) (5 + data.delka_dat) / 256; /* NUM - dolni byte delky zpravy, kvuli big/small indian pouzit vypocet */ dotaz[3] = (unsigned char) (5 + data.delka_dat) - 256 * (int) dotaz[2]; /* ADR - Adresa modulu */ dotaz[4] = adr; /* SIG - Signatura zpravy */ dotaz[5] = sig; /* INST - instrukce */ dotaz[6] = inst; /* DATA - data */ for (i = 0; i < data.delka_dat; i++) { dotaz[7 + i] = data.data[i]; } /* SUMA - kontrolni soucet */ for (i = 0; i < 7 + data.delka_dat; i++) { tmp += dotaz[i]; } dotaz[7 + data.delka_dat] = (unsigned char) ~tmp; dotaz[8 + data.delka_dat] = '\x0d'; #ifdef DEBUG printf ("# Posilam:"); for (i = 0; i < 9 + data.delka_dat; i++) { printf (" %02x", dotaz[i]); } printf ("\n"); #endif if (write (port, dotaz, 9 + data.delka_dat) < 9 + data.delka_dat) { perror ("# posli_instrukci: nedari se poslat vsechny byty na port"); return (-1); } else { return (0); } } int fce_povoleni_konfigurace (int port, unsigned char adresa) { posli_instrukci (port, adresa, 'p', povoleni_konfigurace, no_data); if (prijmi_odpoved (port, adresa, 'p', NULL) > -1) { printf ("Konfigurace povolena\n"); return (0); } else { perror ("Nezdarilo se povoleni konfigurace pristroje\n"); return (-1); } } int fce_kalibrace (int port, unsigned char adresa) { posli_instrukci (port, adresa, 'a', kalibrace, no_data); if (prijmi_odpoved (port, adresa, 'a', NULL) > -1) { printf ("Pristroj kalibrovan\n"); return (0); } else { perror ("Nezdarila se kalibrace pristroje\n"); return (-1); } } int fce_reset (int port, unsigned char adresa) { posli_instrukci (port, adresa, 'r', reset, no_data); if (prijmi_odpoved (port, adresa, 'r', NULL) > -1) { printf ("Pristroj resetovan\n"); return (0); } else { perror ("Nezdaril se reset pristroje\n"); return (-1); } } int fce_cteni_vstupu (int port, unsigned char adresa) { unsigned char odpoved[DELKA_DAT]; struct int16 hodnota; posli_instrukci (port, adresa, 't', cteni_vstupu, no_data); if (prijmi_odpoved (port, adresa, 't', odpoved) > 0) { hodnota.data = (odpoved[0] << 8) + odpoved[1]; printf ("%d\n", hodnota.data); return (0); } else { perror ("Nezdarilo se cteni vstupu!\n"); return (-1); } } int fce_cteni_vystupu (int port, unsigned char adresa) { unsigned char odpoved[DELKA_DAT]; struct int16 hodnota; posli_instrukci (port, adresa, 'u', cteni_vystupu, no_data); if (prijmi_odpoved (port, adresa, 'u', odpoved) > 0) { hodnota.data = (odpoved[0] << 8) + odpoved[1]; printf ("%d\n", hodnota.data); return (0); } else { perror ("Nezdarilo se cteni vystupu!\n"); return (-1); } } int fce_jednorazovy_odmer (int port, unsigned char adresa) { unsigned char odpoved[DELKA_DAT]; struct int16 hodnota; posli_instrukci (port, adresa, 'o', jednorazovy_odmer, no_data); if (prijmi_odpoved (port, adresa, 'o', odpoved) > 0) { hodnota.data = (odpoved[0] << 8) + odpoved[1]; printf ("%.1f\n", ((float) hodnota.data / 10.0)); return (0); } else { perror ("Nezdaril se jednorazovy odmer\n"); return (-1); } } int fce_kontinualni_mereni (int port, unsigned char adresa, int interval) { unsigned char signatura = 0; unsigned char odpoved[DELKA_DAT]; struct int16 hodnota; struct data_spinel data_instrukce; unsigned char vlastni_data[2]; vlastni_data[0] = (unsigned char) (interval >> 8); vlastni_data[1] = (unsigned char) interval; data_instrukce.data = vlastni_data; data_instrukce.delka_dat = 2; posli_instrukci (port, adresa, 'o', kontinualni_mereni, data_instrukce); if (prijmi_odpoved (port, adresa, 'o', odpoved) > -1) { if (interval == 0) { printf ("Konec kontinualniho mereni!\n"); return (0); } printf ("Zacatek kontinualniho mereni!\n"); while (1) { /* Uspani pri prilis dlouhe periode mereni, aby nedoslo k timeout-um */ if (interval > 40) { usleep (interval * 20000 - 500000); } if (prijmi_odpoved (port, adresa, signatura, odpoved) > 0) { hodnota.data = (odpoved[0] << 8) + odpoved[1]; printf ("%.1f\n", ((float) hodnota.data / 10.0)); } signatura++; } } else { perror ("Nezdaril se jednorazovy odmer\n"); return (-1); } } int fce_nastaveni_komunikacnich_parametru (int port, unsigned char adresa, unsigned char nova_adresa, unsigned char rychlost) { unsigned char odpoved[DELKA_DAT]; struct data_spinel data_instrukce; unsigned char vlastni_data[2]; vlastni_data[0] = nova_adresa; vlastni_data[1] = rychlost; data_instrukce.data = vlastni_data; data_instrukce.delka_dat = 2; posli_instrukci (port, adresa, 'n', nastaveni_komunikacnich_parametru, data_instrukce); if (prijmi_odpoved (port, adresa, 'n', odpoved) > -1) { printf ("Komunikacni parametry nastaveny!\n"); return (0); } else { perror ("Nezdarilo se nastaveni komunikacnich parametru!\n"); return (-1); } } int fce_cteni_surove_hodnoty (int port, unsigned char adresa) { unsigned char odpoved[DELKA_DAT]; posli_instrukci (port, adresa, 's', cteni_surove_hodnoty, no_data); if (prijmi_odpoved (port, adresa, 's', odpoved) > 0) { printf ("%d\n", ((int) (odpoved[0] << 8) + (int) odpoved[1])); return (0); } else { perror ("Nezdarilo se cteni surove hodnoty\n"); return (-1); } } int fce_cteni_komunikacnich_parametru (int port) { unsigned char odpoved[DELKA_DAT]; posli_instrukci (port, '\xfe', 'k', cteni_komunikacnich_parametru, no_data); if (prijmi_odpoved (port, '\xfe', 'k', odpoved) > 0) { printf ("Adresa: dekadicky %02d, hexadecimalne %02x \n", odpoved[0], odpoved[0]); if (odpoved[1] == 6) printf ("Rychlost: 9600 baudu\n"); else if (odpoved[1] == 10) printf ("Rychlost: 115200 baudu\n"); else { printf ("Rychlost: decimalne %d, hexadecimalne %x - prevod na baudy", odpoved[1], odpoved[1]); printf (" - prevod na baudy podle tabulky na http://www.papouch.com/"); } return (0); } else { perror ("Nezdarilo se cteni adresy a rychlosti (spatna kom. rychlost?)\n"); return (-1); } } int fce_cteni_cteni_jmena_a_verze (int port, unsigned char adresa) { unsigned char odpoved[DELKA_DAT]; int delka_odpovedi; posli_instrukci (port, adresa, 'j', cteni_jmena_a_verze, no_data); delka_odpovedi = prijmi_odpoved (port, adresa, 'j', odpoved); if (delka_odpovedi > 0) { odpoved[delka_odpovedi] = '\0'; printf ("%s\n", odpoved); return (0); } else { perror ("Nezdarilo se cteni jmena a verze\n"); return (-1); } } int help (void) { printf ("--------------------------------------------------------------\n"); printf ("gpt232 verze 0.1 - Marek Drapal (c) 2007 pod licenci GNU GPL\n"); printf ("--------------------------------------------------------------\n"); printf ("a - adresa pristroje (dekadicky 1-253, 254 je univerzalni)\n"); printf ("r - rychlost v Baudech (napr. 9600)\n"); printf ("p - port (v Linuxu napr. /dev/ttyS0)\n"); printf ("l - kalibrace (na vstup treba pripojit presny odpor 100 Ohmu)\n"); printf ("x - reset pristroje\n"); printf ("y - cteni vystupu - NETESTOVANO\n"); printf ("v - cteni vstupu - NETESTOVANO\n"); printf ("j - jednorazovy odmer\n"); printf ("k - kontinualni mereni\n"); printf ("i - interval mezi odmery u kont. mereni. 0=zastaveni mereni, \n"); printf (" 1-65536 v nasobcich 20 milisekund (tj. hodnota*20 ms)\n"); printf ("s - nastaveni adresy a rychlosti\n"); printf ("n - nova adresa (1-253)\n"); printf ("g - nova rychlost (6-9600, 10-115200, vice viz manual Papouch)\n"); printf ("c - cteni surove hodnoty, vraci 0-65536, pouze v nekterych FW\n"); printf ("m - cteni komunikacnich parametru\n"); printf ("t - cteni jmena a verze firmware\n"); printf ("--------------------------------------------------------------\n"); printf ("Mezi parametrem a hodnotou musi byt mezera.\n"); printf ("Priklady pouziti:\n"); printf ("Jednorazovy odmer na adrese 1, portu /dev/ttyS0 a 9600 Baudu\n"); printf ("./gpt232 -a 1 -r 9600 -p /dev/ttyS0 -j\n"); printf ("Jednorazovy odmer surovych dat na adrese 10, portu /dev/ttyS1\n"); printf ("a pri rychlosti komunikace 115200 Baudu\n"); printf ("./gpt232 -a 10 -r 115200 -p /dev/ttyS1 -c\n"); printf ("--------------------------------------------------------------\n"); printf ("Dotazy na marek@drapal.org, stranky http://marek.drapal.org\n"); printf ("--------------------------------------------------------------\n"); return (0); } int main (int argc, char **argv) { int port; int rychlost = 0; int interval = 65536; int i, akce = 11; unsigned char adresa = 0; unsigned char nova_adresa = 0; unsigned char nova_rychlost = 0; char pismeno; char nazev_portu[DELKA_DAT]; nazev_portu[0] = '\0'; for (i = 1; i < argc; i++) { if (!strncmp ("-", argv[i], 1)) { pismeno = argv[i][1]; switch (pismeno) { /* Port pristroje */ case 'p': if (i + 1 >= argc || !strncmp ("-", argv[i + 1], 1)) { printf ("# Nespecifikovan port!"); } else { strncpy (nazev_portu, argv[i + 1], DELKA_DAT); } break; /* Urceni adresy */ case 'a': if (i + 1 >= argc || !strncmp ("-", argv[i + 1], 1)) { perror ("# Nespecifikovana adresa zarizeni."); } else { adresa = atoi (argv[i + 1]); } break; /* Urceni rychlosti portu (v Baudech) */ case 'r': if (i + 1 >= argc || !strncmp ("-", argv[i + 1], 1)) { perror ("# Nespecifikovana rychlost portu."); } else { rychlost = atoi (argv[i + 1]); } break; /* Urceni intervalu kontinualniho mereni */ case 'i': if (i + 1 >= argc || !strncmp ("-", argv[i + 1], 1)) { perror ("# Nespecifikovan interval mezi vzorky!"); } else { interval = atoi (argv[i + 1]); } break; /* Nova adresa */ case 'n': if (i + 1 >= argc || !strncmp ("-", argv[i + 1], 1)) { perror ("# Nespecifikovana nova adresa!"); } else { nova_adresa = atoi (argv[i + 1]); } break; /* Nova rychlost */ case 'g': if (i + 1 >= argc || !strncmp ("-", argv[i + 1], 1)) { perror ("# Nespecifikovana nova rychlost!"); } else { nova_rychlost = atoi (argv[i + 1]); } break; /* Kalibrace pristroje */ case 'l': akce = 1; break; /* Reset pristroje */ case 'x': akce = 2; break; /* Cteni vystupu */ case 'y': akce = 3; break; /* Cteni vstupu */ case 'v': akce = 4; break; /* Jednorazovy odmer */ case 'j': akce = 5; break; /* Kontinualni mereni */ case 'k': akce = 6; break; /* Nastaveni adresy a rychlosti */ case 's': akce = 7; break; /* Cteni surove hodnoty */ case 'c': akce = 8; break; /* Cteni komun. parametru */ case 'm': akce = 9; break; /* Cteni jmena a verze */ case 't': akce = 10; break; /* Help - pomoc */ case 'h': case '-': default: akce = 11; break; } } else continue; } if (nova_rychlost == 0 && akce == 7) { printf ("# Nespecifikovana nova rychlost, Koncim!\n"); exit (-1); } if (nova_adresa == 0 && akce == 7) { printf ("# Nespecifikovana nova adresa, Koncim!\n"); exit (-1); } if (interval == 65536 && akce == 6) { interval = 50; printf ("# Nespecifikovan interval mezi vzorky, nastavuji 1s\n"); } if (adresa == 0) { adresa = 254; printf ("# Nespecifikovana adresa, zkousim broadcast\n"); } if (rychlost == 0) { rychlost = 9600; printf ("# Nespecifikovana rychlost, zkousim 9600 Baudu\n"); } if (nazev_portu[0] == '\0') { printf ("# Nespecifikovan port, zkousim /dev/ttyS0\n"); strncpy (nazev_portu, "/dev/ttyS0", DELKA_DAT); } port = open_port (nazev_portu, 0); nastav_port (port, rychlost); switch (akce) { case 1: fce_povoleni_konfigurace (port, adresa); fce_kalibrace (port, adresa); break; case 2: fce_reset (port, adresa); sleep (1); break; case 3: fce_cteni_vystupu (port, adresa); break; case 4: fce_cteni_vstupu (port, adresa); break; case 5: fce_jednorazovy_odmer (port, adresa); break; case 6: fce_kontinualni_mereni (port, adresa, interval); break; case 7: fce_povoleni_konfigurace (port, adresa); fce_nastaveni_komunikacnich_parametru (port, adresa, nova_adresa, nova_rychlost); break; case 8: fce_cteni_surove_hodnoty (port, adresa); break; case 9: fce_cteni_komunikacnich_parametru (port); break; case 10: fce_cteni_cteni_jmena_a_verze (port, adresa); break; case 11: help (); break; } close (port); return (0); }