Usare la SX16 sotto LinuxData di pubblicazione: 01-07-2005 | Versione Italiana | (No English Version) Parole chiave: - How-To - Linux - Schede Area SX - |
Spesso per motivi professionali o più semplicemente per hobby, si ha la necessità di implementare nelle proprie applicazioni software uno strato hardware capace di comandare o verificare condizioni di stato da apparati esterni come ad esempio accendere lampadine o verificare un contatto magari collegato ad una porta.
In quest' articolo vediamo come, grazie alla scheda d'espansione SX 16 prodotta da AreaSX S.r.l, sia semplice e veloce permettere ad un
applicazione scritta in C sotto Linux di interagire con il mondo esterno.
La versione di SX16 che andremo ad usare è la stand alone la cui documentazione completa è disponibile all'URL:
http://www.areasx.com/index.php?D=1&id=8091
Questa versione si distingue da quella normale, perchè dotata di un microprocessore PIC a bordo già
programmato con un firmware che semplifica notevolmente l'interfacciamento con un PC.
Oltre al micro è presente anche un modulo RF modello ER400 che rende la SX16 completamente libera da fili
(wireless) che la collegano al computer. L'ER400, prodotto dalla LPRS, è un completo sistema per incapsulare una comunicazione seriale bidirezionale su una portante in radiofrequenza di 433MHz e capace di coprire distanze, in condizioni ottimali, di 250mt.
Per permettere al PC di inviare comandi su onde radio alla scheda SX16 usiamo il modulo RF04.
RF04 è un modulo di ridotte dimensioni che integra un convertitore USB - Seriale della
FTDI (compatibile con il Kernel 2.4) e un ER400 per trasmettere e ricevere il segnale segnale su portante a 433MHz.
Appena collegato ad un PC Linux, RF04 viene riconosciuto automaticamente ed è visibile nella directory /dev/ con il nome ttyUSB0.
Nei paragrafi che seguono oltre ad essere descritto il protocollo completo di comunicazione della SX16 stand alone, è fornito un semplice programma d'esempio scritto in C e compilabile sotto Linux con il programma GCC usando la seguente sintassi:
[root@virtualinux sorgenti_c]# gcc -o nome_file_output nome_file_sorgente.c
esempio:
[root@virtualinux sorgenti_c]# gcc -o sx16_linux_rele SX16_LINUX_RELE.c
Comandare i Relè della SX16
Il protocollo per attivare i relè della SX16, prevede l'invio di un comando composto da sei byte e ritorna altrettanti byte ad operazione eseguita, per indicare lo stato dei relè.
Nella tabella che segue viene descritto byte a byte il comando da inviare
Byte |
Descrizione |
Esadecimale |
1 |
START |
0x33 |
2 |
INDIRIZZO |
0x01 |
3 |
COMANDO |
0x03 |
4 |
RELE' |
da 0x01 a 0x06 |
5 |
STATO |
0x00 OFF - 0x01 ON |
6 |
ZERO |
0x00 |
Analisi dei byte che compongono la risposta inviata dalla SX16 al nostro PC ad operazione completata:
Byte |
Descrizione |
Esadecimale |
1 |
SOR |
0x22 |
2 |
INDIRIZZO |
0x01 |
3 |
COMANDO |
0x03 |
4 |
STATO RELE' |
da 0x00 a 0x63 |
5 |
ZERO |
0x00 |
6 |
ZERO |
0x00 |
Sorgente d'esempio in C:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h>
#define BAUDRATE B19200 #define DEVICE "/dev/ttyUSB0"
//COMUNCAZIONE CON SX16 #define START 0x33 #define RETURN_START 0x22 #define ADDR 0x01 #define WRITE_CMD 0x03 #define ALL_CMD 0xFF #define CH 0x01 #define ON 0x01 #define OFF 0x00 #define ZERO_CHAR 0x00
/////////////////////// //PROTOTIPI /////////////////////// int read_key(); int send2sx16 (int _fd, char *cmd2sx16, char *sx16return);
/////////////////////// //FUNZIONI /////////////////////// //Legge l'input da tastiera int read_key(){ int cmd; while (cmd = getchar()){ if (cmd!=10) break; } return cmd; } //Invia un comando alla SX16 int send2sx16 (int _fd, char *cmd2sx16, char *sx16return){ int res; if (write(_fd, cmd2sx16, 6) < 0) { //Invio sulla seriale fallito return 0; } else { //Invio del comando riuscito usleep(100000); //sleep per 100mSec res = read(_fd,sx16return,6); sx16return[res]=0; return 1; } }
//////////////////////////////// // // MAIN // //////////////////////////////// main (int argc, char * argv[]) { int fd, res, i; int b; int cmd; int line_rele; int state_rele; struct termios oldtio,newtio; char buf[10]; system("clear"); //Pulizia della console printf("----------------------------\n"); printf(" LINUX 2 SX16\n\n"); printf(" Ver: 1.0\n"); printf(" Author: Daniele De Santis\n"); printf(" E-Mail: [email protected]\n\n"); printf(" Test RELE'\n"); printf("----------------------------\n");
printf("Apertua Porta COM\n"); fd = open(DEVICE, O_RDWR | O_NOCTTY); if (fd < 0 ) { printf("Device %s non pesente su questo sistema\n\n", DEVICE); exit(-1); } printf("Inizializzazione della comunicazione seriale\n"); tcgetattr(fd,&oldtio); /* Salva i precedenti settaggi */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 6; tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); //Inizializzazione device seriale terminata printf("Seleziona il relè da settare [1 - 6]\n"); line_rele = read_key(); if(line_rele < 49 && line_rele > 54) line_rele=49; printf("Seleziona lo stato [1 ON - 0 OFF]\n"); state_rele = read_key(); if (state_rele!=48 || state_rele!=49) state_rele == 48; //FORMATTO IL PACCHETTO DA MANDARE ALLA SERIALE buf[0]=0; buf[0]=START; buf[1]=ADDR; buf[2]=WRITE_CMD; buf[3]=(line_rele - 48); if(state_rele==49) buf[4]=ON; else buf[4]=OFF; buf[5]=ZERO_CHAR; if (send2sx16 (fd, buf, buf)) { if((buf[0]==RETURN_START) && (buf[1]==ADDR) && (buf[2]==WRITE_CMD)) { printf("----------------------------\n"); printf(" STATO DEI RELE SULLA SX 16 \n"); printf("----------------------------\n"); res = buf[3]; for (i=0; i<=5; i++){ b = res & 1; res >>=1; if(b){ printf("RELE' %d [X] ON\n", (i + 1) ); } else { printf("RELE' %d [ ] OFF\n", (i + 1)); } } } } printf("\nChiusura comunicazione seriale...\n"); tcsetattr(fd,TCSANOW,&oldtio); printf("Arrivederci :)\n"); close(fd); exit(0); } Il sorgente soprariportato, va copiato e salvato in un file con estensione ".c" (es: SX16_LINUX_RELE.c) e compilato con il programma GCC come l'esempio che segue:
[root@virtualinux sorgenti_c]# gcc -o sx16_linux_rele SX16_LINUX_RELE.c
se la compilazione non ci da messaggi d'errore, troveremo nella stessa path del file sorgente, un file che dovremo rendere eseguibile con il comando chmod
[root@virtualinux sorgenti_c]# chmod +x sx16_linux_rele
a questo punto lanciando l'eseguibile con un "./" davanti al nome dell'eseguibile
[root@virtualinux sorgenti_c]# ./sx16_linux_rele
vedremo sulla nostra console una videata simile a quella che segue:
Lettura della temperatura:
Sulla SX16 è montato un sensore di temperatura di tipo digitale a 8bit della Dallas DS1621 con range di lettura compreso tra -55 e + 128 con una precisione di 0.5 °C
Il comando da inviare per effettuare l'acquisizione della temperatura è il seguente:
Byte |
Descrizione |
Esadecimale |
1 |
START |
0x33 |
2 |
INDIRIZZO |
0x01 |
3 |
COMANDO |
0x02 |
4 |
ZERO |
0x00 |
5 |
ZERO |
0x00 |
6 |
ZERO |
0x00 |
La risposta che riceveremo dalla SX16 ad acquisizione terminata:
Byte |
Descrizione |
Esadecimale |
1 |
SOR |
0x22 |
2 |
INDIRIZZO |
0x01 |
3 |
COMANDO |
0x02 |
4 |
TEMPERATURA |
da 0x00 a 0xC9 |
5 |
TEMPERATURA |
0x00 o 0x80 |
6 |
ZERO |
0x00 |
il 4 byte rappresenta la parte intera del valore della temperatura ed è positivo per valori esadecimali che vanno da 0x00 fino a 0x80 superata questa soglia le letture della temperatura sono negative. Il quinto byte rappresenta il valore decimale della lettura è può avere solo due valori 0x00 che rappresenta ",0" o 0x80 che rappresenta il mezzo grado ",5".
Sorgente d'esempio in C:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h>
#define BAUDRATE B19200 #define DEVICE "/dev/ttyUSB0" #define DELAY_LOOP 10 //In sec
//COMUNCAZIONE CON SX16 #define START 0x33 #define RETURN_START 0x22 #define ADDR 0x01 #define WRITE_CMD 0x03 #define TMP_CMD 0x02 #define READ_CMD 0x01 #define ALL_CMD 0xFF #define CH 0x01 #define ON 0x01 #define OFF 0x00 #define ZERO_CHAR 0x00 #define TEST 0x80
/////////////////////// //PROTOTIPI /////////////////////// int read_key(); int send2sx16 (int _fd, char *cmd2sx16, char *sx16return);
/////////////////////// //FUNZIONI /////////////////////// //Legge l'input da tastiera int read_key(){ int cmd; while (cmd = getchar()){ if (cmd!=10) break; } return cmd; } //Invia un comando alla SX16 int send2sx16 (int _fd, char *cmd2sx16, char *sx16return){ int res; char buffer[7]; if (write(_fd, cmd2sx16, 6) < 0) { //Invio sulla seriale fallito return 0; } else { //Invio del comando riuscito usleep(500000); //sleep per 500mSec res = read(_fd,sx16return,6); return 1; } }
//////////////////////////////// // // MAIN // //////////////////////////////// main (int argc, char * argv[]) { int fd, res, i; auto float temp; int cmd; struct termios oldtio,newtio; unsigned char buf[20];
system("clear"); //Pulizia della console printf("Apertua Porta COM\n"); fd = open(DEVICE, O_RDWR | O_NOCTTY); if (fd < 0 ) { printf("Device %s non pesente su questo sistema\n\n", DEVICE); exit(-1); } printf("Inizializzazione della comunicazione seriale\n"); tcgetattr(fd,&oldtio); /* Salva i precedenti settaggi */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 5;
tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); //Inizializzazione device seriale terminata while (1){ system("clear"); //FORMATTO IL PACCHETTO DA MANDARE ALLA SERIALE buf[0]=0; buf[0]=START; buf[1]=ADDR; buf[2]=TMP_CMD; buf[3]=ZERO_CHAR; buf[4]=ZERO_CHAR; buf[5]=ZERO_CHAR; if (send2sx16 (fd, buf, buf)) { if((buf[0]==RETURN_START) && (buf[1]==ADDR) && (buf[2]==TMP_CMD)) { printf("---------------------------------------\n"); printf(" LINUX 2 SX16\n\n"); printf(" Ver: 1.0\n"); printf(" Author: Daniele De Santis\n"); printf(" E-Mail: [email protected]\n\n"); printf(" LETTURA DELLA TEMPERATURA DALLA SX 16 \n"); printf("---------------------------------------\n"); printf("Temperatura letta dal sensore DS1621: "); temp=0; // Imposto il mezzo grado if (buf[4]==0x80) temp=0.5; if (buf[3] & 0x80) { // Temperatura negativa temp+=(!buf[3])+1; temp=-temp; } else { // Temperatura >0 temp+=buf[3]; } printf("%.2f °C\n", temp); } else { printf("ERRORE -Risposta dalla SX16 non valida\n"); } printf("\nCTRL+C per uscire\n"); usleep(1000000 * DELAY_LOOP); } else { printf("ERRORE -Comunicazione con il device %s fallito\n", DEVICE); exit(-1); } } printf("\nChiusura comunicazione seriale...\n"); tcsetattr(fd,TCSANOW,&oldtio); printf("Arrivederci :)\n"); close(fd); exit(0); }
Modificando il valore dei define
#define DEVICE "/dev/ttyUSB0" #define DELAY_LOOP 10 //In sec
è possibile cambiare il nome del device tty con cui comunicare e il ritardo, in sec, con cui viene letta la temperatura. Per la compilazione e l'esecuzione si deve procedere come per l'esempio precedentemente visto per controllare i rele'.
A compilazione terminate lanciando l'eseguibile avremo a video una schermata come quella che segue:
Acquisizione dello stato dai 24 ingressi:
I 24 ingressi sono divisi a blocchi di otto ed ognuno di questi è caratterizzato da una tipologia d'ingresso specifica. I primi otto (punto 1 nella foto che segue) sono di tipo optoisolato, ideali per collegare segnali elettrici con tensioni superiori ai 5V fino ad un massimo di 24V. Il secondo blocco (punto 2 nella foto che segue) è composto da ingressi di tipo TTL a su cui è possibile applicare livelli di tensione compesi tra 0V e massimo 5V. Il terzo blocco (punto 3) è identico per quanto riguarda i livelli di tensione al secondo blocco con la sola differenza che su ogni linea è presente un filtro antidisturbo del tipo CLC.
La lettura dei 24 ingressi viene effettuata con un unico comando il cui protocollo è descritto nella tabella che segue:
Byte |
Descrizione |
Esadecimale |
1 |
START |
0x33 |
2 |
INDIRIZZO |
0x01 |
3 |
COMANDO |
0x01 |
4 |
ZERO |
0x00 |
5 |
ZERO |
0x00 |
6 |
ZERO |
0x00 |
come risposta dalla SX16 si avrà un pacchetto, sempre di sei byte, rispettante il seguente protocollo:
Byte |
Descrizione |
Esadecimale |
1 |
SOR |
0x22 |
2 |
INDIRIZZO |
0x01 |
3 |
COMANDO |
0x01 |
4 |
INPUT CTC |
da 0x00 a 0xFF |
5 |
INPUT TTL |
da 0x00 a 0xFF |
6 |
INPUT OPT |
da 0x00 a 0xFF |
Sorgente d'esempio in C:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h>
#define BAUDRATE B19200 #define DEVICE "/dev/ttyUSB0" #define DELAY_LOOP 10 //In sec
//COMUNCAZIONE CON SX16 #define START 0x33 #define RETURN_START 0x22 #define ADDR 0x01 #define WRITE_CMD 0x03 #define READ_CMD 0x01 #define ALL_CMD 0xFF #define CH 0x01 #define ON 0x01 #define OFF 0x00 #define ZERO_CHAR 0x00
/////////////////////// //PROTOTIPI /////////////////////// int read_key(); int send2sx16 (int _fd, char *cmd2sx16, char *sx16return);
/////////////////////// //FUNZIONI /////////////////////// //Legge l'input da tastiera int read_key(){ int cmd; while (cmd = getchar()){ if (cmd!=10) break; } return cmd; } //Invia un comando alla SX16 int send2sx16 (int _fd, char *cmd2sx16, char *sx16return){ int res;
if (write(_fd, cmd2sx16, 6) < 0) { //Invio sulla seriale fallito return 0; } else { //Invio del comando riuscito usleep(100000); //sleep per 100mSec res = read(_fd,sx16return,6); sx16return[res]=0; return 1; } }
//////////////////////////////// // // MAIN // //////////////////////////////// main (int argc, char * argv[]) { int fd, res, i; int b; int cmd; struct termios oldtio,newtio; char buf[10];
system("clear"); //Pulizia della console printf("Apertua Porta COM\n"); fd = open(DEVICE, O_RDWR | O_NOCTTY); if (fd < 0 ) { printf("Device %s non pesente su questo sistema\n\n", DEVICE); exit(-1); } printf("Inizializzazione della comunicazione seriale\n"); tcgetattr(fd,&oldtio); /* Salva i precedenti settaggi */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 6; tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); //Inizializzazione device seriale terminata while (1){ system("clear"); //FORMATTO IL PACCHETTO DA MANDARE ALLA SERIALE buf[0]=0; buf[0]=START; buf[1]=ADDR; buf[2]=READ_CMD; buf[3]=ZERO_CHAR; buf[4]=ZERO_CHAR; buf[5]=ZERO_CHAR; if (send2sx16 (fd, buf, buf)) { if((buf[0]==RETURN_START) && (buf[1]==ADDR) && (buf[2]==READ_CMD)) { printf("---------------------------------------\n"); printf(" LINUX 2 SX16\n\n"); printf(" Ver: 1.0\n"); printf(" Author: Daniele De Santis\n"); printf(" E-Mail: [email protected]\n\n"); printf(" STATO DELLE LINEE D'INPUT SULLA SX 16 \n"); printf("---------------------------------------\n"); printf("Stato degli ingressi CLC:\n"); for (i=0; i<=7; i++){ b = buf[3] & 1; buf[3] >>=1; if(b) printf("LINEA CLC %d [X] ON\n", (i + 1) ); else printf("LINEA CLC %d [ ] OFF\n", (i + 1)); } printf("Stato degli ingressi TTL:\n"); for (i=0; i<=7; i++){ b = buf[4] & 1; buf[4] >>=1; if(b) printf("LINEA TTL %d [X] ON\n", (i + 1) ); else printf("LINEA TTL %d [ ] OFF\n", (i + 1)); } printf("Stato degli ingressi OPTOISOLATE:\n"); for (i=0; i<=7; i++){ b = buf[5] & 1; buf[5] >>=1; if(b) printf("LINEA OPTO %d [X] ON\n", (i + 1) ); else printf("LINEA OPTO %d [ ] OFF\n", (i + 1)); } } else { printf("ERRORE -Risposta dalla SX16 non valida\n"); } printf("\nCTRL+C per uscire\n"); usleep(1000000 * DELAY_LOOP); } else { printf("ERRORE -Comunicazione con il device %s fallito\n", DEVICE); exit(-1); } } printf("\nChiusura comunicazione seriale...\n"); tcsetattr(fd,TCSANOW,&oldtio); printf("Arrivederci :)\n"); close(fd); exit(0); }
Per la compilazione e l'esecuzione si deve procedere come per gli esempi precedenti.
Sulla console, lanciando il programma, apparirà una videata come a quella che segue:
Ogni dieci secondi la SX16 viene interrogata dal software e l'attivazione di un singolo ingresso viene
segnalata con una X tra le parentesi "[ ]" della rispettiva riga che ne descrive il tipo di contatto.
Sorgenti in C d'esempio SX16_LINUX_SRC.zip
Segnala questo articolo:
Parole chiave: - How-To - Linux - Schede Area SX -
|