aurora-1.7.3/0000755000000000000000000000000011657113540011514 5ustar rootrootaurora-1.7.3/include/0000755000076400017510000000000011657110353012263 5ustar curtaurora-1.7.3/include/states.h0000644000076400017510000002475111347016405013747 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006-2010 Curtis J. Blank curt@curtronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * Staring with v1.5-0 this work based on Power One's 'Aurora Inverter * Series - Communication Protocol -' document, Rel. 4.6 25/02/09 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * */ #define VersionSH "1.6.0" /* Transmission States */ static char szTransStates[] = "Everything is OK\0" /* # 0 */ "\0" /* # 1 These are all placeholders */ "\0" /* # 2 */ "\0" /* # 3 */ "\0" /* # 4 */ "\0" /* # 5 */ "\0" /* # 6 */ "\0" /* # 7 */ "\0" /* # 8 */ "\0" /* # 9 */ "\0" /* # 10 */ "\0" /* # 11 */ "\0" /* # 12 */ "\0" /* # 13 */ "\0" /* # 14 */ "\0" /* # 15 */ "\0" /* # 16 */ "\0" /* # 17 */ "\0" /* # 18 */ "\0" /* # 19 */ "\0" /* # 20 */ "\0" /* # 21 */ "\0" /* # 22 */ "\0" /* # 23 */ "\0" /* # 24 */ "\0" /* # 25 */ "\0" /* # 26 */ "\0" /* # 27 */ "\0" /* # 28 */ "\0" /* # 29 */ "\0" /* # 30 */ "\0" /* # 31 */ "\0" /* # 32 */ "\0" /* # 33 */ "\0" /* # 34 */ "\0" /* # 35 */ "\0" /* # 36 */ "\0" /* # 37 */ "\0" /* # 38 */ "\0" /* # 39 */ "\0" /* # 40 */ "\0" /* # 41 */ "\0" /* # 42 */ "\0" /* # 43 */ "\0" /* # 44 */ "\0" /* # 45 */ "\0" /* # 46 */ "\0" /* # 47 */ "\0" /* # 48 */ "\0" /* # 49 */ "\0" /* # 50 */ "Command is not implemented\0" /* # 51 */ "Variable does not exist\0" /* # 52 */ "Variable value is out of range\0" /* # 53 */ "EEprom not accessible\0" /* # 54 */ "Not Toggled Service Mode\0" /* # 55 */ "Cannot send the command to internal micro\0" /* # 56 */ "Command not Executed\0" /* # 57 */ "The variable is not available, retry\0"; /* # 58 */ /* Global States */ static char szGlobalStates[] = "Sending Parameters\0" /* # 0 */ "Wait Sun/Grid\0" /* # 1 */ "Checking Grid\0" /* # 2 */ "Measuring Riso\0" /* # 3 */ "DcDc Start\0" /* # 4 */ "Inverter Turn-On\0" /* # 5 */ "Run\0" /* # 6 */ "Recovery\0" /* # 7 */ "Pause\0" /* # 8 */ "Ground Fault\0" /* # 9 */ "OTH Fault\0" /* # 10 */ "Address Setting\0" /* # 11 */ "Self Test\0" /* # 12 */ "Self Test Fail\0" /* # 13 */ "Sensor Test + Measure Riso\0" /* # 14 */ "Leak Fault\0" /* # 15 */ "Waiting for manual reset\0" /* # 16 */ "Internal Error E026\0" /* # 17 */ "Internal Error E027\0" /* # 18 */ "Internal Error E028\0" /* # 19 */ "Internal Error E029\0" /* # 20 */ "Internal Error E030\0" /* # 21 */ "Sending Wind Table\0" /* # 22 */ "Failed Sending Table\0" /* # 23 */ "UTH Fault\0" /* # 24 */ "Remote Off\0" /* # 25 */ "Interlock Fail\0" /* # 26 */ "Executing Autotest\0" /* # 27 */ "\0" /* # 28 Placeholder */ "\0" /* # 29 Placeholder */ "Waiting Sun\0" /* # 30 */ "Temperature Fault\0" /* # 31 */ "Fan Staucked\0" /* # 32 */ "Int. Com. Fail\0" /* # 33 */ "Slave Insertion\0" /* # 34 */ "DC Switch Open\0" /* # 35 */ "TRAS Switch Open\0" /* # 36 */ "MASTER Exclusion\0" /* # 37 */ "Auto Exclusion\0" /* # 38 */ "\0" /* # 39 These are all placeholders for the last 4 */ "\0" /* # 40 */ "\0" /* # 41 */ "\0" /* # 42 */ "\0" /* # 43 */ "\0" /* # 44 */ "\0" /* # 45 */ "\0" /* # 46 */ "\0" /* # 47 */ "\0" /* # 48 */ "\0" /* # 49 */ "\0" /* # 50 */ "\0" /* # 51 */ "\0" /* # 52 */ "\0" /* # 53 */ "\0" /* # 54 */ "\0" /* # 55 */ "\0" /* # 56 */ "\0" /* # 57 */ "\0" /* # 58 */ "\0" /* # 59 */ "\0" /* # 60 */ "\0" /* # 61 */ "\0" /* # 62 */ "\0" /* # 36 */ "\0" /* # 64 */ "\0" /* # 65 */ "\0" /* # 66 */ "\0" /* # 67 */ "\0" /* # 68 */ "\0" /* # 69 */ "\0" /* # 70 */ "\0" /* # 71 */ "\0" /* # 72 */ "\0" /* # 73 */ "\0" /* # 74 */ "\0" /* # 75 */ "\0" /* # 76 */ "\0" /* # 77 */ "\0" /* # 78 */ "\0" /* # 79 */ "\0" /* # 80 */ "\0" /* # 81 */ "\0" /* # 82 */ "\0" /* # 83 */ "\0" /* # 84 */ "\0" /* # 85 */ "\0" /* # 86 */ "\0" /* # 87 */ "\0" /* # 88 */ "\0" /* # 89 */ "\0" /* # 90 */ "\0" /* # 91 */ "\0" /* # 92 */ "\0" /* # 93 */ "\0" /* # 94 */ "\0" /* # 95 */ "\0" /* # 96 */ "\0" /* # 97 */ "Erasing Internal EEprom\0" /* # 98 */ "Erasing External EEprom\0" /* # 99 was Erasing EEPROM */ "Counting EEprom\0" /* # 100 */ "Freeze\0" ; /* # 101 */ /* DcDc Status */ static char szDcDcStatus[] = "DcDc OFF\0" /* # 0 */ "Ramp Start\0" /* # 1 */ "MPPT\0" /* # 2 */ "not used\0" /* # 3 */ "Input Over Current\0" /* # 4 */ "Input Under Voltage\0" /* # 5 */ "Input Over Voltage\0" /* # 6 */ "Input Low\0" /* # 7 */ "No Parameters\0" /* # 8 */ "Bulk Over Voltage\0" /* # 9 */ "Communication Error\0" /* # 10 */ "Ramp Fail\0" /* # 11 */ "Internal Error\0" /* # 12 */ "Input mode Error\0" /* # 13 */ "Ground Fault\0" /* # 14 */ "Inverter Fail\0" /* # 15 */ "DcDc IGBT Sat\0" /* # 16 */ "DcDc ILEAK Fail\0" /* # 17 */ "DcDc Grid Fail\0" /* # 18 */ "DcDc Comm. Error\0" ; /* # 19 */ /* Inverter State */ static char szInverterState[] = "Stand By\0" /* # 0 */ "Checking Grid\0" /* # 1 */ "Run\0" /* # 2 */ "Bulk Over Voltage\0" /* # 3 */ "Out Over Current\0" /* # 4 */ "IGBT Sat\0" /* # 5 */ "Bulk Under Voltage\0" /* # 6 */ "Degauss Error\0" /* # 7 */ "No Parameters\0" /* # 8 */ "Bulk Low\0" /* # 9 */ "Grid Over Voltage\0" /* # 10 */ "Communication Error\0" /* # 11 */ "Degaussing\0" /* # 12 */ "Starting\0" /* # 13 */ "Bulk Cap Fail\0" /* # 14 */ "Leak Fail\0" /* # 15 */ "DcDc Fail\0" /* # 16 */ "Ileak Sensor Fail\0" /* # 17 */ "SelfTest: relay inverter\0" /* # 18 */ "SelfTest: wait for sensor test\0" /* # 19 */ "SelfTest: test relay DcDc + sensor\0" /* # 20 */ "SelfTest: relay inverter fail\0" /* # 21 */ "SelfTest: timeout fail\0" /* # 22 */ "SelfTest: relay DcDc fail\0" /* # 23 */ "Self Test 1\0" /* # 24 */ "Waiting self test start\0" /* # 25 */ "Dc Injection\0" /* # 26 */ "Self Test 2\0" /* # 27 */ "Self Test 3\0" /* # 28 */ "Self Test 4\0" /* # 29 */ "Internal Error (30)\0" /* # 30 */ "Internal Error (31)\0" /* # 31 */ "\0" /* # 32 These are all placeholders */ "\0" /* # 33 */ "\0" /* # 34 */ "\0" /* # 35 */ "\0" /* # 36 */ "\0" /* # 37 */ "\0" /* # 38 */ "\0" /* # 39 */ "Forbidden State\0" /* # 40 */ "Input UC\0" /* # 41 */ "Zero Power\0" /* # 42 */ "Grid Not Present\0" /* # 43 */ "Waiting Start\0" /* # 44 */ "MPPT\0" /* # 45 */ "Grid Fail\0" /* # 46 */ "Input OC\0" ; /* # 47 */ /* Alarm State */ static char szAlarmState[] = "No Alarm\0" /* # 0 */ "Sun Low W001 (1)\0" /* # 1 */ "Input Over Current E001\0" /* # 2 */ "Input Under Voltage W002\0" /* # 3 */ "Input Over Voltage E002\0" /* # 4 */ "Sun Low W001 (5)\0" /* # 5 */ "No Parameters E003\0" /* # 6 was Internal error E003 */ "Bulk Over Voltage E004\0" /* # 7 */ "Comm. Error E005\0" /* # 8 was Internal error E005 */ "Output Over Current E006\0" /* # 9 */ "IGBT Sat E007\0" /* # 10 was Internal error E007 */ "Bulk UV W011\0" /* # 11 was Internal error E008 */ "Internal error E009\0" /* # 12 was Internal error E009 */ "Grid Fail W003\0" /* # 13 */ "Bulk Low E010\0" /* # 14 was Internal error E010 */ "Ramp Fail E011\0" /* # 15 was Internal error E011 */ "Dc/Dc Fail E012\0" /* # 16 */ "Wrong Mode E013\0" /* # 17 */ "Ground Fault (18)\0" /* # 18 */ "Over Temp. E014\0" /* # 19 */ "Bulk Cap Fail E015\0" /* # 20 */ "Inverter Fail E016\0" /* # 21 */ "Start Timeout E017\0" /* # 22 was Internal error E017 */ "Ground Fault E018 (23)\0" /* # 23 */ "Degauss error (24)\0" /* # 24 */ "Ileak Sens. fail E019\0" /* # 25 was Internal error E019 */ "DcDc Fail E012\0" /* # 26 */ "Self Test Error 1 E020\0" /* # 27 was Internal error E020 */ "Self Test Error 2 E021\0" /* # 28 was Internal error E021 */ "Self Test Error 3 E019\0" /* # 29 was Internal error E019 */ "Self Test Error 4 E022\0" /* # 30 was Internal error E022 */ "DC inj error E023\0" /* # 31 was Internal error E023 */ "Grid Over Voltage W004\0" /* # 32 */ "Grid Under Voltage W005\0" /* # 33 */ "Grid OF W006\0" /* # 34 */ "Grid UF W007\0" /* # 35 */ "Z grid Hi W008\0" /* # 36 */ "Internal error E024\0" /* # 37 */ "Riso Low E025\0" /* # 38 */ "Vref Error E026\0" /* # 39 was Internal error E026 */ "Error Meas V E027\0" /* # 40 was Internal error E027 */ "Error Meas F E028\0" /* # 41 was Internal error E028 */ "Error Meas I E029\0" /* # 42 was Internal error E029 */ "Error Meas Ileak E030\0" /* # 43 was Internal error E030 */ "Read Error V E031\0" /* # 44 was Internal error E031 */ "Read Error I E032\0" /* # 45 was Internal error E032 */ "Table fail W009\0" /* # 46 was Empty Wind Table W009 */ "Fan Fail W010\0" /* # 47 */ "UTH E033\0" /* # 48 was Internal error E033 */ "Interlock Fail\0" /* # 49 */ "Remote Off\0" /* # 50 */ "Vout Avg error\0" /* # 51 */ "Battery low\0" /* # 52 */ "Clk fail\0" /* # 53 */ "Input UC\0" /* # 54 */ "Zero Power\0" /* # 55 */ "Fan Stucked\0" /* # 56 */ "DC Switch Open\0" /* # 57 */ "Tras Switch Open\0" /* # 58 */ "AC Switch Open\0" /* # 59 */ "Bulk UV\0" /* # 60 */ "Autoexclusion\0" /* # 61 */ "Grid df/dt\0" /* # 62 */ "Den switch Open\0" /* # 63 */ "Jbox fail\0" ; /* # 64 */ aurora-1.7.3/include/names.h0000644000076400017510000002357711655677305013572 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006-2011 Curtis J. Blank curt@curtronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * Staring with v1.5-0 this work based on Power One's 'Aurora Inverter * Series - Communication Protocol -' document, Rel. 4.6 25/02/09 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * */ #define VersionNH "1.7.2" /* # 50 State Request */ #define opGetState 50 #define _opGetState "State Request" /* # 52 P/N Reading */ #define opGetPN 52 #define _opGetPN "P/N Reading" #define aChar6 0 #define aChar5 1 #define aChar4 2 #define aChar3 3 #define aChar2 4 #define aChar1 5 /* # 58 Version Reading */ #define opGetVer 58 #define _opGetVer "Version Reading" #define aPar1 2 #define aPar1i "Aurora 2 kW indoor" #define aPar1o "Aurora 2 kW outdoor" #define aPar1I "Aurora 3.6 kW indoor" #define aPar1O "Aurora 3.0-3.6 kW outdoor" #define aPar15 "Aurora 5.0 kW outdoor" #define aPar16 "Aurora 6 kW outdoor" #define aPar1P "3-phase interface (3G74)" #define aPar1C "Aurora 50kW module" #define aPar14 "Aurora 4.2kW new" #define aPar13 "Aurora 3.6kW new" #define aPar12 "Aurora 3.3kW new" #define aPar11 "Aurora 3.0kW new" #define aPar1D "Aurora 12.0kW " #define aPar1X "Aurora 10.0kW " #define aPar2 3 #define aPar2A "UL1741" #define aPar2E "VDE0126" #define aPar2S "DR 1663/2000" #define aPar2I "ENEL DK 5950" #define aPar2U "UK G83" #define aPar2K "AS 4777" #define aPar2F "VDE French Model" #define aPar3 4 #define aPar3T "Transformer Version" #define aPar3N "Transformerless Version" #define aPar4 5 #define aPar4W "Wind Version" #define aPar4N "Photovoltaic Version" /* # 59 Measure request to the DSP - Type of Measure */ #define opGetDSP 59 #define _opGetDSP "Measure request to the DSP" #define ToM1 1 /* Grid Voltage (Global) */ #define ToM2 2 /* Grid Currrent (Global) */ #define ToM3 3 /* Grid Power (Global) */ #define ToM4 4 /* Frequency */ #define ToM5 5 /* VBulk was Ileak (Dc/Dc) reading */ #define ToM6 6 /* Ileak (Dc/Dc) was Ileak (Inverter) Reading*/ #define ToM7 7 /* Ileak (Inverter) */ #define ToM8 8 /* Pin 1 (Global) */ #define ToM9 9 /* Pin 2 */ #define ToM21 21 /* Inverter Temperature */ #define ToM22 22 /* Booster Temperatuer */ #define ToM23 23 /* Input 1 Voltage */ #define ToM25 25 /* Input 1 Current (Global) */ #define ToM26 26 /* Input 2 Voltage */ #define ToM27 27 /* Input 2 Current */ #define ToM28 28 /* Grid Voltage (Dc/Dc) */ #define ToM29 29 /* Grid Frequency (Dc/Dc) */ #define ToM30 30 /* Isolation Resistance (Riso) */ #define ToM31 31 /* Vbulk (Dc/Dc) */ #define ToM32 32 /* Average Grid Voltage (VgridAvg) */ #define ToM33 33 /* Vbulk Mid */ #define ToM34 34 /* Power Peak */ #define ToM35 35 /* Power Peak Today */ #define ToM36 36 /* Grid Voltage neutral */ #define ToM37 37 /* Wind Generator Frequency */ #define ToM38 38 /* Grid Voltage neutral-phase */ #define ToM39 39 /* Grid Current phase r */ #define ToM40 40 /* Grid Current phase s */ #define ToM41 41 /* Grid Current phase t */ #define ToM42 42 /* Frequency phase r */ #define ToM43 43 /* Frequency phase s */ #define ToM44 44 /* Frequency phase t */ #define ToM45 45 /* Vbulk + */ #define ToM46 46 /* Vbulk - */ #define ToM47 47 /* Supervisor Temperature */ #define ToM48 48 /* Alim Temperature */ #define ToM49 49 /* Heak Sink Temperature */ #define ToM50 50 /* Temperature 1 */ #define ToM51 51 /* Temperature 2 */ #define ToM52 52 /* Temperature 3 */ #define ToM53 53 /* Fan 1 Speed */ #define ToM54 54 /* Fan 2 Speed */ #define ToM55 55 /* Fan 3 Speed */ #define ToM56 56 /* Fan 4 Speed */ #define ToM57 57 /* Fan 5 Speed */ #define ToM58 58 /* Power Saturation limit (Der.) */ #define ToM59 59 /* Riferimento Anello Bulk */ #define ToM60 60 /* Vpanel micro */ #define ToM61 61 /* Grid Voltage phase r */ #define ToM62 62 /* Grid Voltage phase s */ #define ToM63 63 /* Grid Voltage phase t */ #define _ToM1 "Grid Voltage Reading " /* Global */ #define _ToM2 "Grid Current Reading " /* Global */ #define _ToM3 "Grid Power Reading " /* Global */ #define _ToM3C "Grid Power Calculated" #define _ToM4 "Frequency Reading" #define _ToM5 "Vbulk" /* was"Ileak (Dc/Dc) Reading" */ #define _ToM6 "Ileak (Dc/Dc) Reading" /* was "Ileak (Inverter) Reading" */ #define _ToM7 "Ileak (Inverter) Reading" #define _ToM8 "Pin 1 " /* Global */ #define _ToM9 "Pin 2" #define _ToM21 "Inverter Temperature" #define _ToM22 "Booster Temperature" /* was Environment Temperature */ #define _ToM23 "Input 1 Voltage" /* Global was "West String Voltage" */ #define _ToM25 "Input 1 Current" /* was "West String Current" */ #define _ToM26 "Input 2 Voltage" /* was "East String Voltage" */ #define _ToM27 "Input 2 Current" /* was "East String Current" */ #define _ToM28 "Grid Voltage (Dc/Dc)" #define _ToM29 "Grid Frequency (Dc/Dc)" #define _ToM30 "Isolation Resistance (Riso)" #define _ToM31 "Vbulk (Dc/Dc)" #define _ToM32 "Average Grid Voltage (VgridAvg)" #define _ToM33 "Vbulk Mid" #define _ToM34 "Power Peak" #define _ToM35 "Power Peak Today" #define _ToM36 "Grid Voltage neutral" #define _ToM37 "Wind Generator Frequency" #define _ToM38 "Grid Voltage neutral-phase" #define _ToM39 "Grid Current phase r" #define _ToM40 "Grid Current phase s" #define _ToM41 "Grid Current phase t" #define _ToM42 "Frequency phase r" #define _ToM43 "Frequency phase s" #define _ToM44 "Frequency phase t" #define _ToM45 "Vbulk +" #define _ToM46 "Vbulk -" #define _ToM47 "Supervisor Temperature" #define _ToM48 "Alim Temperature" #define _ToM49 "Heak Sink Temperature" #define _ToM50 "Temperature 1" #define _ToM51 "Temperature 2" #define _ToM52 "Temperature 3" #define _ToM53 "Fan 1 Speed" #define _ToM54 "Fan 2 Speed" #define _ToM55 "Fan 3 Speed" #define _ToM56 "Fan 4 Speed" #define _ToM57 "Fan 5 Speed" #define _ToM58 "Power Saturation limit (Der.)" #define _ToM59 "Reference Ring Bulk" #define _ToM60 "Vpanel micro" #define _ToM61 "Grid Voltage phase r" #define _ToM62 "Grid Voltage phase s" #define _ToM63 "Grid Voltage phase t" #define _Str1P "Input 1 Power" /* was "West String Power" */ #define _Str2P "Input 2 Power" /* was "East String Power" */ #define _DcAcEff "DC/AC Coversion Efficiency" /* # 63 Serial Number */ #define opGetSN 63 #define _opGetSN "Serial Number" /* # 65 Manufacturing Week and Year */ #define opGetMfg 65 #define _opGetMfg "Manufacturing Week and Year" #define aWeekH 2 #define aWeekL 3 #define aYearH 4 #define aYearL 5 /* # 70 Get Time/Date */ #define opGetTime 70 #define _opGetTime "Get Time/Date" /* # 71 Set Time/Date */ #define opSetTime 71 #define _opSetTime "Set Time/Date" /* # 72 Firmware Release */ #define opGetVerFW 72 #define _opGetVerFW "Firmware Release" #define aRel3 2 #define aRel2 3 #define aRel1 4 #define aRel0 5 /* # 75 Energy delivred to grip every 10 seconds ** Experimental ** */ #define opGetCESent 0x4b01 /* 75 01 */ #define _opGetCESent "Energy Sent To Grid" #define aCESMemAdd 0x000a #define aCESMaxCnt 4320 /* # 75 Energy cumulated each day address ** Experimental ** */ #define opGetCEAdd 0x4b02 /* 75 02 */ #define _opGetCEAdd "Daily Cumulated Energy Address" #define aCEAddH 2 #define aCEAddL 3 /* # 76 Energy cumulated in the last 10 seconds */ #define opGetEnergy10Sec 76 #define _opGetEnergy10Sec "Energy cumulated in the last 10 seconds" #define aEnergyH 2 #define aEnergyL 3 /* # 77 System Configuration */ #define opGetConfig 77 #define _opGetConfig "System Configuration" #define aConfCode 2 #define ConfCode0 0 #define ConfCode1 1 #define ConfCode2 2 #define _ConfCode0 "System operating with both strings." #define _ConfCode1 "String 1 connected, String 2 disconnected." #define _ConfCode2 "String 2 connected, String 1 disconnected." /* # 78 Cumulated Energy */ #define opGetCE 78 #define _opGetCE "Cumulated Energy" #define CEpar0 0 /* Daily Energy */ #define CEpar1 1 /* Weekly Energy */ #define CEpar2 2 /* Energy of last 7 days */ #define CEpar3 3 /* Monthly Energy */ #define CEpar4 4 /* Yearly Energy */ #define CEpar5 5 /* Total Energy (total lifetime) */ #define CEpar6 6 /* Partial Energy (cumulated since reset) */ #define _CEpar0 "Daily Energy" #define _CEpar1 "Weekly Energy" #define _CEpar2 "Energy last 7 days" #define _CEpar3 "Monthly Energy" #define _CEpar4 "Yearly Energy" #define _CEpar5 "Total Energy" #define _CEpar6 "Partial Energy" /* # 79 Daily Cumulated Energy ** Experimental ** */ #define opGetCEValue 79 #define _opGetCEValue "Cumulated Energy" #define cCEDailyAddH 2 #define cCEDailyAddL 3 #define cCEDailyEnd 4 #define aCEDailyDaysH 2 #define aCEDailyDaysL 3 #define aCEDailyValH 4 #define aCEDailyValL 5 /* # 80 Time Counter */ #define opGetCounters 80 #define _opGetCounters "Time Counter" #define cTotalRun 0 #define cPartialRun 1 #define cTotalGrid 2 #define cResetPartial 3 #define _cTotalRun "Total Running Time (Lifetime)" #define _cPartialRun "Partial Running Time (since reset)" #define _cTotalGrid "Total Time With Grid Connection" #define _cResetPartial "Reset of Partial (Time & Energy)" /* # 86 Last four alarms */ #define opGetLastAlarms 86 #define _opGetLastAlarms "Last four alarms" /* return codes same as alarms from # 50 State request */ aurora-1.7.3/include/main.h0000644000076400017510000000574511657110353013373 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006-2010 Curtis J. Blank curt@curtronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * Staring with v1.5-0 this work based on Power One's 'Aurora Inverter * Series - Communication Protocol -' document, Rel. 4.6 25/02/09 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * */ #define VersionMH "1.7.2" #ifndef __MAIN_H__ #define __MAIN_H__ #ifdef __cplusplus #define extern "C" { /* respect c++ callers */ #endif #define ProgramName "aurora" typedef int BOOL; /* yet another bool definition */ #define TRUE 1 #define FALSE 0 typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; #define LOBYTE(w) ((BYTE)(w)) #define HIBYTE(w) ((BYTE)(((WORD)(w) >> (BYTE)8) & 0xff)) extern BOOL bVerbose; /* Verbose Mode */ extern BOOL bColumns; extern BOOL bGetInvTime; extern BOOL bGetLocTime; extern int yGetDSP; extern BOOL bGetDSPExtended; extern BOOL bGetDSP3Phase; extern BOOL bHideDSP; extern BOOL bGetEnergy; extern BOOL bCommCheck; extern BOOL bColOutput; extern BOOL bCalcGridPwr; extern BOOL bRptReties; extern BOOL bRptReadPause; extern char RunTime[18]; extern float yCost; extern char *sCostType; extern int yTimeout; extern long unsigned int PID; extern int yMaxAttempts; extern int yReadPause; extern long int yCommPause; extern int yCheckSetTime; extern struct timeval lastcommtv; extern FILE *outfp; extern int ModelID; extern int ClrSerLock(long unsigned int PID); extern int RestorePort(int fdser); #define ttyLCKloc "/var/lock/LCK.." /* location and prefix of serial port lock file */ /* command structure */ #define cAddress 0 #define cCommand 1 #define cCommandEnd 2 #define cParam1 2 #define cParam1End 3 #define cParam2 3 #define cParam3 4 #define cParam4 5 #define cParam5 6 #define cParam6 7 #define cCRC_L 8 #define cCRC_H 9 #define cEND 10 #define cSIZE 10 /* answer structure */ #define aState 0 #define aMState 1 #define aParam1 2 #define aParam2 3 #define aParam3 4 #define aParam4 5 #define aCRC_L 6 #define aCRC_H 7 #define aEND 8 #define aSIZE 8 #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __MAIN_H__ */ aurora-1.7.3/include/comm.h0000644000076400017510000000475511657110341013377 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006-2010 Curtis J. Blank curt@curtronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * Staring with v1.5-0 this work based on Power One's 'Aurora Inverter * Series - Communication Protocol -' document, Rel. 4.6 25/02/09 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * */ #define VersionCH "1.7.3" extern int CommCheck(int fdser, int yAddress); extern int GetState(int fdser, int yAddress); extern int GetPN(int fdser, int yAddress); extern int GetVer(int fdser, int yAddress); extern int GetSN(int fdser, int yAddress); extern int GetVerFW(int fdser, int yAddress); extern int GetMfgDate(int fdser, int yAddress); extern int GetConf(int fdser, int yAddress); extern int GetDSP(int fdser, int yAddress); extern int GetDSPExtended(int fdser, int yAddress); extern int GetDSP3Phase(int fdser, int yAddress); extern int GetCE(int fdser, int yAddress); extern int GetCESent(int fdser, int yAddress, int yGetEnergySent); extern int GetCEDaily(int fdser, int yAddress, int yGetEnergyDaily); extern int GetJoules(int fdser, int yAddress); extern int GetCounters(int fdser, int yAddress); extern int GetTime(int fdser, int yAddress); extern int SetTime(int fdser, int yAddress, BOOL force); extern int CheckSetTime(int fdser, int yAddress); extern int GetLastAlarms(int fdser, int yAddress); extern char VersionC[]; extern char VersionSHc[6]; #define _clearCMD " \0" #define _clrAttemps 1000 #define _szSerBufferLen 11 //#define TimeBase 946706400 #define TimeBase 946684800 #define SecsInMinute 60 #define SecsInHour (60*SecsInMinute) #define SecsInDay (24*SecsInHour) #define SecsInYear (365*SecsInDay) aurora-1.7.3/CRONTAB0000644000076400017510000000066111271162144011653 0ustar curt I use this cron command to collect data when the sun is out. It creates a new data file for each day based on the date. * 5-21 * * * /usr/local/bin/aurora -T -c -e -d -a 2 /dev/ttyS1 >> /data01/Solar/data/`date +\%Y\%m\%d`.dat 2>> /data01/Solar/logs/`date +\%Y\%m\%d`.err or * 5-21 * * * /usr/local/bin/aurora -T -c -e -d -a 2 -o /data01/Solar/data/`date +\%Y\%m\%d`.dat /dev/ttyS1 2>> /data01/Solar/logs/`date +\%Y\%m\%d`.err aurora-1.7.3/Makefile0000644000076400017510000000607211657110273012306 0ustar curt# uncomment and/or set to match processor # ** ONLY IF ** automatic determination of these compile flags is # not working properly. uncommenting these variables will override # automatic determination of them #ARCH = native #TUNE = native CC = gcc CFLAGS = -O2 -Wall -I/usr/include -I./include OFILES = main.o comm.o INSTALLSITEMAN1DIR = ${mandir}/man1 DISTMAN1DIR = docs/man bindir = ${exec_prefix}/bin datarootdir = ${prefix}/share exec_prefix = ${prefix} mandir = ${datarootdir}/man prefix = /usr/local BIT := $(shell getconf LONG_BIT) BIT64 = -Dbit$(BIT) CFLAGS += $(BIT64) GCCVER = $(shell expr `gcc -dumpversion` \>= 4.2) GFLAGS1 = $(shell echo 'int main(){return 0;}' > /tmp/test-arch-tune.c && gcc -v -Q -march=native -O2 /tmp/test-arch-tune.c -o /tmp/test-arch-tune 2>&1 | sed -e 's/[\(|\)]//g' | grep march && rm /tmp/test-arch-tune.c) GNONATIVE = $(shell echo ${GFLAGS1} | grep -e "error" -e "bad value native" | awk '{for (i =1; i <= NF; i++) print $$i}' | grep -e "bad" -e "error") ifeq "$(strip $(GNONATIVE))" "" GFLAGS = $(shell echo ${GFLAGS1} | awk '{for (i =1; i <= NF; i++) print $$i}' | grep -e "^-march=" -e "^-mtune=" | sort -u && rm /tmp/test-arch-tune) endif ifdef GFLAGS ifndef ARCH GARCH1 = $(shell echo ${GFLAGS} | awk '{for (i =1; i <= NF; i++) print $$i}' | grep -e "^-march=" | sed -e 's/-march=//') GARCH = $(shell echo ${GARCH1} | sed -e 's/ native//' -e 's/native //') endif ifndef TUNE GTUNE1 = $(shell echo ${GFLAGS} | awk '{for (i =1; i <= NF; i++) print $$i}' | grep -e "^-mtune=" | sed -e 's/-mtune=//') GTUNE = $(shell echo ${GTUNE1} | sed -e 's/ native//' -e 's/native //') endif endif ifndef ARCH ifdef GARCH ARCH = ${GARCH} else ifeq ($(GCCVER),1) ifeq "$(strip $(GNONATIVE))" "" ARCH = native endif endif endif endif ifndef TUNE ifdef GTUNE TUNE = ${GTUNE} else ifeq ($(GCCVER),1) ifneq "$(strip $(GNONATIVE))" "" TUNE = native endif endif endif endif ifneq "$(strip $(GNONATIVE))" "" ifeq ($(ARCH),native) ARCH = endif ifeq ($(TUNE),native) TUNE = endif endif ifneq "$(strip $(ARCH))" "" ARCH2 = $(shell echo ${ARCH} | awk '{for (i =1; i <= NF; i++) print "-march="$$i}') CFLAGS += $(ARCH2) endif ifneq "$(strip $(TUNE))" "" TUNE2 = $(shell echo ${TUNE} | awk '{for (i =1; i <= NF; i++) print "-mtune="$$i}') CFLAGS += $(TUNE2) endif TARGET = aurora .PHONY: install all: $(TARGET) $(TARGET): check $(OFILES) $(CC) $(OFILES) -o $(TARGET) check: ifeq ($(BIT),64) @echo -n "Compiling for 64bit " else @echo -n "Compiling for 32bit " endif ifneq "$(strip $(GNONATIVE))" "" @echo -n "NoNative " endif ifdef ARCH @echo -n "$(ARCH) " endif ifdef TUNE ifneq ($(ARCH),$(TUNE)) @echo -n "$(TUNE) " endif endif @echo main.o: Makefile main.c include/main.h include/comm.h include/names.h include/states.h $(CC) $(CFLAGS) -c main.c comm.o: Makefile comm.c include/main.h include/comm.h include/names.h include/states.h $(CC) $(CFLAGS) -c comm.c clean: rm -f $(TARGET) *.o install: aurora install -m 4711 $(TARGET) $(bindir) install -m 0644 ${DISTMAN1DIR}/$(TARGET).1 ${INSTALLSITEMAN1DIR} uninstall: rm -f $(bindir)/$(TARGET) ${INSTALLSITEMAN1DIR}/$(TARGET).1 aurora-1.7.3/LICENSE0000644000076400017510000000202710452061027011641 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006 Curtis J. Blank curt@cutronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * */ aurora-1.7.3/README0000644000000000000000000002616211657113312012400 0ustar rootroot I - Intro II - -k option III - Setting the Inverter Time IV - Some Known Issues V - Operation Tib Bits VI - ** NEW -q --energy-sent option ** VII - ** NEW -L, --adjust-time option ** ********************************** I - Intro If you are using this software to download the data from your Aurora Inverter and have a public web site showing your data send me an email with the URL. I will be creating a web page on my Solar Web Site with those links. Tell everyone that you know about this to do that also. Email Solar at curtronics dot com with your URL. If you are using this software to download data from your Aurora Inverter and are doing so using the USB port on the inverter I would like to hear from you. I'm particularly interested it whether or not you are getting lots of CRC errors. I am working with one installation that is constantly getting CRC errors on a USB connection and has to run the program multiple times before it successfully gets the data. I'd like to determine where the problem is in order to work towards eliminating it. Email Solar at curtronics dot com with your info. Thank you. ********************************** II - -k option The -k option is considered ** Experimental ** because it is an un-Publicly documented option so it may or may not provide correct information. It appears the inverter stores the past 365 days of KWH prodcution data in a block of memory. The exact location of that memory block seems to differ in each inverter, it may be model related it may not. A pointer is available that points to the current days value but no offset information has been found to indicate at what position in the block of memory that is. So the beginning and end of the block of memory is not accurately known. aurora attempts to watch for the end of the block and then loop back to the beginning of the block (round robin storage) to continue gathering data. This method works for inverters that have 366 days (current day included) worth of data but for inveters that don't the results of the readings are currently unknown. ********************************** III - Setting the Inverter Time Setting the time on the inverter using the -S option has been known to clear (reset to zero) the energy values reported by the -e option (except the Total Energy reported) and also the history reported by the -k option. It does do this on my inveter that has a manufacture date of 06 16. However on newer inverters it may no longer do this, i.e. zero the data. I know of one inverter that has a manufacture date of 10 42 that setting the time via the -S option did not clear these data values. ********************************** IV - Some Known Issues There have been many reports of communications problems when using the USB port on Aurora inverters. A work around was added awhile back, the -Y option to get around this issue. Recently someone indicated that there needs to be a minimum of 400 uS between the sending of commands to the inverter when communicating via the USB port. With this information in hand the -P option was added and testing was conducted using the -P option on inverters that are communicating via the USB port that exhibit this problem. In some cases it corrected this USB problem, in other cases it did not correct the problem and the -Y option still had to be used. In short, the -P option will cause aurora to not send commands unless the time specified in the -P option has elapsed since the last command was sent. So, if you are experiencing problems with communicating via the USB port try using the -P option starting with a value of 400 and increase or decrease the value until reliable communication is attained. If that does not eliminate the problem then try using the -Y option. There have been reports saying that Power One is claiming the USB port is not designed/should not be used for continuous data gathering/communication. However, this has not been confirmed with Power One. Getting more reports from users that Power One is claiming the USB port is not designed/should not be used for continuous data gathering/communication. So more users are switching to RS-485 adapters, that being said it has been reported that using RTS/CTS control on the serial port when using a RS-485 adapter improves the communication reliability. So the -X option has been added to enable RTS/CTS control on the serial port. ********************************** V - Operation Tib Bits If aurora cannot communicate with the Aurora Inverter it dies a silent death to STDOUT but reports the lack of response to STDERR. This is on purpose for data collection purposes so you can pipe the data to a file without having any error messages in the file. If a CRC error is encountered it will also be reported on STDERR regardless. If in doubt about what's going on, use the verbose option to see. This program has been tested using a standard serial port, i.e. /dev/ttyS1, and a USB serial port, /dev/ttyUSB0 with equal success. The USB serial port was the BB DYNEX variety, DX-UBDB9 using the Prolific Technology, Inc. PL2303 Serial Port. Development and testing was done on a SuSE Linux 10.0 (custom gen'd kernel) AMD Sempron 3000 laptop system, for ease of being able to test by the Aurora Inverter, final destination has it running the data collection on a SuSE Linux 10.0 (custom kernel) AMD Athlon(tm) XP 3000+ server. An update to the platform in use, it is now SuSE Linux 11.4 with their default kernel on a AMD Phenom 1090T with 16G of memory and a battery backed up caching raid controller. ********************************** VI - ** NEW -q --energy-sent option ** This was reverse engineered since it is not in the documentation that is released to the public. Therefore, it may not work on every Aurora inverter. If you suspect it isn't reporting the correct data or they are any problems when using it *PLEASE* report them so as to help improve this option and aurora on the whole. This option will report the amount of energy sent to the grid at 10 second intervals for the past 24 hours. Well that's according to the manufacturer. In use it may report more then 24 hours worth of data, not sure if it will report less then 24 hours so if you see that it does please report that fact. When this option is invoked the timestamp of the reported data may look like YYYYMMDD-nnnn instead of 20111106-09:23:14 for example. Where YYYYMMDD is just that, those characters and not a real date and time, the nnnn will be an incrementing sequence. This is due to not having found a timestamp packet in the data stream as of yet. Once a timestamp packet is found the valid date and time for the energy values will then be reported. This is not a problem IMO, the manufacturers Windoze software gets the same data they just choose to hide it instead of displaying it. In other words, they throw out any data that is received until a timestamp packet comes their way. This will more then likely always occur to some degree, the reason being due to how this historical data is stored in the inverter. At some point in time the timestamp for the oldest data gets overwritten thus is no longer available. So why does aurora choose to display it? Well a couple of reasons. In my experience it can take 10 minutes for this option to run to completion, it's doing a lot of I/O's to the inverter. If nothing, i.e. no output, is displayed during lets say the first 3 minutes, some people may think it's not working when it actually is. Second, even if the date and time is not known for that data it may be possible to sync its date and time using previously recorded data for the time period in question. Once the date and time is displayed if the time looks to be off please report that fact. And note, the time displayed is that of the time that was on the inverter when it stored the data. So if the inverters time is off by 10 minutes, or and hour, that may make it look like something is wrong so check the time on the front panel on the inverter. The -t option could be used to do that but there could be an issue due to timezone settings so the best bet is to look at the inverter. And in the 'It would be nice' department... Before anyone asks, having the ability to skip the data that the date and time is not known for, or picking a starting time, etc., would be nice but... Because of how the data is stored it has to be read through in a serial manner in order to find the date and time for the block of data following it. There are no entry points except for the beginning, there are no offsets or block length values, nothing. So the data has to be read in a serial manner and as long as it's being read, it might as well be displayed. Here is the output in a graphical format: http://www.curtronics.com/Solar/Samples/EnergySentSample.html ********************************** VII - ** NEW -L, --adjust-time ** This option will automatically adjust the inverter's time if it is found to differ from the computer's time and then only if the following constraints are met. If is >= 1 the inverter's time will only be changed to match the time on the computer if the inverter's time differs from the computer's time by or more seconds. If is 0 (zero) a Daylight Savings Time check will be done. This check will only modify the time on the inverter on the day that any Daylight Savings Time change takes effect and only after it has taken effect and also only if the time is found to differ by 15 minutes or more. This check will compare the inverter's current time to the computer's current time. If the results of the comparision indicate the two times differ by 15 minutes or more and it is on the day that a Daylight Savings Time change takes effect, and after it has taken effect, the inverter's time will be set to match that of the computer. The added 15 minute constraint when doing the Daylight Savings Time check is to prevent frivolous changing of the time. The clocks on the inverters tend to have a large amount of drift over time. Since the change to or from Daylight Savings Time is typically an hour, using a minimum window of it differing by 15 minutes will help to insure that the time is only changed once on that day even if this option is continually used throughout the day. It would be safe to use this option all the time it won't change the time unless the constraints are met according to the above. The idea is to have it running using the "0" choice on the day that a Daylight Savings Time change takes effect. That way first thing in the morning once the inverter wakes up its time will be set properly and internal history will be recorded with the correct date and time. ***** WARNING ****** ****** WARNING ****** ****** WARNING ***** Setting the Date and Time has been known to clear all History except "Total Energy" on some inverters. This is not caused by aurora but instead is how some inverters are designed to operate. It is suggested that the -S option be used to determine if your inverter exhibits this behavior and if it does and is unacceptable then do not use this option. aurora-1.7.3/COLUMNS.key0000644000076400017510000000174510456260525012504 0ustar curt -T & -d option Date-Time STR1-V STR1-C STR1-P STR2-V STR2-C STR2-P Grid-V Grid-C Grid-P Grid-Hz DcAcCvrEff InvTemp EnvTemp [OK] -T & -e option Date-Time DailyEnery WeeklyEnergy Last7DaysEnergy MonthlyEnergy YearlyEnergy TotalEnergy PartialEnergy [OK] -T & -d & -e option combined in 1 run Date-Time STR1-V STR1-C STR1-P STR2-V STR2-C STR2-P Grid-V Grid-C Grid-P Grid-Hz DcAcCvrEff InvTemp EnvTemp DailyEnery WeeklyEnergy Last7DaysEnergy MonthlyEnergy YearlyEnergy TotalEnergy PartialEnergy [OK] "OK" will only be printed at the end of the data *if* all data collection from the Inverter was successful. If any data was missed or any CRC errors occurred, "OK" will *not* be printed at the end of the line. This covers periods of time like in the morning when the Inverter is attempting to startup but there is not enough sunlight, i.e power available to startup. This is similiar to when the Inverter shuts down at night due to low sunlight but continually tries to startup to keep running. aurora-1.7.3/main.c0000644000076400017510000012143211657110246011734 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006-2011 Curtis J. Blank curt@curtronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * Starting with v1.5-0 this work based on Power One's 'Aurora Inverter * Series - Communication Protocol -' document, Rel. 4.6 25/02/09 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * * modified 17-jul-2006 cjb 1. v1.2-6 Last 7 Days production value has been dropped * in the v2.3 Communications Protocol doc * modified 27-jul-2006 cjb 1. add bVerbose in ClrSerLock * modified 13-oct-2006 cjb 1. make this v1.3-0 * modified 25-apr-2007 cjb 1. take into account Daylight Savings Time when setting the Aurora's time * modified 29-dec-2008 cjb 1. correct an error in strftime that only may show up in the first or last * week of the year (%G vs %Y) * modified 19-aug-2009 cjb 1. fix determining if serial port is in use * modified 22-aug-2009 cjb 1. disable XON/XOFF flow control on output * modified 29-sep-2009 cjb 1. don't set lckCNT = -1 when clearing stale serial port lock * modified 12-oct-2009 cjb 1. add -o option to output data to a file * modified 25-oct-2009 cjb 1. serial port configuration tweaks * modified 17-oct-2010 cjb 1. added -x option to enable XON/XOFF * modified 07-mar-2010 cjb 1. added -Y retries option and -y option to report the number of attempt made * modified 13-mar-2010 cjb 1. added -P option to delay read after sending command to inverter * 2. rename -P to -U and add -u to report it * 3. changed -U seconds to wait to milli-seconds to wait * modified 28-mar-2010 cjb 1. working on adding more DSP information * modified 27-jul-2010 cjb 1. added -P option to throttle commands sent to the inverter * modified 21-sep-2010 cjb 1. added -A option to read "Last four alarms" * modified 22-jan-2011 cjb 1. added -k option to get up to a years worth of daily generation history * modified 19-mar-2011 cjb 1. added -X option to enable RTS/CTS on the serial port * modified 13-aug-2001 cjb 1. increase the allowed values from 31 to 63 for inverter address (-a) * as allowed in newer inverters * modified 22-aug-2011 cjb 1. clean up compile 'unused-but-set' messages * modified 05-nov-2011 cjb 1. remove size restriction on monetary identifier of the -C option * 2. added -q --energy-sent option * modified 09-nov-2011 cjb 1. added -L --adjust-time option * */ static char VersionM[] = "1.7.3"; char VersionC[7]; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "include/main.h" #include "include/comm.h" #include "include/names.h" BOOL bVerbose = FALSE; BOOL bColumns = FALSE; /* Output data in columns */ int yGetDSP = -1; /* Measure request to the DSP */ BOOL bGetDSPExtended = FALSE; /* Measure request to the DSP more parameters */ BOOL bGetDSP3Phase = FALSE; /* Measure request to the DSP three-phase parameter */ BOOL bHideDSP = FALSE; BOOL bGetEnergy = FALSE; /* Cumulated Energy Readings */ BOOL bGetInvTime = FALSE; /* Display Inverter time flag */ BOOL bGetLocTime = FALSE; /* Display computer time flag */ BOOL bCommCheck = FALSE; BOOL bColOutput = FALSE; BOOL bCalcGridPwr = FALSE; BOOL bXonXoff = FALSE; BOOL bRTSCTS = FALSE; BOOL bRptReties = FALSE; BOOL bRptReadPause = FALSE; int yTimeout = 0; /* read timeout value in us */ long unsigned int PID; int yMaxAttempts = 1; int yReadPause = 0; long int yCommPause = 0; int yCheckSetTime = -1; /* Check the time on the inverter and if different then system time set it */ struct timeval lastcommtv; FILE *outfp; char RunTime[18] = " "; char VersionSHc[6]; int ModelID = 0; float yCost = 0.0; char *sCostType = NULL; /* local Data */ static char VersionCHc[6] = VersionCH; static char VersionMHc[6] = VersionMH; static char VersionNHc[6] = VersionNH; static int yAddress = 0; /* Inverter address 1-63 */ static char *szttyDevice = NULL; static char *outfile = NULL; static BOOL bHelp = FALSE; /* Print the help text */ static BOOL bGetPN = FALSE; /* Get Inverter Part Number */ static BOOL bGetVer = FALSE; /* Get version string */ static BOOL bGetVerFW = FALSE; /* Get firmware version string */ static BOOL bGetSN = FALSE; /* Get Serial Number */ static BOOL bGetMfg = FALSE; /* Get Manufacturing Date */ static BOOL bGetConf = FALSE; /* Get System Configuration */ static BOOL bGetState = FALSE; /* State request */ static BOOL bGetJoules = FALSE; /* Energy cumulated in the last 10 seconds */ static BOOL bGetCount = FALSE; /* Get Time Counters */ static BOOL bSetTime = FALSE; /* Set time flag */ static BOOL bVersion = FALSE; /* Display program version */ static BOOL bGetTime; static BOOL bGetLastAlarms = FALSE; /* Get last four alarms */ static BOOL bFileError = FALSE; static int yGetEnergyDaily = 0; static int yGetEnergySent = 0; static unsigned char yDelay; /* Read wait time */ static int yLockWait = 0; /* Seconds to wait to lock serial port */ char *devLCKfile = NULL; char *devLCKfileNew = NULL; static struct termios oldtio; /* current serial device configuration */ /* local functions */ static int GetParms(int argc, char *argv[]); static void *getMemPtr(size_t mSize); static void Version(); /*-------------------------------------------------------------------------- main ----------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int fdser; /* serial device fd */ FILE *fdserlck = NULL; struct termios newtio; /* serial device configuration */ int rc = 0; time_t timeValue; struct tm timStruct; long unsigned int rPID; char sPID[10]; int bRead, bWrite, lckCNT; int errno_save = 0; int fLen = 0; char *cmdFile = NULL; char *command = NULL; char *SubStrPos = NULL; lastcommtv.tv_sec = 0; lastcommtv.tv_usec = 0; timeValue = time(NULL); timStruct = *(localtime(&timeValue)); strftime(RunTime,18,"%Y%m%d-%H:%M:%S ",&timStruct); PID = getpid(); /* Get command line parms */ if ((!GetParms(argc, argv) && !bVersion) || bHelp) { printf("\nSee http://www.curtronics.com/Solar/ for Solar Site example\n\n"); printf("Usage: %s [Options] Device\t\t\tv%s\n\n",ProgramName,VersionM); printf("Options:\n"); printf(" -A, --last-alarms Get last four alarms (once displayed FIFO queue is cleared)\n"); printf(" -b, --verbose Verbose mode. For maximum effectiveness should be the first option\n"); printf(" on the command line\n"); printf(" -C, --calc-value= Calculate monetary value using * kWh. \":$\" is optional and if\n"); printf(" included the character(s) represented by the \"$\" will be used to\n"); printf(" denote monetary type in the output. Defaults to \"$\"\n"); printf(" -c, --columnize Output data in columns --> for -d, -e, -D, -E, -t, & -T options\n"); printf(" only will disable any other options -- if value ends with an\n"); printf(" \"*\" reporting of that item may not be in inverters firmware\n"); printf(" -d , --get-dsp= Get DSP data. indicates string to get data for. 0 indicates\n"); printf(" both, 1 for only string 1, 2 for only string 2. is required\n"); printf(" for short option and is optional for long option and if\n"); printf(" omitted for long option then data for both input strings will be\n"); printf(" retrieved.\n"); printf(" -D, --get-dsp-extended Get more DSP data\n"); printf(" -e, --get-energy Get Cumulated Energy readings\n"); printf(" -E, --get-dsp-3phase Get 3-Phase DSP data\n"); printf(" -f, --firmware-ver Query for Firmware Version string\n"); printf(" -g, --mfg-date Query for Inverter Manufacturing Date\n"); printf(" -h, --help This text\n"); printf(" -i, --get-count Display Inverter Time Counters\n"); printf(" -j, --get-joules Display Energy cumulated in the last 10 seconds\n"); printf(" -k , --daily-kwh= Get past daily kWh for days (1-366) ** Experimental **\n"); printf(" is required for short option and optional for long option\n"); printf(" and if omitted for long option then all 366 days or as many that\n"); printf(" are found will be displayed\n"); printf(" -L , --adjust-time= Automatically adjust the inverter time if it differs from the computer\n"); printf(" time. If is 0 (zero) do a Daylight Savings Time check. If \n"); printf(" is >= 1 change the inverter time if it differs by or more seconds.\n"); printf(" See the README for more information and constraints\n"); printf(" -l , --delay= Serial port timeout in for read. in 1/10ths seconds\n"); printf(" Default is 1 (0.1 sec)\n"); printf(" -m, --get-conf Query for Inverter System Configuration\n"); printf(" -n, --serial-number Query for Inverter Serial Number\n"); printf(" -o, --output-file= Append data to file (Created if nonexistant)\n"); printf(" -P , --comm-pause= Wait us between sending commands to inverter (1-1000000)\n"); printf(" -p, --part-number Query for Inverter Part Number\n"); printf(" -q , --energy-sent= Get past energy delivered to the grid ** Experimental **\n"); printf(" in 10 second intervals for minutes (1-1440). is\n"); printf(" optional for long option and if omitted all data, ~24 hours\n"); printf(" worth will be reported. It is suggested the -Y option be used\n"); printf(" with this due to the extensive length of time this could take.\n"); printf(" See the README file for important information on this option\n"); printf(" -R , --read-timeout= Timeout value when reading data from the Inverter (ms)\n"); printf(" -r, --calc-grid-power Calc Grid power using Grid Voltage * Grid Current,\n"); printf(" instead of reporting the Inverter's value. --> for \n"); printf(" -d option only, ignored when used with -c option\n"); printf(" (Inverter typically reports a lower value. This\n"); printf(" affects Inverter conversion efficiency value.)\n"); printf(" -S, --set-time Set Inverter Date/Time to system time\n"); printf(" -s, --get-state Get Inverter State\n"); printf(" -T, --get-loctime Display computer Date/Time\n"); printf(" -t, --get-invtime Display Inverter Date/Time\n"); printf(" -V, --version Aurora communications program version\n"); printf(" -v, --inv-version Query for Version string\n"); printf(" -U , --read-pause= Pause milli-seconds after sending command to inverter before\n"); printf(" reading response from inverter (1-10000)\n"); printf(" -u, --rpt-read-pause Report when/that pausing before read\n"); printf(" -w, --lock-wait Seconds to wait to lock serial port. (1-30)\n"); printf(" -X, --rts-cts Enable RTS/CTS on the serial port.\n"); printf(" -x, --xon-xoff Enable XON/XOFF on the serial port.\n"); printf(" -Y , --retries= Retry failed communications with inverter up to times (1-100)\n"); printf(" -y, --rpt-retries Report the number of retires done\n\n"); printf(" *** Required Parameters ***\n"); printf(" -a , --address= Inverter address. 1-31 on older inverters, 1-63 on newer inverters.\n"); printf(" Device Serial Device.\n"); printf("\n"); if (bHelp) exit(0); else exit(2); } if (bVerbose) fprintf(stderr, "\nRunTime %s v%-6s\n",RunTime,VersionM); if (yCost > 0.0 && sCostType == NULL) { sCostType = getMemPtr(2); strcpy(sCostType,"$"); if (bVerbose) fprintf (stderr, "Monetary type \"%s\"\n",sCostType); } if (bVerbose) fprintf (stderr, "PID : %lu\n", PID); if (bVersion) { Version(); exit(0); } if (bVerbose) fprintf(stderr, "\nAttempting to get lock on Serial Port %s...\n",szttyDevice); fdserlck = fopen(devLCKfile, "a"); if (fdserlck == NULL) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for write.\n\n",RunTime,ProgramName,devLCKfile); exit(2); } bWrite = fprintf(fdserlck, "%lu\n", PID); errno_save = errno; fclose(fdserlck); fdserlck = NULL; if (bWrite < 0 || errno_save != 0) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem locking serial device, can't write lock file: %s.\n%s\n\n",RunTime,ProgramName,devLCKfile,strerror (errno_save)); exit(2); } rPID = 0; lckCNT = -1; while(rPID != PID && lckCNT++ < yLockWait) { SubStrPos = NULL; fdserlck = fopen(devLCKfile, "r"); if (fdserlck == NULL) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem locking serial device, can't open lock file: %s for read.\n\n",RunTime,ProgramName,devLCKfile); exit(2); } bRead = fscanf(fdserlck, "%lu", &rPID); errno_save = errno; fclose(fdserlck); fdserlck = NULL; if (bRead == EOF || errno_save != 0) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem locking serial device, can't read lock file: %s.\n%s\n\n",RunTime,ProgramName,devLCKfile,strerror (errno_save)); exit(2); } sPID[0] = '\0'; sprintf(sPID,"%lu",rPID); cmdFile = getMemPtr(strlen(sPID)+14+1); cmdFile[0] = '\0'; sprintf(cmdFile,"/proc/%lu/cmdline",rPID); if (bVerbose) fprintf(stderr, "\nChecking process %lu for lock\n",rPID); fdserlck = fopen(cmdFile, "r"); if (fdserlck != NULL) { fLen = 0; while (fgetc(fdserlck) != EOF) fLen++; if (fLen > 0) { command = getMemPtr(fLen+1); command[0] = '\0'; rewind(fdserlck); bRead = fscanf(fdserlck, "%s", command); } fclose(fdserlck); fdserlck = NULL; if (command != NULL) SubStrPos = strstr(command, ProgramName); } if (cmdFile != NULL) { free(cmdFile); cmdFile = NULL; } if (bVerbose) { fprintf (stderr, "rPID: %lu SubStrPos: %s command: %s",rPID,SubStrPos,command); if (rPID == PID) fprintf (stderr, " = me"); fprintf (stderr, "\n"); } if (command != NULL) { free(command); command = NULL; } if (rPID != PID) { if (SubStrPos == NULL) { if (bVerbose) fprintf (stderr, "\n"); fprintf(stderr, "%s: %s: Clearing stale serial port lock. (%lu)\n",RunTime,ProgramName,rPID); ClrSerLock(rPID); } else if (yLockWait > 0) sleep(1); } } if (bVerbose && rPID == PID) fprintf(stderr, "Appears we got the lock.\n"); if (rPID != PID) { ClrSerLock(PID); if (bVerbose) fprintf (stderr, "\n"); fprintf(stderr, "%s: %s: Problem locking serial device %s, couldn't get the lock for %lu, locked by %lu.\n\n",RunTime,ProgramName,szttyDevice,PID,rPID); exit(2); } if (bVerbose) fprintf(stderr, "\nOpening Serial Port %s... ", szttyDevice); fdser = open(szttyDevice, O_RDWR | O_NOCTTY ); if (fdser < 0) { ClrSerLock(PID); if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem opening serial device, check device name.\n\n",RunTime,ProgramName); exit(2); } if (bVerbose) fprintf(stderr, "Serial Port %s successfully opened.\n", szttyDevice); tcgetattr(fdser, &oldtio); /* save previous port settings */ memset(&newtio, 0, sizeof(newtio)); newtio.c_cflag &= ~PARENB; /* no parity */ newtio.c_cflag &= ~CSTOPB; /* on stop bit */ newtio.c_cflag &= ~CSIZE; /* character size mask */ newtio.c_cflag &= ~HUPCL; /* no hangup */ if (bRTSCTS) newtio.c_cflag |= ~CRTSCTS; /* enable hardware flow control */ else newtio.c_cflag &= ~CRTSCTS; /* disable hardware flow control */ newtio.c_cflag |= CS8 | CLOCAL | CREAD; /* 8 bit - ignore modem control lines - enable receiver */ if (bXonXoff) newtio.c_iflag |= IXON | IXOFF; /* enable XON/XOFF flow control on output & input */ else { newtio.c_iflag &= ~IXON; /* disable XON/XOFF flow control on output */ newtio.c_iflag &= ~IXOFF; /* disable XON/XOFF flow control on input */ } newtio.c_iflag |= IGNBRK | IGNPAR; /* ignore BREAK condition on input & framing errors & parity errors */ newtio.c_oflag = 0; /* set serial device input mode (non-canonical, no echo,...) */ newtio.c_oflag &= ~OPOST; /* enable output processing */ newtio.c_lflag = 0; newtio.c_cc[VTIME] = yDelay; /* timeout in 1/10 sec intervals */ newtio.c_cc[VMIN] = 0; /* block until char or timeout */ if (cfsetospeed (&newtio, B19200)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem setting serial output speed.\n\n",RunTime,ProgramName); if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName); } if (bVerbose) { fprintf(stderr, " Success!\n"); } ClrSerLock(PID); exit(2); } if (cfsetispeed (&newtio, B19200)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem setting serial input speed.\n\n",RunTime,ProgramName); if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName); } if (bVerbose) { fprintf(stderr, " Success!\n"); } ClrSerLock(PID); exit(2); } if (bVerbose) { fprintf(stderr, "Configuring serial device... Flushing unread data first... "); } errno = 0; if (tcflush(fdser, TCIFLUSH)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",RunTime,ProgramName,errno,strerror(errno)); } if (tcsetattr(fdser, TCSANOW, &newtio)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem configuring serial device.\n\n",RunTime,ProgramName); if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName); } if (bVerbose) { fprintf(stderr, " Success!\n"); } ClrSerLock(PID); exit(2); } if (bVerbose) { fprintf(stderr, " Success!\nFlushing serial device buffer..."); } errno = 0; if (tcflush(fdser, TCIOFLUSH)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",RunTime,ProgramName,errno,strerror(errno)); if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName); } if (bVerbose) { fprintf(stderr, " Success!\n"); } ClrSerLock(PID); exit(2); } if (bVerbose) { fprintf(stderr, " Success!\n"); } bCommCheck = TRUE; if (bVerbose) { fprintf( stderr, "\nComm Check: Let's see if the Aurora is listening... "); } outfp = stderr; if (CommCheck(fdser,yAddress) >= 0) { if (bVerbose) fprintf(stderr, "Comm Check: OK\n"); } else { if (bVerbose) fprintf(stderr, "Comm Check: Failure, aborting...\n"); else { if (bRptReadPause) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: No response after %i attempts\n",RunTime,ProgramName,yMaxAttempts); } rc = -1; } bCommCheck = FALSE; if (rc == 0 && bSetTime) { if (!bGetInvTime) bGetLocTime = TRUE; rc = SetTime(fdser,yAddress,FALSE); } if (rc == 0 && yCheckSetTime >= 0) { rc = CheckSetTime(fdser,yAddress); } if (outfile != NULL) { if (! (outfp = fopen(outfile, "a"))) { fprintf(stderr, "%s: %s: Problem opening output file %s\n",RunTime,ProgramName,outfile); bFileError = TRUE; } } else { outfp = stdout; } if (! bFileError) { bGetTime = bGetInvTime | bGetLocTime; if (rc == 0 && bGetTime) rc |= GetTime(fdser,yAddress); if (!bColumns) { if (rc == 0 && bGetPN) rc |= GetPN(fdser,yAddress); if (rc == 0 && bGetSN) rc |= GetSN(fdser,yAddress); if (rc == 0 && bGetVerFW) rc |= GetVerFW(fdser,yAddress); if (rc == 0 && bGetMfg) rc |= GetMfgDate(fdser,yAddress); if (rc == 0 && bGetVer) rc |= GetVer(fdser,yAddress); if (rc == 0 && bGetConf) rc |= GetConf(fdser,yAddress); if (rc == 0 && bGetJoules) rc |= GetJoules(fdser,yAddress); if (rc == 0 && bGetState) rc |= GetState(fdser,yAddress); if (rc == 0 && bGetCount) rc |= GetCounters(fdser,yAddress); if (rc == 0 && bGetLastAlarms) rc |= GetLastAlarms(fdser,yAddress); if (rc == 0 && yGetEnergyDaily > 0) rc |= GetCEDaily(fdser,yAddress,yGetEnergyDaily); if (rc == 0 && yGetEnergySent > 0) rc |= GetCESent(fdser,yAddress,yGetEnergySent); } if (rc == 0 && yGetDSP >= 0) rc |= GetDSP(fdser,yAddress); if (rc == 0 && bGetEnergy) rc |= GetCE(fdser,yAddress); if (rc == 0 && bGetDSPExtended) rc |= GetDSPExtended(fdser,yAddress); if (rc == 0 && bGetDSP3Phase) rc |= GetDSP3Phase(fdser,yAddress); } if (rc >= 0) { if (bColumns && bColOutput) fprintf(outfp, " OK\n"); else fprintf(outfp, "\n"); if (bVerbose) fprintf(stderr, "Complete.\n\n"); } else if (bColumns && bColOutput) fprintf(outfp, "\n"); if (bVerbose) fprintf(stderr, "rc: %d\n\n",rc); if (outfile != NULL && ! bFileError) fclose(outfp); /* all done, exit */ RestorePort(fdser); ClrSerLock(PID); if (bVerbose) fprintf(stderr, "\nComplete.\n\n"); if (rc >= 0) exit(0); exit(1); } /*-------------------------------------------------------------------------- RestorePort Restore Serial Port Settings ----------------------------------------------------------------------------*/ int RestorePort(int fdser) { if (bVerbose) fprintf(stderr, "Restoring Serial Port settings %s...", szttyDevice); if (tcsetattr(fdser, TCSANOW, &oldtio)) { /* restore previous port settings */ if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem restoring serial device settings.\n",RunTime,ProgramName); if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device, check device name.\n",RunTime,ProgramName); } if (bVerbose) fprintf(stderr, " Success!\n"); return 2; } if (bVerbose) { fprintf(stderr, " Success!\nFlushing serial device buffer..."); } errno = 0; if (tcflush(fdser, TCIOFLUSH)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem flushing serial device: (%i) %s\n\n",RunTime,ProgramName,errno,strerror(errno)); if (bVerbose) fprintf(stderr, "Closing Serial Port %s...",szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName); } if (bVerbose) { fprintf(stderr, " Success!\n"); } return 2; } if (bVerbose) fprintf(stderr, " Success!\nClosing Serial Port %s...", szttyDevice); if (close(fdser)) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "%s: %s: Problem closing serial device.\n",RunTime,ProgramName); return 2; } if (bVerbose) fprintf(stderr, " Success!\n"); return 0; } /*-------------------------------------------------------------------------- ClrSerLock Clear Serial Port lock. ----------------------------------------------------------------------------*/ int ClrSerLock(long unsigned int PID) { FILE *fdserlck, *fdserlcknew; long unsigned int rPID; int bWrite, bRead; int errno_save = 0; if (bVerbose) fprintf(stderr, "\ndevLCKfile: <%s>\ndevLCKfileNew: <%s>\nClearing Serial Port Lock (%lu)...", devLCKfile, devLCKfileNew, PID); fdserlck = fopen(devLCKfile, "r"); if (fdserlck == NULL) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem opening serial device lock file to clear PID %lu: %s for read.\n\n",RunTime,ProgramName,PID,devLCKfile); return(0); } fdserlcknew = fopen(devLCKfileNew, "w"); if (fdserlcknew == NULL) { if (bVerbose) fprintf(stderr, "\n"); fprintf(stderr, "\n%s: %s: Problem opening new serial device lock file to clear PID %lu: %s for write.\n\n",RunTime,ProgramName,PID,devLCKfileNew); fclose(fdserlck); return(0); } bRead = fscanf(fdserlck, "%lu", &rPID); while (bRead != EOF) { if (rPID != PID) { errno = 0; bWrite = fprintf(fdserlcknew, "%lu\n", rPID); errno_save = errno; if (bWrite < 0 || errno_save != 0) { fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't write lock file: %s.\n%s\n\n",RunTime,ProgramName,devLCKfile,strerror (errno_save)); fclose(fdserlcknew); return(0); } } bRead = fscanf(fdserlck, "%lu", &rPID); } fclose(fdserlck); fclose(fdserlcknew); errno = 0; if (rename(devLCKfileNew,devLCKfile)) fprintf(stderr, "\n%s: %s: Problem clearing serial device lock, can't update lock file: %s.\n%s\n\n",RunTime,ProgramName,devLCKfile,strerror (errno)); if (bVerbose) fprintf(stderr, " done.\n"); return -1; } /*-------------------------------------------------------------------------- isNumeric ----------------------------------------------------------------------------*/ BOOL isNumeric(char *p) { int i; for (i = 0; i <= strlen(p); i++) if (p[i] != '\0' && ! isdigit(p[i])) return(FALSE); return(TRUE); } /*-------------------------------------------------------------------------- GetParms Reads command line parameters. ----------------------------------------------------------------------------*/ int GetParms(int argc, char *argv[]) { extern char *optarg; extern int optind, opterr, optopt; int c; int i = 0; BOOL b = FALSE; char *pos; char *SubStrPos = NULL; char sPID[10]; static char *Cost = NULL; if (strpbrk(VersionM,"abcdefghijklmnopqurtsuvwxyz") != NULL) fprintf(stderr, "\n**** THIS IS EXPERIMENTAL CODE ****\n"); if (strcmp(VersionM,VersionC) != 0) { fprintf(stderr, "\n**** MODULE VERSION MISMATCH ****\n"); fprintf(stderr, " Main module : %-6s\n",VersionM); fprintf(stderr, " Comm module : %-6s\n",VersionC); return 0; } while (! b && i < argc) if (strcmp(argv[i++],"-b") == 0) b = TRUE; if (b) { fprintf(stderr, "\n"); i = 0; while (i < argc) fprintf(stderr, "%s ",argv[i++]); fprintf(stderr, "\n"); } /* options descriptor */ static struct option longopts[] = { { "address", required_argument, 0, 'a' }, { "last-alarms", no_argument, 0, 'A' }, { "verbose", no_argument, 0, 'b' }, { "calc-value", required_argument, 0, 'C' }, { "columnize", no_argument, 0, 'c' }, { "get-dsp", optional_argument, 0, 'd' }, { "get-dsp-extended", no_argument, 0, 'D' }, { "get-dsp-3phase", no_argument, 0, 'E' }, { "get-energy", no_argument, 0, 'e' }, { "firmware-ver", no_argument, 0, 'f' }, { "mfg-date", no_argument, 0, 'g' }, { "hide-dsp-msg", no_argument, 0, 'H' }, { "help", no_argument, 0, 'h' }, { "get-count", no_argument, 0, 'i' }, { "get-joules", no_argument, 0, 'j' }, { "daily-kwh", optional_argument, 0, 'k' }, { "adjust-time", required_argument, 0, 'L' }, { "delay", required_argument, 0, 'l' }, { "get-conf", no_argument, 0, 'm' }, { "serial-number", no_argument, 0, 'n' }, { "output-file", required_argument, 0, 'o' }, { "part-number", no_argument, 0, 'p' }, { "comm-pause", required_argument, 0, 'P' }, { "energy-sent", optional_argument, 0, 'q' }, { "read-timeout", required_argument, 0, 'R' }, { "calc-grid-power", no_argument, 0, 'r' }, { "set-time", no_argument, 0, 'S' }, { "get-state", no_argument, 0, 's' }, { "get-invtime", no_argument, 0, 't' }, { "get-loctime", no_argument, 0, 'T' }, { "version", no_argument, 0, 'V' }, { "inv-version", no_argument, 0, 'v' }, { "read-pause", required_argument, 0, 'U' }, { "rpt-read-pause", no_argument, 0, 'u' }, { "lock-wait", required_argument, 0, 'w' }, { "rts-cts", no_argument, 0, 'X' }, { "xon-xoff", no_argument, 0, 'x' }, { "retries", required_argument, 0, 'Y' }, { "rpt-retries", no_argument, 0, 'y' }, { NULL, 0, NULL, 0 } }; /* Set command line defaults */ yDelay = 1; if (argc == 1) return 0; /* no parms at all */ while ((c = getopt_long(argc, argv, "a:AbC:cDd:EefgHhijk:L:l:mno:P:pq:R:rSsTtVvU:uw:XxY:y", longopts, NULL )) != EOF) { switch (c) { case 'a': /* Inverter address */ yAddress = atoi(optarg); break; case 'A': bGetLastAlarms = TRUE; break; case 'b': bVerbose = TRUE; break; case 'C': SubStrPos = strstr(optarg, ":"); if (SubStrPos == NULL) yCost = atof(optarg); else { Cost = getMemPtr(SubStrPos-optarg); strncpy(Cost,optarg,SubStrPos-optarg); yCost = atof(Cost); sCostType = getMemPtr(strlen(optarg)-(SubStrPos-optarg)); strcpy(sCostType,SubStrPos+1); free(Cost); Cost = NULL; } if (bVerbose) fprintf(stderr, "\nCost: %f Type: \"%s\"\n",yCost,sCostType); break; case 'c': bColumns = TRUE; break; case 'd': if (optarg == NULL) { yGetDSP = 0; } else { i = atoi(optarg); if (i < 0 || i > 2) { fprintf(stderr, "\n%s: %s: -d value out of range, 0 to 2.\n",RunTime,ProgramName); return 0; } yGetDSP = i; } break; case 'D': bGetDSPExtended = TRUE; break; case 'E': bGetDSP3Phase = TRUE; break; case 'e': bGetEnergy = TRUE; break; case 'f': bGetVerFW = TRUE; break; case 'g': bGetMfg = TRUE; break; case 'H': bHideDSP = TRUE; break; case 'h': bHelp = TRUE; break; case 'i': bGetCount = TRUE; break; case 'j': bGetJoules = TRUE; break; case 'k': /* Get ECC */ if (optarg == NULL) { yGetEnergyDaily = 366; } else { i = atoi(optarg); if (i < 1 || i > 366) { fprintf(stderr, "\n%s: %s: -k value out of range, 1 to 366.\n",RunTime,ProgramName); return 0; } yGetEnergyDaily = i; } break; case 'L': i = atoi(optarg); if (i < 0) { fprintf(stderr, "\n%s: %s: -L value out of range, 0 or >=1 0 = check for DST change >=1 = Reconcile with computer time\n",RunTime,ProgramName); return 0; } yCheckSetTime = i; break; case 'l': /* Get delay time */ i = atoi(optarg); if (i < 0 || i > 255) { fprintf(stderr, "\n%s: %s: -l Illegal delay specified.\n",RunTime,ProgramName); return 0; } yDelay = (unsigned char)i; break; case 'm': bGetConf = TRUE; break; case 'n': bGetSN = TRUE; break; case 'o': outfile = getMemPtr(strlen(optarg)); strcpy(outfile, optarg); break; case 'p': bGetPN = TRUE; break; case 'P': yCommPause = atoi(optarg); if (yCommPause <= 0 || yCommPause > 1000000) { fprintf(stderr, "\n%s: %s: -P Comm Pause micro-seconds (%li) out of range, 1-1000000.\n",RunTime,ProgramName,yCommPause); return 0; } break; case 'q': /* Get Energy Sent */ if (optarg == NULL) { yGetEnergySent = 4320; } else { i = atoi(optarg); if (i < 1 || i > 1440) { fprintf(stderr, "\n%s: %s: -k value out of range, 1 to 1440.\n",RunTime,ProgramName); return 0; } yGetEnergySent = i*3; } break; case 'R': /* read timeout value in us */ yTimeout = atoi(optarg)*1000; break; case 'r': bCalcGridPwr = TRUE; break; case 'S': bSetTime = TRUE; break; case 's': bGetState = TRUE; break; case 'T': bGetLocTime = TRUE; break; case 't': bGetInvTime = TRUE; break; case 'U': yReadPause = atoi(optarg); if (yReadPause < 1 || yReadPause > 10000) { fprintf(stderr, "\n%s: %s: -U Read Pause milli-seconds (%d) out of range, 1-10000.\n",RunTime,ProgramName,yReadPause); return 0; } break; case 'u': bRptReadPause = TRUE; break; case 'w': yLockWait = atoi(optarg); if (yLockWait < 1 || yLockWait > 30) { fprintf(stderr, "\n%s: %s: -w Lock Wait seconds (%d) out of range, 1-30.\n",RunTime,ProgramName,yLockWait); return 0; } break; case 'X': bRTSCTS = TRUE; break; case 'x': bXonXoff = TRUE; break; case 'Y': yMaxAttempts = atoi(optarg); if (yMaxAttempts < 1 || yMaxAttempts > 100) { fprintf(stderr, "\n%s: %s: -Y Retries (%d) out of range, 1-100.\n",RunTime,ProgramName,yMaxAttempts); return 0; } break; case 'y': bRptReties = TRUE; break; case 'V': bVersion = TRUE; break; case 'v': bGetVer = TRUE; break; case '?': /* user entered unknown option */ case ':': /* user entered option without required value */ return 0; default: break; } } if (optind < argc) { /* get serial device name */ szttyDevice = getMemPtr(strlen(argv[optind])); strcpy(szttyDevice, argv[optind]); } else { if (!bVersion && ! bHelp) fprintf(stderr, "\n%s: %s: No serial device specified\n",RunTime,ProgramName); return 0; } pos = strrchr(szttyDevice, '/'); if (pos > 0) { pos++; devLCKfile = getMemPtr(strlen(ttyLCKloc)+(strlen(szttyDevice)-(pos-szttyDevice))+1); devLCKfile[0] = '\0'; strcpy(devLCKfile,ttyLCKloc); strcat(devLCKfile, pos); sprintf(sPID,"%lu",PID); devLCKfileNew = getMemPtr(strlen(devLCKfile)+strlen(sPID)+1); devLCKfileNew[0] = '\0'; strcpy(devLCKfileNew,devLCKfile); sprintf(devLCKfileNew,"%s.%lu",devLCKfile,PID); } else { devLCKfile = NULL; } if (bVerbose) fprintf(stderr, "\nszttyDevice: %s\nyDelay: %i\nyTimeout %i us\ndevLCKfile: <%s>\ndevLCKfileNew: <%s>\n",szttyDevice,yDelay,yTimeout,devLCKfile,devLCKfileNew); if (bSetTime && yCheckSetTime >= 0) { fprintf(stderr, "\n%s: %s: -L and -S are mutually exclusive.\n",RunTime,ProgramName); return 0; } if (yAddress < 1 || yAddress > 63) { fprintf(stderr, "\n%s: %s: Illegal address (%d) specified.\n",RunTime,ProgramName,yAddress); return 0; } if (bVerbose) fprintf(stderr, "Got Params\n"); return -1; } /*-------------------------------------------------------------------------- getMemPtr ----------------------------------------------------------------------------*/ void *getMemPtr(size_t mSize) { void *ptr; ptr = malloc(mSize); if (!ptr) { fprintf(stderr, "\nvproweather: malloc failed\n"); exit(2); } return ptr; } /*-------------------------------------------------------------------------- Version Display program component versions ----------------------------------------------------------------------------*/ void Version() { printf("\nAurora module versions:\n"); printf("Main module : %-6s\n",VersionM); printf("Comm module : %-6s\n",VersionC); printf("main.h : %-6s\n",VersionMHc); printf("comm.h : %-6s\n",VersionCHc); printf("names.h : %-6s\n",VersionNHc); printf("states.h : %-6s\n\n",VersionSHc); } aurora-1.7.3/comm.c0000644000076400017510000020460011657110231011734 0ustar curt/* * aurora - Communications with Magnetek Aurora Inverter * * Copyright (C) 2006-2011 Curtis J. Blank curt@curtronics.com * * 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 may be used and hosted free of charge by anyone for * personal purposes as long as this and all included copyright * notice(s) remain intact. * * Obtain permission before selling the code for this program or * hosting this software on a commercial website, excluding if it is * solely hosted for distribution, that is allowable . In all cases * copyright must remain intact. * * This work based on Magnetek's 'Aurora PV Inverter - Communications * Protocol -' document, Rel. 1.8 09/05/2005 * Staring with v1.5-0 this work based on Power One's 'Aurora Inverter * Series - Communication Protocol -' document, Rel. 4.6 25/02/09 * * Special thanks to Tron Melzl at Magnetek for all his help including, * but not limited to, supplying the Communications Protocol document * * modified 17-jul-2006 cjb 1. Last 7 Days production value has been dropped in the v2.3 Communications Protocol doc * modified 13-oct-2006 cjb 1. correct possible divide by zero when calculating inverter efficiency * modified 25-apr-2007 cjb 1. update set time warning * 2. take into account Daylight Savings Time when setting the Aurora's time * modified 29-dec-2008 cjb 1. correct an error in strftime that only may show up in the first or last * week of the year (%G vs %Y) * modified 19-aug-2009 cjb 1. szSerBuffer needs to be [11] if ending with "\0" * modified 18-sep-2009 cjb 1. add cCommandEnd = '\0' * modified 12-oct-2009 cjb 1. add -o option to output data to a file * modified 30-oct-2009 cjb 1. added errno for read problems * modified 07-mar-2010 cjb 1. fix sizeof pointer passing in memset * 2. use -Y option in Communicate function * 3. in ReadNextChar restore serial port settings and clear lock if exiting * modified 13-mar-2010 cjb 1. if yReadPause is set use it * modified 28-mar-2010 cjb 1. working on adding more DSP information * modified 27-jul-2010 cjb 1. added -P option to throttle commands sent to the inverter * modified 21-sep-2010 cjb 1. fix using wrong param when displaying TransState message * 2. pass TransState the command description so if it gets a non-zero status it can * report for what command it got it * 3. added reporting for "Last four alarms" * modified 30-jul-2011 cjb 1. fixed joule conversion error * modified 06-aug-2011 cjb 1. fixed a integer conversion issue for -k due to some architectures * 2. fixed bizarreness with -bt * 3. adjust TimeBase to true UTC-0000 and take into account timezone in GetTime (-t) * SetTime (-S) functions * modified 22-aug-2011 cjb 1. in SetTime check scanf return value rc * modified 05-nov-2011 cjb 1. added function for -q --energy-sent option * modified 09-nov-2011 cjb 1. added function for -L --reconcile-time option * */ char VersionC[7] = "1.7.3"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "include/main.h" #include "include/comm.h" #include "include/names.h" #include "include/states.h" char RunTime[18]; char VersionSHc[6] = VersionSH; static char szSerBuffer[_szSerBufferLen]; /* serial read/write buffer */ static WORD crcValue; /* local functions */ static int TransState(int TransCode, char Desription[]); static int PrintModel(int ModelID); static long GetCEdata(int fdser, int yAddress, int opcode, int param, char desription[]); static float GetDSPdata(int fdser, int yAddress, int opcode, int param, char desription[]); static time_t GetInvTime(int fdser, int yAddress); static int GetCountersData(int fdser, int yAddress, int param, char *uptime); static int Communicate(int fdser, int yAddress); static int ReadNextChar(int nfd, char *pChar, int timeout); static int ReadToBuffer(int nfd, char *pszBuffer, int nBufSize); static void Delay(int secs, long microsecs); static WORD crc16(char *data_p, unsigned short length); static char* FindString(WORD wRule, char *ptr); static float *szCvrtFloat(char *Buffer); static unsigned long szCvrtShort(char *Buffer); static unsigned long szCvrtLong(char *Buffer); static unsigned long cvrtLong(char *Buffer); /*-------------------------------------------------------------------------- TransState ----------------------------------------------------------------------------*/ int TransState(int TransCode, char Desription[]) { if (bVerbose) fprintf(stderr, "Transmission State Check: %i\n",TransCode); if (TransCode == 0) return(1); fprintf(outfp, "\nTransmission State: %2i Command: \"%s\" %s\n\n",TransCode,Desription,FindString(TransCode, szTransStates)); return(0); } /*-------------------------------------------------------------------------- CommCheck ----------------------------------------------------------------------------*/ int CommCheck(int fdser, int yAddress) { int nCnt; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetVer; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetVer)) { ModelID = szSerBuffer[aPar1]; if (bVerbose) { fprintf(stderr, "Model ID \"%c\" ",ModelID); PrintModel(ModelID); } return(0); } return(-1); } /*-------------------------------------------------------------------------- GetState ----------------------------------------------------------------------------*/ int GetState(int fdser, int yAddress) { int nCnt; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetState; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetState)) { fprintf(outfp, "\nGlobal State: %s\n",FindString((int)szSerBuffer[aMState], szGlobalStates)); fprintf(outfp, "Inverter State: %s\n",FindString((int)szSerBuffer[aParam1], szInverterState)); fprintf(outfp, "Channel 1 Dc/Dc State: %s\n",FindString((int)szSerBuffer[aParam2], szDcDcStatus)); fprintf(outfp, "Channel 2 Dc/Dc State: %s\n",FindString((int)szSerBuffer[aParam3], szDcDcStatus)); fprintf(outfp, "Alarm State: %s\n",FindString((int)szSerBuffer[aParam4], szAlarmState)); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetLastAlarms ----------------------------------------------------------------------------*/ int GetLastAlarms(int fdser, int yAddress) { int nCnt; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetLastAlarms; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetLastAlarms)) { fprintf(outfp, "\nAlarm 1: %s\n",FindString((int)szSerBuffer[aParam1], szAlarmState)); fprintf(outfp, "Alarm 2: %s\n",FindString((int)szSerBuffer[aParam2], szAlarmState)); fprintf(outfp, "Alarm 3: %s\n",FindString((int)szSerBuffer[aParam3], szAlarmState)); fprintf(outfp, "Alarm 4: %s\n",FindString((int)szSerBuffer[aParam4], szAlarmState)); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetPN ----------------------------------------------------------------------------*/ int GetPN(int fdser, int yAddress) { int nCnt; char PartNumber[6]; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetPN; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0) { strncpy(PartNumber, szSerBuffer, 6); fprintf(outfp, "\nPart Number: %s\n",PartNumber); return(0); } return(-1); } /*-------------------------------------------------------------------------- PrintModel ----------------------------------------------------------------------------*/ int PrintModel(int ModelID) { if (outfp != stderr) fprintf(outfp, "\nInverter Version:"); fprintf(outfp, " -- "); switch (ModelID) { case 'i': fprintf(outfp, "%s -- ",aPar1i); break; case 'o': fprintf(outfp, "%s -- ",aPar1o); break; case 'I': fprintf(outfp, "%s -- ",aPar1I); break; case 'O': fprintf(outfp, "%s -- ",aPar1O); break; case '5': fprintf(outfp, "%s -- ",aPar15); break; case '6': fprintf(outfp, "%s -- ",aPar16); break; case 'P': fprintf(outfp, "%s -- ",aPar1P); break; case 'C': fprintf(outfp, "%s -- ",aPar1C); break; case '4': fprintf(outfp, "%s -- ",aPar14); break; case '3': fprintf(outfp, "%s -- ",aPar13); break; case '2': fprintf(outfp, "%s -- ",aPar12); break; case '1': fprintf(outfp, "%s -- ",aPar11); break; case 'D': fprintf(outfp, "%s -- ",aPar1D); break; case 'X': fprintf(outfp, "%s -- ",aPar1X); break; default: fprintf(outfp, "%s -- ","unknown"); break; } fprintf(outfp, "\n"); return(0); } /*-------------------------------------------------------------------------- GetVer ----------------------------------------------------------------------------*/ int GetVer(int fdser, int yAddress) { int nCnt; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetVer; szSerBuffer[cCommandEnd] = '\0'; szSerBuffer[cParam1] = '.'; nCnt = Communicate(fdser, yAddress); crcValue = crc16(szSerBuffer, 8); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetVer)) { PrintModel(szSerBuffer[aPar1]); switch (szSerBuffer[aPar2]) { case 'A': fprintf(outfp, "%s -- ",aPar2A); break; case 'E': fprintf(outfp, "%s -- ",aPar2E); break; case 'S': fprintf(outfp, "%s -- ",aPar2S); break; case 'I': fprintf(outfp, "%s -- ",aPar2I); break; case 'U': fprintf(outfp, "%s -- ",aPar2U); break; case 'K': fprintf(outfp, "%s -- ",aPar2K); break; case 'F': fprintf(outfp, "%s -- ",aPar2F); break; default: fprintf(outfp, "%s -- ","unknown"); break; } switch (szSerBuffer[aPar3]) { case 'T': fprintf(outfp, "%s -- ",aPar3T); break; case 'N': fprintf(outfp, "%s -- ",aPar3N); break; default: fprintf(outfp, "%s -- ","unknown"); break; } switch (szSerBuffer[aPar4]) { case 'W': fprintf(outfp, "%s -- ",aPar4W); break; case 'N': fprintf(outfp, "%s -- ",aPar4N); break; default: fprintf(outfp, "%s -- ","unknown"); break; } fprintf(outfp, "\n"); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetConf ----------------------------------------------------------------------------*/ int GetConf(int fdser, int yAddress) { int nCnt; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetConfig; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); crcValue = crc16(szSerBuffer, 8); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetConfig)) { switch (szSerBuffer[aConfCode]) { case ConfCode0: fprintf(outfp, "\n%s\n",_ConfCode0); break; case ConfCode1: fprintf(outfp, "\n%s\n",_ConfCode1); break; case ConfCode2: fprintf(outfp, "\n%s\n",_ConfCode2); break; default: break; } return(0); } return(-1); } /*-------------------------------------------------------------------------- GetSN ----------------------------------------------------------------------------*/ int GetSN(int fdser, int yAddress) { int nCnt; char SerialNumber[6]; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetSN; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0) { strncpy(SerialNumber, szSerBuffer, 6); fprintf(outfp, "\nSerial Number: %s\n",SerialNumber); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetVerFW ----------------------------------------------------------------------------*/ int GetVerFW(int fdser, int yAddress) { int nCnt; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetVerFW; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetVerFW)) { fprintf(outfp, "\nFirmware: %c.%c.%c.%c\n",szSerBuffer[aRel3],szSerBuffer[aRel2],szSerBuffer[aRel1],szSerBuffer[aRel0]); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetMfgDate ----------------------------------------------------------------------------*/ int GetMfgDate(int fdser, int yAddress) { int nCnt; char MfgWeek[3] = " \0"; char MfgYear[3] = " \0"; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetMfg; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetMfg)) { MfgWeek[0] = szSerBuffer[aWeekH]; MfgWeek[1] = szSerBuffer[aWeekL]; MfgYear[0] = szSerBuffer[aYearH]; MfgYear[1] = szSerBuffer[aYearL]; fprintf(outfp, "\nManufacturing Date: Year %s Week %s\n",MfgYear,MfgWeek); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetCESent ** Experimental ** ----------------------------------------------------------------------------*/ int GetCESent(int fdser, int yAddress, int yGetEnergySent) { int nCnt = 0; time_t timeValLong = 0; time_t timeValLongLast = 0; long int gmtoff = 0; struct tm TS, TSnext; char DT[18] = " "; int addC = 0; int addS = 0; BOOL loop = TRUE; BOOL beg = FALSE; BOOL tsync = FALSE; BOOL cr = FALSE; int value1 = 0; int value2 = 0; static char tBuf[4]; int ffff = 0; int cnt = 0; int i = 0; if (bVerbose)fprintf(stderr, "TimeBase %lu timeValLong %lu\n",(long)TimeBase,timeValLong); timeValLong = GetInvTime(fdser, yAddress); TS = *(localtime(&timeValLong)); gmtoff = TS.tm_gmtoff; if (bVerbose) { strftime(DT,18,"%Y%m%d-%H:%M:%S ",&TS); fprintf(stderr, "Inverter Time: %17s\t0.0 timeValLong %lu gmtoff %li TimeBase %lu\n",DT,timeValLong,gmtoff,(long)TimeBase); } strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = HIBYTE(opGetCESent); szSerBuffer[cParam1] = LOBYTE(opGetCESent); szSerBuffer[cParam1End] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetCESent)) { addS = szCvrtShort(szSerBuffer); if (bVerbose) { fprintf(stderr, "\nszSerBuffer addS: "); for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\naddS 0x%04x %i\n",addS,addS); } addC = addS; if (addC == aCESMemAdd) beg = TRUE; while (loop && nCnt > 0) { strcpy(szSerBuffer, _clearCMD); szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetCEValue; szSerBuffer[cCEDailyAddH] = HIBYTE(addC); szSerBuffer[cCEDailyAddL] = LOBYTE(addC); szSerBuffer[cCEDailyEnd] = '\0'; nCnt = Communicate(fdser, yAddress); cnt++; if (bVerbose) { fprintf(stderr, "%i 0x%02x%02x szSerBuffer addS: ",cnt,HIBYTE(addC),LOBYTE(addC)); for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\n"); } if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetCESent)) { value1 = szCvrtShort(szSerBuffer); value2 = szCvrtShort(szSerBuffer+2); if (value1 == 0xffff) { ffff = value1; tBuf[0] = HIBYTE(value2); tBuf[1] = LOBYTE(value2); value2 = 0xffff; } else if (value2 == 0xffff) { ffff = value2; } else if (ffff != 0) { if (cvrtLong(tBuf) == 0) { tBuf[0] = HIBYTE(value1); tBuf[1] = LOBYTE(value1); tBuf[2] = HIBYTE(value2); tBuf[3] = LOBYTE(value2); value1 = value2 = 0xffff; } else { tBuf[2] = HIBYTE(value1); tBuf[3] = LOBYTE(value1); value1 = 0xffff; } timeValLong = cvrtLong(tBuf) + TimeBase; TS = *(localtime(&timeValLong)); gmtoff = TS.tm_gmtoff; timeValLong -= gmtoff; tBuf[0] = tBuf[1] = tBuf[2] = tBuf[3] = '\0'; ffff = 0; if (bVerbose) { fprintf(stderr, "time change old: timeValLong %lu ",timeValLongLast); fprintf(stderr, "new: timeValLong %lu gmtoff %li\n",timeValLong,gmtoff); TS = *(localtime(&timeValLongLast)); strftime(DT,18,"%Y%m%d-%H:%M:%S ",&TS); fprintf(stderr, "old: %17s",DT); TS = *(localtime(&timeValLong)); strftime(DT,18,"%Y%m%d-%H:%M:%S ",&TS); fprintf(stderr, " new: %17s\n",DT); } if (! tsync) { TSnext = *(localtime(&timeValLong)); timeValLongLast = timeValLong; TS = *(localtime(&timeValLongLast)); TS.tm_sec = TS.tm_min = TS.tm_hour = 0; timeValLongLast = mktime(&TS); TS = *(localtime(&timeValLongLast)); timeValLongLast += (TS.tm_gmtoff - TSnext.tm_gmtoff); TS = *(localtime(&timeValLongLast)); if (TSnext.tm_year == TS.tm_year && TSnext.tm_mon == TS.tm_mon && TSnext.tm_mday == TS.tm_mday) tsync =TRUE; } if (tsync) { while (timeValLongLast < timeValLong) { TS = *(localtime(&timeValLongLast)); strftime(DT,18,"%Y%m%d-%H:%M:%S ",&TS); fprintf(outfp, "\n%17s\t0.0",DT); timeValLongLast += 10; } } else tsync = TRUE; } if (bVerbose && addC == aCESMemAdd) { fprintf(stderr, "addC %04x cnt = %i szSerBuffer addC: ",addC,cnt); for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\n"); } if (addC != aCESMemAdd && addC <= ((aCESMemAdd+(aCESMaxCnt*4))-4) && value1 != 0xffff) { if (cr && ! bVerbose) fprintf(outfp, "\n"); TS = *(localtime(&timeValLong)); gmtoff = TS.tm_gmtoff; if (tsync) { strftime(DT,18,"%Y%m%d-%H:%M:%S ",&TS); fprintf(outfp, "%17s\t%.1f",DT,(float)value1/10); } else fprintf(outfp, "YYYYMMDD-%04i\t%.1f",((cnt-1)*2)+1,(float)value1/10); if (bVerbose) fprintf(outfp, "\t0x%04x",value1); if (tsync) { timeValLong += 10; timeValLongLast = timeValLong; } if (bVerbose) fprintf(outfp, "\n"); cr = TRUE; } if ((addC+2) <= ((aCESMemAdd+(aCESMaxCnt*4))-2) && value2 != 0xffff) { if (cr && ! bVerbose) fprintf(outfp, "\n"); TS = *(localtime(&timeValLong)); gmtoff = TS.tm_gmtoff; if (tsync) { strftime(DT,18,"%Y%m%d-%H:%M:%S ",&TS); fprintf(outfp, "%17s\t%.1f",DT,(float)value2/10); } else fprintf(outfp, "YYYYMMDD-%04i\t%.1f",((cnt-1)*2)+2,(float)value2/10); if (bVerbose) fprintf(outfp, "\t0x%04x",value2); if (tsync) { timeValLong += 10; timeValLongLast = timeValLong; } if (bVerbose) fprintf(outfp, "\n"); cr = TRUE; } addC += 4; if (cnt >= aCESMaxCnt || cnt >= yGetEnergySent || (beg && addC >= addS)) loop = FALSE; else { if (addC > ((aCESMemAdd+(aCESMaxCnt*4))-4)) { if (beg) loop = FALSE; else { addC = aCESMemAdd; beg = TRUE; } } } } else { return(-1); } } } else return(-1); return(0); } /*-------------------------------------------------------------------------- GetCEDaily ** Experimental ** ----------------------------------------------------------------------------*/ int GetCEDaily(int fdser, int yAddress, int yGetEnergyDaily) { int nCnt, lCnt = 1; int addC = 0; int loop = 1; time_t tB, tVal; struct tm tS; char DT[9] = " "; unsigned long days = 0, kwh, last_kwh = 0; tS.tm_sec = tS.tm_min = tS.tm_hour = tS.tm_mon = tS.tm_isdst = 0; tS.tm_mday = 1; tS.tm_year = 100; tB = mktime(&tS); tS = *(localtime(&tB)); strftime(DT,9,"%Y%m%d ",&tS); strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = HIBYTE(opGetCEAdd); szSerBuffer[cParam1] = LOBYTE(opGetCEAdd); szSerBuffer[cParam1End] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetCEAdd)) { addC = szCvrtShort(szSerBuffer); if (bVerbose) fprintf(stderr, "\nDaily Cumulated Energy Epoch %s Address 0x%04x\n",DT,(WORD)addC); else fprintf(outfp, "\n"); while (loop && nCnt > 0) { strcpy(szSerBuffer, _clearCMD); szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetCEValue; szSerBuffer[cCEDailyAddH] = HIBYTE(addC); szSerBuffer[cCEDailyAddL] = LOBYTE(addC); szSerBuffer[cCEDailyEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetCEAdd)) { days = szCvrtShort(szSerBuffer); kwh = szCvrtShort(szSerBuffer+2); if (kwh != 0xffff) { tVal = tB + ((days-1)*86400); tS = *(localtime(&tVal)); strftime(DT,9,"%Y%m%d ",&tS); if (bVerbose) fprintf(stderr, "Daily Cumulated Energy Loop %3d Day %3d Address 0x%04x Days 0x%02x%02x Value 0x%02x%02x\td %li\t%s %7.3f kWh\n",loop,lCnt,(WORD)addC,(BYTE)szSerBuffer[aCEDailyDaysH],(BYTE)szSerBuffer[aCEDailyDaysL],(BYTE)szSerBuffer[aCEDailyValH],(BYTE)szSerBuffer[aCEDailyValL],days,DT,(float)kwh/1000); else fprintf(outfp, "%s %11.3f kWh\n",DT,(float)kwh/1000); addC = addC - 4; lCnt++; } else { if (last_kwh == 0xffff) { fprintf(stderr, "\nDaily Cumulated Energy retreival problem, may not have %d days recorded\n",yGetEnergyDaily); return(0); } if (bVerbose) fprintf(stderr, "Daily Cumulated Energy Loop %3d Loop to memory block beginning\n",loop); addC = addC + (366*4); } last_kwh = kwh; loop++; } else loop = 0; if (loop > 367 || lCnt > yGetEnergyDaily) loop = 0; } return(0); } else if (bVerbose) fprintf(stderr, "\nDaily Cumulated Energy Address retreival problem\n"); return(-1); } /*-------------------------------------------------------------------------- GetCE ----------------------------------------------------------------------------*/ int GetCE(int fdser, int yAddress) { long DAILY, WEEKLY, LAST7DAYS, MONTHLY, YEARLY, TOTAL, PARTIAL; if (bVerbose) fprintf(stderr, "\nAttempting to get Partial Energy value "); if ((PARTIAL = GetCEdata(fdser,yAddress,opGetCE,CEpar6,_opGetCE)) < 0) return(-1); if ((DAILY = GetCEdata(fdser,yAddress,opGetCE,CEpar0,_opGetCE)) < 0) return(-1); if ((WEEKLY = GetCEdata(fdser,yAddress,opGetCE,CEpar1,_opGetCE)) < 0) return(-1); // if ((LAST7DAYS = GetCEdata(fdser,yAddress,opGetCE,CEpar2,_opGetCE)) < 0) return(-1); LAST7DAYS = 0.0; /* do this for now since this has been dropped in the v2.3 Communications Protocol doc */ /* placeholder for the -c option for now */ if ((MONTHLY = GetCEdata(fdser,yAddress,opGetCE,CEpar3,_opGetCE)) < 0) return(-1); if ((YEARLY = GetCEdata(fdser,yAddress,opGetCE,CEpar4,_opGetCE)) < 0) return(-1); if ((TOTAL = GetCEdata(fdser,yAddress,opGetCE,CEpar5,_opGetCE)) < 0) return(-1); if (bColumns) { fprintf(outfp, "%11.3f %11.3f %11.3f %11.3f %11.3f %11.3f %11.3f ",DAILY/1000.0,WEEKLY/1000.0,LAST7DAYS/1000.0,MONTHLY/1000.0,YEARLY/1000.0,TOTAL/1000.0,PARTIAL/1000.0); bColOutput = TRUE; } else { fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar0,DAILY/1000.0); if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(DAILY/1000.0)*yCost); fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar1,WEEKLY/1000.0); if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(WEEKLY/1000.0)*yCost); // fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar2,LAST7DAYS/1000.0); /* see above note */ // if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(LAST7DAYS/1000.0)*yCost); fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar3,MONTHLY/1000.0); if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(MONTHLY/1000.0)*yCost); fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar4,YEARLY/1000.0); if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(YEARLY/1000.0)*yCost); fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar5,TOTAL/1000.0); if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(TOTAL/1000.0)*yCost); fprintf(outfp, "\n%-26s = %11.3f kWh",_CEpar6,PARTIAL/1000.0); if (yCost > 0) fprintf(outfp, "\t(%s %8.3f)",sCostType,(PARTIAL/1000.0)*yCost); fprintf(outfp, "\n"); } return(0); } /*-------------------------------------------------------------------------- GetCEdata ----------------------------------------------------------------------------*/ long GetCEdata(int fdser, int yAddress, int opcode, int param, char desription[]) { int nCnt; unsigned long paramValLong; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opcode; /* set Measure request to the Energy opcode */ szSerBuffer[cParam1] = param; szSerBuffer[cCommandEnd+1] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],desription)) { paramValLong = szCvrtLong(szSerBuffer); if (bVerbose) fprintf(stderr, "value %12lu\n",(unsigned long)paramValLong); } else return(-1); return((int)paramValLong); } /*-------------------------------------------------------------------------- GetDSP ----------------------------------------------------------------------------*/ int GetDSP(int fdser, int yAddress) { float GVR, GCR, GPR, GPRC=0.0, FRQ, INVeff=0.0, INVeffC=0.0, INVtemp, ENVtemp, STR1V, STR1C, STR2V, STR2C; float PVpwr = 0.0; STR1V = STR1C = STR2V = STR2C = -1.0; if ((FRQ = GetDSPdata(fdser,yAddress,opGetDSP,ToM4,_opGetDSP)) < 0) return(-1); if ((GVR = GetDSPdata(fdser,yAddress,opGetDSP,ToM1,_opGetDSP)) < 0) return(-1); if ((GCR = GetDSPdata(fdser,yAddress,opGetDSP,ToM2,_opGetDSP)) < 0) return(-1); if ((GPR = GetDSPdata(fdser,yAddress,opGetDSP,ToM3,_opGetDSP)) < 0) return(-1); if (yGetDSP == 0 || yGetDSP == 1) { if ((STR1V = GetDSPdata(fdser,yAddress,opGetDSP,ToM23,_opGetDSP)) < 0) return(-1); if ((STR1C = GetDSPdata(fdser,yAddress,opGetDSP,ToM25,_opGetDSP)) < 0) return(-1); } if (yGetDSP == 0 || yGetDSP == 2) { if ((STR2V = GetDSPdata(fdser,yAddress,opGetDSP,ToM26,_opGetDSP)) < 0) return(-1); if ((STR2C = GetDSPdata(fdser,yAddress,opGetDSP,ToM27,_opGetDSP)) < 0) return(-1); } if ((INVtemp = GetDSPdata(fdser,yAddress,opGetDSP,ToM21,_opGetDSP)) < 0) return(-1); if ((ENVtemp = GetDSPdata(fdser,yAddress,opGetDSP,ToM22,_opGetDSP)) < 0) return(-1); if (bCalcGridPwr) GPRC = GVR * GCR; if (STR1V >= 0.0 && STR1C >= 0.0) PVpwr = STR1V*STR1C; if (STR2V >= 0.0 && STR2C >= 0.0) PVpwr += STR2V*STR2C; if (PVpwr > 0) { INVeff = (GPR/PVpwr)*100; INVeffC = (GPRC/PVpwr)*100; } if (bColumns) { if (yGetDSP == 0 || yGetDSP == 1) fprintf(outfp, "%11.6f %11.6f %11.6f ",STR1V,STR1C,STR1V*STR1C); else fprintf(outfp, "%11s %11s %11s ","n/a","n/a","n/a"); if (yGetDSP == 0 || yGetDSP == 2) fprintf(outfp, "%11.6f %11.6f %11.6f ",STR2V,STR2C,STR2V*STR2C); else fprintf(outfp, "%11s %11s %11s ","n/a","n/a","n/a"); fprintf(outfp, "%11.6f %11.6f %11.6f %11.6f ",GVR,GCR,GPR,FRQ); if (yGetDSP == 0 || INVeff < 101.0) fprintf(outfp, "%11.6f ",INVeff); else fprintf(outfp, "%11s ","OverRange"); fprintf(outfp, "%11.6f %11.6f ",INVtemp,ENVtemp); bColOutput = TRUE; } else { if (yGetDSP == 0 || yGetDSP == 1) { fprintf(outfp, "\n%-26s = %11.6f V\n",_ToM23,STR1V); fprintf(outfp, "%-26s = %11.6f A\n",_ToM25,STR1C); fprintf(outfp, "%-26s = %11.6f W\n",_Str1P,STR1V*STR1C); } else { fprintf(outfp, "\n%-26s = %11s V\n",_ToM23,"n/a"); fprintf(outfp, "%-26s = %11s A\n",_ToM25,"n/a"); fprintf(outfp, "%-26s = %11s W\n",_Str1P,"n/a"); } if (yGetDSP == 0 || yGetDSP == 2) { fprintf(outfp, "\n%-26s = %11.6f V\n",_ToM26,STR2V); fprintf(outfp, "%-26s = %11.6f A\n",_ToM27,STR2C); fprintf(outfp, "%-26s = %11.6f W\n",_Str2P,STR2V*STR2C); } else { fprintf(outfp, "\n%-26s = %11s V\n",_ToM26,"n/a"); fprintf(outfp, "%-26s = %11s A\n",_ToM27,"n/a"); fprintf(outfp, "%-26s = %11s W\n",_Str2P,"n/a"); } fprintf(outfp, "\n%-26s = %11.6f V\n",_ToM1,GVR); fprintf(outfp, "%-26s = %11.6f A\n",_ToM2,GCR); fprintf(outfp, "%-26s = %11.6f W\n",_ToM3,GPR); if (bCalcGridPwr) fprintf(outfp, "%-26s = %11.6f W\n",_ToM3C,GPRC); fprintf(outfp, "%-26s = %11.6f Hz.\n",_ToM4,FRQ); if (yGetDSP == 0 || INVeff < 101.0) fprintf(outfp, "\n%-26s = %11.1f %s",_DcAcEff,INVeff,"%"); else fprintf(outfp, "\n%-26s = %11s ",_DcAcEff,"over range"); if (bCalcGridPwr) { if (yGetDSP == 0 || INVeffC < 101.0) fprintf(outfp, " (Using Grid Power Reading)\n%-26s = %11.1f %s (Using Grid Power Calculated)",_DcAcEff,INVeffC,"%"); else fprintf(outfp, " (Using Grid Power Reading)\n%-26s = %11s (Using Grid Power Calculated)",_DcAcEff,"over range"); } fprintf(outfp, "\n%-26s = %11.6f C\n",_ToM21,INVtemp); fprintf(outfp, "%-26s = %11.6f C\n",_ToM22,ENVtemp); } return(0); } void PrintBuffer() { int i; for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\n"); } /*-------------------------------------------------------------------------- GetDSPExtended ----------------------------------------------------------------------------*/ int GetDSPExtended(int fdser, int yAddress) { float VB,ILD,ILI,P1,P2,GV,GF,IR,VBD,AGV,VBM,PP,PPT,GVn,WGF,VBp,VBm,ST,AT,HT,T1,T2,T3,F1,F2,F3,F4,F5,PSL,RRB,VPm; char note[3]; if ((VB = GetDSPdata(fdser,yAddress,opGetDSP,ToM5,_opGetDSP)) < 0) return(-1); if ((ILD = GetDSPdata(fdser,yAddress,opGetDSP,ToM6,_opGetDSP)) < 0) return(-1); if ((ILI = GetDSPdata(fdser,yAddress,opGetDSP,ToM7,_opGetDSP)) < 0) return(-1); if ((P1 = GetDSPdata(fdser,yAddress,opGetDSP,ToM8,_opGetDSP)) < 0) return(-1); if ((P2 = GetDSPdata(fdser,yAddress,opGetDSP,ToM9,_opGetDSP)) < 0) return(-1); if ((GV = GetDSPdata(fdser,yAddress,opGetDSP,ToM28,_opGetDSP)) < 0) return(-1); if ((GF = GetDSPdata(fdser,yAddress,opGetDSP,ToM29,_opGetDSP)) < 0) return(-1); if ((IR = GetDSPdata(fdser,yAddress,opGetDSP,ToM30,_opGetDSP)) < 0) return(-1); if ((VBD = GetDSPdata(fdser,yAddress,opGetDSP,ToM31,_opGetDSP)) < 0) return(-1); if ((AGV = GetDSPdata(fdser,yAddress,opGetDSP,ToM32,_opGetDSP)) < 0) return(-1); if ((VBM = GetDSPdata(fdser,yAddress,opGetDSP,ToM33,_opGetDSP)) < 0) return(-1); if ((PP = GetDSPdata(fdser,yAddress,opGetDSP,ToM34,_opGetDSP)) < 0) return(-1); if ((PPT = GetDSPdata(fdser,yAddress,opGetDSP,ToM35,_opGetDSP)) < 0) return(-1); if ((GVn = GetDSPdata(fdser,yAddress,opGetDSP,ToM36,_opGetDSP)) < 0) return(-1); if ((WGF = GetDSPdata(fdser,yAddress,opGetDSP,ToM37,_opGetDSP)) < 0) return(-1); if ((VBp = GetDSPdata(fdser,yAddress,opGetDSP,ToM45,_opGetDSP)) < 0) return(-1); if ((VBm = GetDSPdata(fdser,yAddress,opGetDSP,ToM46,_opGetDSP)) < 0) return(-1); if ((ST = GetDSPdata(fdser,yAddress,opGetDSP,ToM47,_opGetDSP)) < 0) return(-1); if ((AT = GetDSPdata(fdser,yAddress,opGetDSP,ToM48,_opGetDSP)) < 0) return(-1); if ((HT = GetDSPdata(fdser,yAddress,opGetDSP,ToM49,_opGetDSP)) < 0) return(-1); if ((T1 = GetDSPdata(fdser,yAddress,opGetDSP,ToM50,_opGetDSP)) < 0) return(-1); if ((T2 = GetDSPdata(fdser,yAddress,opGetDSP,ToM51,_opGetDSP)) < 0) return(-1); if ((T3 = GetDSPdata(fdser,yAddress,opGetDSP,ToM52,_opGetDSP)) < 0) return(-1); if ((F1 = GetDSPdata(fdser,yAddress,opGetDSP,ToM52,_opGetDSP)) < 0) return(-1); if ((F2 = GetDSPdata(fdser,yAddress,opGetDSP,ToM54,_opGetDSP)) < 0) return(-1); if ((F3 = GetDSPdata(fdser,yAddress,opGetDSP,ToM55,_opGetDSP)) < 0) return(-1); if ((F4 = GetDSPdata(fdser,yAddress,opGetDSP,ToM56,_opGetDSP)) < 0) return(-1); if ((F5 = GetDSPdata(fdser,yAddress,opGetDSP,ToM57,_opGetDSP)) < 0) return(-1); if ((PSL = GetDSPdata(fdser,yAddress,opGetDSP,ToM58,_opGetDSP)) < 0) return(-1); if ((RRB = GetDSPdata(fdser,yAddress,opGetDSP,ToM59,_opGetDSP)) < 0) return(-1); if ((VPm = GetDSPdata(fdser,yAddress,opGetDSP,ToM60,_opGetDSP)) < 0) return(-1); note[0] = '\0'; if (bColumns) { if (AGV == VBD) strcpy(note,"*\0"); fprintf(outfp, "%11.6f %11.6f%s %11.6f%s %11.6f%s %11.6f %11.6f %11.6f %11.6f %11.6f %11.6f%s %11.6f%s %11.6f %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f %11.6f %11.6f%s %11.6f%s %11.6f%s %11.6f%s ",VB,VBM,note,VBp,note,VBm,note,VBD,ILD,ILI,IR,GV,AGV,note,GVn,note,GF,PP,note,PPT,note,ST,note,AT,note,HT,note,T1,note,T2,note,T3,note,F1,note,F2,note,F3,note,F4,note,F5,note,P1,P2,PSL,note,RRB,note,VPm,note,WGF,note); bColOutput = TRUE; } else { if (AGV == VBD) strcpy(note," *\0"); fprintf(outfp, "\nExtended DSP Reporting\n"); fprintf(outfp, "%-31s = %11.6f V\n",_ToM5,VB); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM33,VBM,note); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM45,VBp,note); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM46,VBm,note); fprintf(outfp, "%-31s = %11.6f V\n",_ToM31,VBD); fprintf(outfp, "\n%-31s = %11.6f A\n",_ToM6,ILD); fprintf(outfp, "%-31s = %11.6f A\n",_ToM7,ILI); fprintf(outfp, "%-31s = %11.6f Mohm\n",_ToM30,IR); fprintf(outfp, "\n%-31s = %11.6f V\n",_ToM28,GV); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM32,AGV,note); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM36,GVn,note); fprintf(outfp, "%-31s = %11.6f Hz\n",_ToM29,GF); fprintf(outfp, "\n%-31s = %11.6f W %s\n",_ToM34,PP,note); fprintf(outfp, "%-31s = %11.6f W %s\n",_ToM35,PPT,note); fprintf(outfp, "\n%-31s = %11.6f C %s\n",_ToM47,ST,note); fprintf(outfp, "%-31s = %11.6f C %s\n",_ToM48,AT,note); fprintf(outfp, "%-31s = %11.6f C %s\n",_ToM49,HT,note); fprintf(outfp, "%-31s = %11.6f C %s\n",_ToM50,T1,note); fprintf(outfp, "%-31s = %11.6f C %s\n",_ToM51,T2,note); fprintf(outfp, "%-31s = %11.6f C %s\n",_ToM52,T3,note); fprintf(outfp, "\n%-31s = %11.6f RPM%s\n",_ToM53,F1,note); fprintf(outfp, "%-31s = %11.6f RPM%s\n",_ToM54,F2,note); fprintf(outfp, "%-31s = %11.6f RPM%s\n",_ToM55,F3,note); fprintf(outfp, "%-31s = %11.6f RPM%s\n",_ToM56,F4,note); fprintf(outfp, "%-31s = %11.6f RPM%s\n",_ToM57,F5,note); fprintf(outfp, "\n%-31s = %11.6f W\n",_ToM8,P1); fprintf(outfp, "%-31s = %11.6f W\n",_ToM9,P2); fprintf(outfp, "%-31s = %11.6f W %s\n",_ToM58,PSL,note); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM59,RRB,note); fprintf(outfp, "%-31s = %11.6f V %s\n",_ToM60,VPm,note); fprintf(outfp, "%-31s = %11.6f Hz %s\n",_ToM37,WGF,note); if (AGV == VBD) fprintf(outfp, "(Note: * = May not be in this Inverter's firmware)\n"); } return(0); } /*-------------------------------------------------------------------------- GetDSP3Phase ----------------------------------------------------------------------------*/ int GetDSP3Phase(int fdser, int yAddress) { float VBD,GVPn,GVPr,GVPs,GVPt,GCPr,GCPs,GCPt,FRQPr,FRQPs,FRQPt; char note[3]; if ((VBD = GetDSPdata(fdser,yAddress,opGetDSP,ToM31,_opGetDSP)) < 0) return(-1); if ((GVPn = GetDSPdata(fdser,yAddress,opGetDSP,ToM38,_opGetDSP)) < 0) return(-1); if ((GVPr = GetDSPdata(fdser,yAddress,opGetDSP,ToM61,_opGetDSP)) < 0) return(-1); if ((GVPs = GetDSPdata(fdser,yAddress,opGetDSP,ToM62,_opGetDSP)) < 0) return(-1); if ((GVPt = GetDSPdata(fdser,yAddress,opGetDSP,ToM63,_opGetDSP)) < 0) return(-1); if ((GCPr = GetDSPdata(fdser,yAddress,opGetDSP,ToM39,_opGetDSP)) < 0) return(-1); if ((GCPs = GetDSPdata(fdser,yAddress,opGetDSP,ToM40,_opGetDSP)) < 0) return(-1); if ((GCPt = GetDSPdata(fdser,yAddress,opGetDSP,ToM41,_opGetDSP)) < 0) return(-1); if ((FRQPr = GetDSPdata(fdser,yAddress,opGetDSP,ToM42,_opGetDSP)) < 0) return(-1); if ((FRQPs = GetDSPdata(fdser,yAddress,opGetDSP,ToM43,_opGetDSP)) < 0) return(-1); if ((FRQPt = GetDSPdata(fdser,yAddress,opGetDSP,ToM44,_opGetDSP)) < 0) return(-1); note[0] = '\0'; if (bColumns) { if (GVPn == VBD) strcpy(note,"*\0"); fprintf(outfp, "%11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s %11.6f%s ",GVPn,note,GVPr,note,GVPs,note,GVPt,note,GCPr,note,GCPs,note,GCPt,note,FRQPr,note,FRQPs,note,FRQPt,note); bColOutput = TRUE; } else { if (GVPn == VBD) strcpy(note," *\0"); fprintf(outfp, "\n3-Phase DSP Reporting\n"); fprintf(outfp, "%-26s = %11.6f V %s\n",_ToM38,GVPn,note); fprintf(outfp, "%-26s = %11.6f V %s\n",_ToM61,GVPr,note); fprintf(outfp, "%-26s = %11.6f V %s\n",_ToM62,GVPs,note); fprintf(outfp, "%-26s = %11.6f V %s\n",_ToM63,GVPt,note); fprintf(outfp, "\n%-26s = %11.6f A %s\n",_ToM39,GCPr,note); fprintf(outfp, "%-26s = %11.6f A %s\n",_ToM40,GCPs,note); fprintf(outfp, "%-26s = %11.6f A %s\n",_ToM41,GCPt,note); fprintf(outfp, "\n%-26s = %11.6f Hz%s\n",_ToM42,FRQPr,note); fprintf(outfp, "%-26s = %11.6f Hz%s\n",_ToM43,FRQPs,note); fprintf(outfp, "%-26s = %11.6f Hz%s\n",_ToM44,FRQPt,note); if (GVPn == VBD) fprintf(outfp, "(Note: * = May not be in this Inverter's firmware)\n"); } return(0); } /*-------------------------------------------------------------------------- GetDSPdata ----------------------------------------------------------------------------*/ float GetDSPdata(int fdser, int yAddress, int opcode, int param, char desription[]) { int nCnt; float *paramValFloat; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opcode; /* set Measure request to the DSP opcode */ szSerBuffer[cParam1] = param; szSerBuffer[cCommandEnd+1] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],desription)) { paramValFloat = szCvrtFloat(szSerBuffer); if (bVerbose) fprintf(stderr, "value %12.6f\n",*paramValFloat); } else return(-1); return(*paramValFloat); } /*-------------------------------------------------------------------------- GetJoules ----------------------------------------------------------------------------*/ int GetJoules(int fdser, int yAddress) { int nCnt; unsigned long Joules; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetEnergy10Sec; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetEnergy10Sec)) { Joules = szCvrtShort(szSerBuffer); if (bVerbose) fprintf(stderr, "Joules %12lu\n",(unsigned long)Joules); fprintf(outfp, "\nEnergy in the last 10 seconds (Joules) : %lu\n",(unsigned long)Joules); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetInvTime ----------------------------------------------------------------------------*/ time_t GetInvTime(int fdser, int yAddress) { int nCnt = 0; time_t timeValLong = 0; long int gmtoff = 0; long int gmtoffTB = 0; struct tm tim; char curTime[24]; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetTime; szSerBuffer[cCommandEnd] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0) { if (! TransState((int)szSerBuffer[aState],_opGetTime)) return(-1); timeValLong = time(NULL); tim = *(localtime(&timeValLong)); gmtoff = tim.tm_gmtoff; timeValLong = (time_t)TimeBase; tim = *(localtime(&timeValLong)); gmtoffTB = tim.tm_gmtoff; if (bVerbose) { timeValLong = (time_t)TimeBase - gmtoffTB; fprintf(stderr, "\nTimeBase %12lu\n",(time_t)TimeBase); fprintf(stderr, "gmtoffTB %12li\n",gmtoffTB); tim = *(localtime(&timeValLong)); strftime(curTime,24,"%d-%b-%Y %H:%M:%S\n",&tim); fprintf(stderr, "Base Inverter date/time: %s\n",curTime); } timeValLong = (time_t)szCvrtLong(szSerBuffer) & 0xffffffff; if (bVerbose) { fprintf(stderr, "timeValLong %12lu\n",(time_t)timeValLong); fprintf(stderr, "gmtoff %12li\n",gmtoff); } timeValLong += (time_t)TimeBase; timeValLong -= gmtoff; if (bVerbose) fprintf(stderr, "timeValLong %12lu\n",(time_t)timeValLong); } else if (bVerbose) fprintf(stderr, "GetInvTime no response nCnt %i\n",nCnt); return(timeValLong); } /*-------------------------------------------------------------------------- GetTime ----------------------------------------------------------------------------*/ int GetTime(int fdser, int yAddress) { time_t timeValLong; struct tm tim; char curTime[24]; char fromInv[10] = "Inverter \0"; if (bGetInvTime) { timeValLong = GetInvTime(fdser, yAddress); if (bVerbose) fprintf(stderr, "GetTime: timeValLong %12lu\n",(time_t)timeValLong); } if (bGetLocTime) { timeValLong = time(NULL); fromInv[0] = '\0'; } if (timeValLong != 0 || bGetLocTime) { tim = *(localtime(&timeValLong)); if (!bColumns || (yGetDSP < 0 && !bGetEnergy)) { fprintf(outfp, "\nCurrent %sdate/time: ",fromInv); strftime(curTime,24,"%d-%b-%Y %H:%M:%S\n",&tim); } else { strftime(curTime,24,"%Y%m%d-%H:%M:%S ",&tim); bColOutput = TRUE; } fprintf(outfp, "%s",curTime); return(0); } return(-1); } /*-------------------------------------------------------------------------- CheckSetTime Check if the time on the inverter differs fro the computers time and modify it as requested ----------------------------------------------------------------------------*/ int CheckSetTime(int fdser, int yAddress) { time_t timeValLongInv; time_t timeValLongCur; time_t timeValLongMN; struct tm TS; long int gmtoffCur = 0; long int gmtoffMN = 0; char DateTime[24]; BOOL ChangeTime = FALSE; int rc = 0; timeValLongInv = GetInvTime(fdser, yAddress); timeValLongCur = timeValLongMN = time(NULL); TS = *(localtime(&timeValLongCur)); gmtoffCur = TS.tm_gmtoff; if (bVerbose) { fprintf(stderr, "\ntimeValLongCur %lu timeValLongInv %li diff %li\n",timeValLongCur,timeValLongInv,timeValLongInv-timeValLongCur); strftime(DateTime,24,"%d-%b-%Y %H:%M:%S",&TS); fprintf(stderr, "Current Time: %s gmtoff: %li\n",DateTime,gmtoffCur); TS = *(localtime(&timeValLongInv)); strftime(DateTime,24,"%d-%b-%Y %H:%M:%S",&TS); fprintf(stderr, "Inverter Time: %s gmtoff: %li",DateTime,TS.tm_gmtoff); } if (yCheckSetTime == 0) { TS = *(localtime(&timeValLongMN)); TS.tm_sec = TS.tm_min = TS.tm_hour = 0; timeValLongMN = mktime(&TS); TS = *(localtime(&timeValLongMN)); timeValLongMN += (gmtoffCur - TS.tm_gmtoff); TS = *(localtime(&timeValLongMN)); gmtoffMN = TS.tm_gmtoff; if (gmtoffCur != gmtoffMN && abs(timeValLongCur-timeValLongInv) >= 900) ChangeTime = TRUE; if (bVerbose) { strftime(DateTime,24,"%d-%b-%Y %H:%M:%S",&TS); fprintf(stderr, "\nYesterday: %s gmtoff: %li",DateTime,gmtoffMN); } } else if (abs(timeValLongInv-timeValLongCur) >= yCheckSetTime) ChangeTime = TRUE; if (bVerbose) fprintf(stderr, " ChangeTime: %s\n",ChangeTime ? "TRUE" : "FALSE"); if (ChangeTime) rc = SetTime(fdser,yAddress,TRUE); return(rc); } /*-------------------------------------------------------------------------- SetTime ----------------------------------------------------------------------------*/ int SetTime(int fdser, int yAddress, BOOL force) { int nCnt = 0; int rc; time_t timeValLong; struct tm tim; long int gmtoff = 0; char curTime[24]; char answer; if (bVerbose && force) fprintf(stderr, "Force setting time\n"); if (! force) { printf("\n**** WARNING ***** ***** WARNING ***** ***** WARNING ****\n"); printf("Setting the Date and Time has been known to clear all History\n"); printf(" Except \"Total Energy\" but no guarantees that it won't\n"); printf(" (Not enabled on all models.)\n"); printf("\nAre you sure your want to proceed? y/[n] : "); rc = scanf("%c", &answer); if (rc == EOF || (answer != 'y' && answer != 'Y')) return(1); } timeValLong = time(NULL); tim = *(localtime(&timeValLong)); gmtoff = tim.tm_gmtoff; if (bVerbose) { fprintf(stderr, "\ntimeValLong %12lu\n",(time_t)timeValLong); strftime(curTime,24,"%d-%b-%Y %H:%M:%S",&tim); fprintf(stderr, "setting time to %s DST %i\n",curTime,tim.tm_isdst); } /* adjust time to Aurora's time base */ timeValLong -= (long)TimeBase; timeValLong += gmtoff; if (bVerbose) fprintf(stderr, "timeValLong %12lu\n",(time_t)timeValLong); strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opSetTime; szSerBuffer[cParam1] = (timeValLong >> 24) & 0xff; szSerBuffer[cParam2] = (timeValLong >> 16) & 0xff; szSerBuffer[cParam3] = (timeValLong >> 8) & 0xff; szSerBuffer[cParam4] = timeValLong & 0xff; szSerBuffer[cCommandEnd+4] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0) { if (! TransState((int)szSerBuffer[aState],_opSetTime)) return(-1); printf("\nInverter date/time set."); return(0); } return(-1); } /*-------------------------------------------------------------------------- GetCounters ----------------------------------------------------------------------------*/ int GetCounters(int fdser, int yAddress) { char TRT[18], PRT[18], TGC[18], RPC[18]; if (GetCountersData(fdser, yAddress, cTotalRun, TRT) < 0) return(-1); if (GetCountersData(fdser, yAddress, cPartialRun, PRT) < 0) return(-1); if (GetCountersData(fdser, yAddress, cTotalGrid, TGC) < 0) return(-1); if (GetCountersData(fdser, yAddress, cResetPartial, RPC) < 0) return(-1); fprintf(outfp, "\n%-34s %18s\n","","yyy ddd hh:mm:ss"); fprintf(outfp, "%-34s : %18s\n",_cTotalRun,TRT); fprintf(outfp, "%-34s : %18s\n",_cPartialRun,PRT); fprintf(outfp, "%-34s : %18s\n",_cTotalGrid,TGC); fprintf(outfp, "%-34s : %18s\n",_cResetPartial,RPC); return(0); } /*-------------------------------------------------------------------------- GetCountersData ----------------------------------------------------------------------------*/ int GetCountersData(int fdser, int yAddress, int param, char *uptime) { int nCnt; unsigned long paramValPtr, paramValLong; int years, days, hours, minutes, seconds; strcpy(szSerBuffer, _clearCMD); /* clear Aurora cmd string */ szSerBuffer[cAddress] = yAddress; /* set inverter address */ szSerBuffer[cCommand] = opGetCounters; /* set Measure request to the DSP opcode */ szSerBuffer[cParam1] = param; szSerBuffer[cCommandEnd+1] = '\0'; nCnt = Communicate(fdser, yAddress); if (nCnt > 0 && TransState((int)szSerBuffer[aState],_opGetCounters)) { paramValPtr = szCvrtLong(szSerBuffer); if (bVerbose) fprintf(stderr, "value %12lu\n",(unsigned long)paramValPtr); } else return(-1); paramValLong = (unsigned long)paramValPtr; if (bVerbose) fprintf(stderr, "paramValLong %12lu\n",(unsigned long)paramValLong); years = paramValLong / SecsInYear; paramValLong -= (years * SecsInYear); if (bVerbose) fprintf(stderr, "paramValLong %12li\tyears %3i\n",(unsigned long)paramValLong,years); days = paramValLong / SecsInDay; paramValLong -= (days * SecsInDay); if (bVerbose) fprintf(stderr, "paramValLong %12li\tdays %3i\n",(unsigned long)paramValLong,days); hours = paramValLong / SecsInHour; paramValLong -= (hours * SecsInHour); if (bVerbose) fprintf(stderr, "paramValLong %12li\thours %3i\n",(unsigned long)paramValLong,hours); minutes = paramValLong / SecsInMinute; paramValLong -= (minutes * SecsInMinute); if (bVerbose) fprintf(stderr, "paramValLong %12li\tminutes %3i\n",(unsigned long)paramValLong,minutes); seconds = paramValLong; if (bVerbose) fprintf(stderr, "paramValLong %12li\tseconds %3i\n",(unsigned long)paramValLong,seconds); sprintf(uptime,"%3d %3d %02d:%02d:%02d",years,days,hours,minutes,seconds); if (bVerbose) fprintf(stderr, "uptime: %s\n",uptime); return(0); } /*-------------------------------------------------------------------------- Communicate ----------------------------------------------------------------------------*/ int Communicate(int fdser, int yAddress) { int i = 0; int rc, nCnt; char ch; char szSerBufferSave[_szSerBufferLen]; int CRCrc = -1; int attempts = 1; struct timeval curtv; long long int curtvusecs = 0, lastcommtvusecs = 0, elapsedtvusecs = 0; strcpy(szSerBufferSave, _clearCMD); memcpy(szSerBufferSave, szSerBuffer,_szSerBufferLen); gettimeofday(&curtv, NULL); while(CRCrc < 0 && attempts <= yMaxAttempts) { if (lastcommtv.tv_sec == 0 || lastcommtv.tv_usec == 0) { lastcommtv.tv_sec = curtv.tv_sec; lastcommtv.tv_usec = curtv.tv_usec; } else { curtvusecs = (curtv.tv_sec*1000000) + curtv.tv_usec; lastcommtvusecs = (lastcommtv.tv_sec*1000000) + lastcommtv.tv_usec; elapsedtvusecs = curtvusecs - lastcommtvusecs; } memcpy(szSerBuffer, szSerBufferSave,_szSerBufferLen); if (bVerbose) fprintf(stderr, "\nElapsed time since last comm %llu us\nAttempt %i",elapsedtvusecs,attempts); if (yCommPause > 0 && elapsedtvusecs < yCommPause) { if (bVerbose) fprintf(stderr, " Sleeping for %llu us",yCommPause-elapsedtvusecs); usleep(yCommPause-elapsedtvusecs); } if (bVerbose) fprintf(stderr, "\nClearing read buffer "); while((rc = ReadNextChar(fdser, &ch, 0)) && i < _clrAttemps) { if (bVerbose) fprintf(stderr, ":ch=%i ",ch); /* clear channel and delay */ i++; } if (bVerbose) { fprintf(stderr, ":ch=%i ",ch); if (rc != 0 && i >= _clrAttemps) fprintf(stderr, "max attempts reached (%i)\n",i); else fprintf(stderr, "Success!\n"); } if (bVerbose) { fprintf(stderr, "szSerBufferSave "); if (strcmp(szSerBuffer,szSerBufferSave) == 0) fprintf(stderr, "OK! "); else fprintf(stderr, "ERROR! "); for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBufferSave[i]); } fprintf(stderr, "\n"); } crcValue = crc16(szSerBuffer, 8); szSerBuffer[cCRC_L] = LOBYTE(crcValue); szSerBuffer[cCRC_H] = HIBYTE(crcValue); szSerBuffer[cEND] = '\0'; if (bVerbose) { fprintf(stderr, "command: "); for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\nFlushing serial device buffer... "); } errno = 0; if (tcflush(fdser,TCIOFLUSH)) fprintf(stderr, "Problem flushing before sending command: (%i) %s\n",errno,strerror (errno)); else if (bVerbose) fprintf(stderr, "Success!\nSending command... "); nCnt = write(fdser, &szSerBuffer, cSIZE); /* send it */ if (bVerbose) fprintf(stderr, "sent %d characters\nDraining serial device buffer... ", nCnt); errno = 0; if (tcdrain(fdser)) fprintf(stderr, "Problem draining command: (%i) %s\n",errno,strerror (errno)); else if (bVerbose) fprintf(stderr, "Success!\n"); if (szSerBuffer[cCommand] == opSetTime) Delay(1, 0L); strcpy(szSerBuffer, _clearCMD); if (bVerbose) { fprintf(stderr, "Cleared data buffer: "); for (i = 0; i < cSIZE; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\n"); } if (yReadPause > 0 ) { if (bVerbose) fprintf(stderr, "Waiting %d milli-seconds before reading inverter response\n",yReadPause); else if (bRptReadPause) fprintf(stderr, "\n%s: %s: Waiting %d milli-seconds before reading inverter response",RunTime,ProgramName,yReadPause); usleep(yReadPause*1000); } tcflush(fdser,TCIFLUSH); nCnt = ReadToBuffer(fdser, szSerBuffer, aSIZE); if (bVerbose) { fprintf(stderr, "answer: "); if (nCnt > 0) { for (i = 0; i < nCnt; i++) { fprintf(stderr, "%02x ",(unsigned char)szSerBuffer[i]); } fprintf(stderr, "\nreceived %d characters\n", nCnt); } else fprintf(stderr, "Got %d characters\n", nCnt); } if (nCnt > 0) { crcValue = crc16(szSerBuffer, 6); if ((unsigned char)szSerBuffer[aCRC_L] != LOBYTE(crcValue) || (unsigned char)szSerBuffer[aCRC_H] != HIBYTE(crcValue)) { if (yMaxAttempts == 1 || attempts == yMaxAttempts) if (!bCommCheck) { if (! bVerbose && bRptReadPause) fprintf(stderr, "\n"); fprintf(stderr, "%s: CRC receive error (%i attempts made) %04x %02x %02x\n",RunTime,attempts,crcValue,(unsigned char)szSerBuffer[aCRC_H],(unsigned char)szSerBuffer[aCRC_L]); } } else { if (bVerbose) fprintf(stderr, "CRC receive OK %04x\n",crcValue); CRCrc = 0; } } gettimeofday(&curtv, NULL); lastcommtv.tv_sec = curtv.tv_sec; lastcommtv.tv_usec = curtv.tv_usec; attempts++; } if (CRCrc < 0) return(-1); if (bRptReties) { fprintf(stderr, "\n%s: %s: %i attempts made",RunTime,ProgramName,attempts-1); if (bVerbose) fprintf(stderr, "\n"); } else if (bRptReadPause) fprintf(stderr, "\n"); return(nCnt); } /*-------------------------------------------------------------------------- szCvrtFloat Converts a 4 char string to a float. ----------------------------------------------------------------------------*/ float *szCvrtFloat(char *Buffer) { unsigned char cValue[4]; float *value; cValue[0] = Buffer[aParam4]; cValue[1] = Buffer[aParam3]; cValue[2] = Buffer[aParam2]; cValue[3] = Buffer[aParam1]; value = (float *)cValue; if (bVerbose) fprintf(stderr, "szCvrtFloat %12.6f 0x%02x%02x%02x%02x\n",*value,cValue[3],cValue[2],cValue[1],cValue[0]); return(value); } /*-------------------------------------------------------------------------- szCvrtShort Converts a 2 char string to a long. ----------------------------------------------------------------------------*/ unsigned long szCvrtShort(char *Buffer) { unsigned char cValue[4]; unsigned long *value; cValue[0] = Buffer[aParam2] & 0xff; cValue[1] = Buffer[aParam1] & 0xff; cValue[2] = 0x00; cValue[3] = 0x00; value = (unsigned long *)cValue; if (bVerbose) fprintf(stderr, "szCvrtShort %12lu 0x%02x%02x%02x%02x\n",*value & 0xffff,cValue[3],cValue[2],cValue[1],cValue[0]); return(*value & 0xffff); } /*-------------------------------------------------------------------------- szCvrtLong Converts a 4 char string to a long. ----------------------------------------------------------------------------*/ unsigned long szCvrtLong(char *Buffer) { unsigned char cValue[4]; unsigned long *value = 0; cValue[0] = Buffer[aParam4] & 0xff; cValue[1] = Buffer[aParam3] & 0xff; cValue[2] = Buffer[aParam2] & 0xff; cValue[3] = Buffer[aParam1] & 0xff; value = (unsigned long *)cValue; if (bVerbose) fprintf(stderr, "szCvrtLong %12lu 0x%02x%02x%02x%02x\n",*value & 0xffffffff,cValue[3],cValue[2],cValue[1],cValue[0]); return(*value & 0xffffffff); } /*-------------------------------------------------------------------------- cvrtLong Converts a 4 char string to a long. ----------------------------------------------------------------------------*/ unsigned long cvrtLong(char *Buffer) { unsigned char cValue[4]; unsigned long *value = 0; cValue[0] = Buffer[3] & 0xff; cValue[1] = Buffer[2] & 0xff; cValue[2] = Buffer[1] & 0xff; cValue[3] = Buffer[0] & 0xff; value = (unsigned long *)cValue; if (bVerbose) fprintf(stderr, "cvrtLong %12lu 0x%02x%02x%02x%02x\n",*value & 0xffffffff,cValue[3],cValue[2],cValue[1],cValue[0]); return(*value & 0xffffffff); } /*-------------------------------------------------------------------------- FindString Reads command line parameters. ----------------------------------------------------------------------------*/ char* FindString(WORD wRule, char *ptr) { while(wRule--) { /* walk thru the null terminators */ while(*ptr++) ; } return ptr; } /*-------------------------------------------------------------------------- ReadNextChar Reads the next character from the serial device. Returns zero if no character was available. ----------------------------------------------------------------------------*/ int ReadNextChar(int nfd, char *pChar, int timeout) { int nResult = -1; long int eUsecs, sSecs, cSecs, sUsecs, cUsecs; struct timeval tv; errno = 0; sUsecs = cUsecs = 0; eUsecs = sSecs = cSecs = sUsecs = cUsecs = 0; memset (pChar, 0, sizeof (*pChar)); if (timeout > 0) { eUsecs = 0; gettimeofday(&tv, NULL); sSecs = tv.tv_sec; sUsecs = tv.tv_usec; while (nResult <= 0 && errno == 0 && eUsecs <= timeout) { nResult = read(nfd, pChar, 1); gettimeofday(&tv, NULL); cSecs = tv.tv_sec; cUsecs = tv.tv_usec; eUsecs = ((cSecs-sSecs)*1000000) + (cUsecs-sUsecs); } } else nResult = read(nfd, pChar, 1); if (errno != 0) fprintf (stderr, "\naurora: (TO) Problem reading serial device, (nResult %i) (errno %i) %s.\n",nResult,errno,strerror (errno)); if (nResult == -1) { if (errno == 0) perror("\naurora: (TO) Problem reading serial device. \n"); fprintf (stderr, "\n"); RestorePort(nfd); ClrSerLock(PID); fprintf (stderr, "\n"); exit(2); } if (bVerbose) fprintf(stderr, "RC=%i (%02x)",nResult,(unsigned char)*pChar); if (bVerbose && timeout > 0 && (cUsecs-sUsecs) > 0) fprintf(stderr, " Read waited %i us.",(int)(cUsecs-sUsecs)); return nResult; } /*-------------------------------------------------------------------------- ReadToBuffer Reads data to a buffer until no more characters are available. If the buffer overflows, returns -1. Otherwise, returns the number of characters read. ----------------------------------------------------------------------------*/ int ReadToBuffer(int nfd, char *pszBuffer, int nBufSize) { int nPos = 0; /* current character position */ int attempts = 0; char *pBuf = pszBuffer; int rc = 0; int sanity = 0; while(attempts < nBufSize && sanity < 100) { if (bVerbose) fprintf(stderr, "read attempts %i timeout %ius ",attempts+1,yTimeout); rc = ReadNextChar(nfd, pBuf++, yTimeout); if (rc < 0) return nPos; /* no character available */ if (rc > 0) ++nPos; ++attempts; sanity++; if (bVerbose) fprintf(stderr, "\n"); } if (rc < 0) return -1; /* problem */ return nPos; } /*-------------------------------------------------------------------------- crc16 16 12 5 this is the CCITT CRC 16 polynomial X + X + X + 1. This is 0x1021 when x is 2, but the way the algorithm works we use 0x8408 (the reverse of the bit pattern). The high bit is always assumed to be set, thus we only use 16 bits to represent the 17 bit value. ----------------------------------------------------------------------------*/ #define POLY 0x8408 /* 1021H bit reversed */ WORD crc16(char *data_p, unsigned short length) { unsigned char i; unsigned int data; unsigned int crc = 0xffff; if (length == 0) return (~crc); do { for (i=0, data=(unsigned int)0xff & *data_p++; i < 8; i++, data >>= 1) { if ((crc & 0x0001) ^ (data & 0x0001)) crc = (crc >> 1) ^ POLY; else crc >>= 1; } } while (--length); crc = ~crc; return (crc); } /*-------------------------------------------------------------------------- Delay Delays by the number of seconds and microseconds. ----------------------------------------------------------------------------*/ void Delay(int secs, long microsecs) { static struct timeval t1; t1.tv_sec = (long)secs; t1.tv_usec = microsecs; if ( select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t1) < 0 ) perror("Internal error: error in select()"); return; } aurora-1.7.3/docs/0000755000000000000000000000000011347023067012444 5ustar rootrootaurora-1.7.3/docs/man/0000755000000000000000000000000011656610725013225 5ustar rootrootaurora-1.7.3/docs/man/aurora.10000644000000000000000000001307611656610725014607 0ustar rootroot.TH aurora 1 "13 Mar 2010" aurora "aurora" .SH NAME \fBaurora\fP \- communicate with an Magnetek Aurora Photovoltaic (solar) Power Inverter .SH SYNOPSIS .BI "aurora [Options] -a InverterAddress Device" .SH "REQUIRED PARAMETERS" .TP .B -a, --address=num Inverter address. 1-31 on older inverters, 1-63 on newer inverters. .TP .B Device Serial Device. Example, \fI/dev/ttyUSB0\fP or \fI/dev/ttyS0\fP. .SH OPTIONS .TP .B -A, --last-alarms Get last four alarms (once displayed FIFO queue is cleared) .TP .B -b, --verbose Verbose mode. For maximum effectiveness should be the first option on the command line .TP .B -C, --calc-value= Calculate monetary value using * kWh. ":$" is optional and if included the character(s) represented by the "$" will be used to denote monetary type in the output. Defaults to "$" .TP .B -c, --columnize Output data in columns --> for -d, -e, -D, -E, -t options only, will disable all other options -- if value ends with an "*" reporting of that item may not be in inverters firmware .TP .B -d , --get-dsp= Get DSP data. indicates string to get data for. 0 indicates both, 1 for only string 1, 2 for only string 2. is required for short option and is optional for long option and if omitted for long option then data for both input strings will be retrieved. .TP .B -D, --get-dsp-extended Get more DSP data .TP .B -e, --get-energy Get Cumulated Energy readings .TP .B -E, --get-dsp-3phase Get 3-Phase DSP data .TP .B -f, --firmware-ver Query for Firmware Version string .TP .B -g, --mfg-date Query for Inverter Manufacturing Date .TP .B -h, --help This text .TP .B -i, --get-count Display Inverter Time Counters .TP .B -j, --get-joules Display Energy accumulated in the last 10 seconds .TP .B -k, --daily-kwh= ** Experimental ** Get past daily KWH for days (1-366) is optional for long option and if omitted all 366 days or as many that are found will be displayed. It is suggested that the -Y option be used with this due to the extensive length of time it takes to get all the data from the inverter .TP .B -L, --adjust-time= Automatically adjust the inverter time if it differs from the computer time. If is 0 (zero) do a Daylight Savings Time check. If is >= 1 change the inverter time if it differs by or more seconds. See the README for more information and the constraints this option uses to the determine if the time should be changed due to Daylight Savings Time .TP .B -l, --delay= Serial port timeout in for read. in 1/10ths seconds. Default is 1 (0.1 sec) .TP .B -m, --get-conf Query for Inverter System Configuration .TP .B -n, --serial-number Query for Inverter Serial Number .TP .B -o, --output-file= Append data to file (Created if nonexistant) .TP .B -p, --part-number Query for Inverter Part Number .TP .B -P, --comm-pause= Wait uS between sending commands to inverter (1-1000000) .TP .B -q , --energy-sent= ** Experimental ** Get past energy delivered to the grid in 10 second intervals for minutes (1-1440) starting with the oldest data available. is optional for long option and if omitted all data, ~24 hours worth will be reported. It is suggested that the -Y option be used with this due to the extensive length of time it takes to get all the data from the inverter. See the README file for important information on this option .TP .B -R, --read-timeout= Timeout value when reading data from the Inverter (mS) .TP .B -r, --calc-grid-power Calc Grid power using Grid Voltage * Grid Current, instead of reporting the Inverter's value. --> for -d option only, ignored when used with -c option. (Inverter typically reports a lower value. This affects Inverter conversion efficiency value.) .TP .B -S, --set-time Set Inverter Date/Time to system time .TP .B -s, --get-state Get Inverter State .TP .B -T, --get-loctime Display computer Date/Time .TP .B -t, --get-invtime Display Inverter Date/Time .TP .B -U, --read-pause= Pause milli-seconds after sending command to inverter before reading response from inverter (1-10000) .TP .B -u, --rpt-read-pause Report when/that pausing before read .TP .B -V, --version Aurora communications program version .TP .B -v, --inv-version Query for Version string .TP .B -w, --lock-wait Seconds to wait to lock serial port. (1-30) .TP .B -X, --rts-cts Enable RTS/CTS on the serial port. .TP .B -x, --xon-xoff Enable XON/XOFF on the serial port. .TP .B -Y, --retries= Retry failed communications with inverter up to times (1-100) .TP .B -y, --rpt-retries Report the number of retires done .SH DESCRIPTION \fBaurora\fP is a program that communicates with Aurora Magnetek Photovoltaic (solar) Power Inverters written by Curt Blank. It can retrieve data and statistics from a variety of Aurora Inverters through either USB or Serial interfaces. .SH EXAMPLES aurora -T -c -e -d -a 2 -w 15 /dev/ttyS0 aurora -Y 100 -T -a 2 -w 10 -e /dev/ttyUSB0 .SH "SEE ALSO" .TP \fIhttp://www.curtronics.com/Solar/AuroraData.html\fP .PD .SH AUTHOR This manpage was written by Dustin Kirkland and updated by Curt Blank for Ubuntu systems (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 2 published by the Free Software Foundation. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL. aurora-1.7.3/INSTALL0000644000076400017510000000302511620554614011673 0ustar curt Starting with version 1.6.9 the 'make' tries to determine the optimum compile method. Most of the time you can simply untar and do a 'make' and you're good to go on AMD and Intel platforms. However on other platforms such as ARM processors you may have to manually tweak the Makefile to optimize the compile. Then doing 'make install' will put the binary in /usr/local/bin/ and the manpage in /usr/local/share/man/man1/ (Thank you Dustin Kirkland for initial creation of the manpage.) If you get any errors from the 'make' please provide the output of the 'make' including the command line so that the process can be improved. You can do a 'make check' to determine how the compile will be done. It will report 64bit verses 32bit and the 'arch' and 'tune' flags used for the processor. If it reports 'NoNative' the 'arch' and 'tune' flags will have to be manually set if you chose to do so for optimization. In that case you may have to uncomment and set/adjust the "ARCH =" and "TUNE =" variables at he beginning of the Makefile and set them to the proper values for your processor. Manually setting these variable will override any automatic determination of them. You can then do a 'make check' to see if the values you set will be used. If you set the values to 'native' and you still see NoNative that can be due to gcc not supporting 'native' due to its version or the architecture. You can access the documentation via 'aurora -h' or the manpage, or for more in depth documentation, read the code. [Content moved to README >= v1.6.5] aurora-1.7.3/RELEASENOTES0000644000076400017510000001166411657113540012465 0ustar curt(If anyone's wondering why there were two back to back releases it's because of Daylight Savings Time and now aurora will handle that because the Inverter doesn't.) v1.7.3 - changed permissions on the aurora executable to 4711 for none root users to be able to run aurora. Had it set to 4755 orginally but pulled it back to 4750, needs to be at least 4711. - added -L --adjust-time option. See the README for information regarding this option v1.7.2 - added -q --energy-sent option, see the README for important information regarding this option - remove size restriction on monetary identifier of the -C option - changed permissions on the aurora executable to 4750. This allows users other then root to run aurora and not fail due to inaccessibility of the lock file when reading/writing it - remove size restriction on various strings - checked the TimeBase now that we're back on CST and it checks out correctly. Timezone calculations work. Can remove it from the TODO list v1.7.1 - modify "-d --get-dsp" option. **NOTE** if using "-d" it must be changed to "-d 0", if using "--get-dsp" no change is needed - modify "-C, --calc-value=" added "[:$]" to allow monetary type to be chosen v1.7.0 - clean up some compiler warnings for variable set but not used v1.6.9 - Starting with version 1.6.9 the 'make' tries to determine the gcc 'march' & 'mtune' compile flags for optimization - corrected joule reading, output conversion error - Makefile modifications no longer have to modify the 64bit flag, may not have to modify "arch" & "tune" parameters if gcc >= 4.2 on AMD & Intel platforms - fixed a integer conversion issue for -k due to some architectures - fixed bizarreness with -bt - adjust TimeBase to true UTC-0000 and take into account timezone in GetTime (-t) and SetTime (-S) functions - added 'make check' that reports what the compile will be for architecture wise - tweaked Makefile to determine "arch" & "tune" using gcc if possible - tweaking Makefile for ARM processors - tweaked Makefile again for 'make' versions older then 3.82 which don't have the 'undefine' directive - increase the allowed values from 31 to 63 for inverter address (-a) as allowed in newer inverters v1.6.8 - added -k option to read past days production - this is considered experimental and may or may not work in all instances and especially if there is no data for all the days requested - please report any problems/anomalies encountered - removed -fpack-struct from the compiler CFLAGS - once again caused longopts not to work - added -X option to enable RTS/CTS on the serial port v1.6.7 - added -A option to read "Last four alarms" - fix using wrong param when displaying TransState message - pass TransState the command description so if it gets a non-zero status it can report for what command it got it v1.6.6 - added -P option to throttle commands sent to the inverter v1.6.2 - added -D & -E to report more DSP information v1.6.0 - added -U option to delay read after sending command to inverter and -u option to report it - version numbering now Number dot Number dot Number - manpage now included - 'make' now has 'install' and 'uninstall' options v1.5-7 - fix sizeof pointer passing in memset - add -Y retries option and -y option to report the number of attempts made and use in the Communicate function - in ReadNextChar restore serial port settings and clear lock if exiting v1.5-5 - added -x option to enable XON/XOFF v1.5-1 - serial port configuration tweaks v1.5-0 - Starting with v1.5-0 this work based on Power One's 'Aurora Inverter Series - Communication Protocol -' document, Rel. 4.6 25/02/09 v1.4-5 - add -o option to output data to a file v1.4-4 - don't set lckCNT = -1 when clearing stale serial port lock really bonehead move... As Dierks Bentley would say, What was I thinkin'? v1.4.3 - add cCommandEnd = '\0' v1.4.2 - disable XON/XOFF flow control on output v1.3-4 - fix determining if serial port is in use - correct szSerBuffer size v1.3-3 - correct an error in strftime that only may show up in the first or last week of the year v1.3-2 - take into account Daylight Savings Time when setting the Aurora's time v1.3-1 - new release v1.3-0 - new release v1.2-9 - fix -n & -p problem that left serial port locked - disable all options except -d & -e when -c is used - only format time for columns for options -d or -e - all verbose output goes to stderr - update help v1.2-5 - add locking of serial port v1.0-0 - This work based on Magnetek's 'Aurora PV Inverter - Communications Protocol -' document, Rel. 1.8 09/05/2005 aurora-1.7.3/TODO0000644000076400017510000000006011656364730011335 0ustar curt Can't think of anything more at the moment....