Arnaud Quette
2009-Jan-26 09:42 UTC
[Nut-upsdev] ups emerson liebert GTX2 ESP-II serial protocol demo
2009/1/22 Spiros Ioannou <sivann at gmail.com>> Cher Arnaud, >Hi Spiros,> I send you a demo that reads info from an Emerson Liebert > GXT2-10000T230 through the RS-232 port. This uses the "ESP-II (ESP 2) > Protocol". > Current supported data includes: measurements (voltage, temperature, > etc) , ID strings (serial number, etc), status flags (inverter on, > bypas on, battery charging, etc). > As usual, the original vendor software is in java (Multilink), and really > sucks. > I saw in your acknowledgment pages that you have "serial test boxes" > donated from liebert. >well, that donation was to Russell, and goes back many many years ago...> The program I send you is more complicated than needed, to be able to > support more commands in the future. > > 1) Can you make the driver? > 2) if not, can you point me to a similar driver that I can copy? > > the protocol seems to include a lot more of useless status commands, > unsupported by my ups, but these seem more than enough for a start. >well, I personally won't make the driver (no hardware and no time). but we can help you in doing one, and it's not that hard. have a look at the following docs: http://svn.debian.org/wsvn/nut/trunk/docs/new-drivers.txt?op=file&rev=0&sc=0 http://svn.debian.org/wsvn/nut/trunk/drivers/skel.c?op=file&rev=0&sc=0 creating a new serial driver is not really hard, really! I've seen in the header that you based your work on this: http://www.csc.liv.ac.uk/~greg/projects/liebertserial/ maybe the author would help you, or join the project? cheers, Arnaud -- Linux / Unix Expert R&D - Eaton - http://www.eaton.com/mgeops Network UPS Tools (NUT) Project Leader - http://www.networkupstools.org/ Debian Developer - http://people.debian.org/~aquette/<http://people.debian.org/%7Eaquette/> Free Software Developer - http://arnaud.quette.free.fr/ sample output:> > BATTERY_TIME_REMAIN: 52.0 > BATTERY_VOLTAGE: 263.0 > BATTERY_VOLTAGE_NOMINAL: 275.0 > BATTERY_CURRENT: 0.0 > BATTERY_CAPACITY: 100.0 > BYPASS_VOLTAGE: 231.0 > MAX_LOAD: 10000.0 > LOAD_WATTS: 1033.0 > LOAD_VA: 1245.0 > LOAD_PERCENT: 14.0 > INPUT_FREQUENCY: 49.9 > OUTPUT_FREQUENCY: 49.9 > BYPASS_FREQUENCY: 49.9 > INVERTER_TEMP: 0.0 > BATTERY_TEMP: 0.0 > PFC_TEMP: 0.0 > AMBIENT_TEMP: 25.9 > EXPECTED_RUNTIME: 53.0 > OUTPUT_VOLTAGE: 230.0 > OUTPUT_CURRENT: 5.0 > MODEL_NAME: GXT2-10000T230 > FW_VER: GXT2-00896V04 > SN: 08009R0056BW932 > MANUF_DATE: 04JAN08 > PFC_ON: YES > DC_DC_CONVERTER_STATE: NO > ON_INVERTER: YES > UTILITY_STATE: NO > INRUSH_LIMIT_ON: NO > OVERTEMP_WARNING: NO > BATTERY_TEST_STATE: NO > INPUT_OVERVOLTAGE: NO > ON_BATTERY: NO > ON_BYPASS: NO > BATTERY_CHARGED: YES > BATTERY_LIFE_ENHANCER_ON: NO > REPLACE_BATTERY: NO > BOOST_ON: NO > DIAG_LINK_SET: NO > BUCK_ON: NO > UPS_OVERLOAD: NO > BAD_INPUT_FREQ: NO > SHUTDOWN_PENDING: NO > CHARGER_FAIL: NO > LOW_BATTERY: NO > OUTPUT_UNDERVOLTAGE: NO > OUTPUT_OVERVOLTAGE: NO > BAD_BYPASS_PWR: NO > CHECK_AIR_FILTER: NO > AMBIENT_OVERTEMP: NO > MAIN_CONTROL_MODULE_FAILED: NO > REDUNDANT_CONTROL_MODULE_FAILED: NO > UI_MODULE_FAILED: NO > REDUNDANT_POWER_MODULE_ALARM: NO > REDUNDANT_BATTERY_MODULE_ALARM: NO > USER_MAX_LOAD_ALARM: NO > TRANSFORMER_OVERTEMP_ALARM: NO > INTERNAL_COMMS_LOST: NO > PWR_MOD_FAILED: NO > BAT_MOD_FAILED: NO >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://lists.alioth.debian.org/pipermail/nut-upsdev/attachments/20090126/17184d8b/attachment-0001.htm -------------- next part -------------- #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> #include <signal.h> #include <errno.h> /* Version 1.0 based on liebert.c from R.Gregory (http://www.csc.liv.ac.uk/~greg/projects/liebertserial/) Spiros Ioannou, sivann at gmail.com 2009 Compile with: gcc -O2 upsesp2.c -o upsesp2 Run with: ./upsesp2 /dev/ttyS0 Replace ttyS0 with your serial port (ttyS1, ...etc). */ #define BAUDRATE B2400 #define _POSIX_SOURCE 1 /* POSIX compliant source */ #define FALSE 0 #define TRUE 1 #define NCMD 100 /*max number of commands*/ #define MAXAGE 5 /*max seconds to accept data as current*/ #define IsBitSet(val, bit) ((val) & (1 << (bit))) char str[128]; /***************************************************/ typedef struct { char descr[120]; unsigned char type; /*0:measurement, 1:ascii, 2:status*/ unsigned char length; /*for ascii, how many cmds*/ unsigned char rfmt; /*unit divider for measurement, bit position for status*/ unsigned char cmd[6]; /*UPS command bytes*/ unsigned char rbyteH; /*msb result*/ unsigned char rbyteL; /*lsb result*/ unsigned char rstr[256]; /*string result from multiple commands (lenght>1)*/ time_t when; /*time of last result*/ unsigned char init; /*used to check if struct block has something valid*/ unsigned char supported; /*if this feature is supported*/ } cmd_s; //fill struct //fs(0,&c[0],"lala1", 1,2,3,4,5,6, 11,22,33,44,55,66); void fs( cmd_s * c, char * descr, unsigned char type, unsigned char length, unsigned int rfmt, unsigned int c0, unsigned int c1, unsigned int c2, unsigned int c3, unsigned int c4, unsigned int c5) { int i; strcpy(c->descr,descr); c->cmd[0]=c0;c->cmd[1]=c1;c->cmd[2]=c2;c->cmd[3]=c3;c->cmd[4]=c4;c->cmd[5]=c5; c->rstr[0]=0; c->type=type; c->when=0; c->rfmt=rfmt; c->length=length; c->init=1; c->supported=1; /*supported until proven otherwise (UPS doesn't repond) */ } /* Initialize cmd_s structure with UPS commands and expected reply format */ initcmd(cmd_s *c) { int i; /*Format:*/ /*STRINGID, type, length, rfmt, cmd*/ /*length:string length for ascii info*/ /*type:0 measurement, 1: ascii, 2: status */ /*rmft:unit divider (measurement), bit position (status) */ /*cmd: UPS commands. Checksums (last byte) will be recalculated on write */ //measurements fs(c++,"BATTERY_TIME_REMAIN", 0,0,1, 1,149,2,1,1,161); fs(c++,"BATTERY_VOLTAGE", 0,0,10, 1,149,2,1,2,161); fs(c++,"BATTERY_VOLTAGE_NOMINAL", 0,0,10, 1,161,2,1,13,154); fs(c++,"BATTERY_CURRENT", 0,0,1, 1,149,2,1,3,161); fs(c++,"BATTERY_CAPACITY", 0,0,1, 1,149,2,1,4,161); fs(c++,"BYPASS_VOLTAGE", 0,0,10, 1,144,2,1,1,149); fs(c++,"MAX_LOAD", 0,0,1, 1,161,2,1,8,154); fs(c++,"LOAD_WATTS", 0,0,1, 1,149,2,1,5,161); fs(c++,"LOAD_VA", 0,0,1, 1,149,2,1,6,161); fs(c++,"LOAD_PERCENT", 0,0,1, 1,149,2,1,7,161); fs(c++,"INPUT_FREQUENCY", 0,0,10, 1,149,2,1,8,161); fs(c++,"OUTPUT_FREQUENCY", 0,0,10, 1,149,2,1,9,161); fs(c++,"BYPASS_FREQUENCY", 0,0,10, 1,149,2,1,10,161); fs(c++,"INVERTER_TEMP", 0,0,10, 1,149,2,1,11,161); fs(c++,"BATTERY_TEMP", 0,0,10, 1,149,2,1,12,161); fs(c++,"PFC_TEMP", 0,0,10, 1,149,2,1,13,161); fs(c++,"AMBIENT_TEMP", 0,0,10, 1,149,2,1,14,161); fs(c++,"EXPECTED_RUNTIME", 0,0,1, 1,149,2,1,1,154); fs(c++,"OUTPUT_VOLTAGE", 0,0,10, 1,144,2,1,3,151); fs(c++,"OUTPUT_CURRENT", 0,0,10, 1,144,2,1,4,154); //strings fs(c++,"MODEL_NAME", 1,15,0, 1,136,2,1,4,144); fs(c++,"FW_VER", 1,8,0, 1,136,2,1,19,144); fs(c++,"SN", 1,10,0, 1,136,2,1,27,144); fs(c++,"MANUF_DATE", 1,4,0, 1,136,2,1,37,144); //status fs(c++,"PFC_ON", 2,0,0, 1,148,2,1,1,000); fs(c++,"DC_DC_CONVERTER_STATE", 2,0,1, 1,148,2,1,1,000); fs(c++,"ON_INVERTER", 2,0,2, 1,148,2,1,1,000); fs(c++,"UTILITY_STATE", 2,0,3, 1,148,2,1,1,000); fs(c++,"INRUSH_LIMIT_ON", 2,0,4, 1,148,2,1,1,000); fs(c++,"OVERTEMP_WARNING", 2,0,5, 1,148,2,1,1,000); fs(c++,"BATTERY_TEST_STATE", 2,0,6, 1,148,2,1,1,000); fs(c++,"INPUT_OVERVOLTAGE", 2,0,7, 1,148,2,1,1,000); fs(c++,"ON_BATTERY", 2,0,8, 1,148,2,1,1,000); fs(c++,"ON_BYPASS", 2,0,0, 1,148,2,1,2,000); fs(c++,"BATTERY_CHARGED", 2,0,1, 1,148,2,1,2,000); fs(c++,"BATTERY_LIFE_ENHANCER_ON", 2,0,4, 1,148,2,1,2,000); fs(c++,"REPLACE_BATTERY", 2,0,5, 1,148,2,1,2,000); fs(c++,"BOOST_ON", 2,0,6, 1,148,2,1,2,000); fs(c++,"DIAG_LINK_SET", 2,0,7, 1,148,2,1,2,000); fs(c++,"BUCK_ON", 2,0,9, 1,148,2,1,2,000); fs(c++,"UPS_OVERLOAD", 2,0,0, 1,148,2,1,3,000); fs(c++,"BAD_INPUT_FREQ", 2,0,1, 1,148,2,1,3,000); fs(c++,"SHUTDOWN_PENDING", 2,0,2, 1,148,2,1,3,000); fs(c++,"CHARGER_FAIL", 2,0,3, 1,148,2,1,3,000); fs(c++,"LOW_BATTERY", 2,0,5, 1,148,2,1,3,000); fs(c++,"OUTPUT_UNDERVOLTAGE", 2,0,6, 1,148,2,1,3,000); fs(c++,"OUTPUT_OVERVOLTAGE", 2,0,7, 1,148,2,1,3,000); fs(c++,"BAD_BYPASS_PWR", 2,0,8, 1,148,2,1,3,000); fs(c++,"CHECK_AIR_FILTER", 2,0,10, 1,148,2,1,3,000); fs(c++,"AMBIENT_OVERTEMP", 2,0,2, 1,148,2,1,7,000); fs(c++,"MAIN_CONTROL_MODULE_FAILED", 2,0,0, 1,148,2,1,19,000); fs(c++,"REDUNDANT_CONTROL_MODULE_FAILED", 2,0,1, 1,148,2,1,19,000); fs(c++,"UI_MODULE_FAILED", 2,0,2, 1,148,2,1,19,000); fs(c++,"REDUNDANT_POWER_MODULE_ALARM", 2,0,3, 1,148,2,1,19,000); fs(c++,"REDUNDANT_BATTERY_MODULE_ALARM", 2,0,4, 1,148,2,1,19,000); fs(c++,"USER_MAX_LOAD_ALARM", 2,0,5, 1,148,2,1,19,000); fs(c++,"TRANSFORMER_OVERTEMP_ALARM", 2,0,6, 1,148,2,1,19,000); fs(c++,"INTERNAL_COMMS_LOST", 2,0,7, 1,148,2,1,19,000); fs(c++,"PWR_MOD_FAILED", 2,0,9, 1,148,2,1,19,000); fs(c++,"BAT_MOD_FAILED", 2,0,10, 1,148,2,1,19,000); fs(c++,"OPTION_CARD_FAIL_1", 2,0,0, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_2", 2,0,1, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_3", 2,0,2, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_4", 2,0,3, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_5", 2,0,4, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_6", 2,0,5, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_7", 2,0,6, 1,148,2,1,20,000); fs(c++,"OPTION_CARD_FAIL_8", 2,0,7, 1,148,2,1,20,000); fs(c++,"SUMMARY_ALARM", 2,0,0, 1,148,2,1,22,000); fs(c++,"RECT_UV_STARTUP_FAIL", 2,0,0, 1,148,2,1,24,000); fs(c++,"RECT_FAULT", 2,0,1, 1,148,2,1,24,000); fs(c++,"RECT_OVER_CURRENT", 2,0,2, 1,148,2,1,24,000); fs(c++,"RECT_OVER_TEMP", 2,0,3, 1,148,2,1,24,000); fs(c++,"RECT_INDCTR_OVER_TEMP", 2,0,4, 1,148,2,1,24,000); fs(c++,"RECT_COMM_FAIL", 2,0,5, 1,148,2,1,24,000); fs(c++,"INV_SHUTDOWN_LOW_DC", 2,0,6, 1,148,2,1,24,000); fs(c++,"INV_FAULT", 2,0,7, 1,148,2,1,24,000); fs(c++,"INV_OVER_CURRENT", 2,0,8, 1,148,2,1,24,000); fs(c++,"INV_OVER_TEMP", 2,0,9, 1,148,2,1,24,000); fs(c++,"INV_INDCTR_OVER_TEMP", 2,0,10, 1,148,2,1,24,000); fs(c++,"INV_COMM_FAIL", 2,0,11, 1,148,2,1,24,000); fs(c++,"INV_DC_OFFSET_OVR", 2,0,12, 1,148,2,1,24,000); fs(c++,"INV_CONTACTOR_FAIL", 2,0,13, 1,148,2,1,24,000); fs(c++,"BAT_FAULT", 2,0,1, 1,148,2,1,25,000); fs(c++,"BAT_CONTACTOR_FAIL", 2,0,2, 1,148,2,1,25,000); fs(c++,"CONVERTER_OVER_TEMP", 2,0,4, 1,148,2,1,25,000); fs(c++,"CONVERTER_OVER_AMPS", 2,0,5, 1,148,2,1,25,000); fs(c++,"CONVERTER_FAIL", 2,0,6, 1,148,2,1,25,000); fs(c++,"BALANCER_OVER_TEMP", 2,0,7, 1,148,2,1,25,000); fs(c++,"BALANCER_FAULT", 2,0,8, 1,148,2,1,25,000); fs(c++,"BALANCER_OVER_CURRENT", 2,0,9, 1,148,2,1,25,000); fs(c++,"BY_CB_OPEN", 2,0,10, 1,148,2,1,25,000); fs(c++,"LOAD_IMPACT_XFER", 2,0,11, 1,148,2,1,25,000); fs(c++,"OPERATION_FAULT", 2,0,12, 1,148,2,1,25,000); fs(c++,"OUT_FUSE_BLOWN", 2,0,13, 1,148,2,1,25,000); fs(c++,"ON_JOINT_MODE", 2,0,14, 1,148,2,1,25,000); fs(c++,"MAIN_NEUTRAL_LOST", 2,0,4, 1,148,2,1,26,000); fs(c++,"PARALLEL_LOW_BAT_WARN", 2,0,4, 1,148,2,1,27,000); fs(c++,"PARALLEL_LOAD_SHARE_FAULT", 2,0,5, 1,148,2,1,27,000); fs(c++,"PARALLEL_FAULT", 2,0,6, 1,148,2,1,27,000); fs(c++,"PARALLEL_CONNECT_FAULT", 2,0,7, 1,148,2,1,27,000); fs(c++,"PARALLEL_COMM_FAIL", 2,0,8, 1,148,2,1,27,000); fs(c++,"PARALLEL_SYS_OVER_LOAD", 2,0,9, 1,148,2,1,27,000); fs(c++,"PARALLEL_SYS_XFER", 2,0,10, 1,148,2,1,27,000); c->init=0; /*on first non-init element */ } GetCmdCount(cmd_s * c) { int i; for (i=0;(i<NCMD)&&c++->init;i++) ; return i; } //return array position of command "descr" GetCmdIdbyDesc(cmd_s * c,char *descr) { int i; for (i=0;i<NCMD;i++) { if (!strcmp(descr,c->descr)) return i; //printf("%d: c->descr:%s\n",i,c->descr); c++; } return -1; } //return array position of the same status command with the most recent data GetRecentStatusId(cmd_s *c,unsigned char bit4,unsigned int ncmd) { int i,r=-1; time_t maxt=0; for (i=0;i<ncmd;i++,c++) { if ((c->cmd[1]!=148) || (c->cmd[4]!=bit4)) continue; else if (c->when>maxt) { maxt=c->when; r=i; } } return r; } /***************************************************/ /* print byte in binary*/ void bin_prnt_byte(int x) { int n; for(n=0; n<8; n++) { if((x & 0x80) !=0) printf("1"); else printf("0"); if (n==3) printf(" "); /* insert a space between nibbles */ x = x<<1; } } void sgnl_ignore(int status) { fprintf(stderr,"Signaled\n"); } //calculate checksum unsigned char cksum(unsigned char *buf, int len) { unsigned char sum=0; while(len-->0) sum+=*buf++; return(sum); } int timedwrite(int fd, unsigned char *buf, int len, int msec) { fd_set wfds; struct timeval tv; int retval; int writ=0; while(writ<len) { // Watch fd to see when won't block output. FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = msec/1000; tv.tv_usec = (msec%1000)*1000; retval = select(fd+1, NULL, &wfds, NULL, &tv); if( retval>=0 && FD_ISSET(fd, &wfds) ) { int res = write(fd,buf+writ,len-writ); if( res>0 ) writ+=res; else return(-1); } if( retval<=0 ) return(-1); } return(writ); } int timedread(int fd, unsigned char *buf, int len, int msec) { fd_set rfds; struct timeval tv; int retval; int red=0; while(red<len) { // Watch fd to see when it has input. FD_ZERO(&rfds); FD_SET(fd, &rfds); tv.tv_sec = msec/1000; tv.tv_usec = (msec%1000)*1000; retval = select(fd+1, &rfds, NULL, NULL, &tv); if( retval>=0 && FD_ISSET(fd, &rfds) ) { int res = read(fd,buf+red,len-red); if( res>0 ) red+=res; else return(-1); } if( retval<=0 ) return(-1); } return(red); } /* Send a command and read a measurement */ unsigned int SendCmd_M(int fd, unsigned char *cmd, unsigned char *rbyteH, unsigned char *rbyteL) { unsigned char buf[8]; cmd[5]=cksum(cmd,5); //printf("%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); int res=timedwrite(fd,cmd,6,500); if(res!=6){ fprintf(stderr,"port write error, cmd:"); fprintf(stderr,"%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); return -1; } res=timedread(fd,buf,8,200); if(res!=8){ fprintf(stderr,"read count error (%d<>8),cmd:",res); fprintf(stderr,"%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); return -1; } if(buf[7]!=cksum(buf,7)){ fprintf(stderr,"checksum error for cmd:"); fprintf(stderr,"%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); return -1; } *rbyteH=buf[5]; *rbyteL=buf[6]; return ((unsigned short)buf[5])*256+buf[6]; } /* Send a series of consecutive commands and read text response (2 chars each)*/ int SendCmd_T(int fd, unsigned char *cmd, unsigned char *chrs, int len) { unsigned char buf[8], lcmd[7]; int wres,rres; memcpy(lcmd,cmd,6); for(;len>0;len--) { lcmd[5]=cksum(lcmd,5); wres=timedwrite(fd,lcmd,6,400); if (wres!=6) return -1; rres=timedread(fd,buf,8,400); if (rres!=8) return -1; *chrs++=buf[6]; *chrs++=buf[5]; if (buf[7]!=cksum(buf,7)) return -1; lcmd[4]++; } *chrs=0; return 1; } int main(int argc, char **argv) { int fd, res, i,j,r; struct termios oldtio,newtio; unsigned char buf[255],rbyteH,rbyteL; unsigned int ncmd; cmd_s c[NCMD]; initcmd(&c[0]); ncmd=GetCmdCount(&c[0]); if(argc!=2) { fprintf(stderr,"upsesp2 <serial device>\n"); exit(1); } fd = open(argv[1], O_RDWR | O_NOCTTY ); if (fd < 0) { sprintf(str,"%s:%s",argv[1],strerror(errno)); fprintf(stderr,"%s\n",str); exit(errno); } tcgetattr(fd,&oldtio); /* save current port settings */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE /*| CRTSCTS*/ | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR | IGNBRK; newtio.c_oflag = 0; /* set input mode (non-canonical, no echo,...) */ newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); signal( SIGALRM, &sgnl_ignore ); while (1) { for (i=0;i<ncmd;i++) { //printf("\n***%d:[%03s c0:%03d C1:%03d] init:%03d type:%03d\n",i, c[i].descr,c[i].cmd[0],c[i].cmd[1],c[i].init,c[i].type); if (c[i].supported==0) { //read failed on 1st try, thus marked as not supported //printf("UNSUPPORTED item %s:ignoring\n",c[i].descr); continue; } if (c[i].type==0) { //measurement //todo: check if time is fresh, and if not then SendCmd. Same for strings res=SendCmd_M(fd, c[i].cmd, &rbyteH, &rbyteL); if(res>=0) { c[i].when=time(0); c[i].rbyteL=rbyteL; c[i].rbyteH=rbyteH; printf("%s: %.1f\n",c[i].descr,(256.0*c[i].rbyteH+c[i].rbyteL)/c[i].rfmt); } else { printf("Error getting %s:marking %d as not supported\n",c[i].descr,i); c[i].supported=0; } } else if (c[i].type==1) { //ascii res=SendCmd_T(fd, c[i].cmd, buf, c[i].length); if (res) { strcpy(c[i].rstr,buf); c[i].when=time(0); printf("%s: %s\n",c[i].descr,c[i].rstr); } else { printf("Error getting %s:marking %d as not supported\n",c[i].descr,i); c[i].supported=0; } } else if (c[i].type==2) { //status r=GetRecentStatusId(&c[0],c[i].cmd[4],ncmd); if ((r<0) || (c[i].when-time(0)>MAXAGE)) { //not previous data or old data res=SendCmd_M(fd, c[i].cmd, &rbyteH, &rbyteL); if (res>=0) { c[i].when=time(0); c[i].rbyteL=rbyteL; c[i].rbyteH=rbyteH; if (c[i].rfmt<8) (j=IsBitSet(c[i].rbyteL,c[i].rfmt)); else (j=IsBitSet(c[i].rbyteH,c[i].rfmt) ); if (j) printf("%s: YES\n",c[i].descr); else printf("%s: NO\n",c[i].descr); } else { printf("Error getting %s:marking %d as not supported\n",c[i].descr,i); c[i].supported=0; } } else { //found recent data from other poll if (c[i].rfmt<8) (j=IsBitSet(c[r].rbyteL,c[i].rfmt)); else (j=IsBitSet(c[r].rbyteH,c[r].rfmt) ); if (j) printf("%s: YES\n",c[i].descr); else printf("%s: NO\n",c[i].descr); } } } printf("Sleeping....\n\n"); sleep(2); } tcsetattr(fd,TCSANOW,&oldtio); exit(0); } /* main() */
Arnaud Quette
2009-Jan-26 15:22 UTC
[Nut-upsdev] ups emerson liebert GTX2 ESP-II serial protocol demo
2009/1/26 Spiros Ioannou <sivann at gmail.com>> Thank you Arnaud, I will make the driver and mail it to you.ok, but please mail it to the list.> The other > author doesn't have any time to help with this.he might be interested in promoting nut once the driver is done ;-) cheers, Arnaud> > Thanks, > -Spiros > > On Mon, Jan 26, 2009 at 11:42 AM, Arnaud Quette <arnaud.quette at free.fr> > wrote: > > > > 2009/1/22 Spiros Ioannou <sivann at gmail.com> > >> > >> Cher Arnaud, > > > > Hi Spiros, > > > >> > >> I send you a demo that reads info from an Emerson Liebert > >> GXT2-10000T230 through the RS-232 port. This uses the "ESP-II (ESP 2) > >> Protocol". > >> Current supported data includes: measurements (voltage, temperature, > >> etc) , ID strings (serial number, etc), status flags (inverter on, > >> bypas on, battery charging, etc). > >> As usual, the original vendor software is in java (Multilink), and > really > >> sucks. > >> I saw in your acknowledgment pages that you have "serial test boxes" > >> donated from liebert. > > > > well, that donation was to Russell, and goes back many many years ago... > > > >> > >> The program I send you is more complicated than needed, to be able to > >> support more commands in the future. > >> > >> 1) Can you make the driver? > >> 2) if not, can you point me to a similar driver that I can copy? > >> > >> the protocol seems to include a lot more of useless status commands, > >> unsupported by my ups, but these seem more than enough for a start. > > > > well, I personally won't make the driver (no hardware and no time). > > but we can help you in doing one, and it's not that hard. > > > > have a look at the following docs: > > > http://svn.debian.org/wsvn/nut/trunk/docs/new-drivers.txt?op=file&rev=0&sc=0 > > http://svn.debian.org/wsvn/nut/trunk/drivers/skel.c?op=file&rev=0&sc=0 > > > > creating a new serial driver is not really hard, really! > > I've seen in the header that you based your work on this: > > http://www.csc.liv.ac.uk/~greg/projects/liebertserial/<http://www.csc.liv.ac.uk/%7Egreg/projects/liebertserial/> > > maybe the author would help you, or join the project? > > > > cheers, > > Arnaud > > -- > > Linux / Unix Expert R&D - Eaton - http://www.eaton.com/mgeops > > Network UPS Tools (NUT) Project Leader - http://www.networkupstools.org/ > > Debian Developer - http://people.debian.org/~aquette/<http://people.debian.org/%7Eaquette/> > > Free Software Developer - http://arnaud.quette.free.fr/ > > > > > >> sample output: > >> > >> BATTERY_TIME_REMAIN: 52.0 > >> BATTERY_VOLTAGE: 263.0 > >> BATTERY_VOLTAGE_NOMINAL: 275.0 > >> BATTERY_CURRENT: 0.0 > >> BATTERY_CAPACITY: 100.0 > >> BYPASS_VOLTAGE: 231.0 > >> MAX_LOAD: 10000.0 > >> LOAD_WATTS: 1033.0 > >> LOAD_VA: 1245.0 > >> LOAD_PERCENT: 14.0 > >> INPUT_FREQUENCY: 49.9 > >> OUTPUT_FREQUENCY: 49.9 > >> BYPASS_FREQUENCY: 49.9 > >> INVERTER_TEMP: 0.0 > >> BATTERY_TEMP: 0.0 > >> PFC_TEMP: 0.0 > >> AMBIENT_TEMP: 25.9 > >> EXPECTED_RUNTIME: 53.0 > >> OUTPUT_VOLTAGE: 230.0 > >> OUTPUT_CURRENT: 5.0 > >> MODEL_NAME: GXT2-10000T230 > >> FW_VER: GXT2-00896V04 > >> SN: 08009R0056BW932 > >> MANUF_DATE: 04JAN08 > >> PFC_ON: YES > >> DC_DC_CONVERTER_STATE: NO > >> ON_INVERTER: YES > >> UTILITY_STATE: NO > >> INRUSH_LIMIT_ON: NO > >> OVERTEMP_WARNING: NO > >> BATTERY_TEST_STATE: NO > >> INPUT_OVERVOLTAGE: NO > >> ON_BATTERY: NO > >> ON_BYPASS: NO > >> BATTERY_CHARGED: YES > >> BATTERY_LIFE_ENHANCER_ON: NO > >> REPLACE_BATTERY: NO > >> BOOST_ON: NO > >> DIAG_LINK_SET: NO > >> BUCK_ON: NO > >> UPS_OVERLOAD: NO > >> BAD_INPUT_FREQ: NO > >> SHUTDOWN_PENDING: NO > >> CHARGER_FAIL: NO > >> LOW_BATTERY: NO > >> OUTPUT_UNDERVOLTAGE: NO > >> OUTPUT_OVERVOLTAGE: NO > >> BAD_BYPASS_PWR: NO > >> CHECK_AIR_FILTER: NO > >> AMBIENT_OVERTEMP: NO > >> MAIN_CONTROL_MODULE_FAILED: NO > >> REDUNDANT_CONTROL_MODULE_FAILED: NO > >> UI_MODULE_FAILED: NO > >> REDUNDANT_POWER_MODULE_ALARM: NO > >> REDUNDANT_BATTERY_MODULE_ALARM: NO > >> USER_MAX_LOAD_ALARM: NO > >> TRANSFORMER_OVERTEMP_ALARM: NO > >> INTERNAL_COMMS_LOST: NO > >> PWR_MOD_FAILED: NO > >> BAT_MOD_FAILED: NO > > > > > > > > > >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://lists.alioth.debian.org/pipermail/nut-upsdev/attachments/20090126/9b64dde4/attachment.htm