/* * riello.c: driver core for Riello protocol based UPSes * * Documents describing the protocol implemented by this driver can be * found online at: * * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf * * Copyright (C) 2012 - Elio Parisi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Reference of the derivative work: blazer driver */ #include #include #include "main.h" #include "riello.h" static uint8_t foundheader = 0; uint16_t buf_ptr_length; uint8_t wait_packet = 0; uint8_t foundnak = 0; uint8_t foundbadcrc = 0; uint8_t commbyte; uint8_t requestSENTR; static unsigned char LAST_DATA[6]; uint16_t riello_calc_CRC(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum) { uint16_t i; uint16_t pom, CRC_Word; CRC_Word = 0; switch (type) { case DEV_RIELLOSENTRY: CRC_Word = 0; if (size == 101) { for (i=0; i<100; i++) CRC_Word += buff[i]; } else { for (i=0; i> 4); CRC_Word = (CRC_Word >> 8) ^ pom; buff++; } } else { CRC_Word = 0; for (i=1; iIdentification, &buffer[7], 16); data->Identification[16] = 0; memcpy(data->ModelStr, &buffer[23], 16); data->ModelStr[16] = 0; data->ModelStr[15] = 0; memcpy(data->Version, &buffer[39], 12); data->Version[12] = 0; memcpy(data->Identif_bytes, &buffer[51], 12); data->Identif_bytes[11] = 0; data->NumBat = data->Identif_bytes[7] - 0x30; } void riello_parse_gn(uint8_t* buffer, TRielloData* data) { uint16_t pom_word; uint32_t pom_long; uint8_t j; j = 7; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); if (data->Identif_bytes[0] != '1') pom_long/=100; assert (pom_long < UINT16_MAX); data->NomPowerKVA = (uint16_t)pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); if (data->Identif_bytes[0] != '1') pom_long/=100; assert (pom_long < UINT16_MAX); data->NomPowerKW = (uint16_t)pom_long; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NomUbat = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NomBatCap = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NominalUout = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->NomFout = pom_word; } void riello_parse_rs(uint8_t* buffer, TRielloData* data, uint8_t numread) { uint16_t pom_word; uint8_t j; j = 7; memcpy(data->StatusCode, &buffer[j], 5); data->StatusCode[5] = 0; data->Boost = riello_test_bit(&buffer[j], 9); data->Buck = riello_test_bit(&buffer[j], 8); data->LockUPS = riello_test_bit(&buffer[j], 2); j+=5; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Finp = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uinp1 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); if (!riello_test_bit(&data->StatusCode[0], 3)) pom_word = 0; data->Fout = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uout1 = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Pout1 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Fbypass = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubypass1 = pom_word; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubat = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->BatCap = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->BatTime = pom_word; if (data->BatTime == 0xfff) data->BatTime = 0xffff; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Tsystem = pom_word; if (numread > 42) { pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uinp2 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uinp3 = pom_word; } else { data->Uinp2 = 0; data->Uinp3 = 0; } if (numread > 48) { pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uout2 = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Pout2 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubypass2 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Uout3 = pom_word; pom_word = (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Pout3 = pom_word; pom_word = (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Ubypass3 = pom_word; } else { data->Uout2 = 0; data->Pout2 = 0; data->Ubypass2 = 0; data->Uout3 = 0; data->Pout3 = 0; data->Ubypass3 = 0; } } void riello_parse_re(uint8_t* buffer, TRielloData* data) { uint16_t pom_word; uint32_t pom_long; uint8_t j; j = 23; data->Iinp1 = 0xFFFF; data->Iinp2 = 0xFFFF; data->Iinp3 = 0xFFFF; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Iout1 = pom_word; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Iout2 = pom_word; pom_word = (buffer[j++]-0x30)*4096; pom_word += (buffer[j++]-0x30)*256; pom_word += (buffer[j++]-0x30)*16; pom_word += (buffer[j++]-0x30); data->Iout3 = pom_word; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout1W = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout2W = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout3W = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout1VA = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout2VA = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); data->Pout3VA = pom_long; } void riello_parse_rc(uint8_t* buffer, TRielloData* data) { uint8_t j, i; j = 7; for (i = 0; i < 22; i++, j+=2) { data->StatusCodeT[i] = (char)(buffer[j+1]-0x30); data->StatusCodeT[i] |= ((buffer[j]-0x30) << 4); } data->StatusCodeT[23] = 0; data->StatusCodeT[24] = 0; } void riello_parse_sentr(uint8_t* buffer, TRielloData* data) { uint32_t pom; data->Model = buffer[2]+256*buffer[3]; if (data->Model < 3000) { if ((data->Model % 10) >= 4) { if (buffer[100] & 0x01) requestSENTR = SENTR_EXT176; else requestSENTR = SENTR_ALSO240; } else requestSENTR = SENTR_ONLY192; data->NomPowerKVA = data->Model/10; } else { if (((data->Model-3000) % 10) >= 4) { if (buffer[100] & 0x01) requestSENTR = SENTR_EXT176; else requestSENTR = SENTR_ALSO240; } else requestSENTR = SENTR_ONLY192; data->NomPowerKVA = (data->Model-3000)/10; } if (buffer[76] & 0x08) data->NomPowerKW = ((data->NomPowerKVA * 1000) * 9 / 10) / 1000; else data->NomPowerKW = ((data->NomPowerKVA * 1000) * 8 / 10) / 1000; data->NomPowerKVA *= 1000; data->NomPowerKW *= 1000; if (data->Model < 3000) data->Identif_bytes[0] = '3'; else data->Identif_bytes[0] = '4'; data->Identif_bytes[1] = '3'; data->Identif_bytes[6] = '2'; data->SWversion = buffer[4]+256*buffer[5]; data->Version[0] = (char)(uint8_t)(48 + ((data->SWversion / 1000) % 10)); data->Version[1] = (char)(uint8_t)(48 + ((data->SWversion / 100) % 10)); data->Version[2] = '.'; data->Version[3] = (char)(uint8_t)(48 + ((data->SWversion / 10) % 10)); data->Version[4] = (char)(uint8_t)(48 + (data->SWversion % 10)); if (data->Model < 3000) pom = data->Model*100; else pom = (data->Model-3000)*100; if (buffer[0] == SENTR_EXT176) { data->Uinp1 = (buffer[117]+buffer[118]*256)/10; data->Uinp2 = (buffer[119]+buffer[120]*256)/10; data->Uinp3 = (buffer[121]+buffer[122]*256)/10; data->Iinp1 = (buffer[123]+buffer[124]*256)/10; data->Iinp2 = (buffer[125]+buffer[126]*256)/10; data->Iinp3 = (buffer[127]+buffer[128]*256)/10; data->Finp = buffer[41]+256*buffer[42]; data->Uout1 = (buffer[135]+buffer[136]*256)/10; data->Uout2 = (buffer[137]+buffer[138]*256)/10; data->Uout3 = (buffer[139]+buffer[140]*256)/10; data->Iout1 = (buffer[141]+buffer[142]*256)/10; data->Iout2 = (buffer[143]+buffer[144]*256)/10; data->Iout3 = (buffer[145]+buffer[146]*256)/10; data->Pout1 = buffer[62]; data->Pout2 = buffer[63]; data->Pout3 = buffer[64]; data->Ipout1 = buffer[65]*3; } else { data->Uinp1 = buffer[35]*230/100; data->Uinp2 = buffer[36]*230/100; data->Uinp3 = buffer[37]*230/100; /* TODO: Range-check the casts to uint16_t? */ data->Iinp1 = (uint16_t)(((pom/690)*buffer[38])/100); data->Iinp2 = (uint16_t)(((pom/690)*buffer[39])/100); data->Iinp3 = (uint16_t)(((pom/690)*buffer[40])/100); data->Finp = buffer[41]+256*buffer[42]; if (buffer[79] & 0x80) { data->Uout1 = buffer[59]*2; data->Uout2 = buffer[60]*2; data->Uout3 = buffer[61]*2; } else { data->Uout1 = buffer[59]; data->Uout2 = buffer[60]; data->Uout3 = buffer[61]; } if (buffer[73]) { /* FIXME: Wondering how the addition below works for uint8_t[] buffer... */ /* TODO: Range-check the casts to uint16_t? */ if (buffer[73] < 100) buffer[73]+=256; if (data->Model < 3000) /* singlephase */ data->Iout1 = (uint16_t)(((pom/buffer[73])*buffer[62])/100); else data->Iout1 = (uint16_t)(((pom/buffer[73])*buffer[62])/100/3); data->Iout2 = (uint16_t)(((pom/buffer[73])*buffer[63])/100/3); data->Iout3 = (uint16_t)(((pom/buffer[73])*buffer[64])/100/3); } else { data->Iout1 = 0; data->Iout2 = 0; data->Iout3 = 0; } if ((data->Model & 0x0007) < 4) { data->Iout1 *= 0.9; data->Iout2 *= 0.9; data->Iout3 *= 0.9; } data->Pout1 = buffer[62]; data->Pout2 = buffer[63]; data->Pout3 = buffer[64]; data->Ipout1 = buffer[65]*3; } if (data->Model < 3000) { data->Ipout2 = 0; data->Ipout3 = 0; } else { data->Ipout2 = buffer[66]*3; data->Ipout3 = buffer[67]*3; } data->Fout = buffer[68]+256*buffer[69]; data->BatTime = buffer[6]+256*buffer[7]; data->BatCap = buffer[8]; if ((buffer[0] == SENTR_ALSO240) || (buffer[0] == SENTR_EXT176)) { if (buffer[100] & 0x80) data->Ubat = buffer[43]+256*buffer[44]; else { if (buffer[44] < buffer[43]) data->Ubat = buffer[44]*2; else data->Ubat = buffer[43]*2; } } else data->Ubat = buffer[43]+256*buffer[44]; data->Ubat *= 10; data->Ibat = buffer[45]+256*buffer[46]; if (!buffer[47]) data->Ibat = data->Ibat*10; data->Tsystem = buffer[48]; data->NomBatCap = buffer[74]+256*buffer[75]; switch (buffer[0]) { case SENTR_EXT176: data->Ubypass1 = (buffer[129]+buffer[130]*256)/10; data->Ubypass2 = (buffer[131]+buffer[132]*256)/10; data->Ubypass3 = (buffer[133]+buffer[134]*256)/10; data->Fbypass = buffer[57]+256*buffer[58]; break; case SENTR_ALSO240: data->Ubypass1 = buffer[51]*2; data->Ubypass2 = buffer[53]*2; data->Ubypass3 = buffer[55]*2; data->Fbypass = buffer[57]+256*buffer[58]; break; default: data->Ubypass1 = buffer[51]+256*buffer[52]; data->Ubypass2 = buffer[53]+256*buffer[54]; data->Ubypass3 = buffer[55]+256*buffer[56]; data->Fbypass = buffer[57]+256*buffer[58]; break; } data->StatusCode[0] = 0x08; data->StatusCode[1] = 0x00; data->StatusCode[2] = 0x00; data->StatusCode[3] = 0x00; data->StatusCode[4] = 0x00; /* Overload if (riello_test_bit(&DevData.StatusCode[4], 2)) */ if (buffer[31] & 128) data->StatusCode[4] |= 0x04; /* Bypass if (riello_test_bit(&DevData.StatusCode[1], 3)) */ if (((buffer[31] & 2) || (riello_test_bit(&buffer[32], 0)) || (riello_test_bit(&buffer[32], 12)) || (riello_test_bit(&buffer[32], 13)) || (riello_test_bit(&buffer[32], 14))) && (!(buffer[34] & 8))) data->StatusCode[1] |= 0x08; /* AC Fail if (riello_test_bit(&DevData.StatusCode[0], 1)) */ if (buffer[31] & 8) data->StatusCode[0] |= 0x02; /* LowBatt if ((riello_test_bit(&DevData.StatusCode[0], 1)) && (riello_test_bit(&DevData.StatusCode[0], 0))) */ if (buffer[31] & 16) { if (buffer[31] & 8) { data->StatusCode[0] |= 0x02; data->StatusCode[0] |= 0x01; } } /* Standby if (!riello_test_bit(&DevData.StatusCode[0], 3)) */ if ((buffer[22] & 2) || (buffer[34] & 4) || (buffer[34] & 8)) data->StatusCode[0] &= 0xF7; /* Battery bad (Replace battery) if (riello_test_bit(&DevData.StatusCode[2], 0)) */ if (buffer[31] & 0x40) data->StatusCode[2] |= 0x01; } void riello_init_serial() { wait_packet = 1; buf_ptr_length = 0; foundbadcrc = 0; foundnak = 0; } uint8_t riello_header(uint8_t type, uint8_t a, uint8_t* length) { LAST_DATA[0] = LAST_DATA[1]; LAST_DATA[1] = LAST_DATA[2]; LAST_DATA[2] = LAST_DATA[3]; LAST_DATA[3] = LAST_DATA[4]; LAST_DATA[4] = LAST_DATA[5]; LAST_DATA[5] = (uint8_t) a; switch (type) { case DEV_RIELLOSENTRY: if (((LAST_DATA[4]>=192) && (LAST_DATA[5]==103)) || ((LAST_DATA[4]==176) && (LAST_DATA[5]==164))) { *length = LAST_DATA[5]; return(1); } break; case DEV_RIELLOGPSER: if ((buf_ptr_length==0) && (LAST_DATA[5]>0x20) && (LAST_DATA[4]==0x2)) return(1); break; } return(0); } uint8_t riello_tail(uint8_t type, uint8_t length) { uint8_t number; switch (type) { case DEV_RIELLOSENTRY: number = length; if (buf_ptr_length >= number) return(1); break; case DEV_RIELLOGPSER: if (LAST_DATA[5] == 0x03) return(1); break; } return(0); } uint8_t riello_test_nak(uint8_t type, uint8_t* buffer) { switch (type) { case DEV_RIELLOGPSER: if (buffer[3] == 0x15) return(1); break; } return(0); } void riello_parse_serialport(uint8_t typedev, uint8_t* buffer, uint8_t checksum) { static uint8_t actual_char, int_i; static uint8_t length; actual_char = commbyte; if ((riello_header(typedev, actual_char, &length)) && (!foundheader)) { upsdebugx(5,"Header detected: LAST_DATA:%X,%X,%X,%X,%X,%X buf_ptr:%i \n\r", LAST_DATA[0], LAST_DATA[1], LAST_DATA[2], LAST_DATA[3], LAST_DATA[4], LAST_DATA[5], buf_ptr_length); foundheader = 1; buf_ptr_length = 1; memset(buffer, 0, BUFFER_SIZE); buffer[0] = LAST_DATA[4]; } if ((foundheader) && (buf_ptr_length < BUFFER_SIZE)) buffer[buf_ptr_length++] = actual_char; if ((foundheader) && (riello_tail(typedev, length))) { upsdebugx(5,"\n\rEnd detected: LAST_DATA:%X,%X,%X,%X,%X,%X buf_ptr:%i \n\r", LAST_DATA[0], LAST_DATA[1], LAST_DATA[2], LAST_DATA[3], LAST_DATA[4], LAST_DATA[5], buf_ptr_length); foundheader = 0; for (int_i=0; int_i<6; int_i++) LAST_DATA[int_i] = 0; if (riello_test_nak(typedev, buffer)) { wait_packet = 0; foundnak = 1; } if (riello_test_crc(typedev, buffer, buf_ptr_length, checksum)) { wait_packet = 0; foundbadcrc = 1; } else { wait_packet = 0; foundbadcrc = 0; } } }