www.ghislain.fr

Systeme UNIX C - K8056

Objectifs :

- Gérer une carte Wellman K8056 en IP via le protocol UDP

Crédits :
Définition des besoins :

- Une carte K8056 est connectée sur une serveur de type UNIX/LINUX, via le port série.

- Le Programme serveur nommé 'K8056netd' doit tournée en mode démon et ecouter le port UDP.
Il écoute le port, et lorsqu'une information se présente, la mise en forme est effectuer, le checsum est calculé, et les données sont envoyées vers la carte.

- Le programme client est utilisé sur une station cliente (UNIX/LINUX) et envoie les commandes de gestion vers le port UDP.

- Il est possible d'utiliser un socket en PHP pour commander la carte via une interface WEB.

 

Source serveur :

 

/* k8056ned UDP SERVER By FG 2011 v1.3d */
/* Usage : k8056netd [-h] [-v] [-V] [-d device] [-p PORT] */
/* -- Synoptique -- */
/* On Demonize */
/* Ecoute Port */
/* Si données */
/* Appel le fils */
/*  Verification */
/*  Mise en forme */
/*  transmission vers k8056 */
/* tue le fils */
/* retour ecoute */
/* modif 369 et 373 pf_net par af_net + IPPROTO_UDP + verbose dans la console debug dans le syslog */
/*------------------------------------------------------------------------------*/
/*  Includes                                                     */
/*------------------------------------------------------------------------------*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <termios.h>
#include <math.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <syslog.h>

/*------------------------------------------------------------------------------*/
/*  Definitions                                                     */
/*------------------------------------------------------------------------------*/
#define MSGSIZE 2
#define BAUDRATE B2400
#define NPACK 10
/* Change this to whatever your daemon is called */
#define DAEMON_NAME "k8056netd"
/* Change this to the user under which to run */
#define RUN_AS_USER "root"
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
/*------------------------------------------------------------------------------*/
/*  Variables                                                       */
/*------------------------------------------------------------------------------*/
struct  termios oldtio, newtio ;
int      k8056_port ;
char  k8056_device[16] = "/dev/k8056" ; /*  default device. */
unsigned char  k8056_instruction = 'E';
unsigned char  k8056_addr = 1 ;
unsigned char k8056_relay_address = '0'  ;
int  k8056_trame_number = 5 ;  /* Increase value with a long serial cable or in wireless operation. */
int  verbose = 0 ;
int  debug = 0 ;
char   cmd[6] ;
int  Taillecmd ;
int sockfd, cc;
unsigned int addr_in_size;
u_short portnum = 12345;
struct sockaddr_in *my_addr, *from;
char *msg;
u_long fromaddr;
/*------------------------------------------------------------------------------*/
/*  Affiche l'aide                                                              */
/*------------------------------------------------------------------------------*/
void usage (void) {
 printf("\nk8056ned   Usage:    k8056netd [-h] [-v] [-V] [-d device] [-p PORT] \n");
 printf("      Pour plus de details utiliser l'aide: k8056netd -h\n\n");
printf("      Version 1.3c\n\n");
}
void help (void) {
 printf("\nk8056netd controle la carte 'Velleman K8056' via UDP (Version Francaise par FOURNIER Ghislain - 2011 \n\n");
 printf("Usage:   k8056netd [-h] [-v] [-d device] [-p PORT] \n\n");
 printf("        -h                : Aide\n");
 printf("        -v                : Mode verbeux\n");
 printf("        -V                : Debug\n");
 printf("        -d device         : Rs232 port, par defaut /dev/k8056 (A faire: : ln -s /dev/ttyS0 /dev/k8056 par exemple)\n");
 printf("        -p port           : UDP port, par defaut 180472 \n");
 printf("  ** Command examples **\n");
 printf("  k8056netd -d /dev/ttyS14 -p 1234  : Controle la carte  sur port série /dev/ttyS14, recoie les données sur le port udp 1234.\n\n");
printf("      Version 1.3c\n\n");
}

/*------------------------------------------------------------------------------*/
/*  Ouverture port serie                                               */
/*------------------------------------------------------------------------------*/
void initserie (void) {
 if (verbose) printf("Ouverture du port\n");
 if (debug) syslog( LOG_INFO, "Ouverture du port serie" );
 k8056_port = open(k8056_device, O_RDWR | O_NOCTTY );
 if (k8056_port < 0) {
   if (verbose) { fprintf(stderr, "Erreur d'ouverture du peripherique %s !\n", k8056_device) ; }
  exit(-1);
 }
  tcgetattr(k8056_port,&oldtio);   /* Sauvegarde des parametres courants du port  */
  newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
  newtio.c_oflag = 0;
  tcflush(k8056_port, TCOFLUSH);  /* Flushes written but not transmitted. */
  tcsetattr(k8056_port, TCSANOW, &newtio);
}

/*------------------------------------------------------------------------------*/
/* Checksum (complement à 2 de la somme de 4 octets + 1)                        */
/*------------------------------------------------------------------------------*/
unsigned char checksum(char cmd[]) {
        unsigned char checksum ;
        /*  Ex. VB: checksum = ( 255 - ( ( ( (a+b+c+d / 256 ) - Int( (13 + cmd[1] + cmd[2] + cmd[3]) / 256 ) ) * 256 ) ) + 1 ; */
        /* ( 255 - ((a+b+c+d) modulo 256) ) + 1  Calcul de checkum (complement à 2 de la somme de 4 octets + 1).   */
 if (verbose) printf("Calcul Checksum\n");
 checksum = ~((13 + cmd[1] + cmd[2] + cmd[3]) & 0xFF) + 1 ;
        return checksum ;
}

/*------------------------------------------------------------------------------*/
/* SendCommand Send command to k8056 card                                      */
/*------------------------------------------------------------------------------*/
void SendCommand(char cmd[]) {
 if (debug) syslog( LOG_INFO, "Send command to k8056" );
 int i ;
 tcflush(k8056_port, TCOFLUSH) ;  /* Flushes written but not transmitted. */
 for (i=0 ; i < k8056_trame_number ; i++) {
  write(k8056_port, cmd, 5) ;
  if (verbose) printf(". %d \n",i) ;
  if (verbose) {
   printf(" Donnees envoyer = %s \n",cmd) ;
   Taillecmd = strlen (cmd);
   printf(" Taille Donnees  = %d \n",Taillecmd) ;
  }
  if (verbose) printf("Ecriture sur le port\n");
  if (verbose) printf("Pause entre deux Ecriture\n");
  usleep(5000) ;
 }
}

/*------------------------------------------------------------------------------*/
/* Parse de la ligne de commande pour les arguments otionnels               */
/*------------------------------------------------------------------------------*/
char parse_cmdline(int argc,char *argv[]) {
   int helpflg=0;
   int errflg=0;
   int c;
   while ((c = getopt(argc, argv, "d:p:hvV")) != EOF)
   {
      switch (c)
      {
         case 'd':
             strcpy(k8056_device, optarg) ;
             break;
    case 'p':
             portnum = atoi(optarg);
             break;
  case 'v':
             verbose = 1;
             break;
         case 'V':
             debug = 1;
             break;
         case 'h':
             helpflg++;
          break;
         case '?':
             errflg++;
          break;
      }
        if (helpflg) /* Aide */
 {
           help();
           exit (1);
        }
     }
  //if (optind < 2 || errflg)  /* Usage */
  if (errflg)  /* Usage */
    {
         usage();
         exit (1);
    }
     return 1;
}
/*------------------------------------------------------------------------------*/
/* Preparation a l'envoi des données                                      */
/*------------------------------------------------------------------------------*/
void sendserie(char msg[])
{
if (verbose) { printf(" Données reçues = %d \n",msg[0]) ; }
if (verbose) { fprintf(stdout,"Réception de %s port %d: %s\n",
      (gethostbyaddr((char *)&fromaddr,
         sizeof(fromaddr),
         PF_INET))->h_name,
       from->sin_port,msg); }
 if (verbose) printf("Envoi commande vers la carte k8056:\n Port: %s, Addresse carte: %d, Instruction: %c, Relais #%c\n"
  , k8056_device, k8056_addr, msg[0], msg[1]) ;
 cmd[0] = 13 ;     /* 13          */
 cmd[1] = k8056_addr ;   /* Adresse         */
 cmd[2] = msg[0] ;    /* Instruction        */
 cmd[3] = msg[1] ;    /* # relais, addresse ou status    */
 cmd[4] = checksum(cmd) ;  /* Checksum         */
 if (verbose) printf(" Checksum = %d\n", checksum(cmd)) ;
 SendCommand(cmd) ;
  if (debug) syslog( LOG_INFO, "Send command fait" );

        /* Fermeture port serie */
 tcsetattr(k8056_port, TCSANOW, &oldtio); /* Sauvegarde des anciens parametres du port  */
 if (verbose) printf("Fermeture du port\n");
        close(k8056_port);
 if (verbose) printf("Pause entre deux commandes\n");
 usleep(300000) ;     /* Pause entre 2 commandes. */
     if (verbose) printf("Fait\n");
 if (verbose) printf(".\n");
}

/*------------------------------------------------------------------------------*/
/* Gestion des zombies                                                          */
/*------------------------------------------------------------------------------*/
static void child_handler(int signum)
{
    switch(signum) {
    case SIGALRM: exit(EXIT_FAILURE); break;
    case SIGUSR1: exit(EXIT_SUCCESS); break;
    case SIGCHLD: exit(EXIT_FAILURE); break;
    }
}

/*------------------------------------------------------------------------------*/
/* Daemonization                                                                */
/*------------------------------------------------------------------------------*/
static void daemonize( const char *lockfile )
{
    pid_t pid, sid, parent;
    int lfp = -1;
    /* already a daemon */
    if ( getppid() == 1 ) return;
    /* Create the lock file as the current user */
    if ( lockfile && lockfile[0] ) {
        lfp = open(lockfile,O_RDWR|O_CREAT,0640);
        if ( lfp < 0 ) {
            syslog( LOG_ERR, "unable to create lock file %s, code=%d (%s)",
                    lockfile, errno, strerror(errno) );
            exit(EXIT_FAILURE);
        }
    }
    /* Drop user if there is one, and we were run as root */
    if ( getuid() == 0 || geteuid() == 0 ) {
        struct passwd *pw = getpwnam(RUN_AS_USER);
        if ( pw ) {
            syslog( LOG_NOTICE, "setting user to " RUN_AS_USER );
            setuid( pw->pw_uid );
        }
    }
    /* Trap signals that we expect to recieve */
    signal(SIGCHLD,child_handler);
    signal(SIGUSR1,child_handler);
    signal(SIGALRM,child_handler);
    /* Fork off the parent process */
    pid = fork();
    if (pid < 0) {
        syslog( LOG_ERR, "unable to fork daemon, code=%d (%s)",
                errno, strerror(errno) );
        exit(EXIT_FAILURE);
    }
    /* If we got a good PID, then we can exit the parent process. */
    if (pid > 0) {
        /* Wait for confirmation from the child via SIGTERM or SIGCHLD, or
           for two seconds to elapse (SIGALRM).  pause() should not return. */
        alarm(2);
        pause();
        exit(EXIT_FAILURE);
    }
    /* At this point we are executing as the child process */
    parent = getppid();
    /* Cancel certain signals */
    signal(SIGCHLD,SIG_DFL); /* A child process dies */
    signal(SIGTSTP,SIG_IGN); /* Various TTY signals */
    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
    signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */
    /* Change the file mode mask */
    umask(0222);
    /* Create a new SID for the child process */
    sid = setsid();
    if (sid < 0) {
        syslog( LOG_ERR, "unable to create a new session, code %d (%s)",
                errno, strerror(errno) );
        exit(EXIT_FAILURE);
    }
    /* Change the current working directory.  This prevents the current
       directory from being locked; hence not being able to remove it. */
    if ((chdir("/")) < 0) {
        syslog( LOG_ERR, "unable to change directory to %s, code %d (%s)",
                "/", errno, strerror(errno) );
        exit(EXIT_FAILURE);
    }
    /* Redirect standard files to /dev/null */
    freopen( "/dev/null", "r", stdin);
    freopen( "/dev/null", "w", stdout);
    freopen( "/dev/null", "w", stderr);
    /* Tell the parent process that we are A-okay */
    kill( parent, SIGUSR1 );
}

/*------------------------------------------------------------------------------*/
/* MAIN Ecoute reception données UDP                                            */
/*------------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
    /* Initialize the logging interface */
    openlog( DAEMON_NAME, LOG_PID, LOG_LOCAL5 );
    syslog( LOG_INFO, "starting k8056netd" );
    /* One may wish to process command line arguments here */
  parse_cmdline(argc,argv) ;
 addr_in_size = sizeof(struct sockaddr_in);
 // Détournement du signal émis à la mort du fils (il ne reste pas zombie)
// signal(SIGCHLD, SIG_IGN);
 msg = (char *)malloc(MSGSIZE);
 from = (struct sockaddr_in *)malloc(addr_in_size);
 my_addr = (struct sockaddr_in *)malloc(addr_in_size);
 memset((char *)my_addr,(char)0,addr_in_size);
 my_addr->sin_family = AF_INET;
 my_addr->sin_addr.s_addr = htonl(INADDR_ANY);
 my_addr->sin_port = htons(portnum);
 if((sockfd = socket (PF_INET, SOCK_DGRAM, 0)) < 0){
  if (verbose) fprintf(stderr,"Erreur %d dans socket: %s\n",errno,strerror(errno));
  exit(errno);
 };
 if(bind(sockfd, (struct sockaddr *)my_addr, addr_in_size) < 0){
  if (verbose)  fprintf(stderr,"Erreur %d dans bind: %s\n",errno,strerror(errno));
  if(errno != EADDRINUSE) exit(errno);
 };
 /* On daemonize */
 if (debug) syslog( LOG_INFO, "daemonize k8056netd" );
    daemonize( "/var/lock/" DAEMON_NAME );
    /* Now we are a daemon -- do the work for which we were paid */
 if (verbose)  fprintf(stdout,"Prêt a reçevoir\n");
 if (debug) syslog( LOG_INFO, "On ecoute le port UDP" );
   while(1){
    if (debug) syslog( LOG_INFO, "On est dans la boucle" );
  if((cc = recvfrom (sockfd,msg,MSGSIZE,0,(struct sockaddr *)from,
           &addr_in_size)) == -1){
   if (verbose)  fprintf(stderr,"Erreur %d dans recvfrom: %s\n",
    errno,strerror(errno));
   exit(errno);
  };
  fromaddr = from->sin_addr.s_addr;
  msg[cc] = '\0';
  if (verbose)  fprintf(stdout,"Reçue de  %s port %d: %s\n",
  (gethostbyaddr((char *)&fromaddr,
         sizeof(fromaddr),
         PF_INET))->h_name,
  from->sin_port,msg);
 if (debug) syslog( LOG_INFO, "Reçue de  %s port %d: %s\n",
  (gethostbyaddr((char *)&fromaddr,
         sizeof(fromaddr),
         PF_INET))->h_name,
  from->sin_port,msg);
  /* Ouverture port serie */
  initserie() ;
  if (debug) syslog( LOG_INFO, "init serie fait" );
  /* Formate msg vers cmd et envoie */
  sendserie(msg);
   if (debug) syslog( LOG_INFO, "Send serie fait" );
  /* on attend avant de revenir a l'ecoute */
  sleep(3);
  if (debug) syslog( LOG_INFO, "On repart" );
  // signal(SIGCHLD,SigCatcher);
       /* Finish up */
    if (debug) syslog( LOG_NOTICE, "terminated" );
    closelog();
 }
        /* Quitt... */
 if (verbose) printf("----- ICI ON SORT -----\n");
    return 0;
}
/*------------------------------------------------------------------------------*/

 

Source Client :
 
 

 

/* K8056 Client By FG 2011 v1.3c*/
/* Lecture des parametres
Usage k8056net [-i IP] [-p PORT] [-h] [-v] [-V] [-a card_address] [-S|-C|-P|-T relay#] [-B relay_status] [-A new_address] [-E|-D|-F]
parametres emis:
- instuction (A.B.C.E.D.F.S.T.P.)
- relais (1..8)
recupere parametre
convertion parametre
transmission vers adresse et port
sortie*/
// Includes
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>  /* Standard input/output definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <string.h>  /* String function definitions */
#include <math.h>
#include <fcntl.h>
#include <errno.h>   /* Error number definitions */
// define
#define MSGSIZE 500
// Variables
struct  termios oldtio, newtio ;
unsigned char  k8056_instruction = 'E';
unsigned char  k8056_addr = 1 ;
unsigned char k8056_relay_address = '0'  ;
int  k8056_trame_number = 5 ;  /*  Increase value with a long serial cable or in wireless operation. */
int  verbose = 0 ;
int  debug = 0 ;
char  cmd[6] ;
int  Taillecmd ;
u_short portnum = 12345;
char *msg, *htoname = "192.168.0.1";

/*------------------------------------------------------------------------------*/
/*  Fonctions Usage et Aides                                                    */
/*------------------------------------------------------------------------------*/
void usage (void) {
printf("\nk8056    Usage k8056net [-i IP] [-p PORT] [-h] [-v] [-V] [-a card_address] [-S|-C|-P|-T relay#] [-B relay_status] [-A new_address] [-E|-D|-F]\n");
printf("      Pour plus de details utiliser l'aide: k8056 -h\n\n");
printf("      Version 1.3c\n\n");
}
void help (void) {
printf("\nk8056  est un programme qui controle la carte 'Velleman K8056' via udp (Version Francaise par FOURNIER Ghislain - 2011 \n\n");
printf("Usage k8056net [-i IP] [-p PORT] [-h] [-v] [-V] [-a card_address] [-S|-C|-T relay#] [-B relay_status] [-A new_address] [-E|-D|-F]\n\n");
printf("      Version 1.3c\n\n");
printf("        -h                : Aide\n");
printf("        -v                : Mode verbeux\n");
printf("        -V                : Debug\n");
printf("        -i addresse IP    : Adresse IP du serveur\n");
printf("        -p port           : Port UDP, par defaut 180472 \n");
printf("        -a addresse_carte : Adresse de la carte (1..255) en decimal\n");
printf("        -S|-C|-P|-T relay# : Instructions to Active/Relache/Impulsion/Basculer le relais numero ('1'..'9')\n");
printf("        -A new_address    : Regler la nouvelle adresse de la carte (1..255)\n");
printf("        -B status_relais  : Reglez le relais huit avec octet status_relais (1..255)\n");
printf("        -E|-D|-F          : Stop d'Urgence / Afficher l'adresse / Forcer l'adresse à\n\n");
printf(" ** Command examples **\n");
printf("        k8056 -i 192.168.0.1 -v -E       : Arrêd'urgence toutes les cartes relais (mode verbeux).\n");
printf("        k8056 -i 192.168.0.1 -v -S6      : Active relais #6 (mode verbeux).\n");
printf("        k8056 -i 192.168.0.1 -v -C5      : Relache relais #5 (mode verbeux).\n");
printf("        k8056 -i 192.168.0.1 -v -P3      : Impulsion relais #5 (mode verbeux).\n");
printf("        k8056 -i 192.168.0.1 -v -T5      : Basculer le relais#5 => Active relay #5 (mode verbeux).\n");
printf("        k8056 -i 192.168.0.1 -B 127      : Relache relay #1, Active relais #2..8\n");
printf("        k8056 -i 192.168.0.1 -p 1000 -S1 : Active relais #1 sur le port udp 1000.\n\n");
}

/*------------------------------------------------------------------------------*/
/* Parse the command line to find additionnal arguments                         */
/*------------------------------------------------------------------------------*/
char parse_cmdline(int argc,char *argv[]) {
   int helpflg=0;
   int errflg=0;
   int c;
   while ((c = getopt(argc, argv, "S:C:P:T:A:B:a:d:i:p:EDFhvV")) != EOF)
   {
      switch (c)
      {
         case 'S':
         case 'C':
         case 'P':
         case 'T':
   k8056_instruction = c ;
             k8056_relay_address = optarg[0] ;
             break;
         case 'A':
         case 'B':
   k8056_instruction = c ;
             k8056_relay_address = atoi(optarg);
             break;
         case 'E':
         case 'D':
         case 'F':
   k8056_instruction = c ;   /* The 3rd byte = 0 (k8056_relay_address) */
             break;
  case 'a':
             k8056_addr = atoi(optarg);
             break;
  case 'i':
             //strlcpy(htoname, optarg, sizeof(htoname));
  htoname = optarg;
             break;
  case 'p':
                portnum = atoi(optarg);
             break;
         case 'v':
             verbose = 1;
             break;
         case 'V':
             debug = 1;
             break;
         case 'h':
             helpflg++;
          break;
         case '?':
             errflg++;
          break;
      }
        if (helpflg)
 {
           help();
           exit (1);
        }
     }
     if (optind < 2 || errflg)   /* if optind = 1, option missing. */
    {
         usage();
         exit (1);
    }
     return 1;
}

/*------------------------------------------------------------------------------*/
/* MAIN                         */
/*------------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
  parse_cmdline(argc,argv) ;
   int sockfd, ok, addr_in_size;
  struct sockaddr_in *to;
  struct hostent *toinfo;
  u_long toaddr;
  msg = (char *)malloc(MSGSIZE);
  to = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
 msg[0] = k8056_instruction ;    /* Instruction    */
 msg[1] = k8056_relay_address ;    /* # relais, address or status  */
  if (verbose) printf("Envoi commande vers la carte k8056:\n Port ip: %u, Addresse ip: %s, Instruction: %c, Relais #%c\n"
  , portnum, htoname, msg[0], msg[1]) ;
  if((toinfo = gethostbyname(htoname)) == NULL){
    fprintf(stderr,"Error %d in gethostbyname: %s\n",
      errno,sys_errlist[errno]);
    exit(errno);
  };
  toaddr = *(u_long *)toinfo->h_addr_list[0];
  addr_in_size = sizeof(struct sockaddr_in);
  memset((char *)to,(char)0,addr_in_size);
  to->sin_family = AF_INET;
  to->sin_addr.s_addr = toaddr;
  to->sin_port = htons(portnum);
  if((sockfd = socket (PF_INET, SOCK_DGRAM, 0)) == -1){
    fprintf(stderr,"Error %d in socket: %s\n",errno,sys_errlist[errno]);
    exit(errno);
  };
    ok = 1;
    if(sendto(sockfd,msg,strlen(msg),0,(struct sockaddr *)to,
        addr_in_size) == -1){
      fprintf(stderr,"Error %d in sendto: %s\n",errno,sys_errlist[errno]);
      exit(errno);
    }
        /* Quitt... */
        exit(0);
}
 
 
/*------------------------------------------------------------------------------*/
Problèmes :

.

reception non garantie, il n'est pas certain que la commande soit correctement passé, et que le relais ait basculer.
Pour garantir la réception, il faudrait capter l'état de la LED de la carte.  Voir si c'est possible en 1-wire avec un DS2408.

 

nach oben