CA2332297A1 - Wireless electronic energy meter - Google Patents

Wireless electronic energy meter Download PDF

Info

Publication number
CA2332297A1
CA2332297A1 CA 2332297 CA2332297A CA2332297A1 CA 2332297 A1 CA2332297 A1 CA 2332297A1 CA 2332297 CA2332297 CA 2332297 CA 2332297 A CA2332297 A CA 2332297A CA 2332297 A1 CA2332297 A1 CA 2332297A1
Authority
CA
Canada
Prior art keywords
void
break
case
meter
byte
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Abandoned
Application number
CA 2332297
Other languages
French (fr)
Inventor
Dane Blackwell
Patrick C. Hamilton
Allan L. Scribner
Wilfred Mueller
Dennis M. Needham
Roderick Michael Johnson
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
SmartSynch Ltd
Original Assignee
SmartSynch Ltd
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by SmartSynch Ltd filed Critical SmartSynch Ltd
Priority to CA 2332297 priority Critical patent/CA2332297A1/en
Publication of CA2332297A1 publication Critical patent/CA2332297A1/en
Abandoned legal-status Critical Current

Links

Classifications

    • HELECTRICITY
    • H04ELECTRIC COMMUNICATION TECHNIQUE
    • H04QSELECTING
    • H04Q9/00Arrangements in telecontrol or telemetry systems for selectively calling a substation from a main station, in which substation desired apparatus is selected for applying a control signal thereto or for obtaining measured values therefrom
    • GPHYSICS
    • G01MEASURING; TESTING
    • G01RMEASURING ELECTRIC VARIABLES; MEASURING MAGNETIC VARIABLES
    • G01R21/00Arrangements for measuring electric power or power factor

Landscapes

  • Engineering & Computer Science (AREA)
  • Computer Networks & Wireless Communication (AREA)
  • Compression, Expansion, Code Conversion, And Decoders (AREA)

Abstract

A wireless electrical energy meter is provided comprising an interface board for connecting an electrical power usage meter to a pager board capable of transmitting data to a remote host computer. The interface board includes an input port or connection to the meter for periodically reading data from the meter, and an output port for connection to the pager board for outputting data to the pager board. The data may be transmitted via the pager board and a pager system to a remote host computer. A compact, efficient differential compression method is provided in the interface board to reduce the airtime duration and cost of transmission of data via the pager system to the host computer.

Description

Wireless Electronic Energy Meter A portion of this disclosure of this patent document contains material that is subject to copyright protection. The copyright owner has no objection to the reproduction of the patent document or disclosure as it appears in the Patent and Trademark Office files or records, but otherwise reserves all copyright rights whatsoever.
Field of the Invention The present invention relates generally to electronic energy meters, and more particularly to programmable wireless electronic meters.
Background of the Invention Conventional commercial and industrial electronic meters, such as the meter described in United States Patent 6,112,159 ("the Siemens S4"), the meter described in United States Patent 6,081,204, and the meter described in United States Patent No.
6,094,622, are primarily designed for reading via an optical port. Although collection of meter data by wired and wireless networks has been proposed and such meters generally include ports for connectors other than the optical port that could be used by automated meter reading ("AMR") systems, the widespread development of AMR systems has been uneconomic due to either high equipment costs or significant usage costs when compared to the generally low value of meter readings. The telephone system and wide area networks, such as pager systems, are examples of public wired and wireless networks.
The use of such networks for data collection from meters has generally been uneconomic unless the data is of high value.
There is therefore a need for a lower-cost method for collecting meter readings over wireless wide-area networks, such as pager networks.

Summary of the Invention In one aspect of the invention, an interface board is provided for connecting an electrical power usage meter to a pager board capable of transmitting data to a remote host computer. The interface board includes an input port for connection to the meter for periodically reading load profile data from the meter; and an output port for connection to the pager board for outputting load profile data to the pager board. The load profile data may be transmitted via the pager board and a pager system to a remote host computer. A
compact, efficient differential compression method is provided in the interface board to reduce the airtime cost of transmission of load profile data. A battery charging and power supply system is also provided in which power for transmissions is built up between transmissions so that the power drawn from the meter is reduced.
In another aspect of the invention, an electronic meter is provided with an output port for outputting load profile data to a pager board capable of transmitting data to a remote host computer. A compact, efficient differential compression method is provided to reduce the airtime cost of transmission of load profile data.
Brief Description of the Drawings Figure I is a block diagram showing the functional components of an exemplary electronic energy meter reading system, including the components within the meter enclosure, a pager system, and a host computer.
Figure 2 is a block diagram showing the functional components of an exemplary interface board for use in an electronic energy meter system such as that shown in Figure 1.
Figure 3 is a block diagram showing the functional components of an exemplary electronic energy meter reading system including an electronic energy meter for use with a pager board, a page pager board, a page system, and a host computer.
Figure 4 is a flowchart showing a simplified differential compression process for use in transmission of load profile data by the systems shown in Figures 1 and 3.
Figure 5 is a flowchart showing an improvement to the simplified differential compression process shown in Figure 4 Figure 6 is a flowchart showing a preferred differential compression process.
Figure 7 is a block diagram showing the functional components of an exemplary electronic energy meter system located within the meter enclosure, with the exception of the power supply/charger board.
Figure 8 is a block diagram showing the functional components of an exemplary power supply/charger board for use with the system of Figure 7.
Figure 9 is a detailed schematic circuit diagram of the interface board of Figures 1 and 2.
Figure I O is a detailed schematic circuit diagram of the microprocessor (marked as "CPU") section of the interface board of Figure 9.
Figure I 1 is a detailed schematic circuit diagram of the chip select-decoding (marked as "CS-Decode") section of the CPU portion of Figure 10.
Figure 12 is a detailed schematic circuit diagram of the memory array (marked as "Memory-Array Memory - Sheet 4") portion of the CPU section of Figure 10.
Figure I 3 is a detailed schematic circuit diagram of the serial EEPROM and real-time clock (marked as "RTC and Serial EEProm") portion of the CPU section of Figure 10.

Figure 14 is a detailed schematic circuit diagram of the regulators (marked as "Regulators Pager and Vcc") portion of Figure 9.
Figure 1 S is a detailed schematic circuit diagram of the temperature sensing and analog control (marked as "Temp Sensor Temp-Sense") portion of Figure 9.
Figure 16 is a detailed schematic circuit diagram of the LED options (marked as "Option-LEDs") portion of Figure 9.
Figures 17 and 18 together are a detailed schematic circuit diagram of the power supply/charger board of Figure 1.
Detailed Description of Invention One preferred embodiment of the invention is an interface board that may be used to convert a conventional electronic commercial and industrial meter such as a Siemens S4 meter to wireless operation. Meters such as the Siemens S4 comprise an enclosure, the front and sides of which are a glass cover, a base to which the glass cover is attached, and a meter board that includes an analog to digital converter, a digital signal processor, a micro-controller, and a power supply. For examples of such meters, see Figures 1 of United States Patent Nos. 6,094,622 and 6,081,204. The meter board also contains at least one functional component, generally an optical port, for communication with the outside world, and may include other ports or connectors. The Siemens S4 meter, for example, provides a serial port in addition to the optical port from which the inventors realized that it is possible to extract the same information that is available from the optical port.
While the exemplary interface board to be described below has been designed for use with a meter, such as the Siemens S4, that has a serial port that provides the same data as the optical port, it is within the ability of those skilled in the art to modify the design of the interface board so that it may be connected directly into the circuitry of the meter board, such as to a bus to which a digital signal processor or a micro-controller is connected, so that the interface board may obtain data without use of a low speed serial port. For example, the interface board may be modified so that it can be connected to the option connector 38 shown in Figure 1 of United States Patent No. 6,094,622.
For the purpose of the following description, the meter board may be treated as a black box which, when sent the appropriate commands via its serial port provides the same electrical usage data that it provides to the optical port when interrogated with an optical probe.
Hardware Figure 1 shows, in an overall block diagram, a meter reading system 10 that includes a meter board 12 having an optical port 14 and a serial input/output port 16, an interface board 18 having a first serial port 20 connected to the serial input/output port 16 of the meter board 12 and a second serial input/output port 22, a fixed two-way wireless pager board 24 connected to the second serial input/output port 22, a pager system 26 such as the SkyTel System, and a remote host computer 28. Generally, the interface board 18 obtains electrical usage data from the meter board 12 and packages it in an appropriate form for transmission to a remote host computer 28 via the pager board 24 and the pager system 26. To reduce airtime cost the interface board 14 compresses the electrical usage data using a differential compression method to be described below. The interface board 18 also provides a capability for monitoring power quality that may be turned on and off from the remote host computer 28. If power quality monitoring is turned on, the interface board 18 obtains data including average phase voltages and angles from the meter board 12 using commands sent to the meter serial input/output port 16 and notifies the remote host computer 28 whenever certain criteria indicating a change in power quality to be described below are met.

In an exemplary embodiment described here, the pager board 24 is a Motorola 2-Way ReFlex SOTM Pager Board and the meter board 12 is an unmodified meter board of a Siemens S4 meter. The interface board 18 is installed together with the meter board 12 and the pager board 24 in a Siemens S4 meter enclosure (not shown). The interface board 18 is designed to fit inside the S4 meter enclosure with the meter board 12, the pager board 24, and a power supply and battery charger board 30 so that the resulting combination is entirely within the enclosure of the unmodified Siemens S4 meter enclosure. In Figure 1, the boundary of the enclosure of the Siemens S4 meter is indicated by dashed line 11.
The interface board 18 is built on a conventional printed circuit board designed to fit within the S4 meter enclosure from hardware components that are commercially available. A block diagram of the interface board 18 is shown in Figure 2. The interface board 18 includes a Microchip PIC 17C756-16/IL microprocessor 32, which provides the first serial input/output port 20 for connection to the meter serial input/output port 16 and the second serial input/output port 22 for connection to the pager board 24.
Also connected to the microprocessor 32 by an Inter-Integrated Circuit ("I'C") bus 34 are a real time clock 36 and a serial electrically-erasable read-only memory ("EEPROM") 40.
The microprocessor 32 also contains internal EEPROM (not shown) and is provided with external RAM and flash memory, indicated in Figure 2 by reference numeral 38.
The necessary address decoding circuitry (not shown) to allow the microprocessor 32 to access the external RAM and flash memory 38 is provided on the interface board 18.
Instructions for execution by the microprocessor 32 are stored mainly in the flash memory portion of the external RAM and flash memory 38, except for test software, boot loader software, and reset algorithms, which are stored in the internal EEPROM
within the microprocessor 32. Programmable and configurable parameters are stored in the EEPROM 40.

The interface board 18 has two major functions. The first is to obtain load profile data periodically from the meter board 12 and transmit that data to a host computer 28 via the pager board 24 and the pager network 26. The second major function of the interface board 18 is to monitor power quality data available from the meter board 12.
The meter board 12 provides the three phase voltages and phase angles of the current passing through the mete. The host computer 28 is notified whenever a reportable power quality event as defined below occurs. A primary characteristic of power quality events is that they cannot be reported at scheduled times as can load profiles. Hence other non-scheduled alarm conditions that the interface board 18 may be configured to monitor, such as demand threshold reporting, are described below together with power quality event reporting.
The power supply/charger board 30 is needed to use the interface board 18 in an S4 meter application because the power supply available from the S4 meter board 12 does not have sufficient capacity to power the interface board 18 and the pager board 24 during a transmission. In particular, the pager board 24 requires a large amount of electrical power when it is transmitting. For example, every one second of transmission by the pager board 24 requires approximately 30 seconds of charging assuming that 50 mA of charging current is available from the meter board 12. Also, because the S4 meter is powered from phase C of the current being metered, for the interface board 18 is to continue operating (e.g., providing power outage and restoration notification) during a power failure, it is necessary for the interface board 18 to have backup battery power.
Under normal operating condition (power being provided by the S4 meter), the power supply/charger board 30 recharges a 1.3 A-hr capacity gel lead acid battery (not shown in Figure 1 ) between transmissions by the pager board 24. By doing so, the amount of power drawn from the S4 meter is kept within the amount that can be drawn from the S4 meter. In effect the power supply/charger board 30 levels out the rate of power consumption by the pager board 24.

The power supply/charger board 30 is also controlled by the interface board 18 so as to curtail drawing power from the S4 meter as the available power on phase C drops off during a power failure so that the meter board 12 has power long enough to store its own data.
The battery is also managed by the interface board 18. The system (the combination of the interface board 18, the power supply/charger board 30, and the pager board 24 is referred to herein as "the interface system 13") into either a sleep mode or a kill power mode if phase C power fails. Which mode is chosen can be preset via the pager system 26. In the sleep mode, then the battery will last for approximately 6 days.
If the kill power mode is chosen, then power usage is cut to a minimum, providing approximately 30 days of battery life. The kill power mode would be chosen by a utility if it appeared that a major disaster, such as a hurricane, was about to occur.
In either mode, the interface system 13 will return to normal operation when power is restored to phase C and will then send a power restoration notification to the utility via the pager system 26.
The primary difference between the sleep and kill power modes is that if the interface system 13 is in kill power mode, then the interface system 13 cannot be contacted via the pager system 26 until phase C power is restored, whereas in sleep mode the interface system 13 is able to receive messages via the pager system 26.
The kill power mode is also useful in that S4 meters with the interface system 13 installed may be kept in the kill power mode until ready for field installation for approximately 30 days without disconnecting the battery. Disconnecting the battery requires removal of the meter cover, which is best only done in a clean facility, not in the field.
For shelf storage over 30 days, however, it is necessary to disconnect the battery as even in the kill power mode, the interface system 13 is drawing some power. A battery connector is included that has a "park" position that disconnects the battery completely, thereby allow long term storage of the meter with interface system 13 installed and configured.

Figures 7 and 8 show a somewhat more detailed view of the interface board 18 and the power supply/charger board 30.
Figures 9 - 16 provide a detailed schematic circuit diagram of the currently preferred embodiment of the interface board 18. The components shown in Figures 9 -16 are listed in Appendix B. Similarly, Figures 17 and 18 together provide a detailed schematic circuit diagram of the power supply/charger board 30. The components shown in Figures 17 and 18 are listed in Appendix C. Those skilled in the art of electronic design will understand that these Figures are only one of many alternative ways in which the interface board 18 and power supply/charger board 30 may be constructed.
Alternative Hardware Embodiment The interface board 18 and power supply/charger board 30 are useful for retrofitting existing Siemens S4 meters and other electronic meters that provide or can be adapted to provide load profile, phase voltage, and phase angle data, but which do not have sufficient electrical power or computational power to drive a pager board directly. The power quality monitoring aspect of the interface board 18 disclosed below may also be implemented in existing electronic meters in which a programmable microprocessor has access to digitized phase voltage measurements and phase angles.
Further, the wireless aspect of the interface board 18 may be implemented in an existing electronic meter provided that the meter has a suitable serial port or provided that a suitable serial port can be added for connection to a two-way pager board.
Both aspects of the interface board 18 may be added in a new meter board design.
Figure 3, a modified version of Figure 1 of U.S. Patent No. 5,924,051, shows a possible meter board in block diagram form. In Figure 3, a serial interface component 50 such as a UART replaces the line carrier network interface 48 of Figure 1 of U.S.
Patent No.
5,924,051. In Figure 3, the power supply 42, power connections 44, and battery 47 of Figure 1 of U.S. Patent No. 5,924,051 are not shown. The specification of U.S.
Patent No. 5,924,051 is hereby incorporated by reference. Reference numeral 52 in Figure 3 indicates the modified meter board. Reference numeral 54 indicates the boundary of a meter enclosure for containing the meter board 52 and a pager board 24.
As suggested above, existing meter boards may have to be redesigned in order to provide the necessary electrical power and computational power needed to send load profile and power quality data via a pager system. However, such redesign should be straightforward if the principles disclosed herein with respect to the interface board 18 and power supply/charger board 30 are applied.
Load Profile Reporting A basic function of the interface board 18 is to provide daily load profiles to the host computer 28 for billing purposes. The readings taken for this purpose are scheduled.
15 In order to reduce the cost of airtime, load profile data is compressed in the manner described below before being sent.
Raw load profile data is read from meter serial port 16 via interface serial port 20 and stored in an input buffer by the microprocessor 32. The raw data is a series of 16-bit 20 words, each of which may be either a data value providing the power consumption for a defined interval (typically 15 minutes) or a time/date stamp. If the high order bit of a raw data word is a l, then the word is a time/date stamp. The second highest order bit is a parity bit, leaving 14 data bits for a load profile data value. Hence load profile data values can be in the range 0 to 16383.
In general, the differential compression method used compresses the data values by, where possible, transmitting only differences between successive data values, so long as the differences can be represented by an 8-bit byte. Note that in the following discussion a twos-complement representation of negative numbers is used.

In a simple form of differential compression, compression may turned on and off as the stream of raw data words are read from the input buffer by examining the data value of the current word to determine whether the difference between it and the data value of the last data word read can be represented by a single byte. A
special byte value may be sent to indicate that compression is on for succeeding bytes until turned off. If compression is on, then to indicate that compression will be off for successive data, a special byte value is used. This means that a difference value can have only one of 255 possible values (0x00 to OxFF provides 256 possible differences, less one value for the compression-off special byte value), say -127 to +127, using -128 as the special byte value. When uncompressed values are being sent and a value is encountered that could be sent as a difference from the last value, then a special byte such as OxFF
may be sent as the high byte of the word to indicate that compression is to be on and the low byte should be treated as a difference value. Note that the special byte value indicating that compression is on for succeeding bytes may be the same as the byte value sent while compression is on that a particular difference value. Hence OxFF when compression is on means a difference of negative 1, whereas when compression is off, OxFF means that the next byte is a difference value and that compression is on for succeeding bytes.
The simple differential compression scheme described above may be improved by looking ahead in the input buffer before turning compression off to determine whether the next data word read could be compressed if the current word were sent uncompressed.
For example, if compression is on and a data word having a value of 300 is read and the previous data work had a value of 100, then the difference of +200 would be too large to represent in one byte and under the simple scheme described above, a special byte value of -128 would be sent to turn compression off, followed by the value 300 as a two-byte word. However, suppose the value of the next data word following 300 is 301.
In that case, the simple scheme would send a byte value OxFF to indicate compression is on and then +1 as a byte value. The total cost in bytes to send the two readings would be five bytes. If the byte value of the next word were scanned before sending the compression-off byte value, then a second special value indicating that compression is off only for the next word could be sent instead of the compression-off byte value. The byte cost would then be reduced by one as the 301 value could be sent as a difference without prefacing it by the compression-on byte.
The differential compression scheme may be further improved upon for handling data that varies slowly over a range that is somewhat larger than can be handled with difference encoded in single bytes. To do so, further special bytes are sent while compression is on to indicate that the magnitude of the difference byte should be increased. For example, if the difference value is only slightly outside the range of possible differences, say +130, then a special byte value may be sent before a difference value of +5, indicating that 125 is to be added to the value 5 and the sign of the difference is +. It should be noted that this scheme reduces the number of difference values that can be sent in a byte as some of the possible values are taken as special values.
In the preferred scheme illustrated in Figure 6, the special byte values 126, 127, and 128 indicate adding 125, 252, and 379 to the next byte, respectively. The special byte values -127 turns compression off and the special byte value -126 indicates that compression is to be on, except for the next two bytes, which are to be taken as a 16-bit word.
Flowcharts describing the simple, improved, and a preferred compression processes in more detail are shown in Figures 4, 5, and 5, respectively. The following variables are used:
InBuff input buffer OutBuff output buffer q InBuff pointer (initialized to 0) Q total number of bytes in InBuff r OutBuff pointer (initialized to 0) cMode compression mode flag, a value of 1 means compression is "on" and 0 means compression is "off' (initialized to 0) Word current word read from InBuff nextWord the word after the current word in InBuff oldV previous value of Word (initialized to 0) Base either oldV (if Word is a date/time stamp) or Word (if Word is not a date/time stamp) dV (Word - oldV) dVsign sign of dV
dateStamp flag indicating that the current Word is a date/time stamp The simplified compression process described in Figure 4 begins with initialization of the variables as discussed above (block 110). The first word in the input buffer InBuff is then read (block 112) and its high order bit tested (block 114). If the high order bit is 1, the word is a date/time stamp, so the dateStamp flag is set (block 116).
Otherwise, the word is taken to be one measurement of load profile data, which includes a 14-bit data value preceded by a parity bit. Hence the parity bit must stripped off by AND-ing the word with Ox3FFF (block 118). In block 120, a difference value dV
is determined by subtracting the previous value of the word oldV from the word just read and the sign dVsign of the difference value dV is determined. As the previous value was initialized to zero, the first difference value will be equal to the first word read that is not a date/time stamp.
The process then continues by checking (block 122) to determine whether compression is on. If compression is not on, the absolute value of dV and the dateStamp flag are checked (block 124) to determine whether the absolute value of dV is greater than 125 or if the dateStamp flag is set. If neither is true, then Word must be a data value that differs from its previous value by less than 126 and the operations of block 126 are performed, namely, a word having OxFF as the high order byte and dV as the low order byte is written to the output buffer, the high order byte signifying that compression is on and the low order byte representing the first difference value, and the flag cMode indicating whether compression is on or off is set to value 1. The output buffer pointer is then incremented (block 128) by 2. If compression is not on and the absolute value of the dV is greater than 126 or dateStamp flag is set, then Word is written (block 130) to the output buffer and the output buffer pointer r is incremented 128 by 2.
In the exemplary process shown in Figure 4, difference values may range from -125 to +125, the special byte value -127 (0x81 in hexadecimal) being reserved to act as a flag that compression is to be off for successive transmissions. This scheme only uses 252 (251 difference values, including 0, plus one flag) of the 256 possible values that could be represented in one 8-bit byte in order to be consistent with the choice of special bytes used in the schemes presented in Figures 5 and 6. Clearly, the difference values in the range -127 to +127 could be represented if only one byte (say 128) is used as a flag.
On the other hand, if compression was on at the decision point indicated by block 122, then the absolute value of dV and the dateStamp flag are checked (block 132) to determine whether the absolute value of dV is greater than 125 or if the dateStamp flag is set. If neither is true, then Word must differ from its previous value by less than 126 and compression may remain on. In that case, the operations of block 134 are performed, namely, difference value dV is written to the output buffer and the output buffer pointer r is incremented by 1. If either the absolute value of dV is greater than 125 or the dateStamp flag is set, then compression will have to be turned off as in the scheme shown in Figure 4 cannot handle difference values greater than 125 or two-byte time/date stamps. In that case, the operations of block 136 are performed, namely, special byte 0x81 (decimal 128) is written to the output buffer and the compression flag cMode is cleared (set to value 0) and the operations of block 138 are performed, namely, the current Word, which must be either a data value with the parity bit stripped off or a date/time stamp, is written to the output buffer and the output buffer pointer r is incremented by 3 (one position for the special byte and two for the current Word.
After the output buffer pointer r is incremented in each of the situations above, the operations of block 140 are performed, namely, if the current Word is not a date/time stamp, then the variable oldV holding the previous value of the current Word is set to the value of the current Word and the input buffer point q is incremented by 2.
The input buffer point q is then tested (block 142) against the total number of bytes Q in the input buffer to determine whether the input buffer has been completely read. If it has the process is complete. If not, another word is read (block 112) and processed as above until at block 142 it is determined that the input buffer has been completely read.
The process described above in relation to Figure 4 has two weaknesses that reduce its usefulness in the compression of load profile data in the interface board 18.
The first, and most serious problem, is that it can be fooled into sending more data than would be sent if compression were not used at all. For example, suppose a sequence of load profile readings differed from each other by differences that alternated between values less than 126 and greater than 126. Compression would be turned alternately on and off repeatedly until the sequence ended. Each time compression is turned off an extra byte is used so that for every pair of data values in the sequence, five bytes are sent, rather than the four bytes that would be sent if the compression were never used. In fact even an isolated large difference preceded and followed by a series of small differences cause one extra byte to be sent.
Figure 5 illustrates an improvement to the process described above in relation to Figure 4 that handles the first weakness in that process by scanning ahead in the input buffer to determine whether anything can be gained by delaying going in or out of compression. For example, if compression is already on and the current Word in the input buffer represents a large difference from the previous Word value, but the next Word value in the input buffer represents a small difference from the current Word, then using a byte to turn compression off and then a byte to turn it on again means that five bytes have been sent to represent two two-byte data words. If on the other hand a special byte (0x82 in Figure 5) is sent to indicate that compression is only to off for the current Word, then the total number of bytes needed is four. Similarly, if compression is off and the current Word in the input buffer represents a small difference from the previous Word value, compression may be left off if the next Word value in the buffer represents a large change from the current Word value. In either case, the compressed result can be no longer than no compression at all and will be shorter if the input buffer contains sequences of Word values that differ by small differences. In this discussion, "small differences" means differences of less than 126 in absolute value.
The improved compression process described in Figure 5 begins with initialization of the variables as discussed above (block 210). The first word in the input buffer InBuff is then read (block 212) and its high order bit tested (block 214). If the high order bit is 1, the word is a date/time stamp, so the dateStamp flag is set (block 216).
Otherwise, the word is taken to be one measurement of load profile data, which includes a 14-bit data value preceded by a parity bit. Hence the parity bit must stripped off by AND-ing the word with Ox3FFF as indicated in block 218. As indicated by block 220, a difference value dV is then determined by subtracting the previous value of the Word oldV from the word just read and the sign dVsign of the difference value dV
determined.
As the previous value was initialized to zero, the first difference value will be equal to the first word read that is not a date/time stamp.
The process then continues by checking (block 222) to determine whether compression is on. If compression is not on, then the absolute value of dV and the dateStamp flag are checked (block 224) to determine whether the absolute value of dV is greater than 125 or if the dateStamp flag is set. If either is true, then the current Word is written (block 230) to the output buffer and the output buffer pointer r is incremented by 2 (block 228).
If compression is not on and the absolute value of dV is not greater than 125 and the dateStamp flag is not set, then the next word in the input buffer is read (block 246) into nextWord and checked (block 248) to determine if it is a date/time stamp.
If nextWord is a date/time stamp, then the current Word, which is a data value, is written (block 230) to the output buffer and the output buffer pointer r is incremented by 2 (block 228). If nextWord is not a date/time stamp, then the parity bit is stripped off (block 250) and the absolute value of the difference between nextWord and the current Word checked (block 258). If the absolute value of that difference is less than 126, then the operations of block 226 are performed, namely, a word having OxFF as the high order byte and dV
as the low order byte is written to the output buffer, the high order byte signifying that compression is on and the low order byte representing the first difference value, and the flag cMode is set to value 1 indicating that compression is on. The output buffer pointer r is then incremented by 2 (block 228). On the other hand, if the absolute value of the difference checked at block 258 is not less than 126, then the current Word, which is a data value, is written (block 230) to the output buffer and the output buffer pointer r is incremented by 2 (block 228).
On the other hand, if compression was on at the decision point indicated by block 222, then the absolute value of dV and the dateStamp flag are checked (block 232) to determine whether the absolute value of dV is greater than 125 or if the dateStamp flag is set. If neither is true, then Word must differ from its previous value by less than 126 and compression may remain on. In that case, the operations indicated in block 234 are performed, namely, difference value dV is written to the output buffer and the output buffer pointer r is incremented by 1.
If either the absolute value of dV is greater than 125 or the dateStamp flag is set, then the next word in the input buffer is read (block 260) into nextWord and nextWord is checked (block 262) to determine if it is a date/time stamp. If nextWord is a date/time stamp, then the operations indicated in block 236 are performed, namely, special byte 0x81 (decimal 128) is written to the output buffer and the compression flag cMode is cleared (set to value 0). Then the operations indicated in block 238 are performed, namely, the current Word, which must be either a data value with the parity bit stripped off or a date/time stamp, is written to the output buffer and the output buffer pointer r is incremented by 3 (one position for the special byte and two for the current Word).
If nextWord is not a date/time stamp, then the parity bit is stripped off (block 264). If the current word is a date/time stamp, then nextWord should be compared to the last data value of Word, oldV. Otherwise, nextWord should be compared to the current value of Word. To do this the current value of the dateStamp flag is checked (block 266) and the variable Base is set to the value of oldV if dateStamp is set (block 268) or to the value of Word if dateStamp is not set (block 270). Then the absolute value of the difference between nextWord and Base may be checked (block 272). If the absolute value of the difference is less than 126, then special byte 0x82 (decimal -126) is written (block 274) to the output buffer and operations indicated in block 238 are performed, namely, the current Word, which must be either a data value with the parity bit stripped off or a date/time stamp, is written to the output buffer and the output buffer pointer r is incremented by 3 (one position for the special byte and two for the current Word.
On the other hand, if the absolute value of the difference checked at block 272 is not less than 126, then the operations indicated in block 236 are performed, namely, special byte 0x81 (decimal -127) is written to the output buffer and the compression flag cMode is cleared (set to value 0). The operations indicated in block 238 are performed, namely, the current Word, which must be either a data value with the parity bit stripped off or a date/time stamp, is written to the output buffer and the output buffer pointer r is incremented by 3 (one position for the special byte and two for the current Word).
After the output buffer pointer r is incremented in each of the situations above, the operations indicated in block 240 are performed, namely, if the current Word is not a date/time stamp, then the variable oldV holding the previous value of the current Word is set to the value of the current Word and the input buffer point q is incremented by 2.

The input buffer point q is then tested (block 242) against the total number of bytes Q in the input buffer to determine whether the input buffer has been completely read. If it has the process is complete. If not, another word is read (block 212) and processed as above until at block 242 it is determined that the input buffer has been completely read.
The compression process illustrated in Figure 5 can be improved further by the use range-expansion byte values 126, 127, and -128 to indicate that the next difference value is to be increased in absolute value by 125, 252, or 379, respectively.
If that improvement is added, then the scan-ahead improvement discussed above in relation to Figure ~ should be expanded to scanning ahead past any Words that could be represented by one of the range-expansion byte values 126, 127, or -128 and a difference value to the first Word in the rest of input buffer that is either a data value that can be represented as a single-byte difference without a range-expansion byte, a data value that cannot be represented as a single-byte difference even with a range-expansion byte, or a time/date stamp. In effect, since data values represented by a range-expansion byte and a difference byte require a two-byte word to transmit, they should be ignored for the purpose of scanning ahead. If the expansion of the scan-ahead process is not added, then under some circumstances an extra byte is used because compression may be started and stopped without actually sending a data value as a single byte. For example, a large difference might be followed by several differences that can be represented by a range-expansion byte with a difference byte (a "medium difference"), another large difference, and finally a medium difference. Going into compression and then out again, as would occur if the process described in relation to Figure 5 were used, would take one extra byte beyond not using compression at all.
Figure 6 illustrates a preferred compression scheme incorporating by the range-expansion bytes and the expanded scanning-ahead improvements. It begins with initialization of the variables as discussed above (block 310). The first word in the input buffer InBuff is then read (block 312) and its high order bit tested (block 314). If the high order bit is 1, the word is a date/time stamp, so the dateStamp flag is set (block 316).
Otherwise, the word is taken to be one measurement of load profile data, which includes a 14-bit data value preceded by a parity bit. Hence the parity bit must stripped off by AND-ing the word with Ox3FFF as indicated in block 318. As indicated in block 320, a difference value dV is then determined by subtracting the previous value of the Word oldV from the word just read and the sign dVsign of the difference value dV
determined.
As the previous value was initialized to zero, the first difference value will be equal to the first word read that is not a date/time stamp.
The process then continues by checking (block 322) to determine whether compression is on. If compression is not on, then the absolute value of dV is checked (block 324) to determine whether the absolute value of dV is less than 126. If it is not, then the current Word is written (block 330) to the output buffer and the output buffer pointer r is incremented by 2 (block 328).
If compression is not on and the absolute value of dV is less than 126, then the operations indicated in block 376 are performed, namely, an index m is initialized to 0, and nextWord(m) is initialized to the previous value of Word, if the current Word is a time/date stamp, or to the current value of Word, if Word is a data value, and an index k to the input buffer is initialized to point to the first byte of next Word following the current Word in the input buffer.
Following the initialization (block 376), a loop is run in which the operations indicated in block 378 are performed, namely, m is incremented by 1, nextWord (m) is set to the next word in the input buffer InBuff(k+l,k), and then k is increment by 2. The high order bit of nextWord(m) is then tested (block 380) to determine if nextWord(m) is a time/date stamp. If it is, then the current Word is written (block 330) to the output buffer and the output buffer pointer r is incremented by 2 (block 328). If it is not, then nextWord(m) must be a data value, so the parity bit is stripped-off (block 382) and the absolute value of the difference between nextWord(m) and nextWord(m-1) is checked (block 384) to determine if it is greater than the largest difference that can be represented with the range-expansion bytes (506). If it is, then the current Word is written (block 330) to the output buffer and the output buffer pointer r is incremented by 2 (block 328).
If it is not, a further check (block 386) is made to determine whether the absolute value of the difference is less than 126. If it is, then the operations indicated in block 326 are performed, namely, a word having OxFF as the high order byte and dV as the low order byte is written to the output buffer, the high order byte signifying that compression is on and the low order byte representing the first difference value, and the flag cMode is set to value 1 indicating that compression is on. The output buffer pointer r is then incremented by 2 (block 328). On the other hand, if the absolute value of the difference checked at block 386 is not less than 126, then by testing (block 388) the index k control loops back to the operations indicated by block 378 if all of the words in the input buffer have not already been tested or otherwise the current Word is written (block 330) to the output buffer and the output buffer pointer r is incremented by 2 (block 328).
On the other hand, if compression was on at the decision point indicated by block 322, then the absolute value of dV and the dateStamp flag are checked (block 332) to determine whether the absolute value of dV is greater than 506 or if the dateStamp flag is set. If neither is true, then the absolute value of dV is tested (block 390) to determine if it is less than 126. If it is, then difference value dV is written (block 392) to the output buffer and the output buffer pointer r is incremented by 1 (block 394).
If absolute value of dV is tested 390 to be not less than 126, then absolute value of dV is tested (block 396) to determine if it is less than 253. If it is, then a word having as a high order byte the value Ox7E and as a low order byte a value having an absolute value of dV less 125 and the same sign as dV is written (block 398) to the output buffer and the output buffer pointer r incremented by 2 (block 406).
If absolute value of dV as tested at block 396 is not less than 253, then it is further tested at block 400 to determine if it is less than 380. If it is less than 380, then a word having as a high order byte the value Ox7F and as a low order byte a value having an absolute value of dV less 252 and the same sign as dV is written (block 402) to the output buffer and the output buffer pointer r incremented by 2 (block 406). If it is not less than 380, then a word having as a high order byte the value 0x80 and as a low order byte a value having an absolute value of dV less 379 and the same sign as dV is written (block 404) to the output buffer and the output buffer pointer r incremented by 2 (block 406).
If compression the absolute value of dV was greater than 506 or if the dateStamp flag was on at the decision point indicated by block 332, then the operations indicated by block 408 are performed, namely, an index m is initialized to 0, and nextWord(m) is initialized to the previous value of Word, if the current Word is a time/date stamp, or to the current value of Word, if Word is a data value, and an index k to the input buffer is initialized to point to the first byte of next Word following the current Word in the input buffer.
Following the initialization block 408, a loop is run in which the operations indicated by block 410 are performed, namely, m is incremented by 1, nextWord (m) is set to the next word in the input buffer InBuff(k+l,k), and then k is increment by 2. The high order bit of nextWord(m) is then tested (block 412) to determine if nextWord(m) is a time/date stamp. If it is, then the current Word is written (block 336) to the output buffer and the output buffer pointer r is incremented by 2 (block 338). If it is not, then nextWord(m) must be a data value, so the parity bit is stripped-off (block 414) and the absolute value of the difference between nextWord(m) and nextWord(m-1 ) is checked (block 416) to determine if it is greater than 506, the largest difference that can be represented with the range-expansion bytes. If it is, then the current Word is written (block 336) to the output buffer and the output buffer pointer r is incremented by 2 (block 338). If it is not, a further check (block 418) is made to determine whether the absolute value of the difference is less than 126. If it is, then a byte having the value 0x82 is written (block 374) to the output buffer to indicate that the next word written to the output buffer is not compressed, but that compression should stay on. The operations indicated by block 338 are then performed, namely, the current Word is written to the output buffer and the output buffer pointer r is incremented by 3 (one position for the special byte and two for the current Word).
On the other hand, if the absolute value of the difference checked at block 418 is not less than 126, then the index k is tested (block 420) and control loops back to the operations indicated by block 410 if all of the words in the input buffer have not already been tested or otherwise the operations indicated by block 336 are then performed, namely, special byte 0x81 (decimal -127) is written to the output buffer and the compression flag cMode is cleared (set to value 0). The operations indicated by block 338 are performed, namely, the current Word is written to the output buffer and the output buffer pointer r is incremented by 3 (one position for the special byte and two for the current Word).
After the output buffer pointer r is incremented in each of the situations above, the operations indicated by block 340 are performed, namely, if the current Word is not a date/time stamp, then the variable oldV holding the previous value of the current Word is set to the value of the current Word and the input buffer point q is incremented by 2.
The input buffer point q is then tested (block 342) against the total number of bytes Q in the input buffer to determine whether the input buffer has been completely read. If it has the process is complete. If not, another word is read (block 312) and processed as above until at block 342 it is determined that the input buffer has been completely read.
The compression scheme described in relation to Figure 6 has an advantage over other more complex compression schemes in that the required memory and computational power is quite low. For example, because the process proceeds sequentially through the input buffer, the output buffer may occupy the same memory positions as the input buffer. A major design goal has been to eliminate the possibility that more bytes could be written to the output buffer than have been read from the input buffer, even if the data were extremely perverse. An example of perverse data would be if large differences between data values alternated with same differences throughout the data. The improved simple scheme shown in Figure 5 and the preferred scheme shown in Figure 6 would under such circumstances simply not compress the data at all.
The simple scheme shown in Figure 4 would, however, compress every other value, resulting in an output buffer about 20% than the input buffer.
Those skilled in the art will recognize the detailed flowcharts presented in Figures 4, 5, and 6 are but one possible representation of the invention. They do not necessarily represent the most efficient manner in which the process might be coded. For example, those skilled in the art will recognize that the operations indicated by blocks 408 - 420 could be embodied in a single procedure that could also be used to perform the operations indicated by blocks 376 - 388 and doing so would be within the skill expected of those skilled in the art. Those sets of operations have been shown separately in Figure 6 to aid in the explanation of the overall compression.
Those skilled in the art will also recognize that the use of three as the number of range-expansion bytes is arbitrary. Clearly, depending upon the characteristics of the data to be compressed, more or fewer range-expansion bytes may be used. In fact, the number of range-expansion bytes could be varied dynamically by scanning the input buffer before commencing the compression process.
In a typical situation, the input buffer hold 96 words of load profile data and one time/date stamp. (There may be more than one time/date stamp if the real time clock has been reset during the last day.) In one typical situation in which a compression scheme of the form shown in Figure 6 was used, there were four words of data that required range-expansion bytes and one word of data that could not be compressed. The balance of the data words were compressed without using range-expansion bytes. The result was that 104 bytes were needed to send the 97 two-byte words, a compression of almost 50%
using a fast routine that needs only a single input/output buffer.
In the current embodiment of the interface board 18, it is possible that more than one channel of data may be stored alternating word-by-word in the input buffer. In that case, the compression process treats each channel completely separately from the other.
Power Quality Monitoring The power quality events monitored and reported by the exemplary interface board 18 are ( 1 ) power outage events, (2) high/low voltage events, (3) voltage unbalance events, and (4) momentary interruption/voltage sag events, which are defined as follows:
( 1 ) A power outage event occurs when a phase voltage has dropped to less than a pre-selected percentage (e.g., 50%) of nominal phase voltage for a pre-selected interval of time. For example, an interval in the range of 1 to 10 minutes may be pre-selected for the exemplary interface board 18.
(2) A high/low voltage event occurs when a phase voltage has deviated from nominal phase voltage by a pre-selected percentage for a pre-selected interval of time. For example, a percentage in the range from 5 to 20 percent and an interval in the range of 1 to 30 minutes may be pre-selected for the exemplary interface board 18.
(3) A voltage unbalance event occurs when a phase-to-phase voltage has deviated from the average of the three phase-to-phase voltages by a pre-selected percentage for a pre-selected interval of time. For example, a percentage in the range from 2 to 6 percent and an interval in the range of 15 to 30 minutes may be pre-selected for the exemplary interface board 18.
(4) A reportable momentary interruption/voltage sag ("MIVS") event occurs when an average phase voltage has dropped below the nominal phase voltage a pre-selected number of times within a predefined window of time by more than a pre-selected percentage. For example, a percentage of 20 percent or more, a number of times in the range of 1 to 10, and a window of time in the range of 1 to 60 minutes may be pre-selected for the exemplary interface board 18. Each voltage drop is referred to herein as a MIVS event; if a sufficient number of MIVS
events are recorded, then a MIVS report is made by the interface board 18. Because the monitoring MIVS events is more complex than the monitoring of the other events, it is described in detail below.
The meter board 12 calculates average phase voltages, currents, and angles and updates such data every 300 milliseconds in a register that is accessible via the serial port 16. During an update the data in the register may be incorrect. To use such data for monitoring power quality, the interface board 18 could read the stored phase voltages every 300 milliseconds if its clock were synchronized with the meter board 12 so that the readings would always take place between updates to the stored data. The inventors have found, however, that it is preferable to take not attempt to synchronize with the meter board 12 and instead to poll the meter serial port 16 every 100 milliseconds for the necessary data. By taking more readings than necessary the interface board 18 can weed out any readings that might be taken during the period the phase voltages are being updated (and which therefore may be incorrect) or that might be taken when the meter board is too busy to respond, but still be able to capture the necessary data in most cases.
While the meter board 12 raises a flag during its updating process to signify that data read from the serial port 16 may be incorrect, the inventors have found that bad readings can occur even if the flag is not set. Also, it has been found that under certain circumstances (when the meter board 12 has been polled too soon after powering up), the voltage data may be incorrect (zero or very small). A filter can be used to cause such readings to be ignored or they may be allowed treated as MIVS events so that the malfunctioning of the meter board 12 can be monitored. In any case readings taken while the updating flag is set can be ignored.
An alternative to obtaining data from the meter board serial port 16 may be to connect directly to the data/address bus of the meter board 12 and listen for data being stored.
Assuming that good data has been obtained from the serial port 16 (because it has been read when the meter board 12 is not updating the data and no other problems are causing bad data to be read), the average phase voltages will be read every 100 ms.
However, because the underlying data stored in the meter board 12 changes every 300 ms, the voltage for a particular phase will consist of consecutive groups of three identical readings. The interface board 18 treats each phase voltage reading as if it were an independent average for 100 ms of the phase voltage. The consequences of this treatment are that ( 1 ) a momentary interruption cannot be distinguished from a voltage sag and (2) at a given threshold reduction in average phase voltage, some momentary interruptions or sags may not be detectable.
For example, if the average phase voltage on one phase drops by 20% (to 96 volts if the nominal voltage is 120 volts) for three consecutive 100 ms average phase voltage readings (we are assuming that all readings are good data, so there will be three identical readings), then there could have been an interruption (zero volts, say) for 50 ms and full nominal voltage of 120 volts for 250 ms. On the other hand, there could have been a voltage sag to 96 volts for the entire 300 ms or a deeper sag for a portion of the 300 ms and nominal voltage for the remainder of the 300 ms. If the threshold for reporting is set at 96 volts, then no momentary interruption of less than 50 ms within the 300 ms period can be detected. Hence a phase voltage reading of 96 volts can mean anything from an interruption of 50 ms to a sag to 96 volts for 300 ms. Also, an interruption or sag can cross a boundary between 300 ms periods. To detect an interruption that straddles the boundary, the interruption would have to last for at least 50 ms in one of the two 300 ms periods. In the case of voltage sags, a voltage sag to 96 volts that began in one 300 ms period would have to continue through the entire succeeding 300 ms period or no MIVS
event would be detected by the interface board 18 if the threshold for MIVS
events were set at 96 volts.
A MIVS event is considered to continue until a reading of the relevant average phase voltage is not longer less than the nominal voltage by the predefined percentage.
One option for dealing with bad data is to require that there be two consecutive average voltage readings that are both less than the nominal voltage by the predefined percentage before a MIVS event is treated as having begun. This criterion could be strengthened even further by requiring that three consecutive average voltage readings all be less than the nominal voltage by the predefined percentage before a MIVS
event is treated as having begun. However, in some situations that would mean that an isolated MIVS event would be missed if a reading were ignored because the data was being updated.
In the past monitoring of power quality events has been done by large dedicated instruments, not as an adjunct to a power usage measurement in a conventional commercial and industrial meter such as the Siemens S4. The interface board 18 provides a means for inexpensively turning a meter such as the Siemens S4 into a power-quality monitoring device. While many of the features of the interface board described below make it most useful when coupled with a two-way pager, the interface board can also be used to advantage if it is connected by a land telephone line or a hardwired network connect to a host computer 28. Those skilled in the art will understand that connections such as RS-485, CDPD modem, or telephone modem may also be used.
The host computer is not described in detail here. However, an important feature of the interface board 18 is that it provides for reporting of power quality events such that the host computer may notify the electricity supplier or its customer of power quality events in near real-time. Means such as fax, email, the Internet, telephone, or personal messaging devices such as pagers and web or email-enabled cellular telephones may be used by the host computer 28 to provide nearly immediate notification of power quality events.
All power quality events are logged in a circular buffer. The interface board accepts commands from the host to send out the contents or part of the contents of the buffer via the pager so as to provide data for later diagnosing of problems in the meter.
The fundamental elements of the power quality aspect of the interface board 18 are that it is interrupt driven, uses an ad hoc (circular) buffer to record power quality events that may be reported, is remotely configurable, tracks network time, and uses average phase voltages to determine when a MIVS event has occurred.
The interface board 18 may be reset from the remote host so that the interface board 18 provides load profile data, but not power quality data, to the host 28 via the pager board 254 and pager system 26. This capability makes possible the granting of "migrating licenses" to use the power quality features of fewer than all of the meters installed and licensed for current usage data collection. The utility whose power sales are being metered may wish to collect power quality data only from a small number of meters, but have the capability of re-designating on its own volition which meters collect power quality data.
The interface board 18 also provides running mean power consumption tracking for up to four daily periods and notifies the host 28 if the specified threshold levels are exceeded during those periods. This is treated a power quality event, so that the host 28 is notified in near-real time when the running average exceeds the threshold.
Security (a required password) is provided to prevent unauthorized access to the hardware and firmware. This security is in addition to the meter's own security system.

Encryption is also provided using industry standard methods. The usage and power quality data, key management, and the power quality license data are encrypted separately.
The interface board 18 also provides a report flag that allows all or some power quality reporting functions to be turned off to be set and reset from the host. The interface board 18 can also be configurated from the host 28 to change the definition of a reportable power quality event. For example, the utility may only want to have a report if five MIVS events occur within five minutes.
Further, the interface board 18 also provides for the option of reporting together with a reportable power quality event any partially collected data regarding a condition that would trigger a report of a power quality event if the condition being monitored were to continue. For example, if a high voltage condition were to continue long enough to trigger a report of it as a power quality event, then in the same report to the host, the firmware can be set to also include data being collected on MIVS events for which the defined time interval has not yet elapsed. This capability allows airtime on the pager system 26 to be minimized and the cost of operation of power quality monitoring to reduced.
The interface board 18 also provides for remote selection of the use of compression, binary, or ASCII data formats for transmitting data to the host 28.
If there is CRC corruption, then the interface board 18 is automatically reset to factory defaults.
The interface board 18 keeps its time synchronized with the pager system 26 by resetting the time when the network time changes by one minute. The network's time resolution is one minute, but the interface board 18 has one-second time resolution. The interface board time can be allowed to drift a user configurable time up to five minutes from the network time before it is reset.
All the power quality parameters are configurable from the host 26.
The interface board 18 uses a back-off and retry process when a message sent from the pager board 24 to the host 28 fails to be acknowledged. After three tries the interface board 18 resets the pager board 24 in case the pager board 24 has gone into a loop.
The parameters used to control the effect of all commands that can be executed by the interface board 18 can be changed from the host 28.
The source code for creating the instructions stored in the interface board 18 to perform the functions discussed above is reproduced as Appendix A, which forms a part of this specification.

Appendix A
Copyright ~ 2000 SmartSynch Inc.
#include<pI7c7~6.h>
#include <usartl6.h>
#include <defltkey.h>
#include "delay.h"
#include "iodef.h"
#include "fcode.h"
#include "time.h"
#defineNAK 0x15 #define SOH 0x01 #define EOT 0x04 #define ACK 0x06 #define CAN Oxl8 #detine HOURSYNC
#defineOFFSETI X12 #define OFFSET2 0x4800 #define OFFSET3 Ox~600 #define BLOCKSIZE 256 #define MAXADDRESS 62 #define CLIPSIZE 64 #define PAGER 1 #deFne METER 2 #define RTC 3 #define RTC_ALARM 4 #define RTC_NO_YEAR 5 #define ASCII 1 3D #define BIN 2 #detine PREAMBLE 4 #define XPTPASSW'ORD OX210C
#define FALSE 0 #detine TRUE 1 #define pqMivsDuration pqOffDelay #define pqMivsLimit pqReminderDelay #define pqMivsCount pqOfffime unsigned char abortFlag;
unsigned char economyFlag;
unsigned char sendFlag;
unsigned char onlyDates;
unsigned char binaryFlag;
unsigned char status;
unsigned char timer;
unsigned char pending:
unsigned char padHeaderEven;
unsigned char pqEnabled;
unsigned int iPacketSize;
unsigned char successCount;
unsigned int crc;
unsigned int sum:
unsigned int hemp:
unsigned int Itemp2;
unsigned int Itemp3:
unsigned int Istart:
unsigned int linden:
unsigned int IindexSave;

unsigned int packetStart;
unsigned int packetEnd;
unsigned int tempPacketStart unsigned int bytesSentOK;
unsigned int oldBytesSentOK;
unsigned char moreDataToMeter;
unsigned char justDataToMeter;
unsigned char meterResponse;
unsigned char IastSyncDay;
unsigned char firstPassSync;
unsigned char i,j;
unsigned char received_byte;
unsigned char last byte;
unsigned char iMCmnd;
unsigned int tempSum;
unsigned char dayOfWeek;
unsigned char interval;
unsigned char period;
unsigned int ilndex;
unsigned char hemp;
unsigned char itemp2;
unsigned char iTimer;
unsigned char totalPackets;
unsigned char totalPages;
unsigned char fullPages;
unsigned char partPacket;
unsigned char partPage;
unsigned char packetsForPage;
unsigned char pageNumber;
unsigned char blockNum:
unsigned char checksum;
unsigned char headerSize;
unsigned char headerCount;
unsigned char headAddress;
unsigned char headerOverhead;
unsigned char useTempAddress;
unsigned char valDegrade;
unsigned char attemptCount;
unsigned char pagerMessage;
unsigned char start0l;
unsigned char end0l;
unsigned char noMeterResponse;
unsigned char badHandshake;
unsigned char tempMask;
unsigned char timerMask;
unsigned char exceptionMask;
unsigned char exceptionFlag;
unsigned char spreadDelay:
unsigned char spreadCount;
unsigned char dateFound;
unsigned char neverFound;
unsigned char gotlncomingMessage;
unsigned int stringPointer;
unsigned int stringEnd;
unsigned char clipPointer;
char startMonth;
char startDay;
char endMonth;

char endDay;
struct time tag alarm;
struct time tag time;
unsigned char lastKnownYr;
unsigned char templ,temp2,temp3;
unsigned char meterlPassH,meterlPassM,meterlPassL;
unsigned char meter2PassH,meter2PassM,meter2PassL;
unsigned int modulelPass,module2Pass;
unsigned int password;
unsigned int paramCheckSum;
unsigned char whichPass;
unsigned char lastl,last2;
unsigned int offset;
unsigned char distance;
unsigned char LPintervaI,LPchannels,LPdays:
unsigned int LPavailable;
unsigned char bytesFromMeter;
unsigned int totalBytesFromMeter;
unsigned char totalBlocks;
unsigned char block;
unsigned char startByte;
unsigned char endByte;
unsigned char TxlInPnt;
unsigned char Tx 1 OutPnt unsigned char Tx2InPnt;
unsigned char Tx20utpnt;
unsigned char Rc l InPnt;
unsigned char RclOutPnt;
unsigned char Rc2lnPnt;
unsigned char Re20utPnt;
unsigned int IastCentiTime;
unsigned char firstMainLoopState;
unsigned char registerPagerState:
unsigned char checkAlarmsState;
unsigned char yyl6State;
unsigned char yylbState;
unsigned char putchPagerState;
unsigned char yyyState;
unsigned char yyl8State;
unsigned char xmod_dataState:
unsigned char pagerResponse:
unsigned char handshakeState;
unsigned char unlockState;
unsigned char isLPenabledState;
unsigned char isLPenabIedReturn;
unsigned char getLPdataState;
unsigned char pagerIncomingState;
unsigned char storeAddressState;
unsigned char setNicknameState;
unsigned char getMessageState;
unsigned char executePagerCommandState;
unsigned char executeMultipleState;
unsigned char pqPoIlState;
unsigned char sendCommandState;
unsigned char calcSendDataState;
unsigned char sendPageState:
unsigned char sendPingState;
unsigned char sendAlarmState;
unsigned char sendParsingArrayState:

unsigned char degradeState;
unsigned char exceptionPageState;
unsigned char writeTimeState;
unsigned char getMeterTimeState;
unsigned char getMeterTimeReturn;
unsigned char getPagerTimeState;
unsigned char writeTimeFrom;
unsigned char sendingPage;
unsigned char badSendCommand;
unsigned char MbuffPointer;
unsigned int meterDataChksum;
unsigned char meterSentLowChk;
unsigned char meterSentHiChk;
#pragma udata temp 1 buf unsigned char stlmode;
unsigned char st2mode;
unsigned char stlpulse;
unsigned char st2pulse;
unsigned char pulseCount;
unsigned char pulseState;
unsigned char pulseCycles;
unsigned char beepState;
unsigned char testState;
unsigned char testError;
unsigned char checkErrorState;
unsigned char unlockFlag;
unsigned char spreadTimerState;
unsigned char saveParCheckSumState;
unsigned int voltAfraction;

unsigned int voltBfraction;

unsigned int voltCfraction;

unsigned int unbaIVoItAvg;

unsigned char unbalanceStatus;

unsigned char unbalPollState;

unsigned int unbalVoltAfrac;

unsigned int unbalVoltBfrac;

unsigned int unbalVoltCfrac;

unsigned int unbalAngleAfrac;

unsigned int unbalAngIeBfrac;

unsigned int unbalAngIeCfrac;

unsigned int unbalVoItAB;

unsigned int unbalVoltBC;

unsigned int unbalVoltCA;

unsigned char AsicStatus;

unsigned int suspendPQtime;

unsigned char pqLogOff~tate;

unsigned char hexNibble;

unsigned int pqOfffime;

unsigned char pqMivsScanState;

unsigned char pqMivsBuffPnt;

unsigned char pqDateFound;

unsigned int pqDuration;

unsigned char messageStatus;

unsigned char scanMIVSonly;

unsigned char hadLPerror;

unsigned char compressionEnabled;

unsigned char sendCompletePQ;

unsigned char unanswered~5s;

unsigned char meterComFault;

unsigned char writeMeterSeriaIState;
unsigned char pqVerbosityEnabled;
unsigned char pagerResetHardState;
unsigned char pqReportNow;
unsigned int acOKtimer;
unsigned int messageEnd;
unsigned char terminatorFlag;
unsigned char messageReplied;
unsigned char runSSIcommand;
unsigned int demandSampIeTime;
unsigned char demandWindow;
unsigned char demandReport;
unsigned char sendParametersState;
unsigned char sendDemandReportState;
#pragma udata tempi buf unsigned char TMROchanged;
unsigned char RAM_L,RAM H;
unsigned int pulseTime;
unsigned int beepTimer;
unsigned int putchPagerTime;
unsigned int pagerComAbortTime;
unsigned int meterComAbortTime;
unsigned int degradeTime;
unsigned int exceptionTime;
unsigned char whichPQ;
unsigned char minPQ;
unsigned char maxPQ;
unsigned char storePQeventState;
unsigned char sendPQstatusState;
unsigned char reportPQeventState;
unsigned int pqPendingTime;
unsigned int timeOfDayInc;
unsigned int timeOfDaySync;
unsigned int timeOfDay;
unsigned int execMultiPointer;
unsigned char sendPageReturn;
unsigned char echoBuff(9];
unsigned char command[11];
unsigned char wasReset;
unsigned char addCRCftag;
unsigned char synchToAtomicTime;
unsigned int mTime:
unsigned int prime;
unsigned int deltaTime;
unsigned char deltaTimeNegative;
unsigned char timeSynchMode;
unsigned char writeDecimalDivider;
unsigned char writeDecimalTemp;
unsigned char preamble[4];
unsigned int pagerPoIlTimeout;
unsigned char pagerStatusl;
unsigned char pagerStatus2;
unsigned char reestablishPagerState;
unsigned char noPagesToday:
unsigned char thisMessageUnlocked:
unsigned int messageLength;
unsigned int messageCRC;
unsigned int cryptData;
unsigned int cryptDataLength;

unsigned int expiryTime;
unsigned int expiryDate;
unsigned char eeBuffInPnt;
unsigned char eeBuffOutPnt;
unsigned char pqBuffInPnt;
unsigned char pqBuffOutPnt;
unsigned char buffroPromState;
unsigned char pqLogToPromState:
unsigned char tempPromData;
unsigned int tempPromAddress;
unsigned int pqLogAddress;
unsigned char uritingEeprom;
unsigned char pqLogRequest unsigned char pqScanEnd;
unsigned char eventType;
unsigned int pqScanPointer;
unsigned char timeChanged;
unsigned char timeChangeState;
unsigned char timeAdjustedToday;
unsigned int pqLogMask;
unsigned int sleepTime;
unsigned char outageReportSent;
unsigned char optionToGet;
unsigned char getOptionState;
unsigned char pagerOptionl;
unsigned char pagerOption2;
unsigned char pagerDiagnostics;
unsigned char pagerDiagCounter;
unsigned char oldOptionl;
unsigned char oldOption2;
unsigned char oldStatusl;
unsigned char oldStatus2;
unsigned char newDestination;
unsigned char autoRegisterDestination:
unsigned char yy~l7State;
unsigned char productlnfo;
unsigned char variableOffset;
unsigned char variableNumber;
unsigned char sleepiest;
unsigned char killPower;
unsigned char postDecimalCharacter:
unsigned char serialAtStart;
unsigned int crcEcho;
unsigned char autoRegisterData;
unsigned char pqPollEnabled;
unsigned int demandValue;
unsigned char pulseClear;
unsigned char ioState;
unsigned char demandTimer;
5D unsigned char runTimerState;
unsigned char checkBatteryState;
unsigned char updateAnalogInputsState;
unsigned char IBSenseH;
unsigned char IBSenseL;
unsigned char TempSenseH;
unsigned char TempSenseL;
unsigned char VBSenseH;
unsigned char VBSenseL;
unsigned int batteryTimerl;
unsigned int batteryTimer2;

#pragma udata tempt buf unsigned int RamDATA;
unsigned int RamADDRESS;
union of tag{
unsigned char OLCHAR;
struct OLbits{
unsigned OL0:1;
unsigned OL1:1;
unsigned OL2:1;
unsigned OL3:1;
unsigned OL4:1;
unsigned OL5:1;
unsigned OL6:1;
unsigned OL7:1;
bits;
;OUTLATCH;
union ib tag{

unsigned char IBCHAR;

struct IBbits{

unsigned IB0:1;

unsigned IB1:1;

unsigned IB2:1;

unsigned IB3:1;

unsigned IB4:1;

unsigned IB~:I;

unsigned IB6:1;

unsigned IB7:1;

; bits;

INBUFF;

unsigned int tempGarbage;

unsigned char echoSize;

unsigned int TMROcentiSeconds;

unsigned int tempRamADDRESS;

unsigned int tempRamDATA;

unsigned char tempTBLPTRL;

unsigned char tempTBLPTRH;

unsigned char tempTBLATL;

unsigned char tempTBLATH;

unsigned char pqlnProgress;
unsigned int pqReportTime;
unsigned char pqEvent;
unsigned int pqValue;
unsigned int pqComparison;
unsigned int pqStartTime;
unsigned char pqFault;
unsigned char pqReportSize;
unsigned char pqReportFlag;
unsigned char pqDurationLimit;
unsigned char pqReportDelay;
unsigned char pqReminderDelay;
unsigned char pqEventStatus;
unsigned int pqEventDuration;
unsigned char pqOffDelay;
unsigned int fracMultiplier;
unsigned int fracDivider;

rom char sDefauItEmail[] _ "Edev a xptechnology.com";
rom unsigned char sDefaultPin[9] _ {8,I,0x50,Ox34,Ox31,Ox36,Ox31,Ox30,Ox30};
rom char sDefaultInfoCommands[] _ "71,c0,8a,72,6d,1f c4,c3,a6,08,8f,9B,07,06;";
rom char sDefaultlnfoParsing[22] _ {Oxc0,0,1,Oxff,0x72,0,3,Oxff,Oxc3,0,2,Oxff,Oxa6,0,2,Oxff,Ox06,OxfU,O,Oxf0,3,Oxf f};
#define DEFAULT_INFO_PARSE_LENGTH 22 #define PQ SETUP_LENGTH 108 rom unsigned char sDefaultPQsetup[PQ SETUP_LENGTEI] _ {
40,80,50,1,10,2,0,200,20,20,240,180,1,10,2,0,255,13, 40,80,50,1,10,2,0,200,20,20.240,20,1,10,2,0,255,13, 5,20,10,1,30,2,0,200,40,20,240,180,1,30,2,0,255,13, 5,20,10,1,30,2,0,200,40,20,240,180,1,30,2,0,255,13, 1,6,2,15,30,15,0,200,40,20,240,180,1,30,2,0,255,13, 50,90,80,1,60,10,0,200,40,1,15,3,1,255,1,0,1,1 };
rom char sDefaultKeySeed[17] _ {defaultSeed};
rom char sFailed[] _ " Failed! »"~
rom char sDemandDisabled(] _ "/Demand Disabled";
rom char sPQDisabled[] _ "/PQ Disabled";
rom char sTemporarily[] _ " Temporarily";
rom char sPingVersion[J = "V J1.28 LV";
rom char sAutoReg[4] _ {'J',OxOl.Ox28,0; ;
rom char sBF03FF[4] _ {Oxbf;Ox03,Oxff,O};
rom char sMacc o[] _ " MACC O";
rom char sOut[] _ "OUT";
rom char sUnbal[] _ "UNBAL";
rom char sMivs[] _ "MIVS";
rom char s_com[] _ "/com";
rom char sOK_[] _ "OK ";
rom char s_IN_[] _ " IN ";
rom char sNOV~_[] _ "NOW ";
rom char sC_OUT_[] _ "C OUT ";
rom char sMIVS-(] _ "MIVS ";
#pragma romdata sRevision = 0x4000 rom char sRevision[] _ ".11.28";
rom char sBootBanner[] _ "RevJ 1.28";
rom char sSleepTest[] _ "Sleep Test";
#pragma romdata sCodeSpace = 0x4010 rom int sCodeSpace[6] _ {OxSfff,Ox7fff,Ox9fff,Oxbfff,Oxdfff,Oxffff;;
rom char sKilITest[] _ "Kill Test";
rom char sNewLine[5] _ {OxOa,OxOd,'>','>',0};

/************************************************/
// Main Program //
/************************************************/
void main( void);
/************************************************/
// External Functions in "1a2465.c" //
/************************************************/
void initI2CQ;
char read_ext eeprom(unsigned int address);
void write eeprom_noDelay(unsigned int address, unsigned char data);
void set high endurance(unsigned char block);
/************************************************/
// External Functions in "lpef8583.c" //
/************************************************/
unsigned char to_dec(unsigned char datain, unsigned char maskhi);
unsigned char to_bcd(unsigned char datain);
void rte set_datetime(struct time tag *time);
void rtc_get datetime(struct time tag *time);
void rtc clear_irq(void);
void rtc set irq(void);
void rtc set alarm_datetime(struct time tag *alarm);
void rtc get alarm_datetime(struct time tag *time);
unsigned char rtc_read(unsigned char address);
void rte write(unsigned char address. unsigned char data);
/************************************************/
// Interrupt Service Routines //
/************************************************/
void _TMRO(); // Timer 0 Overflow void_INT(); // RTC alarm void _PIRQ; // Peripherals interupt /************************************************/
// Serial Communication //
/************************************************/
unsigned char kbhitPager(void);
unsigned char getchPager(void):
void putchPager(unsigned char ch);
void putePager(unsigned char ch):
unsigned char kbhitMeter(void);
unsigned char getchMeter(void);
unsigned char getc(unsigned char whichPort);
void putchMeter(unsigned char ch);
void prepReceive(unsigned char whichPort);
void putHexIntPager(unsigned int hexData);
//void putHexCharPager(unsigned char hexData);
/************************************************/
// Eeprom buffering and PQ Log functions //

/*** *********************************************/

void eepromBuffer(unsigned int address, unsigned char data);

void bufferToEeprom(void);

pqLog(unsigned char data);
void void pqLogToEeprom(void);

unsigned int pqScanRemaining(void);

void pqScan(void);

void pqScanReset(void);

getEventType(void);
void /************************************************/
// External RAM functions //
/************************************************/
unsigned int ramReadInt(void);
void ramWriteInt(unsigned int integerToWrite);
unsigned char ramReadUchar(void);
/************************************************/
t0 // Conversions //
/************************************************/
char to ascii(char din);
char toint(char cin);
unsigned int meterFIoatToInt(unsigned char baseExponent);
t5 /************************************************/
// Pager Interface (CLP commands & xmodem) //
/************************************************/
void yyy(void);
20 void yyl6(void):
void yy 17(void): // get configuration and product info.
void yy 18(void);
void getOption(void);
unsigned char yy 1 a(char option, char param);
25 void yylb(void);
void check( unsigned char xmitbuf);
char xmod_data( void);
void pagerResetHard( void);
void reestablishPager(void):
/************************************************/
// Meter Interface (DGCOM) //
/************************************************/
char handshake( void);
char unlock( void);
void findClip( unsigned char command);
char clipFound( void);
void getLPdata( void);
char isLPenabled( void);
void sendLPerror( void):
void calcLPsize(void):
void calcStart( void);
void sendCommand(void);
void executeMultipleCommands(unsigned int start);
void compressLPdata(void);
void getChannellnfo(void); // Restores OIdV and cMode for this channel.
void compressScan(void);
/************************************************/

// Power Quality Functions //

/************************************************/

void storePQevent(void);

unsigned int getDecimal(void);

void sendPQstatus(void);

voidwriteDecimaIToSendBuffer(unsigned int decNumber);

void pqCalc(void);

void pqAddToReport(unsigned char addToReport);

void pqAddIntToReport(unsigned int IntToReport);

void pqLogMeterComFault(void);

voidpqPoll(void);

void calcComparisons(void);

unsigned int fractionMultiply(unsigned int fracData);

void pqLogOff(void);

void pqMivsLog(void);

void pqMivsScan(void);

void pqUnbalanceCalc(void);

unsigned int sqrt(unsigned int sqData, unsigned int guess);

void pqUnbalancePoll(void);

void checkDemand(void);

/************************************************/
// Address Handlers //
/************************************************/
void storeAddress( unsigned char tempFlag);
void setNickname(void);
/************************************************/
// Incoming Message Handlers //
/************************************************/
unsigned char getByte( unsigned int address);
char getMessage( void);
char matchPassword( unsigned int password):
unsigned char checkExpiration(void):
void getChar(void);
void rc4crypt(void);
void executePagerCommand(void);
void runTimer(void);
/************************************************/
// Outgoing Message Handlers //
/************************************************/
void degrade( void);
void sendPage( void);
void BitwiseCrcl6 (int bit);
void CaIcCRConRamAddress(unsigned int address);
void CaIcCRC(unsigned char data);
void addCRC();
void calcSendData( void);
void sendPing( void);
void spreadTimer(void):
void exceptionPage(void);
void reportPQevent(void);
void sendAlarm(void);
void checkMeterErrors(void);
void writeTime( char from);
void writeMeterSerial( void);
void sendParameters(void);
void sendDemandReport(void);
void ioStateUpdate(void);
void convertAndWriteToSendBuffer(unsigned char data):
void writeToSendBuffer(unsigned char data);
/************************************************/
// Setup //
/************************************************/
char invalidParameters(void);
void getParCheckSum(void);
void saveParCheckSum(void);
void putStringInEeprom( static const rom char *data); // for null terminated strings void putArraylnEeprom( static const rom char *data): // for strings that contain nulls void putArrayInExtRam( static const rom char *data); // for strings that contain nulls void writeStringToSendBuffer( static const rom char *data); // for null terminated strings void putArrayInRTC( static const rom unsigned char *data); // for strings that contain nulls void commissionButtonPress( void);
void registerPager( void);
//void syncClocks( void);
void getPagerTime( void);
char getMeterTime( void);
void getSyncTime( void);
char verifyTime(struct time tag *time);
void timeChange(void);
void setParsingArray(void);
void sendParsingArray(void);
void clipEepromToRam(void);
void codeCRC(void);
void codeCRCblock(void);
void read_flash(void);
void updateTimeOfDay(void);
/************************************************/
// Miscellaneous //
/************************************************/
char delay 3sec(unsigned char howMany, unsigned char pattern);
void delay sec(unsigned int howMany);
void delay ms-plus(unsigned int howmany); // delay-ms plus check clear switch and latch if pushed.
void pulse(void);
void testSy~stem( void);
void checkBattery(void);
void updateAnalogInputs(void);
void testlnFlash(void);
void putstring( static const rom char *data, char w~hichPort ):
void powerSaveSleep(void);
/************************************************/
// 32 bit floating point functions. //
/************************************************/
void uintl6ToFloat32(unsigned int datal6, unsigned char float32Address);
int float32Tolntl6(unsigned char float32Address);
void floatMultiply(unsigned char addressA, unsigned char addressB, unsigned char addressAtimesB);
void floatDivide(unsigned char addressA, unsigned char addressB, unsigned char addressAoverB);
void floatAdd(unsigned char addressA, unsigned char addressB, unsigned char addressApIusB);
void floatChangeSign(unsigned char addressA);
void cos128(unsigned int cosData, unsigned char float32Address);
void FL01632U(void);
void FPD32(void);
void FPM32(void);
void FPA32(void);
void INT3216(void);
voidFXM1616U(void);
void FXD3216U(void);
//void calcCos(void);

/************************************************/
l/ Main Program //
/************************************************/
#pragma code main = 0x8000 void main( void)( unsigned char i;
OpenUSARTI(USART_TX_INT OFF&USART_RX_INT_OFF&USART ASYNCH MODE&USART EIGH
T BIT&USART CONT RX, 5);
OpenUSART2(USART_TX_INT OFF&USART RX INT OFF&USART ASYNCH MODE&USART SIGH
T BIT&USART CONT RX, 5);
PAGER_PWR_OUTP = I; // Turn Pager On PAGER_RESET_EN_OUTP = 0; // Turn Pager Reset Enable Off PAGER_RESET OUTP = 1; // Turn Pager Reset inactive TOSTA = Ob01100000;
nCLEAR S W_LED_IO = 0;
nCLEAR S W SET DDRB = I ; // Turn Switch 2 off by setting it to be an input autoRegisterData = 0;
if(!nCLEAR_SW_LED_IO && !nMAG-SWITCH-INP) autoRegisterDcstination ='T;
else if(!nCLEAR SW LED_l0 R& !nTEST JUMP-INP);
received_byte=rtc_read(OxF2);
if(received byte==OxD6);
autoRegisterData = 1;
;
else {
rtc_write(OxF2, OxD6);
delay_ms(10); // Just in case a reset immediately after a rtc_write disturbs the write KILL_PWR_OUTP = l:
rtc write(OxF2, OxFF);
i autoRegisterDestination ='8';
else autoReeisterDestination = 0:
nSTATUS2_LED_OUTP = 0; // Turn LED 2 on nSTI LED_LATCHED_OP = 0; // Turn LED 1 on RW_EXT_IO;
set high endurance(6);
RAM_H = Oxdd;
RAM_L = 0x47:
TMROcentiSeconds = 0;
Install_TMRO( TMRO):
INTSTAbits.TOIE = I
Install_PIV( PIR);
INTSTAbits.PEIE = 1;
CPUSTAbits.GLINTD = 0;
sleepiest = 0;
Tx I InPnt=0;
Tx I OutPnt=0;
Tx2InPnt=0:
Tx2OutPnt=0;

firstMainLoopState=1;
putchPagerState=0;
yyyState=0;
yy 16State=0;
yyl7State=0;
yy 18 State=0;
getOptionState=0;
yy I bState=0;
xmod_dataState=0;
'I0 pagerResetHardState=0;
reestablishPagerState=0;
handshakeState=0;
unlockState=0;
isLPenabIedState=0;
'C5 getLPdataState=0;
pagerIncomingState=0;
storeAddressState=0;
setNicknameState=0;
getMessageState=0;
20 executePagerCommandState=0;
runTimerState=0;
executeMultipleState=0;
registerPagerState=0;
checkAlarmsState=0;
25 sendCommandState=0;
calcSendDataState=0;
sendPageState=0;
sendPingState=0;
sendAlarmState=0;
30 sendParsingArrayState=0;
degradeState=0;
exceptionPageState=0;
writeTimeState=0;
getMeterTimeState=0;
35 getPagerTimeState=0;
sendingPage=0;
sendPageReturn=0;
checkErrorState=0;
sendParametersState=0;
40 sendDemandReportState=0;
pulseState=0;
beepState=0;
testState=0;
checkBatteryState=0;
45 batteryTimerl=0;
batteryTimer2=0;
updateAnaloglnputsState=1;
pulseCycles=0;
pulseTime=0;
50 unlockFlag=0;
addCRCflag = 0;
pagerMessage = 0;
spreadTimerState = 0;
timeAdjustedToday=1;
55 timeChanged = 0;
timeChangeState = OxFF: // non existent statejust to stop from being run b~~
timer function // until after startup.
writeMeterSeriaIState = 0;
pqReportNow=0;
60 sleepTime=0;

outageReportSent=0;
pagerDiagnostics=0;

pagerDiagCounter=0;

oldOption 1=0;

oldOption2=0;

oldStatus 1=0;

oldStatus2=0;

pqPollState=0;

AsicStatus=0;

sendPQstatusState=0;

storePQeventState=0;

reportPQeventState=0;

pqLogOffState=0;

pqMivsScanState=0;

scanMIVSonly=0;

pqPendingTime=0;

timeOfDayInc=0;

timeOfDaySync=0;

updateTimeOfDay();

pqReportSize=0;

suspendPQtime=0;

unbalanceStatus=0;

unbaIPoIlState=0;

compressionEnabled=1;

sendCompletePQ=0;

unanswered~5s=0;

meterComFault=0;

saveParCheckSumState=0;

acOKtimer-0;

runSSIcommand = 0;

timeSynchMode = 2; // Set RTC
to meter time only.

serialAtStart = 0;

delay-sec(3); // wait 1 ~ seconds for pager to stabilize // Loader timeout + delay + check for invalid parameters prepReceive(PAGER);
prepReceive(METER);
nSTILED_LATCHED_OP=1; //Turn LED
1 off R W_EXT_IO;

nSTATUS2_LED_OUTP = 1; // Turn LED 2 off delay=ms_plus(250);

nSTATUS2_LED_OUTP = // Turn 0; LED 2 on nSTI LED_LATCHED_OP = // Turn 0; LED 1 on RW EXT_IO;

delay_ms_plus(2~0);

nST I LED_LATCHED_OP = // Turn l ; LED 1 off RW_EXT_IO;

nSTATUS2 LED OUTP = 1; // Turn LED 2 off newDestination = read ext eeprom(OxOC);
timerMask = read ext eeprom(Ox 10);
exceptionMask = read ext_eeprom(Ox 1 1 );
spreadDelay=read_ext eeprom(Oxl2);
meter 1 Passes=read_ext~eeprom(Ox 13);
meterlPassM=read_ext eeprom(Oxl4);
mcterlPassL=read_ext eeprom(OxlS);
meter2PassH=read eat eeprom(Oxl6);

meter2PassM=read_ext_eeprom(Ox 17);
meter2PassL=read_ext eeprom(OxlB);
modulelPass=read_ext_eeprom(Oxl9);
module( Pass = module( Pass«8;
module 1 Pass += read ext eeprom(Ox I a)&Oxff;
module2Pass=read_ext_eeprom(Ox 1 b);
module2Pass = module2Pass«8;
module2Pass+=read ext_eeprom(Oxlc)&Oxff;
headerSize=read_ext eeprom(Oxld);
economyFlag=read ext eeprom(OxSd);
binaryFlag=read_ext eeprom(OxSe);
compressionEnabled=read_ext eeprom(OxSf);
pqVerbosityEnabled=read_ext eeprom(Oxlfd);
synchToAtomicTime=read_ext_eeprom(Ox 1 fc);
killPower=read_ext eeprom(Oxlfb);
Itemp2 = 0x60;
clipEepromToRamQ;
pqPollEnabled=0; // Enable after initiallizing, resetting pager and checking for valid eeprom data.
pqEnabled=real( ext eeprom(Oxl9A);
useTempAddress = 0;
successCount=rtc read(OxlO);
IastKnownYrrtc read(Oxl2);
fastSymcDay=rtc read(Oxll);
bytesSentOK=rtc_read(Ox l 5);
bytesSentOK = bytesSentOK«8;
bytesSentOK += rtc read(Ox 16)&Oxff;
noPagesToday = 0;
eeBuffInPnt=0;
eeBuffOutPnt=0;
pqBuffInPnt=0;
pqBuffOutPnt=0;
bufffoPromState=0;
pqLogToPromState=0;
writingEeprom=0;
pqLogAddress = rtc_read(OxF3);
pqLogAddress = pqLogAddress«8;
pqLogAddress += rtc_read(OxF4)&Oxff;
if((pqLogAddress<Ox600) /~ ((pqLogAddress&Ox7fft~>OxIFFF)) pqLogAddress = 0x600;
start0l=0;
end01=Oxff;
abortFlag = FALSE;
onlyDates = FALSE;
pending=FALSE;
wasReset = 1;
moreDataToMeter = 0;
justDataToMeter = 0;
meterResponse = 0;
gotIncomingMessage=0;
dayOfWeek=0;
firstPassSync=0;
stringPointer-0x80;
stringEnd=stringPointer+PQ_SETUP LENGTH;
putArrayInRTC(sDefaultPQsetup);
for (i=Oxl8;i<Ox40;i++) // Clear Timer Alarm Times rtcyrite(i,0);

CA 02332297 2001-O1-25 , received byte = read ext eeprom(OxOd); // Rev. Flag Change if you want to if(received byte != 0x18) // force an auto-commission.
eepromBuffer(OxOd,Ox 18);
RamDATA = 0;
RamADDRESS = 0x4400;
for (whichPQ=O;whichPQ<OxEA;whichPQ++){ // Clear PQ temporary variables RAM WRITE;
r for (whichPQ=O;whichPQ<6;whichPQ++) // Calculate PQ Comparison values.
calcComparisonsQ;
RamADDRESS=0x5000; // Mirror Get Meter Info commands in RAM
for(i=O;i< 128;i++) {
RamDATA = read ext eeprom(Ox 11 A + i);
RAM WRITE;
{ _ RamADDRESS=Ox4C00; // Mirror Timer Event Definitions in RAM
for(ltemp=OFFSETI;ltemp<Ox600;ltemp++){
RamDATA = read ext eeprom(ltemp);
RAM WRITE;
i _ RamADDRESS = 0x5200; // Clear MIVS Buffer RamDATA = 0;
pqMivsBuffPnt=0;
do {
RAM_WRITE;
pqMivsBuffPnt++;
while(pqMivsBuffPnt);
demandSampleTime=0;
demandWindow=read_ext eeprom(OxlC6);
RamADDRESS = 0x5400; // Clear Demand Sample Array RamDATA = 0;
demandReport=0;
do {
RAM_V'RITE;
demandReport++;
while(demandReport);
RamADDRESS = Ox~39F; // Clear Demand Temporary variables for(i=0; i< 17; i++) {
RAM_WRITE;
for(whichPQ=O;whichPQ<l7;whichPQ++)( // Load RAM copy of secret key.
received byte = read_ext_eeprom(Ox 1 EO+whichPQ);
RamADDRESS = Ox538C + whichPQ;
RamDATA = received byte;
RAM WRITE;
i rtc write(O.Ox04); // control register = timer on, 32728 Hz, alarm enabled rtc write(8.Ox30); // alarm control = dated alarm. interrupt disabled rtc clear irq();
IastCentiTime = TMROcentiSeconds:
for(;;) {
PqPoll();
CPUSTAbits.GLINTD=1;

lastCentiTime = TMROcentiSeconds;
CPUSTAbits.GLINTD = 0;
TMROchanged=0;
CIrWdt();
1//// Roll Back Timers /////
if (lastCentiTime>32768){
TMROcentiSeconds-=32768;
IastCentiTime-=32768;
if(pulseTime>32768) pulseTime-=32768; else pulseTime=0;
if(putchPagerTime>32768) putchPagerTime-=32768; else putchPagerTime=0;
if(pagerComAbortTime>32768) pagerComAbortTime-=32768; else pagerComAbortTime=0;
if(meterComAbortTime>32768) meterComAbortTime-=32768; else meterComAbortTime=0;
if(degradeTime>32768) degradeTime-=32768; else degradeTime=0;
if(exceptionTime>32768) exceptionTime-=32768; else exceptionTime=0;
if(timeOfDaylnc>32768) timeOfDayInc-=32768; else timeOfDaylnc=0;
if(timeOfDaySync>32768) timeOfDaySync-=32768; else timeOfDaySync=0;
if(suspendPQtime>32768) suspendPQtime-=32768; else suspendPQtime=0;
if(demandSampleTime>32768) demandSampleTime-=32768; else demandSampleTime=0;
if(acOKtimer) {
if(acOKtimer>32768) acOKtimer-=32768;
else acOKtimer=1;
for(i=10;i<13;i++) {
RamADDRESS = 0x4400 + i*Oxl2:
RAM_READ;
pqInPro~ress = RamDATA&OxOf:// Lower nibble only if(pqlnProgress);
RamDATA ~= pqlnProgress+1; // Will only increment up to 0x10 RamADDRESS = Ox4-X00 + i*Ox 12;
RAM WRITE;
r pqEvent=0;
pqMivsLog();
if(meterComFault && meterComFault!=6){
meterComFault += Ox 10;
pqLogMeterComFault();
i ;
// timeOtDay is incremented every 2 seconds so that an int can represent a 27 hour period. (27 // hours allows time delays of 3 hours to be simply added to timeOfDay.) This is created // for time stamps for PQ events but there is no reason it could not be used for other purposes.
// Once a minute the timeOfDay is synchronized to the RTC. timeOfDaylnc is used to increment the // time every two seconds. timeOfDaySync is used to synchronize to the RTC.
When timeOfDay rolls // over 24 hours, a day stamp is added to the log, all set times have 24 hours subtracted from // them, all pqInProgress flags that are set are incremented to indicate that their start times are // actually from the previous day.
if(timeOfDaylnc<lastCentiTime) timeOfDay++;
timeOfDaylnc = lastCentiTime+200;
unbalanceStatus=1; // To run Voltage Unbalance Calc. ever~~ 2 sec.
;
if(timeOfDaySyne<IastCentiTime R:& !timeChangeState);
timeOfDaySync = timeOfDay;
updateTimeOfDay();
if(timeOfDay<10800 && timeOfDaySyne>32400){ // RTC < 0600 & timeOfDay > 1800 // This is a date roll over.

if(dayOfWeek) dayOfWeek++; // Used by weekly events.
time.yr = time.yr%4;
time.yr = time.yr«4;
time.yr += time.mth;
pqLog(to_bcd(time.day));
pqLog(time.yr);
pqLog(0); // Date Stamp for(i=O;i< 10; i++) {
RamADDRESS = 0x4400 + i*Oxl2;
RAM_READ;
pqInProgress = RamDATA&OxOf;// Lower nibble only if(pqlnProgress){
RamDATA ~= pqlnProgress+1; // Will only increment up to 0x10 RamADDRESS = 0x4400 + i*Ox 12;
RAM WRITE;
RamADDRESS = 0x4404 + i*Ox 12;
pqReportTime = ramReadlnt();
if(pqReportTime);
if(pqReportTime>43200) pqReportTime -= 43200;
else pqReportTime= 1;
RamADDRESS = 0x4404 + i*Oxl2;
ramWritelnt(pqReportTime);
if(pqPendingTime) {
if(pqPendingTime>43200) pqPendingTime -= 43200;
else pqPendingTime= 1;
if(pagerPollTimeout>43200) pagerPoIlTimeout -= 43200;
else pagerPollTimeout = 0;
if(sleepTime>43200) sleepTime -= 43200;
else sleepTime = 0;
RamADDRESS = Ox53A8:
pqReportTime = ramReadlnt();
if(pqReportTime);
if(pqReportTime>43200) pqReportTime -= 43200;
else pqReportTime = 1;
RamADDRESS = Oxi3A8;
ramWriteInt(pqReportTime);
pqReportTime = ramReadIntQ;
if(pqReportTime) if(pqReportTime>43200) pqReportTime -= 43200;
else pqReportTime = 1;
RamADDRESS = Ox~3AA;
ramWritelnt(pqReportTime);
i v timeOfDaySync = lastCentiTime+6000;
timeOfDaylnc = IastCentiTime+200;
i if (pulseCycles ~.& pulseTime<IastCentiTime) pulse();
if (putchPagerState && putchPagerTime<lastCentiTime) putchPager(0);
if (yyyState) yyy();
if (yy l6State) yy 16Q;
if (yy 18State) yy 18();
if (yy 17State) yy 17Q;
if (getOptionState) getOption();

if (yy I bState) yy I b();
if (xmod dataState) xmod_dataQ;
if (pagerResetHardState) pagerResetHard();
if (reestablishPagerState) reestablishPager();
if (handshakeState) handshake();
if (unlockState) unlock();
if (isLPenabledState) isLPenabledQ;
if (getLPdataState) getLPdata();
if (storeAddressState) storeAddress(0);
if (setNicknameState) setNickname();
if (getMessageState) getMessage();
if (executePagerCommandState) executePagerCommand();
if (runTimerState) runTimerQ;
if (executeMultipleState) executeMultipleCommands(0);
if (registerPagerState) registerPager();
if (checkAlarmsState) checkAlarmsQ;
if (sendCommandState) sendCommand();
if (calcSendDataState) calcSendData();
if (sendPageState) sendPage();
if (sendPingState) sendPingQ;
if (sendAlarmState) sendAlarm();
if (sendParsingArrayState) sendParsingArray();
if (checkErrorState) checkMeterErrors();
if (sendParametersState) sendParameters();
if (sendDemandReportState) sendDemandReport();
if (degradeState) degrade();
if (exceptionPageState) exceptionPage();
if (writeTimeState) writeTime(0);
if (getMeterTimeState) getMeterTime();
if (getPagerTimeState) getPagerTime();
if (sendPQstatusState) sendPQstatusQ;
if (storePQeventState) storePQevent();
if (reportPQeventState) reportPQevent();
if (pqLogOffState) pqLogOff();
if (pqMivsScanState) pqMivsScan();
if (spreadTimerState) spreadTimer();
if (timeChangeState) timeChange();
if (writeMeterSeriaIState) writeMeterSerial();
if (saveParCheckSumState) saveParCheckSumQ;
if (pqPendingTime && pqPendingTime<timeOtDay && !reportPQeventState) reportPQeventQ;
bufferToEeprom( );
pqLogToEeprom();
if (!nMAG SWITCH INP && !pqLogOff~tate) pqLogOffQ;
checkBatteryQ;
if(updateAnalogInputsState) updateAnalogInputs();
switch (firstMainLoopState){
case 1:
pqLog(OxFD);
tempi = invalidParametersQ;
yy 1 a(4,1 ); // turn off notification (LFs) pagerResetHard();
firstMainLoopState=2;
break;
case 2:
if(!pagerResetHardState){
y~~la(OxOa,Ox80); // enable system updates to Greemvich Mean Time pagerResetHardQ;
firstMainLoopState=3;

break;
case 3:
if(! pagerResetHard State) {
PAGER PWR OUTP = 0;
yyl8Q;
firstMainLoopState=4;
break;
case 4: // Wait until registerPager done.
1D if(!yylBState){
PAGER_PWR OUTP= 1;
if(!pagerResponse) autoRegisterData+=2;
if (!nCLEAR SW LED I0 ~~ tempi){ // If switch 2 pressed or garbage in eeprom then recommission if(!nCLEAR_SW_LED_IO){
if(autoRegisterDestination='T) newDestination = 0x20; // Lafayette if(autoRegisterDestination='8') newDestination = 0x30; // Calgary else newDestination = 0x40; // Manual in the Field Set to Defaults T

else newDestination = Ox 10;
eepromBuffer(OxOC,newDestination); // using default destination commissionButtonPress();
pqPollEnabled= I;
registerPager();
firstMainLoopState=~;
break;
case 5:
if(registerPagerState=0) {
timeChangeState = 0;
timeChange();
firstMainLoopState=6;

break:
case 6: // Wait until syncClocks done.
if(timeChangeState---0) firstMainLoopState=7;
break;
case 7:
initAlarmsQ;
checkAlarms();
firstMainLoopState=8;
break;
case 8: // Wait until checkAlarms done.
firstMainLoopState=9;
break;
case 9:
setAlarmQ; // No Wait.
firstMainLoopState=0;
break;
case 0:
if ((!nTEST JUMP INP ~~ testState) && !sendingPage) testSystem();
switch(pagerIncomingState) {
case 0: // Idle if(!testState && !sendingPage && nTEST JUMP' INP &&
pagerComAbortTime<lastCentiTime);
sendingPage=I;
if(runTimerState=1 );
runTimerState++;

pagerIncomingState=20;
}

else if(demandReport%2) // 1 or 3 but not sendDemandReport();

else if(runSSIcommand){

messageReplied=0;

messageStatus = ACK;

getMessageState = 5;

pagerlncomingState=3;

}

else {

yy 1 b();

pagerIncomingState=1;

rtc_get datetime(&time);

if(timeAdjustedToday==time.hr && !timeChangeState && ((time.min=7 && time.sec>30) ~~ time.min>7)){

if(timeSynchMode!=Oxff) {

timeChanged=1;

if(time.hr-2) timeSynchMode=1;

else timeSynchMode=0;

l l else if(time.hr--2){

timeChanged=I ;

}
}

r break;

case 1:

if(!yy 1 bState) {

if(pagerResponse==1) // Should differentiate neg. vs no response pagerIncomingState=2;

else if(outageReportSent > 1 ){

powerSaveSIeepQ;

}

else {

stlpulse=1;

st2pulse=OxF;

pulseCycles=4;

if(runTimerState---1 ) {

runTimerState++~

pagerlncomingState=20;

i else if(pagerDiagnostics){

pagerIncomingState=10;

i else {

sendingPage=0;

pagerlncomingState=0;

oldOption I=0;

oldOption2=0:

oldStatus 1=0:

oldStatus2=0:

}
break;
case 2:
messageReplied=0;
putcPager(NAK);

CA 02332297 2001-O1-25 , getMessage();

pagerIncomingState=3;

break;

case 3:

if (!getMessageState){

runSSIcommand = 0;

if (pagerMessage){

pagerIncomingState=4;

else {

sendingPage=0;

pagerIncomingState=0;

if(! messageRepl ied) {

if(exceptionMask&8 && exceptionFlag!=~){

exceptionFlag=4;

exceptionPageQ;

if(exceptionMask&0x10 && exceptionFlag-~){

exceptionPage();

;

break;

case 4:

executePagerCommandQ;

pagerIncomingState=5;

break;

case 5:

if (!executePagerCommandState && !saveParCheckSumState){

pagerIncomingState=7;

pqLog(messageStatus);

pqLog(timeOlDay&OxF F);

pqLog(timeOfDay8);

pqLog(pagerMessage);

if(exceptionMask&8 && messageReplied!=2){

sendingPage=0;

exceptionFlag=4;

exceptionPage();

pagerIncomingState=6;

l L

break;

case 6:

if(!exceptionPageState && !sendingPage);

sendingPage=I ;

pagerlncomingState=7;

r break;

case 7:

messageReplied=1;

messageStatus = ACK;

getMessageState = 5;

pagerIncomingState=3;

break;

case 10:

if(5<pagerDiagCounter++) {

pagerDiagCounter-0;

pagerIncomingState=1 1;

optionToGet = 0x09;

getOptionQ;
else {
pagerIncomingState=12;
yy 18();
break;
case 11:
if(! getOptionState) {
if((pagerOption I !=oldOption 1 ) ~~ (pagerOption2!=oldOption2)) {
pqLog(pagerOption2);
pqLog(pagerOption 1 );
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox78);
oldOptionl =pagerOptionl;
oldOption2 = pagerOption2;
pagerlncomingState=0;
sendingPage=0;
break case 12:
if(!yy 18State) {
pagerIncomingState=0;
sendingPage=0;
r break;
case 20:
if(!runTimerState){
pagerIncomingState=0;
sendingPage=0;
break:
if(nAC
OK_INP&&(exceptionMask&I)&&!pending&&!exceptionPageState&&!sendAlarmState);
rtc-get datetime(&alarm);
received byte = read_ext_eeprom(Ox 1 F9):
alarm.hr += received-byte/60;
alarm.min += received byte%60;
validateTime(&alarm);
if(alarm.hr>=24) alarm.hr-=24;
rtc set alarm_datetime(&alarm);
exceptionFlag=1;
exceptionPage();
pending=TRUE;
if ((!nAC
OK_INP)&&(exceptionMask&2)&&!pending&&!exceptionPageStateR;&!sendAlarmState);
rtc_get datetime(&alarm);
received byte = read_ext_eeprom(Ox 1 FA);
alarm.hr += received byte/60;
alarm.min += received byte%60;
validateTime(&alarm);
if(alarm.hr>=24) alarm.hr-=24;
rtc_set alarm_datetime(&alarm);
exceptionFlag=2;
exceptionPageQ;
5$

pending=TRUE;
}
if (firstPassSync) {
firstPassSync=0;
checkMeterErrors();
}
if (!nRTC INT INP){ // Time to send scheduled Data (Pin AO is RTC interrupt) if(!sendAlarmState) sendAlarm();
}
if (timeChanged && !nAC OK_INP && !(exceptionMask&2)){
if(!timeChangeState) timeChange();
}
break;
} // switch (firstMainLoop) } // for ever } // main /************************************************/
// Interrupt Service Routines //
/************************************************/
void TMRO(){ // Timer 0 Overflow _asm // Preload timer to acheive overflow in 10 ms BANKSEL RAM_L
MOVFP RAM_L, TMROL
MOVFP RAM_H, TMROH
_endasm TMROcentiSeconds++;
TMROchanged=1;
void _INT() { // RTC alarm input.
TMROcentiSeconds++; // Just so this isn't a null function.
void PIRA { // Peripherals interupt _asm BANKSEL RamADDRESS
MOVFP RamADDRESS,WREG
MOV WF tempRamADDRESS
MOVFP RamADDRESS+1,WREG
A90VWFtempRamADDRESS+I
MOVFP RamDATA,WREG
MOVWF tempRamDATA
MOVFP RamDATA+1,WREG
MOV WF tempRamDATA+1 MOVPF TBLPTRL,tempTBLPTRL
MOVPF TBLPTRH,tempTBLPTRH
TLRD O,tempTBLATL
TLRD l,tempTBLATH
_endasm if (PIElbits.RBIE && PIRlbits.RBIF){ // PortB change PIE 1 bits.RBIE = 0;
if (PIElbits.TMR3IE && PIRIbits.TMR3IF);// Timer 3 PIE 1 bits.TMR3IE = 0;
;
if (PIE 1 bits.TMR2IE && PIR 1 bits.TMR2IF) {// Timer 2 PIEIbits.TMR2IE=0;
if (PIE 1 bits.TMRI1E && PIR1 bits.TMRI IF) {// Timer I
PIE 1 bits.TMR l IE = 0;
r if (PIEIbits.CA2IE && PIRIbits.CA2IF){ // Capture 2 PIE I bits.CA2lE = 0;
if(PIElbits.CAIIE&&PIRlbits.CAIIF){ //Capture 1 PIE I bits.CA I IE = 0;
if (PIElbits.TXIIE && PIRlbits.TXlIF){ // Transmit 1 Empty if (Tx 1 InPnt != Tx 1 OutPnt) {
_asm BANKSEL TxlOutPnt INCF Tx 1 OutPnt, l MOVFP TxlOutPnt,VVREG
BANKSEL RamADDRESS
MOVWFRamADDRESS
$~

MOVLW 0x40 MOV WF RamADDRESS+I
_endasm RamReadQ;
_asm BANKSEL RamDATA
MOVFP RamDATA,TXREGI
_endasm else {
PIElbits.TXI IE = 0;
// Interrupt driven receive.
// Before sending out any characters that you expect a response from, turn continuous // receive enable (CREN) off, clear the receive registers, set Rcl InPnt=Rcl OutPnt and !/ then turn CREN on again. As characters come in the "receive buffer full"
ISR puts /1 them in a circular buffer in external RAM and increments RclInPnt. The function // handling the incoming characters takes them from the circular buffer and increments // RclOutPnt until it equals RclInPnt.
// What about overflowing 2~6 byte circular buffer? Ignore possibility for now.
if(PIElbits.RCIIE && PIRlbits.RCIIF){// Receive 1 Full _asm BANKSEL Rc I InPnt INCF Rc 1 InPnt. l MOVFP RclInPnt,~VREG

BANKSEL RamADDRESS

MOVWF RamADDRESS

MOVLW 0x41 ~90VWF RamADDRESS+1 BANKSEL RamDATA

MOVPF RCREGI,RamDATA

_endasm RamWriteQ;

L

if (PIE2bits.SSPIE && PIR2bits.SSPIF){// SSP

PIE2bits.SSPIE = 0;

r if (PIE2bits.BCLIE &R. // SSP/I2C
PIR2bits.BCLIF) { Bus Collision PIE26its.BCLIE = 0:

;

if (PIE2bits.ADIE && PIR2bits.ADIF)// A/D Module {

PIE2bits.ADIE = 0:

r if (PIE2bits.CA4IE && PIR2bits.CA4IF){// Capture PIE2bits.CA4lE = 0;

i if (PIE2bits.CA3IE R.& // Capture PIR26its.CA3IF){ 3 PIE2bits.CA3IE = 0;

if (PIE2bits.TX2IE && PIR2bits.TX2IF){// Transmit 2 Empty if (Tx2InPnt != Tx2OutPnt) _asm BANKSEL Tx2OutPnt IIv'CF Tx2OutPnt.1 MOVFP Tx2OutPnt,WREG

$g BANKSEL RamADDRESS
MOV WF RamADDRESS
MOVLW 0x42 MOV WF RamADDRESS+1 _endasm RamRead();
_asm BANKSEL RamDATA
MOVFP RamDATA,TXREG2 _endasm else {
PIE2bits.TX2IE = 0;
) if (PIE2bits.RC2IE && PIR2bits.RC2IF) { // Receive 2 Full _asm BANKSEL Rc2InPnt INCF Rc2InPnt, I
MOVFP Rc2InPnt,WREG
BANKSEL RamADDRESS
MOVWF RamADDRESS
MOVLW 0x43 MOVWFRamADDRESS+1 BANKSEL RamDATA
MOVPF RCREG2,RamDATA
_endasm RamWriteQ;
_asm' BANKSEL RamADDRESS
MOVFP tempRamADDRESS,WREG
MOV~VF RamADDRESS
MOVFP tempRamADDRESS+I,WREG
MOVWF RamADDRESS+1 MOVFP tempRamDATA,WREG
MOV WF RamDATA
MOVFP tempRamDATA+1,WREG
MOV WF RamDATA+1 MOVFP tempTBLPTRL,TBLPTRL
MOVFP tempTBLPTRH,TBLPTRH
TLWT O,tempTBLATL
TLWT l,tempTBLATH
endasm i /******************************************************
Serial Communication unsigned char getchPager(void);
void putchPager(unsigned char ch);
void putcPager(unsigned char ch);
unsigned char getchMeter(void);
void putchMeter(unsigned char ch);
void putHexIntPager(unsigned int hexData);
void putHexCharPager(unsigned char hexData);
*******************************************************/
unsigned char kbhitPager(void){

if (Rcl InPnt!=Rc 1 OutPnt) return( 1 );
return(0);
unsigned char kbhitMeter(void){
if (Rc2InPnt!=Rc2OutPnt) return( 1 );
return(0);
unsigned char getc(unsigned char whichPort){
unsigned char inChar,chTemp;
if(whichPort=PAGER){
Rc I OutPnt++;
RamADDRESS = 0x4100+Rc 1 OutPnt;
else {
Rc2OutPnt++;
RamADDRESS = 0x4300+Rc2OutPnt;
{
RAM_READ;
inChar = RamDATA&Oxff;
return(inChar);
z void putchPager(unsigned char ch) {
if (!putchPagerState) {
putcPager(ch);
putchPagerState=l putchPagerTime=lastCentiTime+5;
;
else putchPagerState=0;
r void putcPager(unsigned char ch){
CPUSTAbits.GLINTD=1;
Tx 1 InPnt++;
RamADDRESS = 0x4000+Tx 1 InPnt;
RatnDATA = ch;
RamVVriteQ;
PIE 1 bits.TX I IE = 1:
CPUSTAbits.GLINTD=0:
r void putchMeter(unsigned char ch) {
unsigned char chTemp;
CPUSTAbits.GLINTD=1;
Tx2InPnt++;
RamADDRESS = 0x4200+Tx2InPnt;
RamDATA = ch;
RamWritep;
PIE2bits.TX2lE= 1;
CPUSTAbits.GLINTD=0;
i void prepReceive(unsigned char whichPort) {
CPUSTAbits.GLINTD=1:
if(whichPort=PAGER) RCSTA 1 bits.CREIv' = 0:
received byte=RCREG1;
received byte=RCREG1; // read hvice to clear buffer Rc 1 OutPnt = Rc l InPnt;
RCSTAIbits.CREN= 1;
PIElbits.RCIIE= l;
else {
RCSTA2bits.CREN = 0;
received byte=RCREG2;
received byte=RCREG2; // read twice to clear buffer Rc2OutPnt = Rc2InPnt;
RCSTA2bits.CREN=1;
PIE2bits.RC2lE= 1;
CPUSTAbits.GLINTD=0;
/************************************************
Eeprom buffering and PQ Log functions void eepromBuffer(unsigned int address, unsigned char data);
void bufferToEeprom(void);
void pqLog(unsigned char data);
void pqLogToEeprom(void);
unsigned int pqScanRemaining(void);
void pqScan(void):
void pqScanReset(void);
************************************************/
void eepromBuffer(unsigned int address, unsigned char data) {
// This function is to be called by any other function that wishes to store data // in the Eeprom. Address and data are stored in a circular buffer in external // RAM and then slowly written to the eeprom by the bufferToEeprom() function.
// If the circular buffer would wrap on itself if this data was put into it then // this function clears any in progress flags for eeprom and then calls the // function that takes data from this circular buffer.
if((eeBufflnPnt+1)=eeBuffOutPnt ~~ (eeBuffInPnt+2)==eeBuffOutPnt ~~
(eeBuff7nPnt+3) =eeBuffOutYnt);
buffToPromState = 0;
pqLogToPromState = 0;
bufferToEeprom();
eeBuffInPnt++;
RamADDRESS = 0x4700+eeBuftInPnt;
ramWriteInt(address);
RamDATA = data;
RAM_WRITE;
eeBuffInPnt += 2;
writingEeprom=I;
void bufferToEeprom(void){
// This function takes data from the circular buffer in external RAM. that was // put there by the eepromBuffer function, and writes it with delays between bytes // to the eeprom. This function is called roughly ever7~ l Oms so the time delay is // simply a second call. This function takes turns with pqLogToEeprom().
if(!buffToPromState){
if(!pqLogToPromState && eeBuffInPnt!=eeBuffOutPnt){
buffToPromState=1;
eeBuffOutPnt++;
RamADDRESS = 0x4700+eeBuffOutPnt:
tempPromAddress = ramReadInt();
RAM_READ;
tempPromData = RamDATA&Oxff;

eeBuffOutPnt += 2;
write eeprom noDelay(tempPromAddress,tempPromData);
f else {
buffroPromState=0;
if(eeBuffInPnt=eeBuffOutPnt) writingEeprom=0;
) void pqLog(unsigned char data){
l/ Similar to eepromBufferQ, this function puts data into a temporary buffer in // external RAM. No addresses are stored because anything put in here just goes // sequentially into the PQ & Communication event log which is a large circular // buffer in the Eeprom.
if((pqBuffTnPnt+I )=pqBuffOutPnt) {
buffToPromState = 0;
pqLogToPromState = 0;
pqLogToEepromQ;
pqBuffInPnt++;
RamADDRESS = 0x4600+pqBuffInPnt;
RamDATA = data;
RAM WRITE;
void pqLogToEeprom(void);
// This function moves the buffered data for PQ & Communication events from RAM
// to eeprom. The position in the Eeprom is stored in two bytes in the // RTC RAM. On startup, if the stored pointer is invalid then it is set to the // beginning of the circular buffer. When the circular buffer wraps the high bit // of the pointer is set to 1 to indicate that the end of the circular buffer is // valid data as well.
if(!pqLogToPromState){
if(!buffroPromState && pqBuffInPnt!=pqBuffOutPnt){
pqLogToPromState=l;
pqBuffOutPnt++;
RamADDRESS = 0x4600+pqBuffOutPnt;
RAM_READ;
tempPromData = RamDATA&Oxff;
pqLogAddress++;
tempPromAddress = pqLogAddress&Ox3FFF;
if(tempPromAddress>Ox 1 FFF) {
pqLogAddress = 0x8600;
tempPromAddress = 0x0600;
if(tempPromAddress=pqScanPointer) {
if(pqScanEnd=2) {
pqScanEnd=0;
else {
pqScanEnd=1;
rtc write(OxF3,pqLogAddress»8);
rtc write(OxF4,pqLogAddress&OxFF);
write eeprom noDelay(tempPromAddress,tempPromData);
W

else {
pqLogToPromState=0;
unsigned int pqScanRemaining(void) {
// This function returns the amount of data still to be scanned in the PQ &
// Communication events log. This is a circular buffer so the value returned // from this function is more meaningful than comparing the pqScanPointer to // pqLogAddress. This does not return a valid result if pqScanPointer is // actually still in the RAM buffer.
if(pqLogAddress&0x8000) {
if(pqScanPointer>pqLogAddress) tempPromAddress = pqScanPointer-pqLogAddress;
else tempPromAddress = pqScanPointer+Ox l9ff pqLogAddress; // pqSP-0x600 + Ox 1 fff pqLA
else {
tempPromAddress = pqScanPointer - 0x600;
;
if(pqScanEnd) tempPromAddress=0;
return(tempPromAddress);
void pqScan(void) {
// This function returns the previous log entry from the PQ and communications // Log. The eventType variable is set by the function and the data associated // with the entry is put into a buffer in RAM including the number of bytes of // data in the entry.
tempPromAddress = pqScanRemaining();
if(tempPromAddress){
eventType = read_ext_eeprom(pqScanPointer--):
if(pqScanPointer<Ox600) pqScanPointer = Ox 1 FFF;
if(eventType=OxFE) tempPromData = 6;
else if(eventType=OxFD) tempPromData = 0;
else if(eventType=OxFC ~~ eventType==OxFB) tempPromData = 2;
else if(eventType>OxAD) tempPromData = 4;
else if(eventType>OxAA) tempPromData = 2;
else if(eventType>OxAO ~~ (eventType>Ox77 && eventType<Ox7D)) tempPromData = 4;
else if(eventType=0 ~~ (eventType>Ox70 && eventType<Ox80)) tempPromData = 2;
else tempPromData = 3;
if(tempPromData<tempPromAddress){
RamADDRESS = Ox44F0;
RamDATA = tempPromData;
RAM_WRITE;
for(i=O;i<tempPromData;i++){
RamDATA = read ext eeprom(pqScanPointer--);
RAM_WRITE;
if(pqScanPointer<Ox600) pqScanPointer = (Ix 1 FFF;
return:

l l pqScanEnd=I;
eventType=Oxff;
void pqScanReset(void){
// This function is used to set the pqScanPointer to the end of the PQ Log // circular buffer. It also resets the flag pqScanEnd to 2 if there is part // of the log in the RAM buffer and to 0 if the log is all in eeprom. tf // pqScanEnd is set to 2 now then it will be set to 0 by pqLogToEeprom when what // is in the RAM buffer now is shuffled into the eeprom.
tempPromData = pqBufflnPnt-pqBuffOutPnt;
if(tempPromData) {
pqScanEnd=2;
else {
pqScanEnd=0;
pqScanPointer = pqLogAddress&Ox 1 fff;
pqScanPointer += tempPromData;
if(pqScanPointer>Oxlfff){
pqScanPointer -= Ox l9ff; // pqScanPointer - Ox 1 fff + 0x600 ) ;
void getEventType(void);
if(eventType=0) received byte=0:
else if(eventType<Ox40)received byte=1;
else if(eventType<Ox~O)received byte=2;
else if(eventType<Ox70)received byte=3;
else if(eventType<Ox78)received byte=4;
else if(eventType<Ox7E) received byte=~;
else if(eventType<Ox80)received-byte=8;
else if(eventType<OxAO) received byte=1;
else if(eventType<OxA 1 ) received byte=9;
else if(eventType<OxA4) received byte=10;
else if(eventType<OxA7) received byte=1 I;
else if(eventType<OxAA) received byte=12;
else if(eventType<OxAB) received byte=13;
else if(eventType<OxBB) received_byte=14;
else if(eventType<OxCO) received byte=1~;
else if(eventT~~pe<OxFB) received byte=9;
else if(eventType<OxFC) received-byte=7;
else if(eventType<OxFD) received byte=6;
else received byte=7;
pqComparison = I;
pqComparison «= received byte;
i /************************************************
External RAM functions unsigned int ramReadInt(void);
void ramWriteInt(unsigned int integerToWrite);
unsigned char ramReadUchar(void);
************************************************/
unsigned int ramRead(nt(void){
unsigned int returnInt;
RAM READ;

CA 02332297 2001-O1-25 , returnInt = RamDATA«8;
RAM_READ;
returnInt += RamDATA&Oxff;
return(returnlnt);
;
void ramWriteInt(unsigned int integerToWrite){
RamDATA = integerToWrite»8;
RAM_WRITE;
RamDATA = integerToWrite&OxFF;
RAM WRITE;
unsigned char ramReadUchar(void){
RAM_READ;
return(RamDATA&Oxff);
T
J
unsigned char getByte( unsigned int address) {
terminatorFlag=0;
RamADDRESS=address;
itemp=0;
for (j=O;j<2;j++){
hemp *= 16;
RAM_READ;
i = RamDATA&Oxff;
if(i=;' II i=~'){
terminatorFlag = 0x03;
return(0);
i = toint(i);
if(i=OxFF);
terminatorFlag = 0x02;
return(0);
;
itemp += i;
return(itemp);
r unsigned int getDecimal(void) {
hemp = 0;
terminatorFlag = 0x02; // Set Null flag RamADDRESS=(start:
while (ltemp<10000){
RAM_READ;
(start++;
i = RamDATA&Oxff;
if((i=',7 II (~ - ~') II (~=/7 II (i- ':')) if((i =':') II (i=:')) terminatorFlag I= 0x01; // Set terminator Flag return(Itemp);
else if(i<Ox30 II i>Ox39){
terminatorFlag = 0x02; // Set Null flag messageStatus = NAK;
return(0);
r i -= 0x30;
(temp *= 10:
GS

(temp += i;
i (start++; // To increment pointer beyond delimiter if a ~ digit number.
return(Itemp);
/************************************************
Conversions char to ascii(char din);
char toint(char cin);
unsigned int meterFIoatToInt(unsigned char baseExponent);
************************************************/
char to_ascii(char din){ // convert byte to char. -3 I for error if ((din>=0)&&(din<10)) return(din+48); // 0 to 9 if ((din>9)&&(din<16)) return(din+5~); // A to F
return (Oxff);
char toint(char cin){ // convert char to int. -1 for error if ((cin>=0x30)&&(cin<=0x39)) return(cin-0x30); // 0 to 9 if ((cin>=0x41 )&&(cin<=0x46)) return(cin-~5); // A to F
if ((cin>=0x61 )&&(cin<=0x66)) return(cin-87): // a to f messageStatus = NAK:
return(Oxff):
unsigned int meterFIoatTolnt(unsigned char baseExponent);
// using pqReportTime as a temporary variable here. It is always updated from external RAM
// after this function is called.
pqReportTime = ramReadlnt();
RAM_READ;
received_bvte = RamDATA&Oxff;
if(received--byte<=baseExponent){
received-byte=baseExponent-received byte:
pqReportTime+=Ox7fff:
pqReportTime = pqReportTime»received byte;
else pqReportTime = Oxffff;
return(pqReportTime);
l /******************************************************
Pager Interface (CLP commands & xmodem) void yyy(void);
void yyl6(void);
void yyl7(void); // get configuration and product info.
void yyl8(void);
void getOption(void);
unsigned char yy 1 a(char option, char param);
void yy 1 b(void);
void check( unsigned char xmitbuf);
char xmod_data( void);
void pagerResetHard( void);
void reestablishPager(void);
//void pagerResetRealIyHard( void);
//void pagerResetSoft( void);
******************************************************/
void yyy(void){

CA 02332297 2001-O1-25 , if (!yyyState) yyyState=1;
if (yyyState<4){
if (!putchPagerState && pagerComAbortTime<IastCentiTime) {
putchPager(Ox l9);
yyyState++;
else if (!putchPagerState) {
yyyState=0;
prepReceive(PAGER);
void yyl6(void){ //Ready to send packet switch(yy 16State) {
case 0:
yy I 6State=1;
case l:
if(!yyyState){
ffYO;
yy 16State=2;
break;
case 2:
if (!yyyState && !putchPagerState) {
putchPager(Ox31 );
yy 16State++;
break;
case 3:
if(!putchPagerState) {
putchPager(Ox36);
yy 16State++;
;
break;
case 4:
if (!putchPagerState) {
putchPager(' ');
yy l6State++;
i break;
case 5:
if (!putchPagerState) {
putchPager(Ox30);
yy 16State++;

break:
case 6:
if (!putchPagerState) {
temp 1=iPacketSize»8;
tempt=to_asci i(temp 1 );
putchPager(temp2);// number of chars in message 2nd nibble yy 16State++;
;
break;
case 7:
if (!putchPagerState) {
temp I=iPacketSize»4:
tempt=temp 1 ~:OxOf;

CA 02332297 2001-O1-25 , tempi=to_ascii(temp2);
putchPager(temp3);1/ number of chars in message 3rd nibble yy 165tate++;
break;
case 8:
if (!putchPagerState) {
temp 1=iPacketSize&OxOf;
tempt=to_ascii(temp 1 );
putchPager(temp2);// number of chars in message to nibble yy 16State++;
break;
case 9:
if (!putchPagerState) {
putcPager(Ox 19);
yy 16State++;
pagerResponse=0;
pagerComAbortTime=lastCentiTime+j00;
) break;
case 10:
if (kbhitPager()) {
received byte = getc(PAGER);
if(received_byte==EOT);
yy 16State++;
pagerComAbortTime=IastCentiTime+j00;
i else yy l6State=0:
else if (pagerComAbortTime<lastCentiTime) yy l6State=0;
break:
case 11:
if (kbhitPager()){
received byte = getc(PAGER);
if(received byte==NAK){
pagerResponse=1;
)')' l6State=0;
;
else if (pagerComAbortTime<IastCentiTime) yy l6State=0;
break;
r void yyl7(void){ // get configuration and product info switch(yy 175tate) {
case 0:
yy l7State=1:
break;
case 1:
if (!yyyState) {
y)')'0;
)')' 17State=2;
f break;
case 2:
if (!yyyState && !putchPagerState) {
if(productInfo) putchPager(Ox33);

else putchPager(Ox31 );
yy 17State++;
i break;
case 3:
if (!putchPagerState) {
putchPager(Ox37);
yy 17State++;
}
break;
case 4:
if (!putchPagerState) {
putcPager(Oxl9);
yy 17State++;
pagerResponse=0;
pagerComAbortTime=IastCentiTime+500;
r break;
case 5:
if (kbhitPager()) {
received byte = getc(PAGER);
writeToSendBuffer(received_byte);
pagerResponse++;
if ((productInfo && pagerResponse>2) ~~ (!productInfo && pagerResponse>33)) yyl7State++;
pagerComAbortTime=IastCentiTime+500;
r else if (pagerComAbortTime<IastCentiTime) yyl7State=0:
break;
case 6:
yy l7State=0;
pagerComAbortTime=lastCentiTime+~00;
break:
}
void yy 18(void) { // get status switch(yy 18State) {
case 0:
yyl8State++;
RamADDRESS = Ox50C0;
RamDATA = 0;
for(pagerResponse=O:pagerResponse<43;pagerResponse++){
RAM_WRITE;
;
break;
case 1:
if (!yyyState){
YYYO>
yy 18State++;
}
break;
case 2:
if (!yyyState && !putchPagerState) {
putchPager(Ox31 );
yy I 8State++;
break;
case 3:
if (!putchPagerState) {

putchPager(Ox38);
yy 18State++;
break;
case 4:
if (!putchPagerState) {
putcPager(Ox 19);
yy 18 State++;
pagerResponse=0;
pagerStatus 1=0;
pagerStatus2=0;
pagerComAbortTime=lastCentiTime+500;
i break;
case 5:
if (kbhitPagerQ){
RamDATA = getc(PAGER);
RamADDRESS = Ox50C0 + pagerResponse;
RAM_WRITE;
pagerResponse++;
if (pagerResponse>42) yyl8State++;
pagerComAbortTime=lastCentiTime+j0;
i else if (pagerComAbortTime<IastCentiTime) yy l8State=0;
break;
case 6:
RamADDRESS = Ox~OCO; // Transmit short message flag RAM_READ;
pagerStatusl =RamDATA&Oxff;
pagerStatusl =toint(pagerStatusl);
pagerStatusl &=OxOE;
pagerStatusl «= 4;
RAM_READ; // Transmit message flag pagerStatus2 = RamDATA&Oxff:
pagerStatus2 = toint(pagerStatus2);
received byte = pagerStatus2&OxOE;
pagerStatusl~= received byte«1;
RAM_READ; // Device Busy received byte = RamDATA&0x01: // Either 0x30 or 0x31 pagerStatusl ~=received byte«1;
RAM_READ; // Out of Range received byte = RamDATA&0x01; // Either 0x30 or 0x31 pagerStatusl ~= received byte;
pagerStatus2 &= l; // Unread Message pagerStatus2 «= 3;
RAM_READ; // Transmitter Status received byte = RamDATA~R,OxOI; // Either 0x30 or Ox31 pagerStatus2 ~= received byte«1;
RAM_READ; // Battery Status received byte = RatnDATA&Oxff;
pagerStatus2~= received byte«4;
RamADDRESS = Ox50D0; // Registration Status RAM_READ:
received byte = RamDATA&0x01; // Either 0x30 or 0x31 pagerStatus2 ~= received_byte;
RAM_READ; // Message Status received byte = RamDATA&0x01;
pagerStatus2 ~= received byte«2;
yy 18State=0;
if(pagerDiagnostics &R, ((pagerStatusl!=oldStatusl) ~~
(pagerStatus2!=oldStatus2)));

CA 02332297 2001-O1-25 , pqLog(pagerStatus2);
pqLog(pagerStatus 1 );
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox79);
oldStatusl =pagerStatusl;
oldStatus2 = pagerStatus2;
pagerComAbortTime=lastCentiTime+500;
break;
void getOption(void){
switch(getOptionState) {
case 0:
getOptionState=1;
pagerOption 1=0;
pagerOption2=0;
break;
case 1:
if (!yyyState){
YYYO
getOptionState=2;
break;
case 2:
if (!yyyState &R. !putchPagerState) {
putchPager(Ox3 I );
getOptionState++;
break:
case 3:
if (!putchPagerState) {
putchPager('a');
getOptionState++;
l break;
case 4:
if (!putchPagerState) {
putchPager(' ');
getOptionState++;
break;
case ~:
if (!putehPagerState) {
putehPager(optionToGet);
getOptionState++;
break;
case 6:
if (!putchPagerState) {
putcPager(Oxl9);
getOptionState++;
pagerComAbortTime=lastCentiTime+500;
i break;
case 7:
if (kbhitPager()) {
received byte = getc(PAGER);

CA 02332297 2001-O1-25 , if (received byte = NAK){
getOptionState = 0;
pagerOption2 = NAK;
else if (received byte = OxFF){
getOptionState++;
else {
getOptionState = 9;
pagerOption 1 = received byte;
pagerComAbortTime=lastCentiTime+j00;
r else if (pagerComAbortTime<IastCentiTime) getOptionState=0;
break;
case 8:
if (kbhitPagerQ){
pagerOption 1 = getc(PAGER);
getOptionState++;
pagerComAbortTime=IastCentiTime+j00;
r else if (pagerComAbortTime<IastCentiTime) getOptionState=0:
break:
case 9:
if (kbhitPager()) {
pagerOption2 = getc(PAGER);
getOptionState=0;
pagerComAbortTime=IastCentiTime+j00;
i else if (pagerComAbortTime<IastCentiTime) getOptionState=0;
break;

unsigned char yyla(char option, char param);
putcPager(Oxl9);

delay_ms(40):

putcPager(Ox 19);

delay_ms(40);

putcPager(Ox 19);

delay_ms(40);

putcPager(' 1');

delay _ms(40);

putcPager('a');

delay_ms(40);

putcPager(' ');

delay_ms(40);

putcPager(option);

delay_ms(40);

prepReceive(PAGER);

if (param=Oxff) {

putcPager(Oa 19);

r else{

putcPager(' ');

delay_ms(40);

putcPager(param);

delay_ms(40);

putcPager(Oal9);

CA 02332297 2001-O1-25 , while (!kbhitPager()); // Can Watch Dog TimeOut here.
itemp=getc(PAGER);
return(itemp);
void yylb(void){ // Read message and Delete switch(yy 1 bState) {
case 0:
yy 1 bState=1;
case 1:
if (!yyyState){
YYYO
yy 1 bState=2;
break;
case 2:
if (!yyyState && !putchPagerState) {
putchPager(Ox31);
yy 1 bState++;
break;
case 3:
if (!putchPagerState) {
putchPager('b');
yy 1 bState++;
break;
case 4:
if (!putchPagerState) {
putcPager(Ox 19);
~~y 1 bState++;
pagerResponse=0;
pagerComAbortTime=IastCentiTime+~00;
i break;
case ~:
if (kbhitPagerQ) {
received byte = getc(PAGER);
if(received_byte=EOT){
pagerResponse=1;
y~~ 1 bState=0:
else if(pagerComAbortTime<lastCentiTime) yylbState=0;
break;
i void check( unsigned char xmitbuf){
putcPager(xmitbuf);
checksum = checksum + xmitbuf;
j++;
ltemp3++;
r char xmod_data( void) unsigned char i;
switch(xmod dataState) {
case 0:
xmod dataState++;

j=0;

checksum = 0;

putcPager(SOH);

putcPager(blockNum);

putcPager(-blockNum);

if (blockNum = 1 ){

if(binaryFlag&&(!status)) check(OxF7);

else check(OxFD);

check(OxIA);

if(useTempAddress) hemp = 0x42;

else hemp = Ox I F;

for(i=O;i<headerSize;i++){

if(useTempAddress) received byte = rtc read(itemp+i);

else received byte = read ext eeprom(itemp+i);

if(received_byte=','){

check(OxIF);

// if(binaryFlag&&(!status)) check(OxIF);

check(Ox lA);

L

else check(received byte);

i check(OxIF);

if(binaryFlag&&(!status)) check(OxIF);

check(':');

if (padHeaderEven) check(':');

Itemp3=headerOverhead;

if(status!=2){

for(i=O;i<4;i++){

if((binaryFlag)&&(!status)) {

check(preamble[i]);

else {

temp 1=preamble[i]4;

tempt=to_ascii(templ);

check(temp2):

temp 1=preamble[i]&OxOf;

tempt=to_ascii(temp 1 );

check(temp2);

for(packetStart;packetStart<packetEnd;packetStart++) {
RamADDRESS = packetStart+OFFSET3:
RAM READ;
hemp = RamDATA&Oxff;
if((status)~~(binaryFlag));
check(itemp);
else{
temp 1=hemp»4;
tempt=hemp&OxOf;
tempi=to_ascii(temp 1 );
check(temp3);
tempi=to_ascii(temp2);
check(temp3);

;

CA 02332297 2001-O1-25 , while (j < 128){
putcPager(0);
j++;
r putcPager(checksum);
pagerResponse=0;
break;
case 1:
if(!PIElbits.TXIIE){ // Wait until all characters have gone out.
pagerComAbortTime = IastCentiTime+500;
xmod dataState=2;
break:
case 2:
if(kbhitPagerQ){
pagerResponse=getc(PAGER);
xmod dataState=0;
else if (pagerComAbortTime<lastCentiTime) xmod_dataState=0;
break;
void pagerResetHard( void){
switch(pagerResetHardState){
case 0:
pagerResetHardState++;
break;
case 1:
pagerResetHardState++;
PAGER RESET_EN_OUTP = 1; // Turn Pager Reset Enable On PAGER RESET_OUTP = 0; /! Turn Pager Reset Active pagerComAbortTime = lastCentiTime+1 ~;
break;
case 2:
if (pagerComAbortTime<lastCentiTime);
pagerResetHardState++;
PAGER RESET OUTP = 1; // Turn Pager Reset Inactive PAGER RESET_EN OUTP = 0: // Turn Pager Reset Enable Off pagerComAbortTime = IastCentiTime+800:
a r break;
case 3:
if (pagerComAbortTime<IastCentiTime) pagerResetHardState=0;
break;

void reestablishPager(void) {
switch(reestablishPagerState) {
case 0:
reestablishPagerState++;
break;
case 1:
pagerResetHard();
reestablishPagerState++;
break:
case 2:
if(!pagerResetHardState){

reestablishPagerState++;
pagerPollTimeout=timeOfDay+300; // 10 minutes.
break;
case 3:
fY 18()>
reestablishPagerState++;
break;
case 4:
if(!yyl8State){
reestablishPagerState++;
if(pagerResponse!=43) reestablishPagerState=7;
break;
case 5:
reestablishPagerState++;
if(pagerStatusl ~~ ((pagerStatus2&3)!=3)) reestablishPagerState=7;
break;
case 6: // All OK
reestablishPagerState=0;
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox7D);
break;
case 7:
if(pagerPollTimeout<timeOfDay){ // 10 minutes elapsed, Abort!
reestablishPagerState=0;
pqLog(pagerStatus2);
pqLog(pagerStatus 1 );
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox7C);
;
else {
reestablishPagerState=3;
break;
/******************************************************
Meter Interface (DGCOM) char handshake( void);
char unlock( void):
void findClip( unsigned char command);
char clipFound( void);
void getLPdata( void);
char isLPenabled( void);
void sendLPerror( void);
void calcLPsize(void);
void calcStart( void);
void pqCalc(void);
void pqPoll(void);
void sendCommand(void);
void executeMultipleCommands(unsigned int start);
void compressLPdata(void);
void getChannellnfo(void); // Restores OIdV and cMode for this channel.

void compressScan(void);
******************************************************~
char handshake( void){
switch(handshakeState) {
case 0:
badHandshake=0;
handshakeState++;
case 1:
prepReceive(METER);
putchMeter(Ox55);
meterComAbortTime = lastCentiTime+4;
handshakeState++;
badHandshake++;
if(badHandshake>5) handshakeState=0;
case 2:
if (kbhitMeter()){
noMeterResponse=0;
if (getc(METER) != 0x55) handshakeState=l;
else {
handshakeState++;
putchMeter(OxAA);
meterComAbortTime = IastCentiTime+4:

I
else if (meterComAbortTime<lastCentiTime) handshakeState=1:
break;
case 3:
if (kbhitMeter()){
if (getc(METER) = OxAA) {
badHandshake=0:
handshakeState=0;
;
else {
handshakeState=1;
i i else if (meterComAbortTime<IastCentiTime) handshakeState=l;
break;

char unlock( void){
switch(unlockState){
case 0:
sum=Ox00ff;
if(whichPass =1){
sum+=meterl Passes;
sum+=meter l PassM;
sum+=meterl PassL:
else {
sum+=meter2PassH;
sum+=,neter2PassM;
sum+=meter2PassL;
unlockState++;
break;

case 1:
putchMeter(OxFF);
if(whichPass=1 ) {
putchMeter(meter 1 PassH);
putchMeter(meterl PassM);
putchMeter(meterl PassL);
f else {
putchMeter(meter2PassH);
putchMeter(meter2PassM);
putchMeter(meter2PassL);
l putchMeter( 1 );
putchMeter(1);
sum += 2;
for(i=O;i<3; i++) {
putchMeter(0);
putchMeter(sum&OxFF): // LSB
putchMeter(sum » 8); // MSB
meterComAbortTime = IastCentiTime+4;
unlockState++;
break;
case 2:
if(kbhitMeter()){
meterResponse = getc(METER);
unlockState++;
i else if (meterComAbortTime<IastCentiTime) {
unlockState=0;
i break;
case 3:
unlockState=0;
if(meterResponse == 0x99) unlockState=1;
i else if ((meterResponse =- Oxdd)~~(meterResponse - 0x33)){
i break;
L
void findClip( unsigned char command){
clipPointer-1;
RamADDRESS=0x5080;
RAM_READ;
received byte = RamDATA&Oxff;
if(received byte-command){
return;
while(clipPointer<(CLIPSIZE-4)){
RAM_READ;
received byte = RamDATA&Oxff;
clipPointer++;
if(received byte=Oxff){
RAM_READ;
received byte = RamDATA&Oxff:
clipPointer++;
if (received byte-command) {

CA 02332297 2001-O1-25 , return;
clipPointer=0;
char clipFound( void){
RamADDRESS=clipPointer+0x5080;
RAM_READ;
received byte = RamDATA&Oxff;
if(received byte!=Oxffj {
offset += distance;
offset += received byte;
RAM_READ;
distance = RamDATA&Oxff;
clipPointer += 2;
return(TRUE);
i return(FALSE);

f void getLPdata( void){
switch(getLPdataState) {
case o:
command[0]=4;
for(i=l;i<1 1:i++) command[i]=0:
command[9]=4;
sendFlag=FALSE;
sendCommandp;
getLPdataState=1;
break;
case l:
if(!sendCommandState) {
RamADDRESS = Ox4b02;
LPinterval = ramReadUchar();
if (LPinterval==0) {
RamADDRESS = Ox4b00;
LPinterval = ramReadUchar();
if (LPinterval&0x40) LPinterval=l;
else if (LPintervalROx20) LPinterval=5:
else if (LPinterval&4) LPinterval=30;
else if (LPinterval&2) LPinterval=15;
else LPinterval=60;
;
RamADDRESS = Ox4b01;
LPchannels = ramReadUcharQ;
RamADDRESS = Ox4b03:
LPavailable = ramReadlnt():
getLPdataState=0;
break;
;
char isLPenabled( void){
switch(isLPenabledState){
case 0:

hadLPerror=0;
lindexSave=lindex;
command[0]=OxSa;
for(i=l;i<ll;i++) command[i]=0;
command[9]=OxSa;
sendFlag=FALSE;
sendCommand();
isLPenabledState=1;
break;
case I:
if(! sendCommandState) {
RamADDRESS = 0x4600;
tempi =ramReadUchar();
if (temp 1 &8) isLPenab(edReturn=1;
else {
if (temp 1 &4) tempt=2;
else tempt=1;
isLPenabledReturn=0:
sendLPerror();
isLPenabledState=0;
break;
) J
void sendLPerror( void) {
lindex=IindexSave;
writeStringToSendBuffer(sBF03FF);
writeToSendBuffer(temp2);
hadLPerror=1:
;
void calcLPsize(void){
Itemp3 = time.hr;
Itemp3 *= 60;
ltemp3 += time.min;
Itemp3 /= LPinterval;
ltemp3 += 15; // Padding.
Itemp2 = 1440/LPinterval; // # of intervals per day.
ltemp2 += 15; // Padding.
Itemp2 *= LPdays;
ltemp2 += ltemp3; // Total # of Intervals.
ltemp2 *= LPchannels;
tempt=(Itemp2/512)+1; // # of k Rounded up.
command[0]=3;
command[1]=tempi;
command(10]=0;
command[9]= tempi+3;

l void calcStart( void);
startMonth=endMonth;
startDay=endDay;
startDay-=LPdays;
if (startDay<=0){
startMonth--;
if ((startMonth = 4)~~(startMonth---6)~~(startMonth-9)I~(startMonth==1 1 ));

startDay=30+startDay;
else;
if (startMonth=2)( if ((time.yr)%4) startDay=28+startDay;
else startDay=29+startDay;
r else;
startDay=31+startDay:
if (startMonth=0) startMonth=12:
startDay=to bcd(startDay);
startMonth=to bcd(startMonth);
startMonth~=0x80;
if(!C,Pdays) endDay++;
#pragma code sendCommand = Ox40C0 void sendCommand(void);
unsigned char i.block;
switch(sendCommandState);
case 0:
if (sendFlag);
convertAndV'riteToSendBuffer(OxBF);
convertAndWriteToSendBuffer(command[0]);
convertAndWriteToSendBuffer(OxFF);
i r sendCommandState=1;
break;
case I:
if(!pqPoIlState);
findClip(command[0]):
offset=0;
distance=0:
totalBlocks=1;
totalB~~tesFromMeter=0;
switch(command[0]){ // These commands have multiple packet returns.
// All others have single packet returns.
case 3:
totalBlocks=command[1 ]* 8;
break;
case Ox06:
totalBlocks=7:
break;
case 0x09:
case OxAB:
totalBlocks=~:
break;
case Ox 10:
totalBlocks=3;
break;
case 0x98:
totalBlocks=2;
g]

break;

case OxAB:

totalBlocks=11:

break;

; // switch block=1;

neverFound=TRUE;

dateFound=FALSE;

handshake();

sendCommandState=2;

if(acOKtimer){

sendCommandState=0;

badSendCommand=11;

break;

case 2:

if (!handshakeState){

if (badHandshake){

badSendCommand=I ;

sendCommandState=0;

l else {

if (unlockFlag) {

unlockQ;

sendCommandState++;

i else {

sendCommandState=5;

;

break;
case 3:
if(!unlockState){
sendCommandState=5;
i l break;
case 4: // Just waiting here for more data when multiple data packets to break; // meter. executeMultipleCommands will change to next state.
case 5:
for(temp2=O;temp2< 1 I ;tempt++) putchMeter(command[tempt]);
r sendCommandState=6:
break;
case 6:
if(!PIE2bits.TX2IE) { // Wait until all characters have gone out.
meterComAbortTime = lastCentiTime+10;
sendCommandState=7;
meterResponse = 0;
r break;
case 7:
if (kbhitMeter()) {
meterResponse = gete(METER):
if (meterResponse = 0x33) { // Transmission Accepted if (unlockFlag){
meterComAbortTime = IastCentiTime+10:
unlockFlag = 0;

else meterComAbortTime = lastCentiTime+$0;
justDataToMeter-0;
sendCommandState=8;
else if (meterResponse = 0x99){ // Checksum error - resend sendCommandState=5;
else if (meterResponse=0x66 && moreDataToMeter) {
sendCommandState=4;
else { // Command not accepted sendCommandState=0;
badSendCommand=2;
else if (meterComAbortTime<IastCentiTime) {
sendCommandState=0;
badSendCommand=3;
break;
case 8:
if (kbhitMeter()) {
bytesFromMeter = getc(METER);
meterDataChksum=bytesFromMeter;
MbuffPointer = 0;
meterComAbortTime = lastCentiTime+10;
sendCommandState=9;
) else if (meterComAbortTime<lastCentiTime) {
sendCommandState=0;
badSendCommand=4;
i break;
case 9:
if (kbhitMeter()) {
received byte = gete(METER);
meterDataChksum = meterDataChksum + received byte:
RamDATA = received byte;
RamADDRESS = Ox4B00 + MbuffPointer;
RAM_WRITE;
MbuffPointer++;
meterComAbortTime = IastCentiTime+10;
if (MbuffPointer>=bytesFromMeter){
sendCommandState=10;
l else if (meterComAbortTime<IastCentiTime) {
sendCommandState=0, badSendCommand=~;
i break;
case 10:
if (kbhitMeterQ){
received byte = getc(METER);
meterDataChksum-=received byte;
meterComAbortTime = lastCentiTime+10:
sendCommandState=1 1;
;

CA 02332297 2001-O1-25 , else if (meterComAbortTime<IastCentiTime) {
sendCommandState=0;
badSendCommand=6;
break;
case 11:
if (kbhitMeter()){
received byte = getc(METER);
sendCommandState=0;
if (meterDataChksum&Oxff) {
badSendCommand=7;
putchMeter(Ox33);
else {
meterDataChksum = meterDataChksum»8;
if (meterDataChksum!=received_byte){
badSendCommand=8;
putchMeter(Ox33);
else if(block < totalBlocks){
sendCommandState=13;
// Next Packet can come in while processing previous.
putchMeter(Ox66);
meterComAbortTime = lastCentiTime+10;
) else {
sendCommandState=13;
putchMeter(Ox33);
else if (meterComAbortTime<IastCentiTime) {
sendCommandState=0;
badSendCommand=9;
) break;
case 13:
if (sendFlag){
if (economyFlag && clipPointer && command[0]!=3){
for(;;) {
if(offset>(totalBytesFromMeter+bytesFromMeter)) break;
else if ((offset+distance)<=totalBytesFromMeter);
if(!clipFound()) break;
r else {
if (offset<=totalBytesFromMeter) startByte=0;
else startByte = offset - totalBytesFromMeter;
if ((offset+distance) <_ (totalBytesFromMeter+bytesFromMeter)) endByte = offset+distance-totalBytesFromMeter;
else endByte = bytesFromMeter;
for(i=startByte;i<endByte;i++) {
// received_byte=Mbuffer[i];
RamADDRESS = Ox4b00 + i;
received byte = ramReadUcharQ;
convertAndWriteToSendBuffer(received_b~~te);
if(endByte<=bytesFromMeter) {

if(!clipFound()) break;

l ) else{ //(!econoFlag~~command[0]-3) for(i=0; i<bytesFromMeter; i++) {
ClrWdtQ;
RamADDRESS = Ox4b00 + i;
received byte = ramReadUchar();
if ((command[0]==3) && economyFlag);
if (dateFound){
writeToSendBuffer(received byte);
if (lindex >= 2500){
tempt=3;
sendLPerror();
sendCommandState=0;
badSendCommand=l0;
return;
;
if (!(i&1)){ // ever~~ other byte /l tempt=Mbuffer[i+I]:
RamADDRESS = Ox4b00 + i + 1;
temp 1 = ramReadUchar();
temp I=temp 1»6;
if (onlyDates&&((temp 1-2)~~(temp 1=3))){
if ((tempt=2)~~(onl~~Dates---3)){
RamADDRESS = Ox4b00 + ];
received byte = ramReadUcharQ;
writeToSendBuffer(received byte);
RamADDRESS = Ox4b00 + i + 1;
received b~~te = ramReadUchar();
writeToSendBuffer(received b~~te);
;
else if (temp 1=2) RamADDRESS = Ox4b00 + i:
received_byte = ramReadUcharQ;
hemp = ramReadUchar():
if (dateFound);
if (((received byte>=endDay)&&(itemp==endMonth))~~(itemp>endMonth)) {
writeToSendBuffer(itemp);
lindex-=2; // Kill last dateStamp dateFound=FALSE;
else {
if ((received_byte==startDay)&:.&(itemp-startMonth));
writeToSendBuffer(received_byte):
dateFound=TRUE;
neverFound=FALSE;
i ) else{convertAndVv'riteToSendBuffer(received byte):
) if (block < totalBlocks);
block++;
totalBytesFromMeter+=bytesFromMeter;
sendCommandState=8;
else sendCommandState=14;
break;
case 14:
if ((((command[0]=3) && economyFlag)&&neverFound)&&!onlyDates){
tempt=4;
sendLPerrorQ;
r if(sendFlag && !(command[0]==3 && compressionEnabled && !hadLPerror)) IindexSave=lindex;
badSendCommand=0: // Send completed OK
sendCommandState=0;
break;
i void executeMultipleCommands(unsigned int start) {
switch(executeMultipleState) {
case 0:
unlockFlag = 0;
moreDataToMeter = 0;
]ustDataToMeter = 0;
execMultiPointet=start-I;
last byte-=, , executeMultipleState=2:
break;
case I:
RamADDRESS=execMultiPointer;
RAM READ;
last byte = RamDATA&Oxff;
executeMultipleState=2;
break:
case 2:
if(last_byte !_';' && last_byte !_':');
iMCmnd=0:
sum=0;
if(moreDataToMeter) {
if(meterResponse!=0x66){ // If you have more data // but the meter doesn't want it, flush it.
while((last byte !_ ;')&&(last byte !_':')&&(last byte !_',')&&(last byte !_'.'));
execMultiPointer++;
RamADDRESS=execMuItiPointer;
RAM_READ;
last byte = RamDATA&Oxff;
sendCommandState=0;
;
else {
sum=command[0];
iMCmnd++:
justDataToMeter=1;
;

moreDataToMeter = 0;
if((last byte =',')~~(last byte ='.'));
execMultiPointer++;
RamADDRESS=execMultiPointer;
RAM_READ;
last byte = RamDATA&Oxff;
if(last byte ='S'){
messageEnd = 0;
while (last byte !_ ;'){
execMultiPointer++;
RamADDRESS=execMuItiPointer;
RAM_READ;
last byte = RamDATA&Oxff;
RamADDRESS = messageEnd + pFFSET2;
RAM_WRITE;
messageEnd++;
runSSIcommand=I;
executeMultipleState=0;
elseif(last byte='L')[
execMultiPointer++:
RamADDRESS=execMuItiPointer;
RAM_READ;
whichPass = RamDATA&Oxff;
whichPass = toint(whichPass);
execMultiPointer++;
RAM_READ;
last byte = RamDATAR.Oxff;
r while(last byte!=;' && last byte!--':' && last byte!=,' && last byte!='.' &~.
!moreDataToMeter);
hemp=getB~ te(execMultiPointer):
command[iMCmnd]=hemp:
sum+=command[iMCmnd];
execMultiPointer+=2;
iMCmnd++;
RamADDRESS=execMultiPointer;
RAM_READ;
last byte = RamDATA&Oxff;
if ((iMCmnd>8) && (last byte !_';') && (last byte !_':') && (last byte !_'.') && (last byte !_ s moreDataToMeter = I ;

if(!runSSIcommand);
if(iMCmnd!=1) unlockFlag=1;
for(;iMCmnd<9;iMCmnd++) command[iMCmnd]=0:
tempSum=sum;
command[10]=sum » 8;
command[9]=sum&OxFF;
executeMultipleState=3;
else executeMultipleState=0;
break;
case 3:
if (command[0] = 3){
unlockFlag=0:

lindexSave=(index;
LPdays=command[I];
isLPenabledQ;
executeMultipleState=4;
;
else executeMultipleState=6;
break;
case 4:
if (! isLPenabIedState) {
if(isLPenabledReturn){
if(economyFlag) {
getLPdataQ;
executeMultipleState=5;
l else {
command[0]=3;
command[ 1 ]=LPdays;
command[10]=tempSum » 8; // GMS check if 0 good enough command[9]=tempSum&.OxFF;
executeMultipleState=6;
else {
sendLPerror();
executeMultipleState=2;
break:
case ~:
if(!getLPdataState);
endDay=time.day;
endMonth=time.mth:
calcStart();
endDay=to bcd(endDay);
endMonth=to bcd(endMonth);
endMonth~=0x80;
caIcLPsizeQ;
executeMuItipIeState=6;
break;
case 6:
sendFlag=TRUE;
if~justDataToMeter) sendCommandState=~:
if (command[0]=0x21 ~~ command[0]=0x22) {
timeChanged=1;
timeSynehMode=2;
sendCommand();
executeMultipleState=7;
break;
case 7:
if(!sendCommandState ~~ sendCommandState= 4){
if(command[0]=3 && compressionGnabled && !hadLPerror);
compressLPdataQ;
executeMultipleState=0:
i else {
executeMultipleState=2;
;

break;
void compressLPdata(void){
// Before starting the compression routine:
// lindex+OFFSET3 points to one address location beyond the end of LP data.
// IindexSave+OFFSET3 points to the beginning of the BF03Ff sequence.
// Save lindex+OFFSET3-1 in cEndLPdata; lindex should be reset to lindexSave+3;
// lindex will be incremented by writeToSendBuffer();
// unsigned int's #define cOldV pqComparison #define cValue pqStartTime #define cInPnt pqReportTime #define cEndLPdata pqOfffime #define cNewV pqValue l/ unsigned char's #define cDeItaVnegative pqEvent #define cDateStamp pqInProgress #detine cChannel pqDurationLimit #detine cMode pqReportDelay #define cScanResult pqEventStatus #define cTempChannel pqReminderDelay cEndLPdata = lindex+OFFSET3-I ;
lindex = IindexSave + 3;
cInPnt = lindex+QFFSET3;
RamADDRESS = 0x5300;
RamDATA=0;
for(eChannel=O;cChannel<Ox30;cChannel++) {
RAM V'RITE;
cChannel=0:
getChannelInfo(); // Restores OIdV and cMode for this channel.
mhile(clnPnt<cEndLPdata) {
CIrWdt();
RamADDRESS=clnPnt+1:
RAM_READ;
received_byte = RamDATA&Oxff;
if ((received byte&0x80)=0){
cDateStamp = 0;
cIv'ewV = received byte&Ox3f;
cNewV «= 8;
RamADDRESS=clnPnt;
RAM_READ;
cNewV += RamDATA&Oxff;
if (cNewV<cOldV) {
cOldV -= cNewV;
cDeltaVnegative = 1;
i else {
cOldV = cNewV - cOldV;
cDeltaVnegative = 0;
else {
cDateStamp= 1;
cNewV = received byte;
cNewV «= 8;

RamADDRESS=cInPnt;

RAM_READ;

cNewV += RamDATA&Oxff;

if(cMode){ // In Compression.

if(cOldV>506 ~~ cDateStamp){

compressScanQ;

if(cScanResult) {

writeToSendBuffer(Ox82);

) else {

writeToSendBuffer(Ox8 l );

cMode = 0;

t writeToSendBuffer(cNewV&Oxft);

writeToSendBuffer(cNewV8);

else {

if(cOldV>379) {

writeToSendBuffer(Ox80);

cOldV -= 379;

else if(cOIdV>252){

writeToSendBuffer(Ox7f);

cOldV -= 252;

else if(cOIdV>125){

writeToSendBuffer(Ox7e);

cOldV -= 125;

) if(cDeltaVnegative){

cOldV--;

cOIdV ~= Oxff;

writeToSendBuffer(cOldV&Oxff);

r l else { // Not In Compression.

if(cOIdV<l26 && !cDateStamp){

compressScan();

if(cScanResult){

if(cDeItaVnegative) coldV--;

cOIdV ~= Oxff;

writeToSendBuffer(cOIdV&Oxff);

writeToSendBuffer(OxFF);

cMode = 1;

else {

writeToSendBuffer(cNewV&Oxff);

writeToSendBuffer(cNewV8);

else {

writeToSendBuffer(cNewV&Oxff);

writeToSendBuffer(cNewV8);

r t if(!cDateStamp){

cOldV = cNewV;
// Save OIdV and cMode for this channel.
RamADDRESS = 0x5300 + (cChannel*3);
ramWriteInt(cOIdV);
RamDATA = cMode;
RAM fVRITE;
cChannel++;
cChannel %= LPchannels;
getChannelInfoQ; // Restores OIdV and cMode for this channel.
cInPnt += 2;
IindexSave = lindex;
void getChannelInfo(void) { // Restores OIdV and cMode for this channel RamADDRESS = 0x5300 + (cChannel*3);
cOIdV = ramReadInt();
RAM_READ;
cMode = RamDATA&Oxff;
void compressScan(void){
// Save cOldV, cInPnt and cNemV in external RAM and restore when completed scan RamADDRESS = 0x5330;
ramWriteInt(cOIdV);
ram WriteInt(cInPnt);
ramWriteInt(eNewV);
cTempChannel = cChannel;
if(!cDateStamp) cOldV = cNewV;
cTempChannel ++;
cTempChannel %= LPchannels;
i cInPnt += 2;
cScanResult= 1;
while(cInPnt<cEndLPdata){
RamADDRESS=cInPnt+1;
RAM_READ;
received byte = RamDATA&Oxff;
if ((received byte&0x80)=0) {
if(cTempChannel---cChannel) {
cNewV = received byte&Ox3f:
cNewV «= 8; _ y RamADDRESS=cInPnt;
RAM_READ;
received byte = RamDATA&Oxff;
cNewV += received byte;
if (cNewV>cOldV) cOIdV = cNewV - cOldV;
else cOIdV -= cNewV;
if (cOldV<126) clnPnt = cEndLPdata; // Return true.
if (cOIdV>506){
cInPnt = cEndLPdata;
cScanResult = 0; // Return false.
i cOIdV = cNewV;
r cTempChannel++;

cTempChannel %= LPchannels;
else if(cTempChannel=cChannel) {
cInPnt = cEndLPdata;
cScanResult = 0; // Return false.
clnPnt += 2;
RamADDRESS = 0x5330;
cOldV = ramReadlnt();
cInPnt = ramReadInt();
cNewV = ramReadInt();
i void CaIcCRC(unsigned char data){
unsigned char i;

for(i=O;i<8; i++) {

_asm BANKSEL crc BCFOx4,Ox0 RLCF crc RLCF crc+1 // if(carry flag set) BTFSS Ox4,Ox0 MOVLW 0x21 XORWF crc.l MOVLWOxIO

XORWF crc+1,1 _endasm SKIPXOR1021:

_asm /1 data = 1;

//BANKSEL data BcFOxa.oxo RLCF PRODL

BANKSEL crc // if(carry flag set) BTFSC Ox4,Ox0 BTG crc,OxO

endasm r void CaIcCRConRamAddress(unsigned int address) {
RamADDRESS = address;
RAM_READ;
received_bvte = RamDATA&Oxff;
CaIcCRC(received byte);
;
void addCRC(void) {
crc=0;
for(hemp=OFFSETS;ltemp<OFFSET3+lindex:ltemp++) {
CaIcCRConRamAddress(Itemp);
CIrWdtQ;

CaIcCRC(Ox00);
CaIcCRC(Ox00);

temp I=crc»8;
tempi=cre&Oxff;
addCRCflag= 1;
convertAndWriteToSendBuffer(tempi);
convertAndWriteToSendBuffer(tempi);
lindexSave=(index;
if(!status){
messageCRC = crc;
cryptData=OFFSETS+I;
cryptDataLength = lindex - 3;
rc4cryptQ;
addCRCflag = 0;
void sendPing(void){
switch(sendPingState) {
case 0:
sendPingState=1;
break;
case 1:
lindex = 0;
writeStringToSendBuffer(sPingVersion);
received byte = read_ext_eeprom(Ox02);
if (received byte<Ox30 ~~ received byte>Ox39) received byte=0x30;
writeToSendBuffer(received byte);
writeToSendBuffer('.');
received byte = read_ext_eeprom(Ox03 );
if(received_byte<Ox30~~received-byte>Ox39)received byte=0x30:
writeToSendBuffer(received byte);
writeToSendBuffer('f);
received byte = read_ext_eeprom(Ox04);
convertAndWriteToSendBuffer(received byte);
received_byte = read_ext_eeprom(OxO~) convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
received byte = read_ext_eeprom(Ox06);
convertAndV'riteToSendBuffer(received_byte);
received byte = read_ext_eeprom(Ox07);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
writeMeterSerial():
sendPingState++, break;
case 2:
if(!writeMeterSerialState){
IindexSave=lindex;
calcSendDataQ;
sendPingState++;
break;
case 3:
if(!calcSendDataState) sendPingState=0;
break;
void exceptionPage(void);

switch(exceptionPageState){
case 0:
exceptionPageState++;
break;
case 1:
if(! sendingPage) {
sendingPage=1;
status=FALSE;
useTempAddress = 0;
if(exceptionFlag!=4) {
pagerMessage=0;
sendParametersQ;
exceptionPageState++;
break;
case 2:
if(sendParametersState=5){
sendParametersState=0;
if(exceptionFlag=I ){
outageReportSent++;
i if(exceptionFlag-4 && pagerMessage) {
writeToSendBuffer(messageStatus):
;
else {
writeToSendBuffer(OxBF);
writeToSendBuffer(0);
writeToSendBuffer(OxFF);
writeToSendBuffer(exceptionFlag);
if(exceptionFlag!=4 ~~ !pagerMessage){
pagerMessage = 0x50 + exceptionFlag; // For logging in Ad-Hoc Buffer i if(exceptionFlag=5) {
received_byte = crcEcho»8;
writeToSendBuffer(received byte);
received byte = crcEchoROxff;
writeToSendBuffer(received-b~~te);
if(exceptionFlag=10) {
for(i=O;i<4;i++) {
RamADDRESS = Ox50fU+i;
RAM_READ;
received byte = RamDATA&Oxff;
writeToSendBuffer(received byte);
r if(exceptionFlag=0) {
writeToSendBuffer(autoRegisterData + newDestination);
writeStringToSendBuffer(sAutoReg); // Flash Version.
received byte = read eat eeprom(Ox02)_ received byte «= 4;
received byte += read_ext_eeprom(Ox03)&OxOf;
writeToSendBuffer(received byte); // Loader Version.
for(i=O;i<2;i++){ // Pager Network Zone RamADDRESS = Ox50fl7+i;
RAM_READ;
received byte = RamDATA&Oxff;
writeToSendBuffer(received byte);

exceptionPageState=4;
break;
case 4:
addCRC();
if(exceptionFlag=1 ~~ exceptionFlag=2){
exceptionPageState++;
spreadTimer();
else {
exceptionPageState=6;
if(exceptionFlag=0 && (autoRegisterDestination ~~ newDestination)){
useTempAddress= 1;
stringPointer = 0x40;
stringEnd = 0x49;
putArrayInRTC(sDefaultPin);
if(autoRegisterDestination) rte write(Ox49,autoRegisterDestination); // P4161007 or P4161008 else rtc write(Ox49,'9'); // P4161009 break;
case J:
if(!spreadTimerState) exceptionPageState++;
break;
case 6:
calcSendData();
exceptionPageState++;
break;
case 7:
if(!calcSendDataState){
exceptionFlag = 0; // To stop a 04 after a 0~ from being changed to a 0~.
exceptionPageState=0;
sendingPage=0;
;
break;
a void updateTimeOfDay(void);
rtc-get datetime(&time);
timeOfDay = time.min;
timeOfDay *= 30;
timeOfDay += time.sec/2;
timeOfDay += time.hr* 1800;
r void checkBattery(void){
switch(checkBatteryState){
case 0:
if(! updateAnalogInputsState){
if(IBSenseH>13) // ~%
batteryTimerl = 0;
else {
if(!batteryTimerl) batteryTimerl = timeOfDay + 1800; // 1 hour else if(batteryTimerl<timeOfDay) checkBatteryState++;
if(VBSenseH<204) // 80%
batteryTimer2 = 0;
else {
if(! batteryTimer2) batteryTimer2 = timeOfDay + 15; // 30 seconds else if(batteryTimer2<timeOfDay) checkBatteryState++;
i if(!checkBatteryState) updateAnalogInputsState = 2;
) break;
case I:
batteryTimer2 = timeOfDay + 1; // 2 seconds VPWM_OUTP = 0;
checkBatteryState++;
break;
case 2:
if(batteryTimer2<timeOfDay) {
updateAnaloglnputsState = 2;
checkBatteryState++;
v break:
case 3:
if(!updateAnaloglnputsState){
if(IBSenseH>OxfO){
ADCONO = 0;
ADCON 1 = Ox I e; // All digital checkBatteryState++;
i else if(IBSenseH<OxOf){
checkBatteryState=7;

else updateAnaloglnputsState = 2;
break;
case 4:
VPWM_OUTP= 1;
checkBatteryState++;
break;
case 5:
if(nOVERCHARGING) batteryTimerl =0;
else {
if(!batteryTimer 1 ) batteryTimerl = timeOfDay + 1800; // 1 hour else if(batteryTimerl<timeOfDay){
checkBatteryState++;
VPWM OUTP = 0;
L
break:
case 6:
if(nOVERCHARGING) checkBatteryState=4;

break;
case 7: // PWM control starts here break;
case 8:
break;
case 9:
break;
/******************************************************
Power Quality Functions void storePQevent(void);
unsigned int getDecimal(void);
void sendPQstatus(void);
void writeDecimalToSendBuffer(unsigned int decNumber);
void pqCalc(void);
void pqAddToReport(unsigned char addToReport);
void pqLogMeterComFault(void);
void pqPoll(void);
void calcComparisons(void);
unsigned int fractionMultiply(unsigned int fracData);
void pqLogOff(void);
void pqMivsLog(void);
void pqMivsScan(void);
void pqUnbalanceCalc(void);
unsigned int sqrt(unsigned int sqData, unsigned int guess):
void pqUnbalancePoll(void);
void checkDemand(void);
******************************************************/
void storePQevent(void){
switch (storePQeventState){
case 0: // Store Percentage case 3: // Store Duration (Total for MIVS) case 6: // Store Report Delay case 9: // Store Reminder Delay (Count for MIVS) case 12: // Store Off Delay (Individual Duration of MIVS) case 15: // Store Report Flags if(storePQeventState=15) itemp = getB~~te(lstart);
else hemp=getDecimal();
minPQ = (whichPQ* 18)+0x80+storePQeventState;
maxPQ=minPQ+l;
minPQ = nc read(minPQ);
maxPQ = rtc_read(maxPQ);
if( hemp<minPQ) {
itemp=minPQ;
messageStatus = NAK;
i if(itemp>maxPQ) {
hemp=maxPQ;
messageStatus = NAK;
;
hemp = storePQeventState/3;
(temp += whichPQ*6:
hemp += Ox 1 A2;
eepromBuffer(Itemp,itemp);
storePQeventState += 3;

break;
case 18:
saveParCheckSum();
storePQeventState=0;
break;
void sendPQstatus(void);
switch (sendPQstatusState);
case 0:
sendPQstatusState++;
break;
case I:
status=1;
useTempAddress= 1;
sendParameters();
sendPQstatusState++;
break;
case 2:
if(!sendParametersState) sendPQstatusState=0;
if(sendParametersState=~) {
sendPQstatusState++;
;
break;
case 3:
writeToSendBuffer('f);
hemp = (whichPQ*6) + Ox 1A2;
received byte = read_ext_eeprom(Itemp++); // Percentage received byte = to_bcd(received_byte);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
hemp=read ext_eeprom(Ox 19C); // Nominal A
hemp = (temp«8;
(temp += read_ext_eeprom(Oxl9D)&Oxff:
writeDecimalDivider= 10;
postDecimalCharacter ='V';
writeDecimalToSendBuffer(hemp);
hemp=read ext_eeprom(Oxl9E); //Nominal B
hemp = hemp«8;
hemp += read_ext_eeprom(Ox 19F)&Oxff;
writeDecimalToSendBuffer(hemp);
hemp=read ext_eeprom(Ox 1 AO); // Nominal C
Itemp = hemp«8:
(temp += read_ext_eeprom(Ox 1 A 1 )&Oxff;
writeDecimalToSendBuffer(hemp);
writeToSendBuffer('f);
hemp = (whichPQ*6) + OxlA3;
received byte = read_ext_eeprom(ltemp++); // Duration Limit received byte = to_bcd(received byte);
convertAndfVriteToSendBuffer(received byte);
writeToSendBuffer('f);
received byte = read ext eeprom(Itemp++); // Report Delay if(received byte>99);

writeToSendBuffer((received byte/100)+0x30) received byte % 100;
received byte = to_bcd(received byte);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
received byte = read ext eeprom(ltemp++); // Reminder Delay if(received byte>99){
writeToSendBuffer((received_byte/100)+0x30);
received byte %= 100;
received byte = to_bcd(received_byte);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
received byte = read_ext eeprom(Itemp++); // Off Delay if(received_byte>99) {
writeToSendBuffer((received byte/100)+0x30);
received byte % 100;
l received byte = to_bcd(received_byte);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
received~byte = read_ext_eeprom(ltemp); // Report Flags convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
sendParametersState++, sendPQstatusState++;
break;
case 4:
if (!sendParametersState) sendPQstatusState=0;
break;
void writeDecimaIToSendBuffer(unsigned int decNumber) {
writeDecimalTemp = decNumber % writeDecimalDivider;
decNumber /= writeDecimalDivider:
received_byte = decNumber/10000;
if(received byte){
received-byte += 0x30;
writeToSendBuffer(received byte);
L
l decNumber %= 10000;
if(received byte ~~ (decNumber/1000)){
received byte = decNumber/1000;
received byte += 0x30;
writeToSendBuffer(received_byte);
i decNumber %= 1000;
if(received byte ~~ (decNumber/100)){
received byte = decNumber/100:
received byte += 0x30;
writeToSendBuffer(received byte);
L
l decNumber %= 100;

if(received byte ~~ (decNumber/10)){
received byte = decNumber/10;
received byte += 0x30;
writeToSendBuffer(received byte);
) received byte = decNumber%10;
received byte += 0x30;
svriteToSendBuffer(received byte);
svriteToSendBuffer('.');
decNumber=writeDecimalTemp;
decNumber *= 100;
decNumber += writeDecimalDivider/2;
decNumber /= writeDecimalDivider;
if(decNumber>99) decNumber-99;
received byte = decNumber/10;
received byte += 0x30;
writeToSendBuffer(received byte);
received_byte = decNumber%10;
if(received byte){
received byte += 0x30;
writeToSendBuffer(received byte);
f svriteToSendBuffer(postDecimalCharacter);
svriteToSendBuffer(' ');
void pqCalc(void){
// "pqInProgress" high bit indicates whether it seas in fault condition with the previous scan.
// Lower nibble set to 1 when event goes into fault condition and incremented whenever the // day rolls over. This is used when calculating the event duration.
// Cleared after Off Delay or when coming out of fault condition when not yet an event.
// "pqStartTime" present time saved whenever a PQ event goes into fault condition.
// "pqReportTime" when event starts this is initially set to time when event becomes an event // and then set to the time when the report should be sent out and then set to the time to // send reminder messages.
// "pqEventStatus" set to 1 when event becomes an event, incremented with each report or reminder.
// Cleared after Off Delay.
// "pqOffrime" calculated every time the PQ event comes out of fault condition. If this time is // surpassed with the PQ event still not in a fault condition then the event is considered to // have terminated.
// "pqEventDuration" calculated and saved every time the PQ event comes out of fault condition.
// If the off delay is surpassed then this value will be used in logging the event.
// "pqMivsCount" incremented with each AZIVS and when the MIVS limit is reached then the MIVS log is // scanned to see how many of there were in the occurrance window. If there were enough in // occurance svindosv to initiate a report then a report is sent and this value is zeroed.
// If there mere not enough in the occurance window then this value is left at the number that // were in the occurance svindosv.
if(AsicStatus=1 ) {
RamADDRESS = Ox536D:
voltAfraction = meterFloatToInt(Ox89); // Convert Va from 24 bit Float to 64xVa 16 bit Int voltBfraction = meterFIoatTolnt(Ox89);// Convert Vb from 24 bit Float to 64xVb 16 bit Int voltCfraction = meterFloatTolnt(Ox89);// Convert Vc from 24 bit Float to 64xVc 16 bit Int if(pqPollState=30 ~~ pqPolIState=40){
voltCfraction = 0;
s if(AsicStatus=1 ~~ pqPoIlState=30 ~~ pqPoIIState=40){
if(demandWindosv) checkDemandQ;
AsicStatus=0; // To stop a second calculation on previously manipulated data.
l~~

if(!pqEnabled) return;

for(pqEvent=O;pqEvent<l3;pqEvent++){

if((pqPoIlState=30 ~~ pqPollState=40) && pqEvent=0) pqEvent=2;

if((pqEvent%3)=0) pqValue = voltAfraction;

else if((pqEvent%3)=1) pqValue = voltBfraction;

else pqValue = voltCfraction;

RamADDRESS = Ox~3B0 + pqEvent*2;

pqComparison = ramReadlnt();

if(pqEvent<2) 1/ Out AB

RamADDRESS = Ox53D0;

else if(pqEvent<3) // Out C

RamADDRESS = Ox53D8;

else if(pqEvent<6) // Hi RamADDRESS = Ox53E0:

else if(pqEvent<9) // Lo RamADDRESS = Ox53E8;

else if(pqEvent<10) // Unbalance RamADDRESS = Ox~3F0;

else // MIVS

RamADDRESS = Ox53F8:

RAM_READ;

pqDurationLimit = RamDATA&Oxff;

RAM_READ;

pqReportDelay = RamDATA&Oxff;

RAM_READ;

pqReminderDelay = RamDATA&Oxff; // pqMivsLimit for MIVS

RAM_READ;

pqOffDelay = RamDATAR,Oxff; // pqMivsDuration for MIVS

RAM_READ:

pqReportFlag = RamDATA&Oxff;

RamADDRESS = oxaaoo + pqEvent*Oxl2;

RAM_READ;

pqInProgress = RamDATA&Oxff;

RAM_READ;

pqEventStatus = RamDATA&Oxff;

pqStartTime = ramReadInt();

pqReportTime = ramReadInt();

pqOfffime = ramReadIntQ;

pqEventDuration = ramReadlnt();

if(pqValue < pqComparison) pqFault= 1;

else pqFault = 0;

if(pqEvent>2 && pqEvent<6){ // if High event.

if(pqFault) pqFault=0;

1~1 else pqFault=1;
if(pqEvent=9){
if(unbalanceStatus>2){
if(unbalanceStatus=3) pqFault=0;
else pqFault=I;
unbalanceStatus=0;
else { // Only end up here when pqReportNow is set.
if(pqlnProgress&0x80) pqFault=1;
else pqFault=0;
if((pqInProgress&0x80) && pqFault){

if(pqEvent<10){ //NotMIVS

if((pqEventStatus && pqReportNow && !(pqEventStatus=1 && !(pqReportFlag&0x02))) ~~ (pqReportTime<timeOfDay && pqEventStatus=0 && !(pqReportFlag&0x02))){

if(pqReportFlag&0x01 ){

received byte = OxA 1+pqEvent;

if(pqEvent=2 && pqlnl'rogress&0x20) // OUT C/com received b~~te = OxAF;

pqAddToReport(received byte);

pqAddIntToReport(pqStartTime);

if(pqEvent=9){

pqAddIntToReport(unbalVoItAB);

pqAddIntToReport(unbalVoltBC);

pqAddIntToReport(unbalVoltCA);

received byte = read ext eeprom(Ox 1 BA);

pqAddToReport(received_byte);

pqAddIntToReport(unbalVoltAfrac);

pqAddIntToReport(unbalVoItBfrac);

pqAddlntToReport(unbalVoltCfrac);

pqAddIntToReport(unbalAngIeAfrac):

pqAddIntToReport(unbalAngIeBfrac):

pqAddIntToReport(unbalAngIeCfrac);

v else if(pqEvent!=2){

pqAddIntToReport(pqValue);

pqAddIntToReport(pqComparison);

;

received byte = pqEventStatus;

if(!received byte) received byte=1;

pqAddToReport(received byte);

;
if(pqReportTime<timeOfDay ~~ (pqEventStatus && pqReportNow));
if(pqEventStatus=0) { // It has not been identified as an event.
pqReportTime = pqReportDelay;
pqReportTime *= 3;
pqReportTime /= 2;
else {
pqReportTime = pqReminderDelay;
pqReportTime *= 30;
}
pqReportTime += timeOfDay;
if(pqReportFlag&0x01 && !pqReportNow){
if((pqEventStatus=0 && !(pqReportFlag&0x02)) II (pqEventStatus=1 && pqReportFlag&0x02) ~~ (pqEventStatus>I ~C.&
pqReportFlagROx04));
// meterComFault can only be set at this point if we are reporting an outage on phase C.

// we are setting it equal to 6 here to inhibit a fault flagged after the poever comes back // on when communication is delayed momentarily.
if(meterComFault) meterComFault=6;
if(!pqEventStatus && !(pqReportFlag&0x02)){
if(!pqPendingTime ~~ pqPendingTime>pqReportTime) pqPendingTime = pqReportTime;
else {
pqPendingTime = timeOfDay;
) pqReportTime += 10; // To avoid double entries.
i RamADDRESS = 0x4404 + pqEvent*Ox 12; // Save Report Time ramWritelnt(pqReportTime);
if(pqEventStatus<2 ~~ pqReportNow) pqEventStatus++;
RamADDRESS = 0x4401 + pqEvent*Oxl2; // Save Event Status RamDATA = pqEventStatus;
RAM WRITE;
r else if((pqInProgress&0x80) && !pqFault); // Terminating fault condition.
if(pqEventStatus ~~ pqEvent>9){ // Has been defined as an event already.
if(pqEvent<10); // Outage. Hi/Lo or Unbalance received_byte = pqlnProgress&Ox I F: // strip upper 3 hits if(received byte=2){
pqEventDuration = 43200-pqStartTime;
pqEventDuration += timeOfDay;
if(pqEventDuration<timeOfDay) // overflow.
pqEventDuration=OxFFFF;

else if(received byte>2){
pqEventDuration=OxFFFF;
i else pqEventDuration = timeOfDay-pqStartTime:
pqOffTime = pqOffDelay:
pqOffTime *= 30;
pqOftTime += timeOtDay;
RamADDRESS = 0x4406 + pqEvent*Oxl2; // Save pqOfffime ramWriteInt(pqOfff ime);
ramWriteInt(pqEventDuration);
else { // MIVS
if(pqlnProgress=0x82){
pqEventDuration = 32768-pqStartTime;
pqEventDuration += IastCentiTime;
else if(pqlnProgress>Ox82);
pqEventDuration=OxFFFF;
;
else pqEventDuration = fastCentiTime-pqStartTime;
pqMivsLog();
pqMivsCount++;
received byte = pqOffFime;
if(received byte>=pqReminderDelay && Ox01&pqReportFlag){

pqMivsScan();
RamADDRESS = 0x4407 + pqEvent*Oxl2; // Save pqMivsCount RamDATA = pqMivsCount // Low byte only RAM_WRITE;
pqlnProgress = 0; // Will have been modified in pqMivsScan() RamADDRESS = 0x4400 + pqEvent*Ox 12; // Clear pqInProgress RamDATA = Ox7f&pqInProgress;
if(!pqEventStatus){
if(pqEvent=2) RamDATA = 0x60&pqInProgress; // Leave Meter Com Fault else RamDATA = 0;
J
RAM WRITE;
else if(!(pqInProgress&0x80) && pqFault){ // Start of fault condition.
pqReportTime = pqDurationLimit;
pqReportTime *= 30;
pqReportTime += timeOfDay;
if(!pqEventStatus){
RamADDRESS = 0x4402 + pqEvent*Oxl2; // Set pqStartTime Hi Byte if(pqEvent>9) ramWriteInt(lastCentiTime);
else ramWriteInt(timeOfDay);
ramWriteInt(pqReportTime);
RamDATA = 0x81 ~pqInProgress; // Set pqInProgress else {
RamDATA = Ox80~pqlnProgress; // Set pqInProgress RamADDRESS = 0x4400 + pqEvent*Ox 12; // Set pqlnProgress RAM_WRITE;
else { // !pqlnProgress && !pqFault if(pqEventStatus && pqOfffime<timeOfDay){
// add statistics to report.
// PQ event is logged in reverse order because it is read from present back.
pqLog(pqEventDuration&OxFF);
pqLog(pqEventDuration»8);
pqLog(pqStartTime&OxFF);
pqLog(pqStartTime»8);
pqLog(OxA 1+pqEvent);
if(pqReportFlag&0x01 && pqReportFlag&0x08 &R, !(pqEventStatus=I &&
pqReportFlag&0x02)){
pqReportTime = pqReportDelay;
pqReportTime *= 3;
pqReportTime /= 2;
pqReportTime += timeOfDay;
if(!pqPendingTime ~~ pqPendingTime>pqReportTime) pqPendingTime = pqReportTime;
received byte = OxB 1+pqEvent;
pqAddToReport(received byte);
pqAddIntToReport(pqStartTime);
pqAddIntToReport(pqEventDuration);
r RamADDRESS = 0x4400 + pqEvent*Oxl2; // Clear pqlnProgress RamDATA = pqInProgress&0x40; // Except bit 6 RAM WRITE;

RamDATA = 0;
RAM WRITE;
if(pqEvent=8 && unbalanceStatus<3 && !pqReportNow) pqEvent=9; // to skip unbalance.
if(pqPoIlState=30) {
if(pqEvent=2) pqEvent=10;
else pqEvent=12;
if(pqPollState=40){
pqEvent=12;
r t5 ) t pqReportNow=0;
void pqAddToReport(unsigned char addToReport){
if(pqReportSize<255){
RamADDRESS = pqReportSize+px4500;
RamDATA = addToReport;
RAM_WRITE;
pqReportSize++;
void pqAddIntToReport(unsigned int IntToReport){
received byte = IntToReport»8;
pqAddToReport(received byte);
received-byte = IntToReport&Oxff;
pqAddToReport(received byte);
i void pqLogMeterComFault(void){
RamADDRESS = Ox53DC;
RAM_READ;
pqReportFlag = RamDATA&Oxff;
RamADDRESS = 0x4424;
RAM_READ;
pqInProgress = RamDATA&Oxff;
if((pqInProgress&Ox40~0){ // Start of fault session.
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox77);
pqInProgress (= 0x40; // Set bit 6 RamDATA = pqlnProgress;
RamADDRESS = 0x4424;
RAM_V~RITE;
T

if(meterComFault==4 ~~ meterComFault==6) { // End of fault session.
pqlnProgress &= OxBF; // Clear bit 6 RamDATA = pqInProgress;
RamADDRESS = 0x4424;
RAM WRITE;
if(meterComFault>6){
meterComFault -= Ox 10;
pqLog(timeOfDay&OxFF);

pqLog(timeOfDay»8);
pqLog(Ox'76);
if(pqReportFlag&0x80){
pqAddToReport(0);
else {
pqLog(lastCentiTime&OxFF);
pqLog(lastCentiTime»8);
received byte = meterComFault + 0x70;
pqLog(received byte);
if(pqReportFlag&0x80){
received byte = lastCentiTime»12;
received byte += meterComFault«4;
pqAddToReport(received byte);
received byte = IastCentiTime»4;
pqAddToReport(received byte);
void pqPoll(void) {
unsigned char iPQ;
do{
if(TMROH<RAM H) // Somehow overflowed TMRO without setting interrupt flag.
_TMROQ;
if(nAC_OK_INP) acOKtimer = (astCentiTime+200;
else if(acOKtimer<IastCentiTime){
acOKtimer = 0;
sleepTime = 0;
outageReportSent = 0:
if(acOKtimer && !sleepTime) sleepTime = timeOfOay+1800;
else if(acOKtimer && sleepTime<timeOfDay && outageReportSent =0) powerSaveSleep();
if(acOKtimer && (pqEnabled ~~ demandWindow) &&. pqPollState!=31 &&
pqPollEnabled) pqPoIIState=30;
switch(pqPoIIState) {
case 0:
if(!sendCommandState && (pqEnabled ~~ demandWindow) && !pqLogOffState &&
pqPoIlEnabled);
prepReceive(METER):
putchMeter(Ox55);
meterComAbortTime = lastCentiTime+10;
pqPoIlState=I ;
r break;
case 1:
if (kbhitMeter()){
received byte = getc(METER);
if (received byte = 0x99) pqPollState=3;
else if (received byte != 0x55) pqPollState=0;
else {
if(meterComFault && meterComFault!=6);
meterComFault=4;
pqLogMeterComFauItQ;
if(!pqPendingTime &R pqReportFlag&0x80);

// pqReportFlag set in pqLogMeterComFaultQ
pqPendingTime = timeOfDay + 30;
if(meterComFault) pqLogOff();
meterComFault=0;
unanswered55s=0;
pqPoIIState=2;
putchMeter(OxAA);
meterComAbortTime = IastCentiTime+4;
else if (meterComAbortTime<lastCentiTime){
RamADDRESS = Ox53DC;
RAM_READ;
pqReportFlag = RamDATA&Oxff;
received byte = pqReportFlag&0x60;
if(meterComFault!=6 && ((unanswered55s=1 && received byte=0x00) ~~ (unanswered55s=6 && received byte=0x20) ~~ (unanswered55s=13 && received byte---0x40))){
meterComFault=1;
pqLogMeterComFauItQ;
l if(unanswered~5s<20){
unanswered>js++;
pqPollState=0;
r else {
meterComFault=2;
pqLogMeterComFaultQ;
unanswered55s=0;
pqPollState=40;
meterComAbortTime = lastCentiTime + 4300:
RamADDRESS = 0x4424;
RAM_READ;
pqlnProgress = RamDATAR.Oxff;
pqlnProgress ~= 0x20; // Set bit ~ (Out C/com) RamDATA = pqlnProgress;
RamADDRESS = 0x4424;
RAM WRITE;
break;
case 2:
if(kbhitMeterQ){
if (getc(METER) = OxAA) {
pqPollState=3;
l else pqPoIIState=0;
r else if (meterComAbortTime<IastCentiTime) pqPolIState=0:
break;
case 3:
putchMeter(Ox80);
for(iPQ=1;iPQ<9;iPQ++) {
putchMeter(0);
putchMeter(Ox80);
putchMeter(0);

if(unbalanceStatus=2) pqUnbalanceCalc();
else pqCalc(); // Calculate the previously read data.
pqPoIlState=4;
break;
case 4:
if(!PIE2bits.TX2IE){ // Wait until all characters have gone out.
meterComAbortTime = IastCentiTime+10;
pqPoIIState=5;
break;
case 5:
if (kbhitMeterQ){
received byte = getc(METER);
if (received byte - 0x33) { // Transmission Accepted meterComAbortTime = lastCentiTime+4;
pqPollState=6;
r else if (received byte = 0x99) { // Checksum error - resend pqPollState=3;
i else { // Command not accepted pqPollState=0;
;
r else if (meterComAbortTime<lastCentiTime) {
pqPollState=0;
r break:
case 6:
if (kbhitMeterQ);
bytesFromMeter = getc(METER);
meterDataChksum=bytesFromMeter;
MbuffPointer = 0;
meterComAbortTime = IastCentiTime+4;
pqPolIState=17;
else if (meterComAbortTime<IastCentiTime) {
pqPolIState=0;
break:
case 17:
if (kbhitMeterQ) {
received byte = gete(METER);
meterDataChksum = meterDataChksum + received byte;
RamDATA = received byte;
RamADDRESS = 0x5361 + MbuffPointer;
RAM_WRITE;
MbuffPointer++;
meterComAbortTime = lastCentiTime+4;
if (MbuffPointer>42){
pqPollState++;
r ;
else if (meterComAbortTime<lastCentiTime) pqPoIIState=0;
break:
case 18:
1~8 if (kbhitMeterQ){
AsicStatus = getc(METER);
if (sendCompletePQ=4) {
convertAndWriteToSendBuffer(AsicStatus);
sendCompIetePQ = 0;
meterDataChksum = meterDataChksum + AsicStatus;
MbuffPointer++;
meterComAbortTime = IastCentiTime+4;
pqPol IState=19;
i else if (meterComAbortTime<IastCentiTime) {
pqPollState=0;
break;
case 19:
if (kbhitMeterQ){
received byte = getc(METER);
meterDataChksum-=received byte;
meterComAbortTime = lastCentiTime+4;
pqPoIIState=20;
else if (meterComAbortTime<lastCentiTime) AsicStatus=0;
pqPolIState=0;
i break;
case 20:
if (kbhitMeter()) {
received byte = getc(METER);
pqPoIlState=0;
bytesFromMeter-44;
if (meterDataChksum&Oxff) {
putchMeter(Ox33);
AsicStatus=0;
else {
meterDataChksum = meterDataChksum»8;
if (meterDataChksum---received byte);
// nCLEAR SW_LED_IO=0;
// nCLEAR SW_SET DDRB = !nCLEAR SW SET DDRB;
pulseClear= 1;
if(unbalanceStatus=1 ){
pqPol I State=21;
;
r else {
AsicStatus=0;
i putchMeter(Ox33);
r r else if (meterComAbortTime<IastCentiTime) {
pqPolIState=0;
AsicStatus=0;
break;
case 21:
pqUnbalancePoll();
pqPolIState++;

break;

case 22:

if(unbalPollState) pqUnbalancePoll();

else {

bytesFromMeter=44;

pqPoIlState=0;
) break;

case 30:

if(meterComFault!=3 && meterComFault!=6){

meterComFault=3;

pqLogMeterComFault();

f AsicStatus=0;

pqCalcQ; // Keep track of when to report event.

meterComAbortTime = lastCentiTime+20;

pqPolIState++:

break;

case 31:

if (meterComAbortTime<IastCentiTime) {

if(acOKtimer) pqPollState=30;

else {

pqPollState=40;

meterComAbortTime = lastCentiTime + 10;

break;
case 40:
if (kbhitMeter()) {
if (getc(METER) ---- OxOd) {
pqPollState=41;
putchMeter('O');
putchMeter('K');
putchMeter(OxOd);
meterComAbortTime = IastCentiTime+50;
if(meterComFault!=6){
meterComFault=~;
pqLogMeterComFault();
else if (meterComAbortTime<IastCentiTime);
// V'e get to this point from a loss and restoration of the AC OK signal or after 40 seconds of silence.
// Either way let's extend the "Out C/Com" PQ event.
AsicStatus=0;
pqCalc(); // Keep track of when to report event.
pqPol lState=0;
break;
case 41:
if(kbhitMeterQ){
pqPolIState= 40;
meterComAbortTime = IastCentiTime+20;
r else if (meterComAbortTime<IastCentiTime) pqPollState=0:
break;
i ; while (!TMROchanged && sendCommandState<2);

#pragma code calcComparisons = 0x6000 void calcComparisons(void){

switch(whichPQ) {

case 0: // Outage A&B

// hemp = (pqNominal/10)*(percentage/100)*64= pqNominal*percentage*8/125 fracMultiplier=read ext eeprom(OxlA2);//Percentage fracMultiplier *= 8;

fracDivider= 125;

hemp=read ext_eeprom(Oxl9C);

ltemp = ltemp8;

(temp += read_ext_eeprom(Ox 19D)&Oxff;

hemp = fractionMultiply(ltemp);

RamADDRESS = Ox53B0;

ram WriteInt(ltemp);

(temp=read ext eeprom(Ox 19E);

(temp = hemp8;

(temp += read_ext_eeprom(Ox 19F)&Oxff;

hemp = fractionMultiply(ltemp);

RamADDRESS = Ox53B2;

ramWriteInt(ltemp);

RamDATA = read_ext_eeprom(OxlA3);// Duration Limit RamADDRESS = Ox53D0;

RAM_WRITE;

RamDATA = read ext eeprom(OxlA4);// Report Delay RAM_WRITE;

RamDATA = read ext eeprom(Ox// Reminder Delay 1A5);

RAM_WRITE;

RamDATA = read ext eeprom(Ox// Off Delay 1 A6);

RAM_WRITE;

RamDATA = read ext eeprom(OxlA7);// Report Flags RAM
WRITE;

_ break case 1: // Outage C

// ltemp = (pqNominal/10)*(percentage/100)*64= pqNominal*percentage*8/125 fracMultiplier=read ext eeprom(OxlAB);//Percentage fracMultiplier *= 8;

fracDivider = 125;

hemp=read ext_eeprom(Ox l AO);

ltemp = hemp8;

hemp += read_ext_eeprom(Ox 1 A 1 )&Oxff;

hemp = fractionMultiply(ltemp);

RamADDRESS = Ox53B4;

ramWriteInt(ltemp);

RamDATA = read_ext_eeprom(Ox// Duration Limit I A9);

RamADDRESS = Ox53D8;

RAM_WRITE;

RamDA T A = read ext eeprom(Ox// Report Delay I AA);

RAM_WRITE;

RamDATA = read ext eeprom(OxIAB);// Reminder Delay RAM_WRITE;

RamDATA = read ext eeprom(Ox// Off Delay 1 AC);

RAM_WRITE;

RamDATA = read ext eeprom(OxIAD);// Report Flags RAM_WRITE;

break; //

case 2: // Hi Voltage //hemp=(pqNominal/10)*((100+percentage)/100)*64=pqNominal*(100+percentage)*8/12 fracMultiplier=read_ext eeprom(OxIAE);

fracMultiplier+= 100;

fracMultiplier *= 8;

fracDivider = 125;

hemp=read ext_eeprom(Ox 19C);

ltemp = ltemp8;

hemp += read_ext_eeprom(Ox 19D)&Oxff;

Itemp = fractionMultiply(Itemp);

RamADDRESS = Ox53B6;

ramWriteInt(Itemp);

hemp=read ext_eeprom(Oxl9E);

Itemp = Itemp8;

hemp += read_ext_eeprom(Ox 19F)&Oxff;

Itemp = fractionMultiply(ltemp);

RamADDRESS = Ox53B8;

ramWriteInt(Itemp);

ltemp=read ext_eeprom(OxIAO);

(temp = ltemp8;

hemp += read_ext_eeprom(Ox t A 1 )&Oxff;

ltemp = fractionMultiply(Itemp);

RamADDRESS = Ox~3BA;

ramWriteInt(Itemp);

RamDATA = read_ext_eeprom(Ox 1/ Duration Limit lAF);

RamADDRESS = Ox53E0;

RAM_WRITE:

RamDATA = read ext_eeprom(Ox !/ Report Delay 1 BO);

RAM_W RITE;

RamDATA = read ext eeprom(Oxl// Reminder Delay B 1 );

RAM_W RITE;

RamDATA = read ext_eeprom(Ox // Off Delay 1 B2);

RAM_WRITE;

RamDATA = read ext eeprom(Ox // Report Flags 1 B3 );

RAM_WRITE;

break;

case 3: // Low Voltage // (temp = (pqNominal/10)*((100-percentage)/100)*64 = pqNominal*(100-percentage)*8/125 fracMultiplier = read_ext_eeprom(Ox 1 B4);

fracMultiplier = 100-fracMultiplier;

fracMultiplier *= 8;

fracDivider= 125;

hemp=read ext_eeprom(Ox ( 9C );

hemp = ltemp8;

hemp += read_ext_eeprom(Ox 19D)&Oxff;

ltemp = fractionMultiply(Itemp);

RamADDRESS = Ox53BC;

ramWritelnt(ltemp);

Itemp=read ext_eeprom(Oxl9E);

hemp = hemp8;

ltemp += read_ext_eeprom(Ox 19F)&Oxff;

(temp = fractionMultiply(Itemp);

RamADDRESS = Ox53BE;

ramWritelnt(Itemp);

hemp=read ext_eeprom(Ox l AO);

hemp = ltemp8;

hemp+=read_ext_eeprom(OxIAI)&Oxff:

(temp = fractionMultiply(Itemp);

RamADDRESS = Ox53C0;

ramWriteInt(Itemp);

RamDATA = read_ext_eeprom(OxIBS);// Duration Limit RamADDRESS = Ox53E8;

RAM_WRITE;

RamDATA = read ext eeprom(Ox// Report Delay 1 B6);

RAM_WRITE;

RamDATA = read ext eeprom(Ox// Reminder Delay 1 B7);

RAM_WRITE;

RamDATA = read ext eeprom(Ox// Off Delay I B8);

RAM_WRITE;

RamDATA = read ext eeprom(Ox// Report Flags 1 B9);

RAM_WRITE;

break;

case 4: // Voltage Unbalance uintl6ToFloat32(64,Ox10);

uintl6ToFloat32(3,Ox14);

uintl6ToFloat32(4256,Ox 18);

uintl6ToFloat32(IOOOO,OxIC);

floatDivide(Ox 1 C,Ox 14,0x1 C);

fracMultiplier = read_ext_eeprom(Oxl; // Percentage BA) fracDivider = 100 - fracMultiplier;

fracMultiplier+= 100;

RamDATA = fracDivider&OxFF;

RamADDRESS = Ox53C2;

RAM_WRITE;

RamDATA = fracMultiplier&OxFF;

RAM_WRITE;

RamDATA = read_ext_eeprom(Ox// Duration Limit 1 BB);

RamADDRESS = Ox53F0;

RAM_WRITE;

RamDATA = read ext_eeprom(Ox// Report Delay 1 BC);

RAM_WRITE;

RamDATA = read ext eeprom(OxIBD);// Reminder Delay RAM_WRITE;

RamDATA = read ext eeprom(Ox// Off Delay 1 BE);

RAM_WRITE:

RamDATA = read ext eeprom(Ox// Report Flags I BF);

RAM WRITE;

uintl6ToFloat32( 1,0x58);

floatChangeSign(Ox58);

uintl6ToFloat32(I 192,0x40);

uintl6ToFloat32(1000,Ox3C);

floatDivide(Ox40,Ox3C,Ox40);

uint 16ToFloat32(6890,Ox3C);

floatDivide(Ox58,Ox3C,Ox3C);

uint 16ToFloat32( 122,0x44);

floatDivide(Ox58,Ox44,Ox44);

uint I 6ToFloat32( 17800,0x48);

floatDivide(Ox58,Ox48,Ox48);

uint l6ToFloat32( 128,Ox4C);

floatChangeSign(Ox58);

floatDivide(Ox58,Ox4C,Ox4C);

floatDivide(Ox58,Ox10,Ox10);

uint l6ToFloat32(2,Ox5C);

floatChangeSign(OxSC);

break;

case 5: // MIVS

// hemp = (pqNominal/10)*(percentage/100)*64= pqNominal*percentage*8/125 fracMultiplier = read ext // Percentage eeprom(OxlCO);

fracMultiplier *= 8;

fracDivider = 125;

hemp=read ext eeprom(Oxl9C);

Itemp = (temp«8;
Itemp += read_ext_eeprom(Oxl9D)&Oxff;
Itemp = fractionMultiply(Itemp);
RamADDRESS = Ox53C8;
ramWritelnt(Itemp);
(temp=read ext_eeprom(Oxl9E);
(temp = hemp«8;
hemp += read_ext eeprom(Ox 19F)&Oxff;
ltemp = fractionMultiply(ltemp);
RamADDRESS = Ox53C4;
ramWriteInt(ltemp);
(temp=read ext_eeprom(OxIAO);
hemp = hemp«8;
hemp += read_ext_eeprom(Ox 1 A 1 )&Oxff;
hemp = fractionMultiply(ltemp);
RamADDRESS = Ox53C6;
ramWriteInt(ltemp);
RamDATA = read_ext_eeprom(Ox 1 C 1 ); // Occurance Window RamADDRESS = Ox53F8;
RAM_WRITE;
RamDATA = read ext eeprom(OxlC2); // Report Delay RAM_WRITE;
RamDATA=read ext eeprom(OxlC3); // MIVS Count Limit RAM_WRITE;
RamDATA = read ext eeprom(OxIC4); // MIVS Individual Max Duration RAM_WRITE;
RamDATA = read ext eeprom(Ox I CS); I/ Report Flags RAM_WRITE;
break ;
unsigned int fractionMultiply(unsigned int fracData) {
AARGBO=fracMultiplier»8;
AARGB1=fracMultiplier&Oxff;
BARGBO=fracData»8;
BARGB 1=fracData&Oxff;
FXM 1616UQ;
BARGBO=fracDivider»8;
BARGE 1=fracDivider&Oxff:
FXD3216UQ;
fracData = AARGB2;
f~acData = fracData«8;
fracData += AARGB3:
return(fracData);
void pqLogOff(void) {
switch(pqLogOffState) {
case 0:
pqLogOffState++; // If pqPendingTime is set because of a previous swipe // then delay sending until after this session.
if(suspendPQtime=pqPendingTime) pqPendingTime = 0;
if(!meterComFault) {
suspendPQtime = IastCentiTime + 100;// Temp PQ Disable switch debounce.
pqLog(timeOYDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox7E):
received byte=read ext eeprom(OxIFF);
if(received byte){

pqAddToReport(Ox7E);
pqAddIntToReport(timeOfDay);
else { // Log off to clear intermittent zero reads after a power down or MIVS
// This was helping at one time but may not be necessary anymore.
suspendPQtime = lastCentiTime + 30;
pqLogOffState = 31;
break;
case I:
case 31:
if(!sendCommandState && !pqPollState){
pqLogOff~tate++;
command[0]=0x79;
command [ 1 ]=0x04;
for(i=2;i<1 1;i++) command[i]=0;
command[9]=Ox7d;
sendFlag=FALSE;
sendCommand(); // There is a small chance that there will be an overlap here.
break;
case 32:
if(!sendCommandState && suspendPQtime<IastCentiTime){
pqLogOffState = 0;
break;
case 2:
if(!sendCommandState && suspendPQtime<lastCentiTime){
pqLogOffState = 18;
pqLogOffState -= read ext eeprom(Ox 1 FE):
i break;
case 19:
pqLogOffState=21;
suspendPQtime=0;
break;
case 20:
if(nMAG_SWITCH INP){
pqLogOffState++;
suspendPQtime = lastCentiTime + 100; // Temp PQ Disable switch debounce.
l break;
case 21:
if(suspendPQtime<lastCentiTime) {
received byte = read ext eeprom(Ox 1 FF);
if(received byte);
if(!pqPendingTime) {
suspendPQtime = received byte;
suspendPQtime *= 30;
suspendPQtime += timeOfDay;
pqPendingTime = suspendPQtime;
pqAddToReport(Ox7F);
pqAddIntToReport(timeOfDay);
l pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox7F);
pqLogOff~tate=0;

t break;
default : // 3 to 18 if(suspendPQtime<IastCentiTime){
pqLogOffState++;
suspendPQtime = IastCentiTime + 6000;
if(!nMAG_SWITCH INP){
pqLogOffState=20;
}
break;
void pqMivsLog(void){
// Puts time stamps based on IastCentiTime into a circular buffer for MIVS
only cu 0x5200-Ox52FF
// pqEvent = 10, 1 I or 12 corresponding to MIVS on phase B, C or A. This is put into the upper // nibble of the high byte of the time stamp and the upper 12 bits of IastCentiTime are saved // in the lower nibble and the low byte. A single 0x00 is put into the log every time the // IastCentiTime variable is rolled back 32768.
if(pqEvent=0) {
pqMivsBuffPnt++;
RamADDRESS = 0x5200+pqMivsBuffPnt;
RamDATA = 0;
RAM_WRITE:
else if (pqEventDuration>pqMivsDuration) {
pqMivsBuffPnt++;
RamADDRESS = Ox~200+pqMivsBuffPnt;
RamDATA = IastCentiTime»4;
RamDATA &= OxFF;
RAM_WRITE;
pqMivsBuffPnt++;
RamDATA = lastCentiTime» 12;
RamDATA += pqEvent«4;
RAM WRITE;
r // For now we will log every MIVS in the Ad-Hoc buffer as well for debugging purposes.
if(pqEvent){
pqLog(pqEventDuration&OxFF):
pqLog(pqEventDuration»8);
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(OxB 1+pqEvent);
void pqMivsScan(void){
// This function scans back in time from the present in the MIVS log counting the number of MIVS
// in a phase that are within the occurrance window. Occurance window is stored in external // RAM at Ox44BB in minutes (1 to 60). Time stamps are based on 16x IastCentiTime (0.16s) // Occurance window is multiplied by 37~ (= 60* 100/16) to be in the same scale. First the # of // lastCentiTime rollbacks to scan past are calculated and then the lowest value of timeStamp to // include in the count is calculated.
// pqMivsCount (same as pqOfffime) will be zeroed if it is an event and left at however many are within the occurance // window if it is not an event. pqEvent must not be modified, pqReportDelay and pqMivsLimit (same // as pqReminderDelay) are used here. pqlnProgress and pqGventStatus may be modified as long as pqInProgress // is set to zero after running pqMivsScan. Any other temporary pq variables can be modified without // needing to restore them.
// pqStartTime used here for occurance window in 0.16s increments.

// pqComparison used here for earliest time stamp to include.
// pqDurationLimit used here for # of rollbacks to scan past.
// pqlnProgress used here for a MIVS log pointer.
// pqEventStatus used here for a flag to end scanning.
// pqReportTime used here for the time between now and the earliest MIVS
within the occurance window.
// pqValue used here for time stamps to be evaluated.
#define pqMIVSoccurance pqStartTime #define pqMIVSrollbacks pqDurationLimit #define pqMIVSpointer pqInProgress #define pqMIVSscanExit pqEventStatus #define pqMIVSwindow pqReportTime #define pqMIVStime pqValue RamADDRESS = Ox53f8; !/ get occurance window RAM_READ;
pqMIVSoccurance = RamDATA&Oxff;
pqMIVSoccurance *= 375; // 6000/16 if(scanMIVSonly){
pqMivsCount = pqReportDelay; // pqMivsCount used temporarily.
pqMivsCount *= 75; // 300/16 pqMivsCount /=4; // 300/16 pqMIVSoccurance += pqMivsCount;
pqMivsCount=0;
pqComparison = lastCentiTime»4;
if(pqMIVSoccurance<pqComparison) {
pqMIVSrollbacks=0; // # of rollbacks pqComparison -= pqMIVSoccurance; // earliest time stamp to include else ;
pqComparison = pqMIVSoccurance - pqComparison;
pqMIVSrollbacks = pqComparison/2048; // # of rollbacks pqMIVSrollbacks++;
pqComparison %= 2048;
pqComparison = 2048 - pqComparison; // earliest time stamp to include pqMIVSscanExit=0;
pqMIVSpointer = pqMivsBuffPnt;
do ;
RamADDRESS = 0x5200+pqMIVSpointer;
RAM_READ;
received_byte = RamDATA&Oxff;
pqMIVSpointer--;
if(pqMIVSpointer=pqMivsBuffPnt) {
pqMIVSscanExit=1;
;
else if(!received byte) {
if(pqMIVSrollbacks) {
pqMIVSrollbacks--;
else {
pqMIVSscanExit=I;
else {
pqMIVStime = received byte&OxOf;

pqMIVStime «= 8;
RamADDRESS = 0x5200+pqMIVSpointer;
RAM_READ;
pqMIVStime += RamDATA&Oxff;
pqMIVSpointer--;
if(pqMIVSpointer=pqMivsBuffPnt) {
pqMIVSscanExit=1;
received byte »= 4;
if(!pqMIVSrollbacks && pqMIVStime<pqComparison){
pqMIVSscanExit=1;
else if(pqEvent=received byte);
pqMivsCount++;
pqMIVSwindow = pqMIVSrollbacks*2048;
pqMIVSwindow += pqMIVStime;
pqMIVSwindow -= pqComparison;
~ while (!pqMIVSscanExit);
if(pqMivsCount>=pqMivsLimit && !scanMIVSonI~~);
if(!pqPendingTime){
pqPendingTime = pqReportDelay;
pqPendingTime *= 3;
pqPendingTime /= 2;
pqPendingTime += timeOfDay;
i pqMIVSwindow = pqMIVSoccurance - pqMIVSwindow:
pqMIVSwindow *= 2;
pqMIVSwindow /= 2~;
pqMIVSwindow *=2;
pqAddToReport(OxA 1+pqgvent);
pqAddlntToReport(timeOfDay);
pqAddToReport(pqMivsCount);
pqAddIntToReport(pqMIVSwindow);
pqLog(timeOfDay&OxFF):
pqLog(time0lDay»8);
pqLog(OxAI+pqEvent):
pqMivsCount=0;
;
void pqUnbalanceCalc(void){
// A voltage unbalance condition is determined from the GyrBox Phase Information.
// This function does the // calculation to determine if a fault condition is present and sets a flag if so. The function // pqCalc uses this fault flag to keep track of the duration of the voltage unbalance event and !/ determines when it becomes an event for logging and reporting.
RamADDRESS = Ox~340:
unbalVoltAfrac = meterFloatTolnt(Ox89): U Convert Va from 24 bit Float to 64xVa 16 bit Int unbalVoltBfrac = meterFloatToInt(Ox89); // Convert Vb from 24 bit Float to 64xVb 16 bit Int unbalVoltCfrac= meterFIoatToInt(Ox89); // Convert Ve from 24 bit Float to 64xVc 16 bit Int unbalAngIeBfrac = meterFloatToInt(Ox88); // Convert Angle of Vb from 24 bit Float to 128x 16 bit Int unbalAngleCfrac = meterFloatToInt(Ox88); // Convert Angle of Vc from 24 bit Float to 128x 16 bit Int // Angle A between Va and Vb is the angle of Vb.
unbalAngIeAfrac = unbalAngleBfrac;
// Angle B between Vb and Vc is the angle of Vc - the angle of Vb if(unbalAngleCfrac>unbalAngleBfrac) unbalAngleBfrac = unbalAngleCfrac - unbalAngIeBfrac;
else unbalAngIeBfrac = unbalAngIeBfrac - unbalAngleCfrac;
// Angle C between Vc and Va is 360(x128) - the angle of Vc unbalAngleCfrac = 46080 - unbalAngleCfrac;
unbalVoltAvg = unbalVoItAfrac/3; // Calculate the average Phase Voltage unbaIVoItAvg += unbalVoltBfrac/3;
unbalVoltAvg += unbalVoltCfrac/3;
uint 16ToFloat32(unbal V oltAfrac,Ox00);
uintl6ToFloat32(unbalVoltBfrac,Ox04);
uintl6ToFloat32(unbalVoltCfrac,Ox08);
uint 16ToFloat32(unbal VoltAvg,OxOC);
floatMultiply(OxOC,OxIO,OxOC); // Multiply by 1/64 floatDivide(OxIC,OxOC,Ox20); // 10000/(3Vavg) floatDivide(Ox20,OxOC,Ox20); // 10000/(3(Vavg**2)) cos128(unbalAngIeAfrac,Ox24); // cos(A) cos128(unbalAngleBfrac,Ox28): l/ cos(B) cos128(unbalAngIeCfrac,Ox2C); // cos(C) for( i=0; i<9; i+=4) {
floatMultiply(i,OxlO,i); // Multiply by I/64 floatMultiply(i,i,Ox30+i); // Va**2 for(i=O;i<9;i+=4) {
floatMultiply(0x24+i,OxSC,Ox24+i); // -2cos(A) floatMultiply(Ox24+i,i,Ox24+i); //-2Va*cos(A) floatMultiply(0x24+i,(4+i)%OxOC,Ox24+i); //-2VaVb*cos(A) floatAdd(Ox24+i,Ox30+i,Ox24+i): // Va**2 - 2VaVb*cos(A) floatAdd(0x24+i,Ox30+((4+i)%OxOC),Ox24+i); // Va**2 + Vb**2 - 2VaVb*cos(A)==
Vab**2 floatMultiply(0x24+i,Ox20,Ox24+i); // (Vab**2 * 100**2)/(3*Vavg**2) unbalVoltAB = float32ToIntl6(OX24); // Approx. Vab as a % of average squared unbalVoltBC = float32ToIntl6(OX28); // Approx. Vbc as a % of average squared unbalVoItCA = float32ToIntl6(OX2C);// Approx. Vca as a % of average squared unbalVoltAB = sqrt(unbaIVoItAB,100);
unbalVoltBC = sqrt(unbalVoltBC,100);
unbalanceStatus=3;
unbalVoltCA = sqrt(unbalVoltCA,100);
unbalVoltAvg = unbalVoltAB + unbalVoltBC + unbaIVoItCA:
unbalVoltAvg /= 3;
fracMultiplier = 10000;
fracDivider=unbaIVoItAvg;
unbalVoItAB = fractionMultiply(unbaIVoItAB);
unbalVoltBC = fractionMultiply(unbalVoItBC);
unbalVoltCA = fractionMultiply(unbalVoItCA);
RamADDRESS = Ox53C2;
RAM_READ;
// unbalVoltAvg used here for lower limit comparison.
unbalVoltAvg = RamDATA&Oxff;
unbalVoltAvg *= 100;
if(unbalVoltAB<unbalVoltAvg ~~ unbaIVoItBC<unbalVoltAvg ~~
unbalVoltCA<unbalVoItAvg) unbalanceStatus=4;
RamADDRESS = Ox53C3;
RAM_READ;
// unbaIVoItAvg used here for upper limit comparison.
unbalVoltAvg = RamDATA&Oxff;
unbalVoltAvg *= 100;
if(unbaIVoItAB>unbalVoltAvg ~~ unbalVoItBC>unbalVoltAvg ~~
unbalVoItCA>unbaIVoItAvg) unbalanceStatus=4;
unsigned int sqrt(unsigned int sqData, unsigned int guess) {
unsigned int guess2;
unsigned char negativeFlag;
// This function accepts a value that should be beUveen 50 squared and 150 squared if the guess // is 100. The return value is an approximation of the square root times 100.
It will extrapolate // beyond 50 to 150 but errors become larger the farther from 100 you get.
This is OK for voltage // unbalance calculations where the difference from the average should be less than 6%.
guess *= 100;
for(i=O;i<3;i++) {
guess /= 100;
if(guess<2) guess=2;
guess2 = guess*guess;
if(guess2>sqData) {
guess2 -= sqData;
negativeFlag=0;
r else {
guess2 = sqData-guess?;
negativeFlag=1;
) if(guess2<1300){
guess2 *= 50;
guess2 /= guess;
else {
guess? /= guess:
guess? *= 50:
guess *= 100:
if(negativeFlag) guess += guess2;
else guess -= guess2;
return(guess);
void pqUnbalancePoll(void) {
// This function will be called by pqPoll so decisions of when to run it will be made by pqPoll.
// This function reads the GyrBox Phase Information from the meter. While waiting for the data // to come back from the meter the Asic data from the previous meter read is processed. The // data from this read is processed when the data from the next Asic read is being waited on.
unsigned char iPQ;
switch(unbalPollState) {
case 0:
putchMeter(Ox55);
meterComAbortTime = lastCentiTime+4;
unbaIPolIState++;
break;
case 1:
if (kbhitMeterQ){

if (getc(METER) != 0x55) unbalPoIIState=0;
else {
unbalPol lState++;
putchMeter(OxAA);
meterComAbortTime = IastCentiTime+4;
i else if (meterComAbortTime<lastCentiTime) unbalPollState=0;
break;
case 2:
if (kbhitMeter()) {
if (getc(METER) = OxAA) {
unbalPollState++;
}
else unba1Po11State=0;
r else if (meterComAbortTime<IastCentiTime) unbaIPoIlState=0;
break;
case 3:
putchMeter(Ox94);
for(iPQ=I;iPQ<9;iPQ++){
putchMeter(0);
}
putchMeter(Ox94);
putchMeter(0);
pqCalc(); // Calculate the previously read data.
unbalPollState++;
break;
case 4:
if(!PIE2bits.TX2IE){ // Wait until all characters have gone out.
meterComAbortTime = lastCentiTime+10;
unbalPoIlState=5;
r break:
case 5:
if (kbhitMeter()) {
received byte = getc(METER);
if (received byte ---- 0x33) { // Transmission Accepted meterComAbortTime = IastCentiTime+4;
unbaIPolIState++;
i else if (received byte--- Ox99); // Checksum error - resend unbalPoIlState=3;
;
else { // Command not accepted unbalPollState=0;
r else if (meterComAbortTime<lastCentiTime) {
unbaIPolIState=0;
}
break;
case 6:
if (kbhitMeterQ){
bytesFromMeter = getc(METER);
meterDataChksum=b~~tesFromMeter;
MbuffPointer = 0;
meterComAbortTime = IastCentiTime+4;
unbalPolIState=22;

else if (meterComAbortTime<IastCentiTime) {
unbalPollState=0;
break;
case 22:
if (kbhitMeter()){
received byte = getc(METER);
meterDataChksum = meterDataChksum + received byte;
RamDATA = received byte;
RamADDRESS = 0x5340 + MbuffPointer;
RAM_WRITE;
MbuffPointer++;
meterComAbortTime = lastCentiTime+4;
if (MbuffPointer>32){
unbalPoIlState++;
else if (meterComAbortTime<IastCentiTime) {
unbaIPolIState=0;
break;
case 23:
if (kbhitMeter()) {
received byte = getc(METER);
meterDataChksum-=received byte;
meterComAbortTime = lastCentiTime+4;
unba1Po11State++;
else if (meterComAbortTime<lastCentiTime) {
unbalPollState=0;
break;
case 24:
if (kbhitMeterQ) {
received-byte = getc(METER);
unbalPoIlState=0;
if (meterDataChksum&Oxft) {
putchMeter(Ox33);
;
else {
meterDataChksum = meterDataChksum»8;
if (meterDataChksum-received byte) {
// nCLEAR_SW_LED_IO=0;
// nCLEAR_SW_SET_DDRB = !nCLEAR SW SET DDRB;
unbalanceStatus=2;
i r putchMeter(Ox33);
r ;
else if (meterComAbortTime<lastCentiTime) {
unbalPoIlState=0;
break:
;
void checkDemand(void){
// unsigned int's #define demandThreshold pqComparison #define demandTemp pqStartTime #define demandReminderDelay pqReportTime #define demandSampIeAverage pqOfffime #define demandWindowAverage pqValue #definedemandOKdelay pqEventDuration // unsigned char's #define demandWindowRemainder pqInProgress #define demandDataPointerpqDurationLimit #define demandSampleNumber pqReportDelay #definedemandMaxExp pqFault Int if(AsicStatus){
RamADDRESS = 0x5389;
demandValue = meterFloatToInt(read ext eeprom(Ox 1 cb)); // Convert Watts from 24 bit Float to 16 bit else demandValue = 0;
RamADDRESS = Ox539F;
RAM_READ;
demandSampleNumber = RamDATA&Oxff;
demandSampIeAverage = ramReadlnt();
demandSampIeNumber++;
demandTemp = demandSampleAverage % demandSampIeNumber;
demandTemp *=demandSampleNumber-1;
demandTemp += demandValue % demandSampIeNumber;
demandTemp += demandSampIeNumber/2;
demandTemp /= demandSamp(eNumber;
demandSampleAverage /= demandSampleNumber:
demandSampIeAverage *= demandSampIeNumber-I:
demandSampIeAverage += demandValue/demandSampIeNumber:
demandSampleAverage += demandTemp;
if(demandSampleTime<IastCentiTime) {
RamADDRESS = Ox53A8;
demandReminderDelay = ramReadIntQ;
demandOKdelay = ramReadInt();
demandWindowAverage = ramReadlnt();
RAM_READ;
demandW indowRemainder = RamDATA&Oxff;
RAM_READ;
demandDataPointer = RamDATA&Oxff;
demandSampIeTime = demandWindow;
demandSampleTime *= 47; // (60s* 100)/128 demandSampleTime += lastCentiTime;
demandSampleNumber = 0;
RamADDRESS = 0x5400 + demandDataPointer;
demandTemp = ramReadIntQ;
RamADDRESS = 0x5400 + demandDataPointer;
ramWriteInt(demandSampleAverage);
demandDataPointer += 2;
demandWindowAverage -= demandTemp/128;
demandTemp % 128;
if (demandTemp > demandWindowRemainder){
demandWindowRemainder += 128 - demandTemp;
demandWindowAverage --;
else demandWindowRemainder -= demandTemp;
demandWindowAverage+= demandSampleAverage / 128;
IZJ

demandWindowRemainder+= demandSampIeAverage%128;
if (demandWindowRemainder > 128){
demandWindowRemainder-= 128;
demand W indowAverage ++;
) received byte = timeOfDay/450;
if ((received byte<read ext eeprom(Oxlc7)) ~~ (received byte>=read ext_eeprom(Oxlca))) demandTemp = Ox 1 d2;
else if (received byte<read ext eeprom(OxlcB)) demandTemp = Ox 1 cc;
else if (received byte<read ext eeprom(Oxlc9)) demandTemp = Ox 1 ce;
else demandTemp = Ox 1 d0;
received byte = read ext_eeprom(demandTemp+1 );
demandThreshold = read ext_eeprom(demandTemp);
demandThreshold = (demandThreshold«8) + received byte;
if(demandWindowAverage>demandThreshold &c~.
demandSampleAverage>demandThreshold &&
demandThreshold){
if(demandReminderDelay < timeOfDay) {
demandReport = 1;
demandReminderDelay = read_ext eeprom(Ox 1 D4);
demandReminderDelay *= 30;
demandReminderDelay += timeOfDay;
ioState = read_ext eeprom(demandTemp+12); J/ Timer to run when over.
ioStateUpdate();
demandOKdelay = 0;
else if((demandWindowAverage<demandThreshold ~~ !demandThreshold) &&
demandReminderDelay){
if(!demandOKdelay){
demandOKdelay = read ext eeprom(Ox 1 d5);
demandOKdelay *= 30;
demandOKdelay += timeOfDay;
;
else if(demandOKdelay<timeOfDay) {
demandReminderDelay=0;
demandOKdelay=0;
if(read_ext_eeprom(Ox 1 d6)) demandReport= 1;
ioState = read_ext eeprom(demandTemp+13); // Timer to run when OK.
ioStateUpdate();
L
if(demandReport){
RamADDRESS = Ox53A2;
ramWriteInt(demandWindowAverage);
ramWriteInt(demandThreshold);
ramWritelnt(timeOfDay);
if(demandReport=2) demandReport=3;
RamADDRESS = Ox53A8;
ramWriteInt(demandReminderDelay);
ramWriteInt(demandOKdelay);
ramWriteInt(demandWindowAverage);
RamDATA = demandWindowRemainder;
RAM_WRITE;
RamDATA = demandDataPointer;
RAM WRITE;

RamADDRESS = Ox539F;
RamDATA = demandSampIeNumber;
RAM_WRITE;
ramWriteInt(demandSampleAverage);
~******************************************************
Address Handlers void storeAddress( unsigned char tempFlag);
void setNickname(void);
******************************************************/
void storeAddress( unsigned char tempFlag)( switch(storeAddressState){
case 0:
storeAddressState++;
if (tempFlag) headAddress=0x42;
else headAddress=OxIF;
headerSize=0;
headerCount=1;
(temp=(start;
RamADDRESS=(temp;
RAM_READ:
last byte = RamDATA&Oxff;
if(tempFlag && (last byte=.' ~~ last_byte--=:'));
useTempAddress=0;
storeAddressState=0:
i break;
case I:
RamADDRESS=(temp;
RAM_READ;
last byte = RamDATA&Oxff;
if(last_byte !='E' && last byte !='P') storeAddressState=10; // Bad Address else storeAddressState++;
break;
case 2:
while (last byte !_':' RR last byte!--':' && headerSi-r_e<1~9AXADDRESS);
headerSize++;
if (last byte =',') headerCount++;
Itemp++~
RamADDRESS=(temp;
RAM_READ;
last byte = RamDATA&Oxff;
if(headerSize<MAXADDRESS && headerSize>(headerCount*7));
headerSize=0;
(temp=Istart storeAddressState++;
r else storeAddressState=10; // Bad Address break;

case 3:
RamADDRESS=(temp;
RAM_READ;
last byte = RamDATA&Oxff;
if (last byte !_';' && last byte!--':'){
if(headAddress=0x42) rtc write(headerSize+headAddress,last byte);
else eepromBuffer(headerSize+headAddress,last byte) headerSize++;
hemp++;
) else storeAddressState=5;
break;
case 5:
if(headAddress=0x42) rtcyrite(headAddress-2,headerSize);
else eepromBuffer(headAddress-2,headerSize);
storeAddressState++;
break;
case 6:
if(headAddress =0x42) rtc write(headAddress-l,headerCount);
else eepromBuffer(headAddress-I,headerCount);
storeAddressState++;
break;
case 7:
storeAddressState=0;
break;
case 10: // Bad Address. The calling function will set state back to 0.
break;
void setNickname(void){
switch(setNicknameState){
case 0:
setNicknameState++;
(temp=OxAO;
RamADDRESS=lstart RAM_READ;
last byte = RamDATA&Oxff;
break;
case 1:
l start++;
RamADDRESS=lstart;
RAM_READ;
received_bvte = RamDATA&Oxff:
if ((last byte = OxSc) && (received byte =='n')) {
(start++;
RamADDRESS=lstart;
RAM_READ;
received byte = RamDATA&Oxff;
eepromBuffer(Itemp,OxOd);
else eepromBuffer(Itemp,last_byte);
ltemp++;

last byte=received byte;
setNicknameState++;
break;
case 2:
if (last byte !_ ;' && last byte !_':' && hemp<OxDA) setNicknameState=1;
else if(Itemp<OxDA){
eepromBuffer((temp,';');
setNicknameState++;
}
else {
setNicknameState=0;
break;
case 3:
setNicknameState=0;
break;
}
~******************************************************
Incoming Message Handlers unsigned char getByte( unsigned int address);
char getMessage( void);
char matchPassword( unsigned int password);
unsigned char checkExpiration(void);
void getChar(void);
void executePagerCommand(void);
******************************************************~
char getMessage( void){
switch(getMessageState) {
case 0:
getMessageState=1;
lindex=0;
pagerResponse=0;
noPagesToday = 0;
thisMessageUnlocked = 0;
pagerComAbortTime=lastCentiTime+jp0;
messageStatus=ACK;
break;
case 1:
if (kbhitPagerQ){
received byte=getc(PAGER);
pagerResponse++;
if (pagerResponse>2){
getMessageState=2;
pagerResponse=1;

pagerComAbortTime=IastCentiTime+500;
i else if (pagerComAbortTime<lastCentiTime) getA9essageState=Oxff;
break;
case 2:
if (kbhitPager()){
RamDATA = getc(PAGER);
RamADDRESS = lindex+OFFSET2;
RAM_WRITE;
lindex++;
pagerResponse++;

pagerComAbortTime=IastCentiTime+500;
if(pagerResponse>128){
getMessageState=3;
) else if (pagerComAbortTime<lastCentiTime) getMessageState=Oxff;
break;
case 3:
if (kbhitPager()) { // We should check the checksum and NAK if wrong here!!
received byte = getc(PAGER);
getMessageState=4;
putcPager(ACK);
pagerComAbortTime=lastCentiTime+500;
else if (pagerComAbortTime<lastCentiTime) getMessageState=Oxff;
break;
case 4:
if (kbhitPager()){
received byte = getc(PAGER):
if(received byte=EOT) {
getMessageState=~;
messageEnd = lindex;
putcPager(ACK);
pagerComAbortTime=IastCentiTime+100;
else {
getMessageState=1;
pagerResponse=I;
pagerComAbortTime=IastCentiTime+500;
;
else if (pagerComAbortTime<IastCentiTime) getMessageState=Oxff;
break;
case ~:
if (pagerComAbortTime<lastCentiTime) ( // .lust used as a time delay here.
pagerMessage=0;
getMessageState++;
for(lstart=OFFSET2;lstart<messageEnd+OFFSET2;lstart++){
RamADDRESS=(start;
RAM_READ;
i = RamDATA&Oxff;
RAM_READ;
j = RamDATA&Oxff;
if ((i =_':') && (j =':')) {
RamADDRESS = lstart;
RamDATA = 0:
RAM_V'RITE; // Clear the ":." for next scan for concatenated commands RAM_WRITE;
Istart+=2;
crc=0;
CaIcCRConRamAddress(lstart);
CaIcCRConRamAddress(lstart+I );
pagerMessage = getByte(Istart);
lstart+=2;
if((pagerMessage=OxOD)~ ~(pagerMessage=Ox07)~ ~ (pagerMessage=Ox I D)~ I
(pagerMessage==0x27)~~(pagerMessage=Ox92)~~(pagerMessage=Ox96)~~
(pagerMessage=OxOE)~~
thisMessageUnlocked) {
break:
;

else{
if(!binaryFlag){
for (itemp=O;itemp<4;itemp++){
CaIcCRConRamAddress(Istart+itemp);
) messageLength = getByte(Istart);
messageLength «= 8;
messageLength += getByte(Istart+2);
messageLength -= 8;
messageCRC = getByte(lstart+messageLength);
messageCRC «= 8;
messageCRC += getByte(lstart+2+messageLength);
(start += 4;
cry°ptData = (start;
cyptDataLength = messageLength - 4;
else {
for (itemp=O;hemp<2;itemp++){
CaIcCRConRamAddress((start+itemp);

RamADDRESS = (start;
messageLength = ramReadlnt();
messageLength -= 6;
RamADDRESS = (start + messageLength:
messageCRC = ramReadlnt():
(start +_ ~;
cryptData = (start;
cryptDataLength = messageLength - 2;

if(messageLength>750) {
pagerMessage = 0;
break;
rc4crypt();
if(!binaryFlag) cr)~ptDataLength »= 1;
for (hemp=O;itemp<cryptDataLength;itemp++) {
CaIcCRConRamAddress((start+itemp):
i CaIcCRC(Ox00):

CaIcCRC(Ox00);

if(messageCRC != crc){

pagerMessage = 0;

putHexIntPager(messageCRC);

putHexIntPager(crc);

break;

RamADDRESS = (start;

expiryTime = ramReadInt();

expiryDate = ramReadInt();

password = ramReadInt();

i=matchPassword(password);

(start+=6;

if ((i=1 )II(i=~)) i thisMessageUnlocked = 1:

else if (i=3){

RamADDRESS=(start;

RAM_READ;

j = RamDATA&Oxff;

whichPass=toint(j);
if ((whichPass=I )~~(whichPass=2)){
(start++;
thisMessageUnlocked = 1;
;
if(thisMessageUnlocked){
received byte = checkExpiration();
if(received byte){
thisMessageUnlocked = 0;
pagerMessage = 0;
crcEcho = messageCRC;
exceptionFlag = ~;
) else {
pagerMessage = 0;
break:
;
r break;
case 6:
getMessageState=Oxff;
if(pagerMessage){
RamADDRESS=lstart;
for(hemp=lstart;Itemp<messageEnd+OFFSET2;ltemp++) { // if ok password, scan for closing ":' RAM_READ;
i = RamDATA&Oxff;
if(i =_':') getMessageState=0:
if(getMessageState) // Did not find a ";' pagerMessage=0;
break;
case Oxff:
messageStatus=NAK;
getMessageState=0;
break;
r char matchPassword( unsigned int password) {
whichPass=0;
if (password =XPTPASSWORD) whichPass=3;
else if (password=modulelPass) whichPass=I;
else if (password=module2Pass) whichPass=2;
return(whichPass);
r unsigned char checkExpiration(void) {
rtc_get datetime(&time);
alarm.yr = expiryDate» 12;
alarm.mth = expiryDate»8;
alarm.mth &= OxOF;
alarm.day = expiryDate&OxFF:
if(alarm.yr != time.yr);
1~~

if(alarm.yr=-((time.yr+1 )%4)) {
if(alarm.mth=1 && time.mth=12 && (31-time.day+alarm.day)<8){
return (0);
}
return (1);
if(alarm.mth !=time.mth){
if(alarm.mth=(time.mth+1 )) {
if((31-time.day+alarm.day)<11){ // Specify 1 week max but allow 11 days return (0); // except for Feb. 28th (sloppy is OK) i return (2);
}
if(alarm.day<time.day ~~ alarm.day>(time.day+7)) return (3);
if(alarm.day!=time.day ~~ timeOfDay<expiryTime) return (0);
return (4);
void getChar(void){
RamADDRESS=lstart;
RAM_READ;
received byte = RamDATA&Oxff;
Istart++;
terminatorFlag = 0;
if(received_byte=;' ~~ received b~~te--=:') terminatorFlag++;
void rc4crypt(void){
// unsiened rot's #define cyptDataPos pqReportTime // unsigned char's #define xCrypt pqEvent #dctine yCrypt pqInProgress #define keyPosition pqDurationLimit #define keyByte pqReportDelay #define sxCrypt pqEventStatus #define syCrypt pqReminderDelay #define keyLength pqOffDela~~
#define cryptDataByte pqFault RamADDRESS = 0x5500;
RamDATA = 0;
xCrypt=0;
do {
RAM_WRITE;
RamDATA++;
xCrypt++;
while(xCrypt);
xCrypt = 0;
yCrypt = 0;
keyPosition = 0;
RamADDRESS = Ox538C;
RAM_READ;
keyLength = RamDATA&Oxff;
if(!ke~~Length){

if(!binaryFlag && !addCRCflag){
for(cryptDataPos=O;cryptDataPos<cryptDataLength;cryptDataPos+=2) {
cryptDataByte = getByte(cryptData+cryptDataPos);
RamADDRESS = (cryptDataPos» 1 ) + cryptData;
RamDATA = cryptDataByte;
RAM_WRITE;

J

J
return;
RamADDRESS = Ox538D + keyLength;
ram W ritelnt(messageCRC);
keyLength += 2;
do {
RamADDRESS = Ox538D + keyPosition;
RAM_READ;
keyByte = RamDATA&Oxff;
RamADDRESS = 0x5500 + xCrypt;
RAM_READ;
sxCrypt = RamDATA&Oxff;
yCrypt += sxCrypt + keyByte;
RamADDRESS = 0x5500 + yCrypt;
RAM_READ;
RamADDRESS = 0x5500 + xCrypt;
RAM_WRITE;
RamADDRESS = 0x5500 + yCrypt;
RamDATA = sxCrypt;
RAM V~RITE;
keyPosition++;
if(keyPosition=keyLength) keyPosition=0:
xCrypt++;
while(xCrypt);
xCrypt = 0;
y,Crypt = 0;
cryptDataPos = 0;
do J
xCr~~pt++;
RamADDRESS = 0x5500 + xCrypt;
RAM_READ;
sxCrypt = RamDATA&Oxff;
yCrypt += sxCrypt;
RamADDRESS = 0x5500 + yCrypt;
RAM_READ;
syCrypt = RamDATA&Oxff;
RamADDRESS = 0x5500 + xCrypt;
RAM_WRITE;
RamADDRESS = 0x5500 + yCrypt;
RamDATA = sxCrypt;
RAM_WRITE:
sxCrypt += syCrypt;
RamADDRESS = 0x5500 + sxCrypt:
RAM READ:

syCrypt = RamDATA&Oxff;
if(!binaryFlag && !addCRCflag) cryptDataByte = getByte(cryptData+cryptDataPos);
else {
RamADDRESS=cryptData+cryptDataPos;
RAM_READ;
cryptDataByte = RamDATA&Oxff;
cryptDataByte ~= syCrypt;
RamADDRESS = cryptDataPos;
if(!binaryFlag && !addCRCflag) RamADDRESS »= 1:
RamADDRESS += cryptData;
RamDATA = cryptDataByte;
RAM WRITE;
cryptDataPos ++;
if(!binaryFlag && !addCRCflag) cryptDataPos ++;
} while(cryptDataPos<cryptDataLength);
r void spreadTimer(void){
switch(spreadTimerState){
case 0:
spreadTimerState++;
spreadCount=0;
exceptionTime = lastCentiTime + 300;
break;
case 1:
if (exceptionTime<lastCentiTime){
spreadCount++;
if(spreadCount>=spreadDelay){
spreadTimerState=0;
}
else {
exceptionTime = lastCentiTime + 300:
spreadTimerState++;
yy 18();
;
break;
case 2:
if(!yy l8State){
RamADDRESS = Ox50CF; // Pbuffer[ 15}
RAM_READ;
received byte = RamDATA&Oxff;
if(received_byte) {
spreadTimerState=0;
if(degradeState) abortFlag = TRUE;
else {
if(degradeState){
st 1 pulse=OxF;
st2pulse=OxA;
}
else {
st 1 pulse=OxA;
st2pulse=OxB;
IJJ

pulseCycles=3;
spreadTimerState=1;
) break;
f 1 0 void degrade( void){
if(degradeState=0){
if ( valDegrade = 0 ){
attemptCount=1;
valDegrade=2;
) else {
if (valDegrade >= 32) {
abortFlag = TRUE;
else {
spreadDelay = 20;
spreadTimer();
degradeState=I;
;
r else if (!spreadTimerState) {
if(abortFlag) {
degradeState=0;
spreadDelay=read ext eeprom(Ox 12);
W
else if (valDegrade>degradeState) {
spreadDelay = 20;
spreadTimer();
degradeState++;
else {
attemptCount++;
valDegrade+=valDegrade;
degradeState=0;
spreadDelay=read ext eeprom(Oxl2);
reestablishPager();
r r ;
#pragma code executePagerCommand = OxA000 void executePagerCommand(void) {
switch(pagerMessage){
case I: // Resend last data switch (executePagerCommandState){
case 0:
executePagerCommandState=I ;
break;
case I:
end01=Oxff;
start0l = getByte(lstart);
if (!terminatorFlag){
itemp=getByte(lstart+2);

if (!terminatorFlag){
end01 = hemp;
l calcSendDataQ;
executePagerCommandState=2;
break;
case 2:
if (!calcSendDataState);
executePagerCommandState=0;
start0l=0;
end01=Oxff;
break;
break;
case 2: // Set or Reset Economy flag getChar();
if(! terminatorFlag) {
economyFlag=toint(received byte);
eepromBuffer(OW d,economyFlag);
saveParCheckSum();
else messageStatus=NAK;
break;
case 3: // Execute command string immediately switch (executePagerCommandState){
case 0:
executePagerCommandState++;
status=0;
useTempAddress = 0;
sendParameters();
break;
case 1:
if(sendl'arametersState==~);
executePagerCommandState++;
break;
case 2:
executeMultipleCommands(lstart);
executePagerCommandState++;
break;
case 3:
if(!executeMultipleState) {
sendParametersState++;
executePagerCommandState++;
r break;
case 4:
if(!sendParametersState) {
executePagerCommandState=0;
i break;
break;
case 4: // Set binary transmission flag getCharQ;
if(!terminatorFlag){
received byte=toint(received byte);

if(binaryFlag!=received byte){
binaryFlag = received byte;
eepromBuffer(OxSe,binaryFlag);
saveParCheckSum();
f else messageStatus=NAK;

break;

case 5: // Execute timer'n' command string switch (executePagerCommandState){

case 0:

executePagerCommandState++;

status=0;

useTempAddress = 0;

RamADDRESS=lstart;

RAM READ;

timer = RamDATA&Oxff;

timer = toint(timer);

if (timer>7){

executePagerCommandState=0;

messageStatus = NAK;

else {

iIndex=timer;

iIndex*=128;

iIndex+=5;

ilndex+=Oa4C00;

RamADDRESS=iIndex;

RAM_READ;

received byte = RamDATA&Oxff;

if(received byte='P'){

reestablishPager();

executePagerCommandState=10;

;

else {

sendParameters();

r break;

case 1:

if(sendParametersState---5) {

executePagerCommandState++;

t break;

case 2:

executeMultipleCommands(ilndex);

executePagerCommandState++;

break;

case 3:

if(!executeMultipleState) {

if(runSSIcommand){

executePagerCommandState=0;

sendParametersState=0;

) else {

sendParametersState++~

executePagerCommandState=4;

r ;

break;
case 4:
if(! sendParametersState) executePagerCommandState=0;
break;
case 10:
if(!reestablishPagerState) executePagerCommandState=0;
break;
}
break;
case 6: // Change stored Destination ID(s) switch (executePagerCommandState){
case 0:
executePagerCommandState= t ;
break;
case 1:
storeAddress(FALSE);
executePagerCommandState=2;
break;
case 2:
if(!storeAddressState) executePagerCommandState=3;
if(storeAddressState==10) { // Bad Address.
executePagerCommandState=0;
storeAddressState=0;
messageStatus = NAK;
break;
case 3:
eepromBuffer(OxOC,O); // using new destination newDestination = 0;
saveParCheckSumQ;
executePagerCommandState=0;
break;
r break;
case Ox27:
economyFlag = 0;
case 7: // Send status string switch (executePagerCommandState){
case 0: // In get Status executePagerCommandState=1;
break;
case 1: // In get Status RamADDRESS=lstart;
RAM READ;
echoSize = RamDATA&Oxff;
if (echoSize =_ '/') {
(start++;
RamADDRESS=lstart;
for(echoSize=O;echoSize<9;echoSize++) RAM_READ;
timer = RamDATA&Oxff;
lstart++;
if (timer ='/') break;
echoBuffjechoSize]=timer;
r else{
1~7 echoBuff[0]='0';
echoSize=1;
status= I ;

useTempAddress= 1;

storeAddress(TRUE); // from incoming to eeprom storage executePagerCommandState=2;

break;

case 2: // In get Status if(!storeAddressState) executePagerCommandState=3;

if(storeAddressState=10) { // Bad Address.

executePagerCommandState=0;

storeAddressState=0;

) break;

case 3: // In get Status executePagerCommandState=5;

break;

case 5: // In get Status lindex=0;

convertAnd WriteToSendBuffer(pagerMessage);

pagerMessage=7; // If was 0x27 change to 0x07 writeTime(RTC);

executePagerCommandState=6;

break;

case 6: // In get Status if(!writeTimeState) executePagerCommandState=7;

break;

case 7: // In get Status writeToSendBuffer('f);

headerSize = read_ext eeprom(Ox 1 d);
for (i=O;i<headerSize;i++)( received_byte = read_ext_eeprom(i+Ox 1 f);
writeToSendE3uffer(received byte);
writeToSendBuffer('f);
convertAndWriteToSendBuffer(timerMask);
convertAndWriteToSendBuffer(exceptionMask);
writeToSendBuffer('f);
received byte = read ext_eeprom(OxSd); // economyFlag received byte ~= binaryFlag«I;
received byte ~= compressionEnabled«2;
received byte ~= pqEnabled«3;
received byte ~= pqVerbosityEnabled«4;
received byte ~= pagerDiagnostics«5;
received byte ~= killPower«6;
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
convertAndWriteToSendBuffer(spreadDelay);
writeToSendBuffer('f);
writeToSendBuffer(to ascii(binaryFlag));
writeToSendBuffer('f);

for (timer-O;timer<echoSize;timer++) writeToSendBuffer(echoBuff(timer]); //Echo characters writeToSendBuffer('f);
tempMask=1;

for(i=O;i<8; i++) {

if ((timerMask&tempMask) ~~ !economyFlag){

(temp=i;

Itemp*=128;

Itemp+=OFFSET 1;

j=0;

tempt=to_ascii(i);

writeToSendBuffer(temp2);

getAlarm(i);

itemp=to_bcd(alarm.mth);

convertAndWriteToSendBuffer(itemp);

hemp=to_bcd(alarm.day);

convertAndWriteToSendBuffer(hemp);

hemp=to_bcd(alarm.hr);

convertAndWriteToSendBuffer(hemp);

hemp=to_bed(alarm. mi n);

convertAndVhriteToSendBuffer(hemp);

hemp=to_bcd(alarm.sec);

convertAndWriteToSendBuffer(hemp);

do {

last byte=read_ext_eeprom(Itemp);

if (j=0 ~~ j=4) { // Period or add spread delay itemp=to_ascii(last byte);

writeToSendBuffer(itemp);

;

else if (j<4) { // interval, hour & minute hemp=to_bcd(last byte);

convertAndWriteToSendBuffer(itemp);

i else { // meter commands writeToSendBuffer(last byte);

j++;

hemp++;

; while (j<j ~I last byte !_';');

tempMask*=2;

executePagerCommandState=8;

break;

case 8: // In get Status if (!economyFlag) {

hemp=rtc_read(Oxl3); // Zone convertAndWriteToSendBuffer(hemp);

hemp=rtc_read(Oxl4); // Subzone convertAndWriteToSendBuffer(itemp);

writeToSendBuffer('f); // Bytes sent OK

itemp=rtc_read(Ox 15 );

convertAndWriteToSendBuffer(itemp);

hemp=rtc_read(Ox 16);

convertAndWriteToSendBuffer(hemp);

writeToSendBuffer('f); // RTC Time and Alarm time writeTime(RTC_NO_YEAR);
executePagerCommandState=9;

t else executePagerCommandState=1 I ;
break;
case 9: // In get Status if(!writeTimeState){
executePagerCommandState=10;
writeTime(RTC_ALARM);
break;
case 10: // In get Status if(!writeTimeState) executePagerCommandState=1 1;
break;
case 1 1: // In get Status writeToSendBuffer('f);
received byte = read_ext_eeprom(OxOe); // Meter Error Mask convertAndWriteToSendBuffer(received byte);
received byte = read_ext_eeprom(OxOf); // Meter Error Mask convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
writeMeterSerial();
executePagerCommandState++;
break;
case 12: // In get Status if (!writeMeterSerialState) {
writeToSendBuffer('f);
convertAndWriteToSendBuffer(synchToAtomicTime);
writeToSendBuffer('f);
economyFlag = read ext eeprom(OxSd): // Set it back if command 0x27 addCRCQ;
calcSendDataQ;
executePagerCommandState++;
t break;
case 13: // In get Status if (!calcSendDataState) executePagerCommandState=0;
break;
;-break;
case 8: // Set Timer and Exception Mask switch (executePagerCommandState) {
case 0:
executePagerCommandState=1;
break;
case 1:
executePagerCommandState=2;
break;
case 2:
executePagerCommandState=3;
break;
case 3:
timerMask=getByte(lstart);
exceptionMask=getByte(Istart+2);
validateTimerMask();
eepromBuffer(Ox I O,timerMask);
eepromBuffer(Oxll,exceptionMask);
saveParCheckSum();
rtc set irqQ;

executePagerCommandState=0;
if(acOKtimer && exceptionMask&0x02) powerSaveSleep();
break;
;
break;
case OxOB: // Change spread Delay spreadDelay=getByte(lstart);
eepromBuffer(Ox l2,spreadDelay);
saveParCheckSumQ;
break;
case OxOc: // Clear bytesSentOK
bytesSentOK=0;
rtc write(Ox15,0);
rtc_write(Ox16,0);
break;
case OxOd: // Ping switch (executePagerCommandState){
case 0:
executePagerCommandState=1;
break;
case 1:
status=2;
useTempAddress= I;
storeAddress(TRUE);
executePagerCommandState=2;
break;
case 2:
if(!storeAddressState) executePagerCommandState=3;
if(storeAddressState---10) { // Bad Address.
executePagerCommandState=0;
storeAddressState=0;
break;
case 3:
valDegrade=0;
sendPing();
executePagerCommandState=4;
break;
case 4:
if(!sendPingState){
executePagerCommandState=0;
useTempAddress = 0;
;
break;
r break;
case OxOe: /1 Commission over the air newDestination = 0x50;
eepromBuffer(OxOC,newDestination); // using default destination commissionButtonPress();
break;
case OxOf: // Send Dates only onlyDates = 2;
pagerMessage=Ox 10;
executePagerCommandState=I ;
break:
case 0x10: // Send Power Outage Data switch (executePagerCommandState){
case 0:
executePagerCommandState=1;
onlyDates = 3;
break;
case 1:
useTempAddress = 0;

status=0;

economyFlag= I;

sendParameters();

executePagerCommandState=2;

break;

case 2:

if(sendParametersState=S){

received byte=getByte(Istart);

command[0]=3;

command[1]=received byte;

for(i=2;i<9;i++) command[i]=0;

command [ 10]=0;

command[9]=received byte+3;

sendFlag=TRUE;

sendCommandQ;

executePagerCommandState=3;

break:

case 3:

if(!sendCommandState);

onlyDates = FALSE;

sendParametersState++:

executePagerCommandState=4;

break;

case 4:

if(!sendParametersState){

economyFlag=read ext_eeprom(OxSd);//
Restore economyFlag executePagerCommandState=0;

i break:

break;

case Ox 11: // Send specific Dates of Load Profile switch (executePagerCommandState) {
case 0:
executePagerCommandState++;
break;
case 1:
useTempAddress = 0;
status=0;
economyFlag= 1;
startDay=getByte(Istart);
startDay=to dec(startDay,OxfD);
startMonth=getByte(lstart+2);
startMonth=to_dec(startMonth,Oxfl~);
endDay=1;
RamADDRESS=Istart+4;
RAM READ;
hemp = RamDATA&Oxff;
if (itemp !_';' && hemp !_':'){

hemp=toint(itemp)* 16;

RamADDRESS=lstart+5;

RAM_READ;

endDay = itemp+toint(RamDATA&Oxff);

;

if (!startMonth ~~ !startDay){

executePagerCommandState=0;

messageStatus = NAK;

else {

sendParameters();

executePagerCommandState=2;

break;

case 2:

if(sendParametersState=5) executePagerCommandState=3;

break;

case 3:

if (time.mth=startMonth){

if (time.day>startDay) LPdays=time.day-startDay;

else LPdays=0;

;

else {

if (time.mth<startMonth) time.mth+=12;

LPdays=(31 *((time.mth-startMonth)-1 ))+time.day+(31-startDay):

) time.day=startDay+endDay;

time.mth=startMonth;

time.period=1;

time.interval=1;

validateTime(&time):

if(time.mth> 12) time.mth-=12;

endMonth=time.mth;

endDay=time.day;

startDay=to bcd(startDay);

startMonth=to bcd(startMonth);

startMonth~=0x80;

endDay=to bcd(endDay);

endMonth=to bcd(endMonth);

endMonth~=0x80;

isLPenabledQ;
executePagerCommandState=4;
break;
case 4:
if(! isLPenabledState){
if(isLPenabledReturn) executePagerCommandState=5;
else{
executePagerCommandState=8;
sendParametersState=0;
r l break:

case 5:
getLPdataQ;
executePagerCommandState=6;
break;
case 6:
if(! getLPdataState) {
caIcLPsize();
sendFlag=TRUE;
sendCommand();
executePagerCommandState=7;
l l break;
case 7:
if(!sendCommandState) {
if(compressionEnabled && !hadLPerror) compressLPdataQ;
sendParametersState++;
executePagerCommandState=8;
break;
case 8:
if(! sendParametersState) {
economyFlag=read ext_eeprom(Ox~d);// Restore economyFlag executePagerCommandState=0;
break;
i break;
case Ox 12: // Get Parsing Array switch (executePagerCommandState){
case 0:
executePagerCommandState=1;
break;
case 1:
Itemp2 = 0x60;
sendParsingArray();
executePagerCommandState=2;
break;
case 2:
if(!sendParsingArrayState) executePagerCommandState=0;
break;
i break;
case 0x13: // Set Parsing Array ltemp2 = 0x60;
setParsingArray();
Itemp2 = 0x60;
clipEepromToRam();
break;
case 0x14: // Force WatchDog Timeout if(exceptionMask&8){ // echo mode (change zone to force an autoregistration) received byte = rtc read(Ox I 3) + 1;
rtc write(Oxl3,received_byte);
i for(;;);
break;
case Ox I 5: // Set Meter Password if (whichPass=1 ) {

meter I Passes=getByte(Istart);

meter 1 PassM=getByte(lstart+2);

meterl PassL=getByte(lstart+4);

eepromBuffer(Oxl3,meterlPassH);

eepromBuffer(Ox l4,meterl PassM);

eepromBuffer(Ox 1 S,meter 1 PassL);

if (whichPass=2){

meter2PassH=getByte(lstart);

meter2PassM=getByte(Istart+2);

meter2PassL=getByte(Istart+4);

eepromBuffer(Ox 16,meter2PassH);

eepromBuffer(Ox 17,meter2PassM);

eepromBuffer(Ox 18,meter2PassL);

saveParCheckSumQ;

break;

case Ox 16: // Set Module Passwords temp 1=getByte(Istart);

password=tempi;

password=password8;

tempt=getByte(Istart+2);

password+=tempt;

if (whichPass=1){

module 1 Pass=password;

eepromBuffer(Ox l9,temp I );

eepromBuffer(Ox 1 a,temp2);

i if (whichPass=2) {

module2Pass=password;

eepromBuffer(Ox 1 b,temp 1 );

eepromBuffer(Ox 1 c,temp2);

saveParCheckSum();

break;

case Ox 1 c: // Set Site Name switch (executePagerCommandState) {

case 0:

executePagerCommandState=1;

break;

case 1:

setNicknameQ;

executePagerCommandState=2;

break;

case 2:

if(!setNicknameState) {

saveParCheckSumQ;

executePagerCommandState=0;

break;

break;

case Oxld: // Get Meter Information (& Site Name) switch (executePagerCommandState) {

case 0: // In get Meter Info executePagerCommandState=1;

break;

case 1: // In get Meter Info status=1;

useTempAddress= 1;

storeAddress(TRUE); // from incoming to eeprom storage executePagerCommandState=2;

break;

case 2: // In get Meter Info if(!storeAddressState){

executePagerCommandState=3;

if(storeAddressState=10) { // Bad Address.

executePagerCommandState=0;

storeAddressState=0;

break;

case 3: // In get Meter Info (index=0;

convertAndWriteToSendBuffer(pagerMessage);

command[0]=Ox6f;

for(i=l;i<1 (;i++) command[i]=0;

command[9]=Ox6f;

sendFlag=FALSE;

sendCommand();

executePagerCommandState++;

break;

case 4: // In get Meter Info if(!sendCommandState){

executePagerCommandState++;

r break;

case 5: // In get Meter Info writeToSendBuffer('/');

if(!badSendCommand){

for(i=17;i!=255;1--){

// itemp = Mbuffer[i];

RamADDRESS = Ox4b00 + i:

hemp = ramReadUcharQ;

if (hemp<Ox20 ~~ itemp>Ox7E) itemp ='$ ;

writeToSendBuffer(itemp);

writeToSendBuffer(OxOd);

writeToSendBuffer('f);

command[O]=0x02;

for(i=l;i<ll;i++) command[i]=0;

command[9]=0x02;

sendFlag=FALSE;

sendCommandQ;

executePagerCommandState++;

break;

case 6: // In get Meter Info if(!sendCommandState) {

executePagerCommandState++;

break;

case 7: // In get Meter Info if(!badSendCommand){

RamADDRESS = Ox4b00;

hemp = ramReadUchar();

convertAndWriteToSendBuffer(itemp);

RamADDRESS = Ox4b01;

hemp = ramReadUcharQ;

convertAndWriteToSendBuffer(hemp);
RamADDRESS = Ox4b03;

hemp = ramReadUcharQ;

convertAndWriteToSendBuffer(itemp);

RamADDRESS = Ox4b02;

hemp = ramReadUchar();

convertAndWriteToSendBuffer(hemp);

writeToSendBuffer('f );

command [0]=0x01;

for(i=l;i<1 l;i++) command[i]=0;

command [9]=0x01;

sendFlag=FALSE;

sendCommandQ;

executePagerCommandState++;

break;

case 8: // In get Meter Info if(! sendCommandState) {

executePagerCommandState++;

break;

case 9: // In get Meter Info if(!badSendCommand){

RamADDRESS = Ox4b02;

hemp = ramReadUcharQ;

convertAndWriteToSendBuffer(hemp);

RamADDRESS = Ox4b01;

hemp = ramReadUchar();

convertAndWriteToSendBuffer(itemp);

RamADDRESS = Ox4b00;

hemp = ramReadUchar();

convertAndWriteToSendBuffer(hemp);
f writeToSendBuffer('f);

command[0]=Ox9F;

for(i=l;i<I l;i++) command[iJ=0;

command[9]=Ox9F;

sendFlag=FALSE;

sendCommand();

executePagerCommandState++;

break;

case 10: // In get Meter Info if(!sendCommandState) executePagerCommandState++;

break:

case 1 1: // In get Meter Info if(!badSendCommand){

RamADDRESS = Ox4b02:

hemp = ramReadUchar();

hemp*=3;

itemp+=5;

RamADDRESS = Ox4b00 + hemp;

received byte = ramReadUchar();

convertAndWriteToSendBuffer(received byte):

;
writeToSendBuffer(OxOd);
writeToSendBuffer('f);
writeMeterSerial();
executePagerCommandState++;
break;

case 12: // In get Meter Info if(!writeMeterSerialState) executePagerCommandState++;
break;
case 13: // In get Meter Info writeToSendBuffer(OxOd);
(temp=OxAO;
do {
last byte=read_ext_eeprom(ltemp);
writeToSendBuffer(last byte);
(temp++;
} while (last byte !_ ;' && hemp<OxDA);
writeToSendBuffer(OxOd);
Itemp2 = OxDA;
clipEepromToRamQ;
economyFlag=1; // Always use Parsing Array executeMultipleCommands(0x5000);
executePagerCommandState++;
break;
case 14: // In get Meter Info if(!executeMuItipIeState) executePagerCommandState++;
break;
case I ~: // In get Meter Info ltemp2 = 0x60;
clipEepromToRam();
economyFlag=read_ext eeprom(Ox~d);// Restore economyFlag writeToSendBuffer('f);
writeTime(PAGER);
executePagerCommandState++;
break;
case 16:
if(!writeTimeState) {
writeToSendBuffer('f);
addCRCQ;
calcSendDataQ;
executePagerCommandState++;
}
break;
case 17: // In get Meter Info if(!calcSendDataState) executePagerCommandState=0:
break;
r break;
case Ox 1 e: // Set Timer Event switch (executePagerCommandState) {
case 0:
executePagerCommandState++;
rtc_get datetime(&alarm);
RamADDRESS=lstart;
RAM_READ;
timer = toint(RamDATA&Oxff);
RamADDRESS=(start+1;
RAM_READ;
alarm.period = toint(RamDATA&Oxff);
alarm. interval=getByte(lstart+2):
alarm.interval=to dec(alarm.interval,Oxf>)):

if (alarm.period=0 ~~ alarm.period>3 ~~ !alarm.
interval){

messageStatus = NAK;

executePagerCommandState=0;

i break;

case 1:

if(alarm.period=1 && alarm.interval>96) alarm.interval=96;

if(alarm.period=2 && alarm.interval>7) alarm.
interval=7;

if(alarm.period=3 && alarm.interval>31) alarm.interval=31;

if(alarm.period=3) {

alarm.day = alarm.interval;

alarm.hr-getByte(Istart+4);

alarm.hrto dec(alarm.hr,OxfD);

alarm.min=getByte(lstart+6);

alarm.min=to_dec(alarm.min,OxfO);

RamADDRESS=Istart+8;

RAM_READ;

alarm.addSpreadDelay = RamDATA&Oxff;

alarm.addSpreadDelay = toint(alarm.addSpreadDelay);

alarm.sec=0;

saveAlarm(timer);

ltemp=timer;

ltemp*=128;

(temp+=OFFSET1;

eepromBuffer(hemp++,alarm.period);

eepromBuffer(hemp++,alarm.interval);

eepromBuffer(Itemp++,alarm.hr);

eepromBuffer(ltemp++,alarm.min);

eepromBuffer(hemp++,alarm.addSpreadDelay);

Itemp2=0;

ltemp3=lstart+9;

do {

RamADDRESS=Itemp3;

RAM_READ;

last byte = RamDATA&Oxff;

RamDATA = 0; // To clear possible embedded SSI
commands.

RamADDRESS=Itemp3; // So they won't be confused with concatenated RAM_WRITE; // SSI commands.

eepromBuffer((Itemp+ltemp2).last bye);

Itemp3++;

Itemp2++;

while (last byte !_';');

last byte = 1 timer;

timerMask ~= last byte;

eepromBuffer(Ox 1 O,timerMask);

saveParCheckSum();

executePagerCommandState++;

break;

case 2:

if(!saveParCheckSumState) executePagerCommandState++;

break;

case 3:

initAlarm(timer);

checkAlarm(timer);

setAlarm();

ltemp=timer;

(temp*=128;

RamADDRESS=(temp+0x4C00;

(temp+=OFFSET(;

for(i=0; i< I 28; i++) {

RamDATA = read ext_eeprom(ltemp + i);

RAM WRITE;

executePagerCommandState=0;

break;

;

break;

case Oxlf: // Set Meter Information Command String ltemp2 = Ox 11 A;

i=0;

do {

RamADDRESS=(start;

RAM_READ;

last byte = RamDATA&Oxff;

RamDATA = last byte;

RamADDRESS=0x5000+i;

RAM_W RITE;

eepromBuffer(ltemp2,last_byte);

i++;

(start++;

Itemp2++;

while (last byte !_';' && i<128);

saveParCheckSum();

break;

case 0x20: // Set Meter Information Parsing Array Itemp2 = OxDA;

setParsingArray();

break;

case Ox21: // Set Meter Error Codes mask hemp=getByte(Istart);

eepromBuffer(OxOe,itemp);

hemp=getByte(Istart+2);

eepromBuffer(OxOf,itemp);

saveParCheckSumQ;

break;

case 0x22: // Get Meter Information Parsing Array switch (executePagerCommandState){

case 0:

executePagerCommandState=1;

break;

case 1:

Itemp2 = OxDA;

sendParsingArray();

executePagerCommandState=2;

break;

case 2:

i f(! sendParsingArrayState) executePagerCommandState=0;

break;

break;
case 0x23: // Set or Clear Economy, Binary and Compression flags // bit 0 = econo flag, bit 1 = binaryFlag and bit 2 = compression flag.
hemp = getByte(Istart);
economyFlag = hemp&0x01;
binaryFlag = itemp8,Ox02;

binaryFlag = 1;

compressionEnabled = hemp&0x04;

compressionEnabled = 2;

pqVerbosityEnabled = itemp&0x10;

pqVerbosityEnabled = 4;

pagerDiagnostics = hemp&0x20;

pagerDiagnostics = 5;

killPower = itemp&0x40;

killPower = 6;

synchToAtomicTime = getByte(Istart+2);

eepromBuffer(OxSd,economyFlag);

eepromBuffer(OxSe,binaryFlag);

eepromBuffer(OxSf,compressionEnabled);

eepromBuffer(Oxl fd,pqVerbosityEnabled);

eepromBuffer(Oxl9b,pagerDiagnostics);

eepromBuffer(Ox 1 fc,synchToAtomicTime);

eepromBuffer(Ox 1 fb,killPower);

saveParCheckSum();

break;

case 0x24: // Recalculate Flash and Loader CRC's switch (executePagerCommandState) {

case 0:

executePagerCommandState++;

status=1;

useTempAddress= 1;

sendParameters();

break;

case 1:

if(! sendParametersState) {

executePagerCommandState=0;

else if(sendParametersState=5){

executePagerCommandState++;

break;

case 2:

pqPollEnabled=0;

executePagerCommandState++;

break;

case 3:

if(!pqPollState ~~ acOKtimer) codeCRCQ;

pqPollEnabled= 1;

sendParametersState++;

executePagerCommandState++;

break;

case 4:

i f(! sendParametersState) {

executePagerCommandState=0;

break;

break;

case 0x26: // Set Secret Key if (executePagerCommandState==0) {

for(whichPQ=O;whichPQ<l7;whichPQ++){

received byte = getByte(Istart);

RamADDRESS = Ox538C + whichPQ;

RamDATA = received byte;

1$1 RAM_WRITE;
eepromBuffer(Ox 1 E0+whichPQ,received_byte);
if(!whichPQ && received byte=Oxff) -writeMeterSerialQ; /% Just used here to get a default key.
Istart += 2;
executePagerCommandState++;
v r else if (!writeMeterSerialState) {
saveParCheckSumO;
executePagerCommandState=0;
break;
case 0x28: // Get Pager Configuration and Product Info.
switch (executePagerCommandState){
case 0:
executePagerCommandState++;
status=0;
useTempAddress= l;
sendParameters0;
break:
case 1:
if(!sendParametersState) executePagerCommandState=0;
else if(sendParametersState=~){
executePagerCommandState++;
productInfo = 0;
yyl7Q;
r break;
case 2:
if(!yy 17State) f executePaeerCommandState++;
productlnfo = I;
>'>' 170, r break;
case 3:
if(!yyl7State){
executePagerCommandState++;
sendParametersState++;
break;
case 4:
if(!sendParametersState) esecutePagerCommandState=0;
break;
r break:
case 0x29: // Miscellaneous variable Set variableOffset = getByte(Istart+2);
variableNumber = getByte(lstart+4);
Istart += 6;
if(variableOffset<2 && variableNumber<3 && (variableOffset+variableNumber)<3) f variableNumber += variableOffset;
for(;variableOffset<variableNumber;variableOffset++){
received byte = getByte(Istart);
lstart += 2;
if (received byte<IS) received byte = 15;
eepromBuffer(OxlF9+variableOffset,received byte);
saveParCheckSum();
}
break;
case Ox2A: // Miscellaneous variable Get if (!executePagerCommandState){
variableOffset=getByte(lstart+2);
variableNumber-getByte(Istart+4);
if(variableOffset<2 && variableNumber<3 && (variableOffset+variableNumber)<3){
executePagerCommandState++;
status=0;
useTempAddress = 0;
sendParametersQ;
r else {
if(!sendParametersState) executePagerCommandState=0;
else if(sendParametersState=5){
sendParametersState++;
writeToSendBuffer(0);
writeToSendBuffer(variableOffset);
writeToSendBuffer(variableNumber);
variableNumber += variableOffset;
for(;variableOffset<variableNumber;variableOffset++) received byte = read_ext_eeprom(Ox 1 F9+variableOffset);
writeToSendBuffer(received byte);
;
l break;
case Ox2B: // Kill Power Test if(exceptionMask&8){ // echo mode (change zone to force an autoregistration) received byte = rtc read(Oxl3) + I;
rtc write(Ox l3,received byte);
i KILL_PWR_OUTP= 1;
if(exceptionMask&8) { // change zone back if kill fails.
messageStatus = NAK:
received byte--;
rtc write(Oxl3,received byte);
i break;
case Ox2C: // Read/Write I/O
if (!executePagerCommandState) {
ioState=getByte(lstart);
if(ioState!=Oxff){
executePagerCommandState++;
status=0;
useTempAddress = 0;
sendParameters();
;
r else {
if(!sendParametersState) executePagerCommandState=0;
else if(sendParametersState=5){

sendParametersState++;
ioStateUpdateQ;
writeToSendBuffer(ioState);
) break;

case 0x80: // PQ Enable RamADDRESS=lstart RAM_READ;

tempi =toint(RamDATA&Oxff);

if(pqEnabled!=temp 1 ){

eepromBuffer(Ox l9A,temp 1 );

pqEnabled=tempi;

saveParCheckSumQ;

) break;

case 0x81: // PQ Set Outage Phase A&B

if (executePagerCommandState=0){

whichPQ=0;

storePQeventQ;

executePagerCommandState=1;

t else if(!storePQeventState) {

calcComparisons();

executePagerCommandState=0;

break;

case 0x82: // PQ Get Outage Phase A&B

if (executePagerCommandState=0) {

whichPQ=0;

sendPQstatusQ;

executePagerCommandState=1;

else if(!sendPQstatusState) {

executePagerCommandState=0;

i break;

case Ox83: // PQ Set Outage Phase C

if (executePagerCommandState---0){

whichPQ=1;

storePQevent();

executePagerCommandState=1;

else if(!storePQeventState) {

calcComparisons();

executePagerCommandState=0;

i break;

case 0x84: // PQ Get Outage Phase C

if (executePagerCommandState=0){

whichPQ=1;

sendPQstatusQ;

executePagerCommandState=1;

else if(!sendPQstatusState) {

executePagerCommandState=0;

break;

case Ox8~: // PQ Set Hi Voltage if(executePagerCommandState =0)( whichPQ=2;

storePQeventQ;

executePagerCommandState=1;

else if(!storePQeventState) {

calcComparisons();

executePagerCommandState=0;
) break;

case 0x86: // PQ Get Hi Voltage if (executePagerCommandState=0) {

whichPQ=2;

sendPQstatus();

executePagerCommandState=1;

) else if(!sendPQstatusState) {

executePagerCommandState=0;

break;

case 0x87: // PQ Set Low Voltage if (executePagerCommandState=0){

whichPQ=3;

storePQevent();

executePagerCommandState=1;

;

else if(!storePQeventState) ;

calcComparisonsQ;

executePagerCommandState=0:

r break;

case 0x88: // PQ Get Low Voltage if (executePagerCommandState=0) {

whichPQ=3;

sendPQstatus();

executePagerCommandState=1;

else if(!sendPQstatusState) {

executePagerCommandState=0:

break;

case 0x89: // PQ Set Phase Unbalance if (executePagerCommandState=0) {

whichPQ=4;

storePQevent();

executePagerCommandState=1;

v else if(!storePQeventState) {

calcComparisons();

executePagerCommandState=0;

;

break;

case Ox8a: // PQ Get Phase Unbalance if (executePagerCommandState=0) {

whichPQ=4;

sendPQstatusQ;

executePagerCommandState=1;

t else if(!sendPQstatusState) {

executePagerCommandState=0;

break;

case OxBb: // PQ Set MIVS

if (executePagerCommandState=0){

whichPQ=5;

storePQevent();

executePagerCommandState=l;

else if(!storePQeventState) {

calcComparisonsQ;

executePagerCommandState=0;

break;

case OxBc: // PQ Get MIVS

if (executePagerCommandState=0){

whichPQ=5;

sendPQstatus();

executePagerCommandState=1;

else if(!sendPQstatusState) {

executePagerCommandState=0;

break;

case Ox8d: // PQ Nominal Voltage ltemp=getDecimal();

if(Itemp<800) // Less than 80.0 Volts messageStatus = NAK;

tempt = hemp8;

eepromBuffer(Ox l9C,temp 1 );

tempi = hemp&OxFF;

eepromBuffer(Ox l9D,temp 1 );

ltemp=getDecimalQ;
if(ltemp<800) // Less than 80.0 Volts messageStatus = NAK;
temp 1 = hemp»8;
eepromBuffer(Ox l9E,temp 1 );
tempt = Itemp&OxFF;
eepromBuffer(Ox l9F,temp 1 );
(temp=getDecimal();
if(ltemp<800) // Less than 80.0 Volts messageStatus = NAK;
temp 1 = (temp»8;
eepromBuffer(Ox 1 AO,temp 1 );
temp 1 = hemp&OxFF;
eepromBuffer(Ox 1 A l ,tempt );
saveParCheckSum();
for (whichPQ=O;whichPQ<6;whichPQ++) // Calculate PQ Comparison values.
calcComparisons();
break;
case OxBe: // Send PQ Log switch (executePagerCommandState){
case 0:
pqLogRequest=getDecimalQ;
pqLogMask = getByte(Istart);
pqLogMask «= 8;
pqLogMask += getByte(lstart+2);
executePagerCommandState++, break;

case 1:

pqSeanReset();

status=1;

useTempAddress=0;

sendParameters();

executePagerCommandState++;

break;

case 2:

if(sendParametersState=5){

writeToSendBuffer('f);

received byte = pqScanPointer8;

convertAndWriteToSendBuffer(received byte);

received byte = pqScanPointer&OxFF;

convertAndWriteToSendBuffer(received byte);

writeToSendBuffer('f);

executePagerCommandState++;

break;

case 3:

if(pqScanEnd=0) {

executePagerCommandState++;

break;

case 4:

pqScanQ;

if(eventType=Oxff);

executePagerCommandState++;

sendParametersState++;

else if(!pqLogRequest ~~ !eventType){

getEventTypeQ;

if(pqLogMask & pqComparison){

convertAndWriteToSendBuffer(eventType);

RamADDRESS = Ox44F0;

RAM_READ;

received byte = RamDATA&Oxff;

for(i=O;i<received byte;i++) {

RamADDRESS = Ox44F I+i:

RAM_READ:

convertAndWriteToSendBuffer(RamDATA&Oxff):

l writeToSendBuffer('f);
if(! eventType) {
if(!pqLogRequest){
executePagerCommandState++;
sendParametersState++;
l i pqLogRequest--;
break;
case 5:
if(! sendParametersState) executePagerCommandState=0;
break;
break;
case OxBf: // Send Complete PQ RAM Buffer if(!executePagerCommandState){

sendCompletePQ=1;
if(pqEnabled && !pqLogOff~tate){
executePagerCommandState++;
reportPQevent();
) else messageStatus = NAK;
else if(!reportPQeventState){
executePagerCommandState=0;
break;
case 0x90: // Set Temp PQ Disable Switch Parameters hemp=getDecimal();
if(itemp<I){
itemp=1;
messageStatus = NAK;
if(itemp>15) {
itemp=15;
messageStatus = NAK;
eepromBuffer(OxIFE,itemp);
itemp=getDecimalQ;
if(itemp>30){
hemp=30;
messageStatus = NAK;
eepromBuffer(Ox 1 FF,itemp);
saveParCheckSum();
break;
case 0x91: // Get Temp PQ Disable Switch Parameters if (!executePagerCommandState) executePagerCommandState++;
status=1;
useTempAddress= 1;
sendParameters();
else {
if(!sendParametersState) executePagerCommandState=0;
else if(sendParametersState =5) {
sendParametersState++;
writeToSendBuffer('f);
received byte = read_ext_eeprom(Ox 1 FE): // Off Time received byte = to_bcd(received byte);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
received byte = read_ext_eeprom(Ox 1 FF); // Report Delay received byte = to_bcd(received_byte);
convertAndWriteToSendBuffer(received byte);
writeToSendBuffer('f);
break;
case 0x92: // Send Long "NOW" Values if (!executePagerCommandState) sendCompIetePQ=2;
// intentionally no break here.
case 0x93: // Send Short "NOW" Values 1$g switch (executePagerCommandState){
case 0:
if(!pqLogOffState && pqEnabled){
executePagerCommandState++;
reportPQeventQ;
l else {
executePagerCommandState = 2;
status=1;
useTempAddress= 1;
sendParametersQ;
L
l break;
case l:
if(!reportPQeventState){
executePagerCommandState = 0;
f break;
case 2:
if(! sendParametersState) executePagerCommandState=0;
else if(sendParametersState==5){
sendParametersState++;
writeStringToSendBuffer(sPQDisabled):
if(pqEnabled) writeStringToSendBuffer(sTemporarily);
writeToSendBuffer('f);
i break;
) break;
case Ox94: // Set Demand parameters for(whichPQ=O;whichPQ<26;whichPQ++) {
received_byte = getByte(lstart);
if(!whichPQ) demandWindow=received byte;
eepromBuffer(OxlC6+whichPQ.received byte);
Istart += 2;
saveParCheckSum();
break:
case Ox95: // Get Demand parameters if (!executePagerCommandState) {
executePagerCommandState++;
status=1;
useTempAddress = 1;
sendParametersQ;
l i else {
if(!sendParametersState) executePagerCommandState=0;
else if(sendParametersState=5) {
sendParametersState++:
writeToSendBuffer('/');
for(itemp=O;itemp<26;itemp++){
received byte = read_ext_eeprom(Ox 1 C:6+itemp);
convertAndWriteToSendBuffer(received byte);
riteToSendBuffer('f);

break;

case 0x96: // Send Demand status switch (executePagerCommandState){

case 0:

if(!pqLogOff~tate && demandWindow){

executePagerCommandState++;

sendDemandReport();

demandReport=2;

else {

executePagerCommandState = 2;

status=I;

useTempAddress= 1;

sendParameters();
) break;

case 1:

if(!sendDemandReportState){

executePagerCommandState = 0;
) break;

case 2:

if(! sendParametersState) executePagerCommandState=0;

else if(sendParametersState=S){

sendParametersState++;

writeStringToSendBuffer(sDemandDisabled):

if(demandWindow) writeStringToSendBuffer(sTemporaril~-);

writeToSendBuffer('/');

break:

break;
default:
messageStatus = NAK;
break;
// switch f void runTimer(void){
switch(runTimerState) case 0:
runTimerState++;
break;
case 1: // Main Loop increments this when the pager is available.
break;
case 2:
iIndex=demandTimer;
iIndex*=128;
iIndex+=5;
iIndex+=Ox4C00;
sendParameters();
runTimerState++;
break;
case 3:
if(sendParametersState=5) {
runTimerState++;
;

break;
case 4:
executeMultipleCommands(iIndex);
runTimerState++;
break;
case 5:
if(!executeMultipleState){
if(runSSIcommand){
runTimerState=0;
sendParametersState=0;
r else {
sendParametersState++;
runTimerState++;
) break;
case 6:
if(!sendParametersState) runTimerState=0;
break;
~******************************************************
Outgoing Message Handlers void degrade( void);
void sendPing( void);
void sendPage( void);
void BitwiseCrcl6 (int bit);
void CaIcCRC(unsigned char data);
void addCRC(void);
void calcSendData( void);
void spreadTimer(void);
void exceptionPage(void);
void reportPQevent(void);
void sendAlann(void);
void checkMeterErrors(void);
void writeTime( char from);
void writeMeterSerial( void);
void sendParameters(void);
void sendDemandReport(void);
void convertAndWriteToSendBuffer(unsigned char data);
void writeToSendBuffer(char data);
******************************************************~
void sendPage( void){
switch(sendPageState) {
case 0:
sendPageState=l;
Itemp3=0;
preamble[0]=pageNumber;
preamble[3]=successCount;
noPagesToday = 0;
break;
case 1:
temp 1=OxaO;
if(badHandshake) tempi+=0x10;
if(abortFlag) tempi += 0x40;
abortFlag = 0;

if(wasReset) temp 1 -= 0x20;
wasReset = 0;
if(nAC_OK INP) tempt -= 0x80;
preamble[2]=temp 1;
degrade();
sendPageState=2;
break;
case 2:
if(!degradeState && !reestablishPagerState){
if(abortFlag) {
sendPageState=0;
sendPageReturn=1;
pqLog(attemptCount+OxAO);
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(pagerMessage);
else sendPageState=3;
break;
case 3:
if ((pageNumber>=start0l )&&(pageNumber<=end01 )){
sendPageState=4;
yyl6p;
r else sendPageState=5;
break;
case 4:
if(!yyl6State)( if(pagerResponse) sendPageState=5;
else {
sendPageState=0;
sendPageReturn=0;
pqLog(pagerStatus2);
pqLog(pagerStatusl);
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox7B);

break;
case ~:
blockNum = 0;
sendPageState=6;
break;
case 6:
if(packetsForPage){
blockNum++;
if (blockNum = 1 ) {
if ((partPacket)&& (totalPackets = t )) // first and only packet packetEnd+=partPacket;
else { // first full packet if (status~~binaryFlag) packetEnd+=128-headerOverhead-PREAMBLE;
else packetEnd+=64-(headerOverhead/2)-PREAMBLE;
if (status) packetEnd-=PREAMBLE;

]
else{ // second or later packet if (((packetsForPage = 1 ) && fullPages ---- 0) && partPacket) // last packet packetEnd+=partPacket;
else {
if ((status)~~(binaryFlag)) // not the last ASCII packet packetEnd+=128;
else // not the last binary packet packetEnd+=64;
temp I=preamble[2];
temp 1+=attemptCount&OxOf;
preamble[2]=templ;
if ((pageNumber>=start0l)&&(pageNumber<=end01)){
xmod_data();
sendPageState=7;
else {
packetStart=packetEnd;
if(packetsForPage=1 ){
sendPageState=0;
sendPageReturn=1;
]
else sendPageState=8;
L
i else sendPageState=9;
break;
case 7:
if(!xmod dataState){
if(pagerResponse=ACK) sendPageState=8;
else {
sendPageState=0;
sendPageReturn=0;
L
break;
case 8:
packetsForPage--;
if(packetsForPage) sendPageState=6;
else sendPageState=9;
break;
case 9:
putcPager(EOT):
pagerResponse=0;
pagerComAbortTime=IastCentiTime+j00;
sendPageState=10;
break;
case 10:
if(kbhitPagerQ){
received byte = getc(PAGER);
pagerComAbortTime=lastCentiTime+1 j00;
pagerPolITimeout=timeOfDay+270; // 9 minutes for testing with Emu.
sendPageState=1 1;
else if (pagerComAbortTime<lastCentiTime) sendPageState=0;

break;
case 1 1:
if (pagerComAbortTime<lastCentiTime) { // used for time delay here YY 180;
sendPageState=12;

l break;
case 12:
if(!yylBState){
RamADDRESS = Ox50C1;
RAM_READ;
received byte = RamDATA&Oxff;
received byte = toint(received byte);
received byte &= OxC;
if ((received byte = 0)~~(received byte == 4)~~(pagerPollTimeout<timeOtDay)) ( // negative response sendPageState=0;
sendPageReturn=0;
if(pagerPollTimeout<timeOfDay) {
pqLog(pagerStatus2);
pqLog(pagerStatus 1 );
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(Ox7A);
reestablishPagerQ;
;
else if (received byte == 8); // positive response pqLog(attemptCount):
pqLog(timeOfDay&OxFF):
pqLog(timeOfDay»8);
pqLog(pagerMessage);
valDegrade = 0;
bytesSentOK+=(tempi; // update total good bytes rtc write(OxlS,bytesSentOK»8);
rtc_write(Ox 16,bytesSentOK&OxFF);
sendPageState=0;
sendPageReturn=1;

else {
pagerComAbortTime=lastCentiTimc+~00;
sendPageState=1 1;
break:
;
f void calcSendData( void){
unsigned char icalc, fullSize, firstSize;
switch(calcSendDataState) {
case 0:
calcSendDataState=1;
messageReplied=2;
successCount++;
rte_write(OxlO,successCount);
if (useTempAddress);
headerSize = rtc read(Ox40):
headerCount = rtc read(Ox41 );
z else {

headerSize = read_ext eeprom(Ox I D);
headerCount = read ext eeprom(Ox 1 E);
fullSize = 128;
padHeaderEven=0;
headerOverhead = headerSize + 3 + headerCount;
if ((!status)&&binaryFlag) headerOverhead ++;
firstSize = 120 - headerOverhead:
if ((!status)&&binaryFlag) firstSize += PREAMBLE;
else if (!status){
fullSize=64;
padHeaderEven = firstSize%2;
firstSize/=2; // This should be rounded down.
headerOverhead += padHeaderEven;
(temp=IindexSave;
totalPackets=0;
for(;;){
for(icalc=O;icalc<(BLOCKSIZE/128):icalc++) ( // Count the v-hole packets +
remainder totalPackets++;
if(icalc){ // if second or later packet if (hemp <= fullSize){ // and there is less than a packet left partPacket=(temp; // partPacket is just the remainder if (hemp = 0) // if remainder happens to be zero totalPackets--; // reduce the total number of packets goto carryon; // done counting packets Itemp-=fullSize; // greater than a full packet left so // reduce by 1 packet and keep counting else { // if first packet if (hemp <= firstSize) { // if this is the only packet and header fits partPacket=ltemp; // partPacket is the remainder if (hemp == 0) totalPackets--; // othemvise there are no packets goto canyon: // done counting packets i Itemp-=firstSize; // reduce hemp by packetSize less headerSize and preamble ; //else keep counting packets // for(icalc=O;icalc<(BLOCKSIZE/128);icalc++) // for(;;) carryon: // Now calculate everything else based on the packet count // and the remainder fullPages=totalPages=totalPackets/(BLOCKSIZE/128);
partPage = totalPackets%(BLOCKSIZE/128); // number of packets short of a full block if ((partPacket) && (!partPage)) { // if the remainder is the only packet fullPages--; // there are no full pages totalPages--; // or total pages partPage=(BLOCKSIZE/128); // and partPage = packet size l if (partPage) totalPages++; // if there is a partial page increase total pages tempPacketStart = 0; // start at the beginning iPacketSize = BLOCKSIZE;
preamble[I]=totalPages;
break;
case 1:
pageNumber= 1;

if (fullPages>0) calcSendDataState=3;
else calcSendDataState=7;
break;
case 3:
valDegrade = 0;
calcSendDataState=4;
break;
case 4:
// Depending on how sendPage is done this may need to be repeated if send page fails.
packetStart = tempPacketStart;
packetEnd = tempPacketStart;
packetsForPage = (BLOCKSIZE/128);
sendPageQ;
calcSendDataState=~;
break;
case S:
if(!sendPageState && !reestablishPagerState){
if(sendPageReturn=0) calcSendDataState=4;
else calcSendDataState=6;
l break;
case 6:
fuIIPages--;
tempPacketStart = packetStart; // update the start point if (abortFlag) calcSendDataState=10;
else if (fullPages>0) {
calcSendDataState=3;
i else calcSendDataState=7;
pageNumber++;
break;
case 7:
if (partPage > 0){ // if there are partial pages valDegrade = 0; // reset the backoff and retry variable calcSendDataState=8;
else calcSendDataState=10;
break;
case 8:
// Depending on how sendPage is done this may need to be repeated if send page fails.
packetStart = tempPacketStart; // send whatever packets remain packetEnd = tempPacketStart;
packetsForPage = partPage;
iPacketSize = ((partPage-1 )* 128);
if ((status=1 )~~(binaryFlag)){ // if this an binary/status page iPacketSize+=partPacket;
if (partPage=1 ) { // if this is the first packet iPacketSize+=headerOverhead+PREAMBLE;
if(status) iPacketSize+=PREAMBLE;
v else if (status=2) {
iPacketSize = partPacket + headerOverhead;

else { // if this is an ascii page iPacketSize+=(partPacket*2);

if (partPage=I ) // if this is the first packet iPacketSize+=headerOverhead+(PREAMBLE*2);
sendPageQ;
calcSendDataState=9;
break;
case 9:
if(!sendPageState && !reestablishPagerState){
if(sendPageReturn=0) calcSendDataState=8;
else calcSendDataState=10;
break;
case 10:
calcSendDataState=0;
break;
void sendAlarm(void) {
switch(sendAlarmState) {
case 0:
sendAlarmState++;
break;
case 1:
if(!sendingPage) {
sendingPage=1;
rtc_clear irq();
sendAlarmState++;
t break;
case 2:
status=FALSE;
useTempAddress = 0;
tempMask=1;
sendAlarmState++;
break;
case 3:
pending=FALSE;
tempMask=1;
rte-get datetime(&time);
iTimer-0;
sendAlarmState++;
break;
case 4:
sendAlarmState=10;
if (timerMask&tempMask) {
getAlarm(iTimer);
if (lessOrEqual(&alarm,&time)) {
pending=TRUE;
iIndex=iTimer;
iIndex*=128;
ilndex+=5;
iIndex+=Ox4C00;
RamADDRESS=ilndex;
RAM_READ;
received byte = RamDATA&Oxff;
if(received byte ='P') reestablishPagerQ;

sendAlarmState=20;

}
else {

lindex=0;

pagerMessage = 0x40 + iTimer;

// writeToSendBuffer(pagerMessage);

writeToSendBuffer(OxFF);

writeMeterSerialQ;

sendAlarmState=5;

}
}

}
break;

case 5:

if(!writeMeterSerialState){

writeTime(RTC);

sendAlarmState++;

}
break;

case 6:

if(! writeTimeState) {

executeMultipleCommands(iIndex);

sendAlarmState++;

i break;

case 7:

if(!executeMultipleState) {

if(runSSIcommand) sendAlarmState=9;

else {

addCRCQ;

if(alarm.addSpreadDelay) spreadTimer();

sendAlarmState=8;

}

break;

case 8:

if(!spreadTimerState) {

calcSendDataQ;

sendAlarmState=9;

break;

case 9:

if(!calcSendDataState){

initAlarm(iTimer);

checkAlarm(iTimer);

sendAlarmState=10;

}
break;

case 10:

tempMask*=2;

iTimer++;

if(iTimer<8) sendAlarmState=4;

else if(pending) sendAlarmState=3:

else sendAlarmState=I 1;

break;

case 11:
setAlarmQ;
sendingPage=0;
sendAlarmState=0;
break;
case 20:
if(! reestablishPagerState) sendAlarmState=9;
break;
}
}
void writeMeterSerial( void){
switch(writeMeterSerialState) {
case 0:
if(acOKtimer){
writeMeterSerialState = 8;
}
else {
writeMeterSeriaIState++;
command[0]=0x87;
for(i=l;i<ll;i++) command[i]=0;
command[9]=0x87;
sendFlag=FALSE;
sendCommandQ;
}
break;
case 1:
if (!sendCommandState){
v~riteMeterSeria(State=8;
if(!badSendCommand) {
writeMeterSerialState=2;
RamADDRESS = Ox538C;
if(ramReadUchar()==Oxft) { // Set encryption ke~~ to factory default.
writeMeterSerialState=5;
RamADDRESS = Ox4b00;
stringPointee=ramReadInt(); // temp use for moving meter stringEnd=ramReadlnt(); // serial # to secret key area RamADDRESS = Ox538C;
RamDATA = 4;
RAM_WRITE;
ram W ritelnt(stringPointer);
ramWritelnt(stringEnd);
stringPointer=Ox4B84;
stringEnd=stringPointer+16;
putArrayInExtRam(sDefaultKeySeed);
addCRCflag = 1; // To force binary handling of encryption.
messageCRC = defaultCRC;
cryptData = Ox4B84;
cryptDataLength = 16;
rc4crypt();
addCRCflag = 0;
RamADDRESS = Ox538C;
RamDATA = 16;
RAM_WRITE;
eepromBuffer(Oxl e0,16);
for( i=0; i< 16; i++) {
RamADDRESS = Ox4B84 + ;;
RAM_READ;
RamADDRESS = Ox538D + i;

RAM_WRITE;
received byte = RamDATA&Oxff;
eepromBuffer(Oxlel+i,received byte);
break;
case 2:
case 5: // Will need a saveParCheckSum writeMeterSerialState++;
for(i=O;i<4;i++) received byte=read_ext_eeprom(Oxlfl -f i);
RamADDRESS = Ox4b00 + i;
last byte = ramReadUcharQ;
if(last byte != received byte) writeMeterSerialState = 4;
break;
case 3:
writeMeterSeriaIState=7;
break;
case 4:
writeMeterSerialState=6;
for(i=O;i<4;i++)( RamADDRESS = Ox4b00 + i;
received byte = ramReadUchar();
eepromBuffer(Oxlfl+i,received b~~te);
i r break;
case 6:
writeMeterSerialState++;
saveParCheckSum();
break;
case 7:
mriteMeterSeriaIState = 0;
if(!status ~~ serialAtStart) convertAndVVriteToSendBuffer(0): // To pad a 4 byte serial # up to ~ bytes.
RamADDRESS = Ox4b03;
received byte = ramReadUchar();
convertAndWriteToSendBuffer(received byte);
if(status && !serialAtStart) writeToSendBuffer(' ');
RamADDRESS = Ox4b02;
received byte = ramReadUchar();
convertAndWriteToSendBuffer(received byte);
RamADDRESS = Ox4b01:
received byte = ramReadUchar();
if(status && !serialAtStart){
writeToSendBuffer(to ascii(received_byte»~));
writeToSendBuffer(' ');
writeToSendBuffer(to ascii(received byte&OxOf));
i else convertAndWriteToSendBuffer(received_byte);
RamADDRESS = Ox4b00;
received byte = ramReadUchar();
convertAndWriteToSendBuffer(received byte);
break;
case 8: // Copy from eeprom to meter incoming buffer.

CA 02332297 2001-O1-25 ' writeMeterSerialState--;
for( i=0; i<4; i++) {
RamDATA = read_ext_eeprom(Ox 1 fl + i);
RamADDRESS = Ox4b00 + ;;
RAM WRITE;
) break;
L
l 1~
#pragma code reportPQevent = OxC000 void reportPQevent(void){
switch(reportPQeventState){
case 0:
reportPQeventState++;
pqReportNow=1;
break;
case I:
if(!pqReportNow) reportPQeventState=3;
break;
case 3:
if(!sendingPage ~~ pagerMessage=OxBf ~) pagerMessage=0x92 ~~
pagerMessage=0x93){
sendingPage=1;
pqPendingTime=0;
// status=FALSE;
status=TRUE; // For now.
useTempAddress= 1;
if(pagerMessage!=OxBf&& pagerMessage!=0x92 R& pagerMessage!=0x93);
pagerMessage = OxAO:
useTempAddress = 0;

sendParameters();
reportPQeventState=4;
i break;
case 4:
if(! sendParametersState) {
reportPQeventState=0;
if(sendParametersState=~) {
reportPQeventState++;
r break;
case ~:
if(!pqVerbosityEnabled){
writeToSendBuffer(' ');
for(pqValue=O;pqValue<pqReportSize;pqValue++) {
RamADDRESS = pqValue+0x4500;
RAM_READ;
pqEvent = RamDATA&Oxff;
convertAndWriteToSendBuffer(pqEvent);

writeToSendBuffer(' ');

else {
writeToSendBuffer('f);
for(pqValue=O;pqValue<pqReportSize:pqValue++) {
RamADDRESS = pqValue+Ox4~00;
1~1 RAM_READ;
pqEvent = RamDATA&Oxff;
if(pqEvent<Ox7E){
writeToSendBuffer(' ');
writeToSendBuffer('X');
convertAndWriteToSendBuffer(pqEvent);
if (pqEvent){
pqValue++;
RamADDRESS = pqValue+0x4500;
RAM_READ;
pqEvent = RamDATA&Oxff;
convertAndWriteToSendBuffer(pqEvent);
i else if(pqEvent=Ox7E ~~ pqEvent=Ox7F){
access.
writeStringToSendBuffer(sMacc o);
if(pqEvent=Ox7E){
writeToSendBuffer('F');
writeToSendBuffer('F');
else writeToSendBuffer('N');
writeToSendBuffer(' ');

pqValue++;

RamADDRESS = pqValue+Ox4~00;

pqValue++;

(temp = ramReadInt();

received byte = Itemp/1800;

convertAndWriteToSendBuffer(to_bcd(received byte));

writeToSendBuffer(':');

hemp % 1800;

received byte = Itemp/30;

convertAndWriteToSendBuffer(to bcd(received byte));

writeToSendBuffer(':');

received byte = ltemp%30;

received byte *= 2;

convertAndWriteToSendBuffer(to bcd(received-byte));

i else {

if (pqEvent>OxBO){

pqEvent -= OxB 1;

pqEventStatus = 1; // Used here to indicate a restoration report.

else {

pqEvent -= OxA 1;

pqEventStatus = 0; // Used here to indicate not a restoration report.

) writeToSendBuffer(' ');
if(pqEvent<3 ~~ pqEvent=14);
writeStringToSendBuffer(sOut);
else if(pqEvent<6){
writeToSendBuffer('H');
writeToSendBuffer('f );
else if(pqEvent<9){
writeToSendBuffer('L');
writeToSendBuffer('O');

else if(pqEvent<10){
writeStringToSendBuffer(sUnbal);
else {
writeStringToSendBuffer(sMivs);
writeToSendBuffer(' ');
if(pqEvent!=9) {
received byte = pqEvent%3;
if(received byte=0) writeToSendBuffer('A');
else if(received byte=1) writeToSendBuffer('B');
else writeToSendBuffer('C');
if(pqEvent=14){
writeStringToSendBuffer(s com);
pqEvent=2;
writeToSendBuffer(' ');
i if(pqEventStatus){ // Restoration report.

writeStringToSendBuffer(sOK~;

pqValue++;

RamADDRESS = pqValue+0x4500;

pqValue++;

(temp = ramReadlnt();

received byte = Itemp/1800;

convertAndWriteToSendBuffer(to bcd(received byte));

writeToSendBuffer(':');

Itemp % 1800;

received byte = ltemp/30;

convertAndWriteToSendBuffer(to bcd(received_byte));

writeToSendBuffer(':');

received byte = ltemp%30;

received byte *= 2;

convertAndWriteToSendBuffer(to bcd(received byte));

writeToSendBuffer(' ');

if(pqEventStatus){ // Restoration report.

pqValue++;

RamADDRESS = pqValue+Oa4~00;

pqValue++;

hemp = ramReadIntQ;

received_byte = Itemp/1800;

convertAndWriteToSendBuffer(to bcd(received byte));

writeToSendBuffer(':');

hemp %= 1800;

received byte = Itemp/30;

convertAndWriteToSendBuffer(to bed(received byte));

writeToSendBuffer(':');

received_byte = ltemp%30;

received byte *= 2;

convertAndWriteToSendBuffer(to bcd(received byte));

writeToSendBuffer(' ');

i else if(pqEvent<10){ // Outage, Unbalance or Hi/Lo if(pqEvent!=2) {

if(pqEvent<9) received byte= 1;

else received byte = 0;

if(pqEvent=9){

for(itemp=0; hemp<3; itemp++) {

pqValue++;

RamADDRESS = pqValue+0x4500;

pqValue++;

hemp = ramReadInt();

writeDecimalDivider= 100;

postDecimalCharacter ='%';

writeDecimalToSendBuffer(hemp);
) pqValue++;

RamADDRESS = pqValue+0x4500;

RAM READ;

Itemp = RamDATA&Oxff;

(temp += 0x30;

writeToSendBuffer(ltemp);

writeToSendBuffer('%');

writeToSendBuffer(' ');

if(pqEvent<9) received byte= 1;

else received byte = 0;

for(itemp=received byte;itemp<3;itemp++){

pqValue++;

RamADDRESS = pqValue+0x4500;

pqValue++;

ltemp = ramReadInt();

writeDecimalDivider = 64;

postDecimalCharacter ='V';

writeDecimalToSendBuffer(hemp):

if(pqEvent=~){

for(itemp=O;itemp<3:itemp++) pqValue++;

RamADDRESS = pqValue+0x4500;

pqValue++;

hemp = ramReadlnt();

writeDecimalDivider = 128;

postDecimalCharacter ='d';

writeDecimaIToSendBuffer(Itemp);

writeToSendBuffer('#');
pqValue++;
RamADDRESS = pqValue+0x4500;
RAM_READ;
received byte = RamDATA&Oxff;
convertAndWriteToSendBuffer(to bcd(received byte));
writeToSendBuffer(' ');
i l else {
pqValue++;
RamADDRESS = pqValue+0x4500;
RAM_READ;
received byte = RamDATA&Oxff;
convertAndWriteToSendBuffer(to bcd(received byte));

writeStringToSendBuffer(s_IN_);

pqValue++;

RamADDRESS = pqValue+0x4500;

pqValue++;

hemp = ramReadInt();

received byte = Itemp/60;

convertAndWriteToSendBuffer(to bcd(received byte));

writeToSendBuffer('m');

received byte = hemp%60;

convertAndWriteToSendBuffer(tobcd(received_byte));

writeToSendBuffer('s');

writeToSendBuffer(' ');

;

pqReportSize = 0;

if (sendCompletePQ=1){

sendCompletePQ=0;

for (pqEvent=O;pqEvent<l3;pqEvent++) {

writeToSendBuffer('(');

convertAndWriteToSendBuffer(to bcd(pqEvent));

writeToSendBuffer(')');

for (i=0; i< 10; i++) {

RamADDRESS = i+0x4400 + (pqEvent* 18);

RAM_READ;

received_byte = RamDATA&Oxff;

convertAndWriteToSendBuffer(received byte):

i r ?

writeToSendBuffer(' ');
i writeStringToSendBuffer(sNOW~;
reportPQeventState=6;
break:
case 6:
if(acOKtimer ~~ pqPoIlState=40){
writeStringToSendBuffer(sC OUT );
outageReportSent++;
reportPQeventState=7;
else if(!unbalanceStatus){
writeDecimalDivider = 64:
postDecimalCharacter ='V';
writeDecimalToSendBuffer(unbalVoltAfrac);
writeDecimalToSendBuffer(unbalVoItBfrac);
writeDecimaIToSendBuffer(unbalVoItCfrac);
writeDecimalDivider = 128;
postDecimalCharacter ='d';
writeDecimalToSendBuffer(0);
writeDecimalToSendBuffer(unbalAngleAfrac);
hemp = 46080 - unbalAngIeCfrac;
writeDecimalToSendBuffer(hemp);
if (sendCompletePQ=2) {
sendCompletePQ=0;
for(i=O:i<18:i+=3) {
RamADDRESS = Ox534F + i;
hemp = meterFloatTolnt(Ox89);
writeDecimalDivider = 64;
if(i<9) else postDecimalCharacter ='A';
postDecimalCharacter ='d';
writeDecimaIToSendBuffer(hemp);
) writeDecimalDivider = 100;
postDecimalCharacter ='°/d;
writeDecimalToSendBuffer(unbalVoltAB);
writeDecimaIToSendBuffer(unbalVoItBC);
writeDecimalToSendBuffer(unbaIVoItCA);
writeDecimalDivider=read ext eeprom(OxlD7);
demandMaxExp = read ext_eeprom(Ox 1 CB);
postDecimalCharacter ='W
for(i=28;i<43;i+=3){
RamADDRESS = 0x5361 + i;
ltemp = meterFIoatToInt(demandMaxExp);
writeDecimalToSendBuffer(ltemp);
reportPQeventState=7;
r break;
case 7:
writeStringToSendBuffer(sMIVS );
scanMIVSonly=1;
RamADDRESS = Ox~3F9; // get report delay RAM_READ;
pqReportDelay = RamDATA&Oxff;
pqEvent=12;
pqMivsScan();
convertAndWriteToSendBuffer(to bcd(pqMivsCount));
writeToSendBuffer(' ');
for(pqEvent=lO:pqEvent<l2;pqEvent++) pqMivsScanQ:
convertAndV'riteToSendBuffer(to bcd(pqMivsC'ount)):
writeToSendBuffer(' '):
scanMIVSonly=0;
writeToSendBuffer('/');
reportPQeventState=8;
break;
case 8:
sendParametersState++;
reportPQeventState++;
break;
case 9:
if (! sendParametersState);
reportPQeventState++;
r break:
case 10:
reportPQeventState=0;
if(!executePagerCommandState) sendingPage=0;
break;
void putHexIntPager(unsigned int hexData);
hexNibble = hexData» 12;

putcPager(' ');
hexNibble = to_ascii(hexNibble);
putcPager(hexNibble);
hexData &= Oxfff;
hexNibble = hexData»8;
hexNibble = to_ascii(hexNibble);
putePager(hexNibble);
hexData &= Oxff;
hexNibble = hexData»4;
hexNibble = to_ascii(hexNibble);
putcPager(hexNibble);
hexNibble = hexData&Oxf;
hexNibble = to_ascii(hexNibble);
putcPager(hexNibble);
putcPager('h');
void writeTime( char from) {
switch(writeTimeState) {
case 0:
writeTimeState=3;
writeTimeFrom = from;
if (from=METER){
getMeterTime();
writeTimeState=1;
i else if (from==PAGER);
getPagerTimeQ;
writeTimeState=2;
else if (from=RTC ~~ from---RTC NO YEAR) rtc-get datetime(&time);
else if (from---RTC_ALARM) rte_get alarm datetime(&time);
break;
case I:
if (!getMeterTimeState) {
writeTimeState=3;
i r break;
case 2:
if (!getPagerTimeState);
writeTimeState=3;
r r break;
case 3:
if ((writeTimeFrom!=RTC_ALARM)&&(writeTimeFrom!=RTC_NO YEAR)) convertAndWriteToSendBuffer(to bcd(time.yr));
convertAndWriteToSendBuffer(to bcd(time.mth));
convertAndWriteToSendBuffer(to bcd(time.day));
convertAndWriteToSendBuffer(to bcd(time.hr));
convertAndWriteToSendBuffer(to_bed(time.min));
convertAndWriteToSendBuffer(to bcd(time.sec));
writeTimeState=0;
break;
void checkMeterErrors(void){
switch(checkErrorState){

case 0:
checkErrorState++;
break;
case 1:
checkErrorState++;
break;
case 2:
if(!sendingPage){
sendingPage=1;
checkErrorState++;
reestablishPagerQ;
break;
case 3:
if(!reestablishPagerState){
if(exceptionMask&4){
command(O]=0x83; // Get meter error codes.
for(i=l;i<ll;i++) command[i]=0;
command[9]=0x83;
sendFlag=FALSE;
sendCommandQ;
checkErrorState++;
v else{
checkErrorState = 7;
break;
case 4:
if (!sendCommandState){
checkErrorState=7;
if(!badSendCommand){
received byte = read ext_eeprom(OxOe);
RamADDRESS = Ox4b00;
hemp = ramReadUchar();
received byte &= hemp;
last byte = read ext eeprom(OxOfJ;
RamADDRESS = Ox4b01;
itemp = ramReadUchar();
last byte &= itemp;
if(received_byte ~~ last byte){
status=0;
useTempAddress = 0;
pagerMessage = 0;
sendParametersQ;
checkErrorState=5;
l T

break:
case ~:
if(sendParametersState=5) {
sendParametersState=0;
pagerMessage = 0x53;
writeToSendBuffer(OxBF);
writeToSendBuffer(Ox83);
writeToSendBuffer(OxFF);
for( i=0; i<7; i++) {
RamADDRESS = Ox4b00 + i;
hemp = ramReadUchar();

writeToSendBuffer(itemp);

r received byte = read_ext_eeprom(OxOe);

writeToSendBuffer(received_byte);

received byte = read_ext_eeprom(OxOf);

writeToSendBuffer(received byte);

addCRCQ;

spreadTimer();

checkErrorState++;

}

break;

case 6:

if(! spreadTimerState) {

calcSendDataQ;

checkErrorState++;

break;

case 7:

if(!calcSendDataState) {

checkErrorState=0;

sendingPage=0;

if(noPagesToday){

received byte = rtc read(Ox 13) + 1;

rtc write(Oxl3,received byte);

for(;;);

noPagesToday = I;
l i break;
}
void sendParameters(void){
// If calling function is using a temporary address then it must check for sendParametersState being set // to zero (when it is waiting for sendParametersState being set to S) because of a problem with the address.
switch (sendParametersState){
case 0:
sendParametersState++;
if(useTempAddress) storeAddress(TRUE): // from incoming to eeprom storage else sendParametersState++; // skip to case 2 break;
case 1:
if(!storeAddressState) sendParametersState++;
if(storeAddressState=10) { // Bad Address.
sendParametersState=0;
storeAddressState=0:
}
break:
case 2:
lindex=0;
if(pagerMessage=0x10 && onlyDates=2) writeToSendBuffer(OxOf);
else convertAndWriteToSendBuffer(pagerMessage);
serialAtStart= 1;
writeMeterSerial();
sendParametersState++;

break;
case 3:
if(! writeMeterSerialState) {
serialAtStart = 0;
writeTime(RTC);
sendParametersState++;
break;
case 4:
if(!writeTimeState){
sendParametersState++;
break;
case 5: // Calling function adds data at this point and increments sendParametersState break;
case 6:
addCRC();
calcSendDataQ;
sendParametersState++;
break;
case 7:
if (!calcSendDataState) sendParametersState=0;
break;
;
void sendDemandReport(void){
switch (sendDemandReportState){
case 0:
sendDemandReportState++;
break;
case 1:
status=1;
useTempAddress= I;
if(demandReport=1 ) {
pagerMessage = OxCO;
useTempAddress = 0;
sendParametersQ;
sendDemandReportState++;
break;
case 2:
if(! sendParametersState) {
sendDemandReportState=5:
if(sendParametersState =~) {
sendDemandReportState++;
pagerComAbortTime = lastCentiTime + 3000; // 30 seconds ) break;
case 3:
if(demandReport!=2){
writeToSendBuffer('f);
RamADDRESS = Ox53A2;
demandWindowAverage = ramReadInt();
demandThreshold = ramReadInt();
demandT'emp = ramReadlnt();
writeDecimalDivider = read_ext eeprom(Ox 1 D7);
postDecimalCharacter ='W' Ig~

writeDecimalToSendBuffer(demandWindowAverage);
writeDecimalToSendBuffer(demandValue);
writeDecimaIToSendBuffer(demandThreshold);
writeDecimalDivider = 30;
postDecimalCharacter = 0x27; // single quote writeDecimalToSendBuffer(demandTemp);
writeToSendBuffer('f);
pagerComAbortTime = IastCentiTime; // clear delay sendDemandReportState++;
) else if(pagerComAbortTime < lastCentiTime){
sendDemandReportState++;
break;
case 4:
sendDemandReportState++;
sendParametersState++;
break;
case 5:
if(!sendParametersState){
sendDemandReportState++;
break;
case 6:
sendDemandReportState=0;
demandReport=0;
if(!executePagerCommandState) sendingPage=0;
break:
void ioStateUpdate(void){
received byte=read ext_eeprom(OxID6);
if(received_byte & 0x80 ~~ pagerMessage=Ox2c) {
received byte = ioState R, 0x03;
if(received byte=0)RLY1DRV LATCHED OP=0;
else if(received byte=0x01) RLY1DRV LATCHED OP= 1;
else if(RLY I DRV LATCHED OP) ioState ~= Ox0l;
else ioState &= OxPC:
received byte = ioState & OxOC;
if(received byte=0) RLY2DRV_LATCHED_OP = (1;
else if(received byte---0x04) RLY2DRV_LATCHED OP= 1;
else if(RLY2DRV_LATCHED OP) ioState ~= 0x04;
else ioState &= OxF3;
received byte = ioState & 0x30;
if(received byte---0) RLY3DRV_LATCHED_OP = 0;
else if(received_byte=0x10) RLY3DRV LATCHED_OP= 1;
else if(RLY3DRV_LATCHED OP) ioState ~= OxlO;
else ioState &= OxCF;
received byte = ioState & OxCO;
if(received byte=0) RLY4DRV_LATCHED_OP = 0;
else if(received byte=0x40) RLY4DRV LATCHED OP = l;
else if(RLY4DRV_LATCHED OP) ioState ~= 0x40;
else ioState &= Ox3F;
RW EXT IO;

else if(ioState<8){
demandTimer = ioState;
runTimer();
void convertAndWriteToSendBuffer( unsigned char data){
if(status){
writeToSendBuffer(to ascii(data»4));
writeToSendBuffer(to ascii(data&OxOf));

else {
writeToSendBuffer(data);
}
void writeToSendBuffer(unsigned char data){
if(lindex<2550 ~~ addCRCflag) {
RamADDRESS = lindex+OFFSET3;
RamDATA = data;
RAM_W RITE;
lindex++;
/******************************************************
Setup char invalidParameters(void);
void getParCheckSum(void);
void saveParCheckSum(void);
void putStringInEeprom( static const rom char *data); // for null terminated strings void putArrayInEeprom( static const rom char *data); // for strings that contain nulls void putArrayInExtRam( static const rom char *data): // for strings that contain nulls void putArrayInRTC( static const rom char *data); // for strings that contain nulls void commissionButtonPress( void);
void registerPager( void);
void getPagerTime( void);
char getMeterTime( void);
void getSyncTime( void);
char verifyTime(struct time tag *time);
void timeChange(void);
void setParsingArray(void);
void sendParsingArray(void);
void clipEepromToRam(void);
void codeCRC(void);
void codeCRCblock(void);
void read flash(void);
******************************************************/
char invalidParameters(void){
unsigned char invTemp,invTemp2;
// confirm that header is valid and do a 16 bit checksum of everything in eeprom that is set by the // commission function.
headerSize = read_ext_eeprom(Ox 1 d);
if (headerSize>MAXADDRESS) return(TRUE);
received_byte = read_ext_eeprom(Ox 1 f);
if((received byte!='P')&;&(received byte!=E')) return(TRUE);
if(received byte--'P'){
invTemp2=0;

CA 02332297 2001-O1-25 ' for(invTemp=0; invTemp<7; invTemp++) {
received byte = toint(read ext eeprom(Ox20+invTemp)) invTemp2 +_ (received byte);
if (received byte>9) retum(TRUE);
if (invTemp2=0) return(TRUE);
}
getParCheckSum();
received byte = read_ext_eeprom(Ox0);
invTemp = paramCheckSum»8;
if (received byte!=invTemp) return(TRUE);
received byte = read_ext_eeprom(Ox 1 );
invTemp = paramCheckSum&Oxff;
if (received byte!=invTemp) return(TRUE);
return(FALSE);
}
void getParCheckSum(void) {
unsigned int iPar;
paramCheckSum=0;
for(iPar=OxOd; iPar<Ox600; iPar++) {
received byte = read ext eeprom(iPar);
paramCheckSum += received byte;
i ClrWdtQ;
r void saveParCheckSum(void) {
switch(saveParCheckSumState) {
case 0:
saveParCheckSumState++;
pqPollEnabled=0;
pqLogToPromState=0;
while(writingEeprom) bufferToEepromQ;
break;
case 1:
if(!pqPoIlState ~~ acOKtimer){
getParCheckSum();
eepromBuffer(OxO,paramCheckSum»8);
eepromBuffer(Ox l,paramCheckSumROxFF);
pqPollEnabled=1;
saveParCheckSumState=0;
break;

l void putStringInEeprom( static const rom char *data){
do{
eepromBuffer(stringPointer++,*data);
while(*data++);
r void putArrayInEeprom( static const rom char *data) { // for strings that contain nulls do{
eepromBuffer(stringPointer++,*data++);
}while(stringPointer<stringEnd);
r void putArrayInExtRam( static const rom char *data){// for strings that contain nulls do{
RamADDRESS = stringPointer;
RamDATA = *data++;
RAM_WRITE;
stringPointer++;
}while(stringPointer<stringEnd);
void writeStringToSendBuffer( static const rom char *data) { // for null terminated strings do{
RamADDRESS = (index+pFFSET3;
RamDATA = *data++;
RAM_WRITE;
(index++;
}while(*data);
void putArraylnRTC( static const rom unsigned char *data); !/ for strings that contain nulls do{
received byte=stringPointer;
rtc v°rite(received byte,*data++);
stringPointer++;
} while(stringPointer<stringEnd);
;
void commissionButtonPress( void) {
nCLEAR_SW_LED_IO = 0;
nCLEAR_SW_SET_DDRB = 0; // Turn switch 2 on by setting it to an output eepromBuffer(Ox 10,0): // timer Mask all deactivated timerMask=0;
eepromBuffer(Oxl I,Ox08); // exception Mask all deactivated except echo mode exceptionMask=0x08;
eepromBuffer(OxOe,1 ); // meter error Maskl all deactivated except battery test eepromBuffer(OxOf,O); // meter error Mask2 all deactivated eepromBuffer(Ox 1 d,21 ); // headerSize eepromBuffer(Ox 1 e, l ); // headerCount stringPointer-Ox 1 f;
putStringInEeprom(sDefaultEmail);
// Change spreadDelay to be a multiple of 3 second intervals between 0 and 256. Get random number by taking // the low byte of TMRO counter. This overflows every 1/4 of a millisecond so the communication with the // pager to turn line feeds off before running commissioning should make this random enough. Tested with a // communication with meter so that I could display the value on the pager port and seemed quite random.
spreadDelay = TMROL;
eepromBuffer(Oxl2,spreadDelay);
meterlPassH=0; // Make all Meter passwords 000 meter( PassM=0;
meter 1 PassL=0;
eepromBuffer(Ox13,0);
eepromBuffer(Ox14,0);
eepromBuffer(Ox 15,0);
meter2PassH=0;
meter2PassM=0;
meter2PassL=0;
eepromBuffer(Ox16,0);
eepromBuffer(Ox 17,0);
eepromBuffer(Ox 18.0);
module I Pass=1:
eepromBuffer(Ox 19,0);
eepromBuffer(Ox I a, l );

module2Pass=2;
eepromBuffer(Ox 1 b,0);
eepromBuffer(Ox 1 c,2);
economyFlag=TRUE; // Default to Econo mode eepromBuffer(OxSd,economyFlag);
binaryFlag=FALSE; // Default to ascii transmission mode eepromBuffer(OxSe,binaryFlag);
compressionEnabled=FALSE; // Default to not in compression eepromBuffer(OxSf,compressionEnabled);
pagerDiagnostics=FALSE; // Default to not in pager diagnostics mode eepromBuffer(Oxl9b,pagerDiagnostics);
for(i=O;i<CLIPSIZE;i++){ // Erase Parsing Array eepromBuffer(i+Ox60,Oxff);
Itemp2 = 0x60;
clipEepromToRam();
for (i=O;i<8;i++){ // Clear timer command strings (temp=i;
(temp*=128;
(temp+=OFFSET1;
eepromBuffer(ltemp,0);
eepromBuffer(ltemp+I,0);
eepromBuffer(Itemp+2,0);
eepromBuffer(ltemp+3,0);
eepromBuffer(ltemp+4,0);
eepromBuffer(ltemp+5, ;');
eepromBuffer(OxAO,';'); // Nickname Cleared stringPointer=OxllA;
putStringlnEeprom(sDefauItInfoCommands);
for(i=O;i<CLIPSIZE;i++){ // Erase Default Info Parsing Array (temp = OxDA;
(temp += i;
eepromBuffer(ltemp,Oxff);
stringPointer-OxDA;
stringEnd=stringPointer+DEFAULT INFO PARSE LENGTH;
putArrayInEeprom(sDefaultlnfoParsing);
eepromBuffer(Ox1E0,0);// Secret key Cleared.
RamADDRESS = Ox538C;
RamDATA = 0;
RAM WRITE;
pqEnabled = 0;
eepromBuffer(Ox l9A,pqEnabled);
pqVerbosityEnabled=1;
eepromBuffer(Oxlfd,pqVerbosityEnabled);
synchToAtomicTime = 0x85;// 5 seconds max allowable error & use L2 to write time.
eepromBuffer(Oxlfc,synchToAtomicTime);
killPower=1; // Kill power instead of sleeping.
eepromBuffer(Ox I fb,killPower);
received byte = 0x04; // high byte of 1200 (nominal voltage) eepromBuffer(Oxl9C,received byte);
eepromBuffer(Oxl9E,received byte);
eepromBuffer(OxlAO,received byte);
received byte = OxBO; // low byte of 1200 (nominal voltage) eepromBuffer(Oxl9D,received byte);
eepromBuffer(Oxl9F,received byte);
eepromBuffer(Ox 1 A l ,received byte);

for (whichPQ=O;whichPQ<36;whichPQ++){ // Store default PQ values received byte=(whichPQ*3)+0x82;
received byte = rtc_read(received byte);
hemp = whichPQ+Ox 1 A2;
eepromBuffer(ltemp,received byte);
eepromBuffer(Ox 1 FE,3); // PQ disable switch Off Time eepromBuffer(Ox 1 FF,O);// PQ disable switch Report Delay demandWindow = 0;
eepromBuffer(OxIC6,demandWindow);
eepromBuffer(OxICB,OxBF); // demand Maximum exponent eepromBuffer(Ox1D7,1); //demand display divider eepromBuffer(Ox 1 F9,15); // Power Off reminder delay eepromBuffer(OxlFA,2~5); // Power On reminder delay saveParCheckSum();
// Above this point Setup parameters, below Operational parameters (less volatile, often used) if (pagerMessage!=OxOe) {
rtc write(Ox 13,0); // Pager Network Zone rtc write(Ox 14,0); // Pager Network Zone rtc write(Ox10,0); // successCount successCount=0;
bytesSentOK=0;
rtcyrite(Ox15,0); // bytesSentOK MSB
rtc write(Ox16,0); // bytesSentOK LSB
for (whichPQ=O;whichPQ<6;whichPQ++) // Calculate PQ Comparison values.
calcComparisons();
rtc_clear irq(); // clear rtc interrupt if set lindex=0;
RamADDRESS = Ox538C;
received byte = 0; // To disable encryption.
selected.
RamDATA = received_b~-te;
RAM_WRITE;
eepromBuffer(OxleO.received byte);
writeMeterSeriaIQ; // Not used for write buffer, just here to put serial into eeprom & set default key.
nCLEAR_Sf~~ SET DDRB = 1; // Turn snitch 2 off by setting it to an input delay_sec(2);
void registerPager( void){
switch(registerPagerState) {
case 0:
registerPagerState=10;
pagerComAbortTime = IastCentiTime + 10;
break;
case 10:
if (pagerComAbortTime<lastCentiTime) {
>'Y 180 registerPagerState=l;
i break;
case I:

if(!yy l8State){
RamADDRESS = Ox50C3;
RAM_READ;
temp 1 = RamDATA&Oxff;
if(temp 1 !='0') {
pagerComAbortTime = lastCentiTime + 50(l;
registerPagerState=10;
st 1 pulse=Oxa;
st2pulse=Oxa;
pulseCycles=S;
else {
RamADDRESS = Ox50C6;
RAM_READ;
tempt = RamDATA&Oxff; // Get zone byte I
RamADDRESS = Ox50C7;
RAM_READ;
tempi = RamDATA&Oxff; // Get zone byte 2 RamDATA = tempt;
RamADDRESS = Ox50fb;
RAM_WRITE;
RamDATA = tempi;
RAM_WRITE;
// If Not Registered or new zone if(newDestination~~(temp2!=rtc read(Oxl3))~~(temp3!=rtc read(Oxl4))) registerPagerState=2;
else registerPagerState=0;
i break;
case 2:
oldBytesSentOK = bytesSentOK;
nSTI LED_LATCHED_OP = 0; // Turn LED 1 on RW_EXT_IO;
nSTATUS2_LED_OUTP = 0; // Turn LED 2 on registerPagerState=3;
break:
case 3:
if (pagerComAbortTime<IastCentiTime){
exceptionFlag=0;
exceptionPageQ;
registerPagerState=4;
i break;
case 4:
if(! exceptionPageState) {
pagerComAbortTime = IastCentiTime + 3000;
if (oldBytesSentOK---bytesSentOK) registerPagerState=3;
else registerPagerState=5;
break;
case 5:
if(!autoRegisterDestination){
pagerComAbortTime = IastCentiTime + 300;
yyl8(); // Get/Save zone and subzone registerPagerState=6;

else registerPagerState=0;
break;
case 6:
if(!yy l8State){
RamADDRESS = Ox50C6;
RAM READ;
itemp = RamDATA&Oxff;
rtc write(Oxl3,itemp);
RamADDRESS = Ox50C7;
RAM READ;
itemp = RamDATA&Oxff;
rtc write(Oxl4,itemp);
registerPagerState=0;
l i break;
i r void getPagerTime( void) {
switch(getPagerTimeState) {
case 0:
getPagerTimeState=I ;
YY180;
break;
case 1:
if (!yy l8State){
if(pagerResponse=43) getPagerTimeState=2' else getPagerTimeState=0;
;
break:
case 2:
RamADDRESS = Ox~OD4;
RAM_READ;
time.hi=toint(RamDATA&Oxff)* 10;
RAM_READ;
time.hr+=toint(RamDATA&Oxff);
RAM_READ;
time.min=toint(RamDATA&Oxff)* 10;
RAM_READ;
time.min+=toint(RamDATA&Oxff):
RAM_READ;
time.day=toint(RamDATA&Oxfl)* 10;
RAM_READ:
time.day+=toint(RamDATA&Oxff);
RAM_READ;
time.mth=toint(RamDATA&Oxff)* 10;
RAM_READ;
time.mth+=toint(RamDATA&Oxff);
RAM_READ;
time.yr-toint(RamDATA&Oxff)* 10;
RA~4_READ;
time.yr+=toint(RamDATA&Oxff);
time.sec=0;
getPagerTimeState=0;
break;
r r char getMeterTime( void){

switch(getMeterTimeState){

case 0:

getMeterTimeState=1;

time.day=0;

time.mth=0;

command[0]=2;

for(i=l;i<ll;i++) command[i]=0;

command[9]=2;

sendFlag=FALSE;

sendCommandQ;

break;

case 1:

if (!sendCommandState){

if(badSendCommand) {

getMeterTimeState=4;

else {

RamADDRESS = Ox4b00;

itemp = ramReadUchar();

dayOfWeek=to_dec(itemp,OxFO);

RamADDRESS = Ox4b01;

hemp = ramReadUchar();

time.yr=to_dec(itemp,OxFO);

IastKnownYr-time.yr;

RamADDRESS = Ox4b02;

hemp = ramReadUchar();

time.day=to_dec(itemp,OxFO);

RamADDRESS = Ox4b03;

itemp = ramReadUchar();

time.mth=to_dec(itemp,OxFO);

rtc write(Oxl2,lastKnownYr);

command[0]=1;

command[9]=1;

getMeterTimeState=2;

sendFlag=FALSE;

sendCommandQ;

l break;

case 2:

if (!sendCommandState){

if(badSendCommand){

getMeterTimeState=4;

else {

RamADDRESS = Ox4b00;

itemp = ramReadUchar();

time.sec=to_dec(itemp,OxFO);

RamADDRESS = Ox4b01;

itemp = ramReadUchar();

time.min=to_dec(itemp,OxFO);

RamADDRESS = Ox4b02;

itemp = ramReadUcharQ;

time.hr=to dec(itemp,OxFO);

getMeterTimeState=3;

l break;
case 3:
if (!verifyTime(&time)) getMeterTimeState=4;
else {
getMeterTimeReturn=1;
getMeterTimeState=0;
;
break;
case 4:
getMeterTimeState=5;
getMeterTimeReturn=0;
getPagerTime();
break;
case 5:
if (!getPagerTimeState) getMeterTimeState=0;
break;
i t void getSyncTime( void){
// Get current time rtc-get datetime(&time);
time.sec=0;
time.min=0;
time.period=1;
time. interval=1;
// If current time is equal or past sync time add 1 day to time if (time.hr >= HOURSYNC) {
time.hr=HOURSYNC;
addTime(&time);
validateTime(&time);
if(lastSyncDay!=time.day) {
IastSyncDay=time.day;
rtc_write(Oh 1 1,lastSyncDay);
firstPassSync=1;
time.hr=HOURSYNC;
time.yr-time.yr%4;
i char verifyTime(struct time_tag *time) {
if ((time->mth=0)~~(time->day=0)) return(FALSE);
if ((time->yr>99)~~(time->mth>12)~~(time->hr>23)~~(time->min>59)~~(time->sec>59)) return(FALSE);
if ((time->mth-4) ~~ (time->mth=6) ~~ (time->mth=9) ~~ (time->mth=11)){
if (time->day>30) return(FALSE);
i else if (time->mth = 2){
if ((time->yr%4) > 0){
if (time->day>28) return(FALSE);
else if (time->day>29) return(FALSE);
i else if (time->day>31) return(FALSE);
return(TRUE);
r void timeChange(void){
switch(timeChangeState){

C3Se 0:
timeChangeState++;
break;
case 1:
if(!sendingPage){
sendingPage=1;
timeChangeState++;
if(!synchToAtomicTime ~~ timeSynchMode>1){
getMeterTimeQ;
timeChangeState = 5;
break;
case 2:
getPagerTime();
timeChangeState++;
break;
case 3:
if (!getPagerTimeState){
prime = time.min;
timeChangeState++;
// getPagerTime();
i break;
case 4:
if (!getPagerTimeState) {
if(pTime = time.min){
getPagerTimeQ;
pagerComAbortTime = IastCentiTime;
) else {
timeChangeState++;
prime = time.min;
if(pTime > 29) prime -= 30;
prime *= 60;
getMeterTime();
break;
case 5:
if (!getMeterTimeState){
timeChangeState=11;
if(getMeterTimeReturn){
timeChangeState=10;
if(synchToAtomicTime && timeSynchMode<2) timeChangeState=6:
r break;
case 6:
timeChangeState++;
mTime = time.min;
if(mTime > 29) mTime -= 30;
mTime *= 60;
mTime += time.sec;
deltaTimeNegative = 0;
if(pTime > mTime) {
deltaTime = prime - m'f ime;

if(deltaTime>899){
deltaTimeNegative= 1;
deltaTime = 1800 - deltaTime;
) else {
deltaTime = mTime - prime;
if(deltaTime<900) deltaTimeNegative = 1;
else deltaTime = 1800 - deltaTime;
if(deltaTime>300){
timeSynchMode=Oxff; // Error flag.
exceptionFlag=10;
exceptionPageQ;
RamADDRESS = Ox50f0;
ramWritelnt(mTime);
ramWritelnt(pTime);
timeChangeState=10;
if(deltaTime<256){
received byte = deltaTime&Oxff;
if(received_byte<(synchToAtomicTime&Ox7~){
if(timeSynchMode) timeChangeState=10;
else // check RTC and only change if in error.
timeChangeState=12;
i ;
break;
case 7:
mTime = time.min;
mTime *= 30;
mTime += time.sec/2;
mTime += time.hr* 1800;
pqLog(mTime&OxFF);
pqLog(mTime»8);
pqLog(OxFC);
if(deltaTimeNegative) {
if(time.sec>=deltaTime) time.sec -= deltaTime;
else {
deltaTimeNegative = deltaTime%60; // deltaTimeNegative used for deltaSeconds if(time.sec>=deltaTimeNegative){
time.sec -= deltaTimeNegative;
deltaTimeNegative = 0;
else {
time.sec += 60 - deltaTimeNegative;
deltaTimeNegative= 1;
deltaTimeNegative += deltaTime/60; // deltaTimeNegative used for deltaMinutes if(time.min>=deltaTimeNegative) time.min -= deltaTimeNegative;
else {
time.min += 60 - deltaTimeNegative;
if(time.hr) time.hr--;
else {

time.hr = 23;
// Time adjusted to previous day. Now what?
else {

time.sec += deltaTime;

time.min += time.sec/60;

time.sec % 60;

if(time.min>59){

time.min -= 60;

if(time.hr!=23) time.hr ++;

else {

time.hr = 0;

!/ Time adjusted to next day.
Now what?
}

}
}

command[0]=0x21;

sum = 0x21;

received byte = to_bcd(time.sec);

command[1]=received byte;

sum += received byte;

received byte = to_bcd(time.min);

command[2]=received byte;

sum += received byte;

received_byte = to_bcd(time.hr);

command[3]=received byte;

sum += received byte;

for(i=4:i<9;i++) command[i]=0;

command[10]=sum 8;

command[9]=sumR.OxFF;

sendFlag=FALSE;

unlockFlag=l;

whichPass=1;

if(synchToAtomicTime&0x80) whichPass++;

sendCommandQ;

timeChangeState++;

break;

case 8:

if (!sendCommandState){

if(badSendCommand && badSendCommand!=4){

exceptionFlag=9;

exceptionPageQ;

timeChangeState++;

getMeterTimeQ;

}

else {

timeChangeState=10;
}

}
break;

case 9:

if (!getMeterTimeState){

timeChangeState=11;

if(getMeterTimeReturn){

timeChangeState=10;

break;
case 10:
b time.yr=time.yr%4;

rtc set_datetime(&time);

rtc write(Ox00,Ox04);

time.sec += 10; // So that a recalculation from scratch can't skip an event.

for(timer=O;timer<8;timer++) {

getAlarm(timer);

if ((alarm.period!=0) && (alarm.interval!=0)) {

if (lessOrEqual(&time,&alarm)) {

initAlarm(timer);

checkAlarm(timer);

time.sec += 10; // checkAlarm redefines time l timeOfDaySync = timeOfDay;

timeOfDay = time.min;

timeOfDay *= 30;

timeOfDay += time.sec/2;

timeOfDay += tirne.hr* ( 800;

time.yr = time.yr4;

time.yr += time.mth;

pqLog(to bcd(time.day));

pqLog(time.yr);

pqLog(timeOfDay&OxFF);

pqLog(timeOfDay8);

pqLog(timeOfDaySyncR,OxFF);

pqLog(timeOfDaySync8);

pqLog(OxFE);

if(timeOfDay>timeOfDaySync){

timeOfDaySync = timeOfDay - timeOfDaySync;

timeChangeState= I;

else {

timeOfDaySync = timeOfDaySync - timeOfDa~
;

timeChangeState = 0;

;

for(i=O;i< 12;i++) {

RamADDRESS = 0x4404 + [*px 12;

pqReportTime = ramReadInt();

if(pqReportTime){

if(timeChangeState){

pqReportTime += timeOfDaySync;

else {

if(pqReportTime>timeOfDaySync) pqReportTime -= timeOfDa~~Sync;

else pqReportTime = 1;
r RamADDRESS = 0x4404 + i*px 12;
ram WriteInt(pqReportTime);
if(pqPendingTime){
if(timeChangeState) {
pqPendingTime += timeOfDaySync;

}
else {
if(pqPendingTime>timeOfDaySync) pqPendingTime -= timeOfDaySync;
else pqPendingTime = I;
}
if(pagerPollTimeout){
if(timeChangeState){
pagerPollTimeout += timeOfDaySync;
}
else {
if(pagerPoIlTimeout>timeOfDaySync) pagerPollTimeout -= timeOtDaySync;
else pagerPolITimeout = 0;
timeChangeState=11;
break;
case 11:
setAlarmQ;
timeChangeState=l3;
break;
case 12:
timeOfDaySync = timeOfDay;
timeOfDaySyne = time.min;
timeOfDaySync *= 30;
timeOfDaySync += time.sec/2;
timeOfDaySync += time.hr* 1800;
if(timeOfDaySync>timeOfDay) timeOfDaySync -= timeOfDay;
else timeOfDaySync = timeOfDay - timeOfDaySync;
timeChangeState=13;
it(timeOfDaySync>(synchToAtomicTime&Ox7fj){
timeChangeState=10;
break;
case 13:
sendingPage=0;
timeOfDaySync = IastCentiTime+6000;
timeOfDayInc = lastCentiTime+200;
timeChanged=0;
timeChangeState=0;
rtc_get datetime(&time);
timeAdjustedToday = time.hr;
if(timeSynchMode!=2 ~~ time.min>6) timeAdjustedToday++~
if(timeAdjustedToday>23) , timeAdjustedToday = 0;
break;
) void setParsingArray(void){
i=0;
do {
RamADDRESS=Istart;
RAM READ;
itemp = RamDATA&Oxff;
if (hemp !_';' R,& hemp !_':'){
received byte=toint(itemp)* 16;

RAM_READ;
received byte += toint(RamDATA&Oxff);
eepromBuffer(ltemp2++,received_byte);
}
Istart+=2;
i++;
} while(itemp !_';' && itemp !_':' && i<CLIPSIZE);
i--; // To account for i incremented for';' while (i<CLIPSIZE){
eepromBuffer(Itemp2++,Oxff);
i++;
r saveParCheckSum();
void sendParsingArray(void){
switch (sendParsingArrayState){
case 0:
sendParsingArrayState++;
break;
case l:
status=0;
useTempAddress= 1;
sendParametersQ;
sendParsingArrayState++;
break;
case 2:
i f(! sendParametersState) sendParsingArrayState=0;
else if(sendParametersState=5) sendParsingArrayState++;
break;
case 3:
clipEepromToRam(); // ltemp2 should be set before sendParsingArray called sendParsingArrayState++;
break;
case 4:
for(i=O:i<CLIPSIZE;i++)( RamADDRESS = 0x5080+i;
RAM_READ;
received byte = RamDATA;
writeToSendBuffer(received_byte);
i r (tempt = 0x60;
clipEepromToRamQ;
sendParametersState++;
sendParsingArrayState++;
break;
case ~:
if(!sendParametersState) sendParsingArrayStatc=0;
break;
void clipEepromToRam(void){
i=0;
RamADDRESS = 0x5080;
while(i<CLIPSIZE){
received byte=read ext eeprom(ltemp2++);

RamDATA = received byte;
RAM WRITE;
i++; _ void codeCRC(void){
// unsigned int's #define codeEnd pqComparison #define codeStart pqStartTime #define codeAddress pqReportTime // unsigned char's #define codePageNumber pqEvent #define codeDataH pqInProgress 'I5 #define codeDataL pqDurationLimit crc=0;
for(codePageNumber-O;codePageNumber<6;codePageNumber++) {
codeStart = codePageNumber;
codeStart *= 0x2000;
codeStart += 0x4000;
RamADDRESS = 0x4010+codePageNumber;
read_flash();
codeEnd = RamDATA;
if((codeEnd-codeStart)>Oxl fff){
crc=Oxffff;
return;
codeCRCbIockQ;

CaIcCRC(0);

CaIcCRC(0);

eepromBuffer(Ox04,crc8);

eepromBuffer(OxOS,crc&OxFF);

writeToSendBuffer('/');

convertAndWriteToSendBuffer(crc8);

convertAndWriteToSendBuffer(crc&OxFF);

writeToSendBuffer('/');

crc=0;

codeStart = 0;

codeEnd = Ox3fff;

codeCRCblockQ;

CaIcCRC(0);

CaIcCRC(0);

eepromBuffer(Ox06,crc8);

eepromBuffer(Ox07,crc&OxFF):

convertAnd WriteToSendBuffer(crc8);

convertAndWriteToSendBuffer(crc&OxFF);

writeToSendBuffer('f);

void codeCRCblock(void){
for (codeAddress=codeStart:codeAddress<codeEnd;codeAddress++) {
RamADDRESS = codeAddress;
read_flash();
codeDataH = RamDATA»8;
codeDataL = RamDATA&OxFF;
CaIcCRC(codeDataH);
CaIcCRC(codeDataL);
ClrWdtQ;
;

void read_flash(void){
CPUSTAbits.GLINTD= 1;
_asm BANKSEL RamADDRESS
MOVFP RamADDRESS,TBLPTRL
MOVFP RamADDRESS+1,TBLPTRH
TABLRD O,O,RamDATA //Nothing read just to load the Table Latch NOP
TLRD l,RamDATA+1 TLRD O,RamDATA
_endasm CPUSTAbits.GLINTD = 0;
) /******************************************************
Miscellaneous char delay_3sec(unsigned char howMany, unsigned char pattern);
void delay sec( int howMany);
void delay ms~lus( int howmany); // delay_ms plus check clear switch and latch if pushed.
void pulse(void);
void beep(void);
void testSystem( void);
******************************************************/
void pulse(void){
switch(pulseState){
case 0: // Start if (pulseCycles!=0){
st 1 mode = st 1 pulse;
st2mode = st2pulse;
if(stimode=O~~stlmode =OxOA)nSTILED LATCHED OP=0;
else nST 1 LED_LATCHED_OP = 1;
if (st2mode=0 ~~ st2mode==OxOA) nSTATUS2 LED OUTP = 0;
else nSTATUS2_LED_OUTP = 1:
RW_EXT_IO;
pulseCount=0;
if(stlmode>0 &R, stlmode<4) pulseState= 1;
else if(st2mode>0 && st2mode<4) pulseState = 2;
else pulseState = 3;

break;
case 1: // Pulse Status 1 switch(pulseCount){
case 0:
case 2:
case 4:
pulseCount++;
nSTILED_LATCHED OP= 1; RV'_EXT_IO;
pulseTime = lastCentiTime + 1 ~;
break;
case 1:
if(stlmode<2) pulseCount+=2;
case 3:
if(stlmode<3) pulseCount+=2;
case 5:
pulseCount++;
nST 1 LED_LATCHED_OP = 0; RW_EXT_IO;
if(pulseClear){

pulseClear=0;

nCLEAR_S W_LED_IO = 0;

nCLEAR S W SET DDRB = 0;

pulseTime = IastCentiTime + 10;

break;

case 6:

pulseCount=0;

nSTILED_LATCHED_OP= 1; RW_EXT_IO;

nCLEAR_SW_SET_DDRB = 1;

if(st2mode>0 && st2mode<4) pulseState = 2;

else {

pulseState = 3;

pulseCount=1;

;

pulseTime = lastCentiTime + 45;

break;

i break;

case 2: // Pulse Status 2 switch(pulseCount) {

case 0:

case 2:

case 4:

pulseCount++;

nSTATUS2_LED_OUTP= 1;

pulseTime = lastCentiTime + 15;

break;

case 1:

if(st2mode<2) pulseCount+=2;

case 3:

if(st2mode<3) pulseCount+=2;

case 5:

pulseCount++;

nSTATUS2 LED_OUTP = 0;

pulseTime = lastCentiTime + 10;

break;

case 6:

pulseCount=0;

nSTATUS2_LED_OUTP= I;

if(stl mode>0 && st I mode<4) pulseState = 4;

else {

pulseState = 3;

pulseCount=1;

;
pulseTime = lastCentiTime + 45;
break;
break;
case 3: // Toggle Only switch(pulseCount){
case 0:
pulseCount++;
pulseTime = lastCentiTime + 75;
break;
case 1:
pulseCount++;
if (stl mode = OxA) {
nSTILED_LATCHED_OP= 1; RW_EXT_IO;
;

if (st2mode = OxA){
nSTATUS2 LED OUTP= 1;
if (st2mode = OxB){
nSTATUS2 LED OUTP = 0;
pulseTime = lastCentiTime + 75;
break;
case 2:
pulseState = 4;
break;
break;
case 4: // Return to Start pulseCycles--;
pulseState = 0;
break;
#pragma code testSystem = OxE000 void testSystem( void) {
switch(testState){
case 0:
testState = 1;
break;
case 1:
YY I 8Q;
testError = 0;
if (nAC OK INP) { // Power is out! Dont' even Try to talk to Meter testError = 0x80;
badSendCommand = 0;
else {
command[0]=0x94;
for(i=l;i<I 1;i++) command[i]=0;
command[9]=0x94;
noMeterResponse=1;
sendFlag=FALSE;
sendCommand();

1=0;
testState = 2;
break;
case 2: // Test RTC, Eeprom and External Memory rtc write(i+245,OxA~);
hemp=rtc read(i+245);
if (itemp!=OxAS) {
testError = Ox04~testError;
;
write eeprom noDelay(i+8086,OxA5);
hemp=read_ext_eeprom(i+8086);
if (itemp!=OxAS){
testError = 0x01 ~testError;
) if (i++>9) testState = 3;
break;
case 3: // Wait for meter, pager and last pulse cycle complete if (!yyl8State && !pulseCycles && !sendCommandState){
if (badSendCommand) {

if(noMeterResponse) testError= OxAO~testError;
else if(badHandshake) testError= OxCO~testError;
else testError = OxEO~testError;
r if (pagerResponse=0) testError = Ox l0~testError;
else if (pagerResponse!=43) testError = Ox l8~testError;
if (testError=0) {
stlpulse=OxA; st2pulse=OxB; pulseCycles=2;
1 0 testState = 9;
else {
testState = 4;
) break;
case 4: // Meter Error if (testError&0x80) {
testError = Ox7F&testError;
if(testError&0x40) {
testError = Ox3F&testError;
if(testError&0x20) {
testError = Ox 1 F&testError;
stlpulse=0x2; st2pulse=0x3; pulseCycles=2;
j else {
stlpulse=0x2; st2pulse=0x2; pulseCycles=2;
L
else {
if(testError&0x20) {
testError = Ox 1 F&testError;
stlpulse=0x2; st2pulse=Oxl; pulseCycles=2;

else {
stlpulse=0x2; st2pulse=0x0; pulseCycles=2;

L
l testState = 9:
else testState = 5;
break;
case 5: // Pager Error if (testError&;OxlO) {
testError = OxOF&testError;
if(testError&0x08) {
testError = 0x07&testError;
stlpulse=0x3; st2pulse=0x3; pulseCycles=2;
else {
stlpulse=0x3; st2pulse=0x0; pulseCycles=2;
testState = 9;
i J
else testState = 6;
break:
case 6: // RTC Memory Error if (testError&0x04) {
testError = 0x03&testError;
stlpulse=Oxl; st2pulse=0x1; pulseCycles=2;

testState = 9;
else testState = 7;
break;
case 7: // External RAM Memory Error if (testError&0x02) {
testError = 0x01 &testError;
stlpulse=0x1; st2pulse=0x0; pulseCycles=2;
testState = 9;
) else testState = 8;
break;
case 8: // Eeprom Memory Error if (testError&0x01 ) {
testError = 0;
stlpulse=0x1; st2pulse=OxA; pulseCycles=2;
testState = 9;
break;
case 9: // Wait until Finished flashing if (testError=0){ // Don't Wait if (nTEST JUMP INP) testState = 0;
else testState = 1;
else if (pulseCycles=0) {
testState = 4;
i break;

l void delay_sec( unsigned int howMany){
unsigned int i;
for (i=O:i<howMany;i++){
delay_ms-plus(1000);
ClrWdtQ:

i void delay-ms~lus( unsigned int howMany) { // delay-ms plus check clear switch and latch if pushed.
unsigned int iDelay;
for (iDelay=O:iDelay<howMany;iDelay++){
delay-ms(1);
if(!nCLEAR_SW LED_IO){
nCLEAR SW LED_IO = 0;
nCLEAR SW SET DDRB = 0: // Turn switch 2 on by setting it to an output r i void updateAnalogInputs(void) {
switch(updateAnalogInputsState){
case 0:
break;
case 1:
ADCONO = 0x01;
ADCON1 = Ox4C;
break;
case 2:
ADCONO = OxO~:

break;

case 3:

while(ADCONO&0x04);// will wait here but should be done anyway (about 100 micro-seconds) IBSenseH = ADRESH;

IBSenseL = ADRESL;

ADCONO = 0x81;

break;

case 4:

ADCONO = 0x85;

break;

case 5:

while(ADCONO&0x04);// will wait here but should be done anyway (about 100 micro-seconds) TempSenseH = ADRESH;

TempSenseL = ADRESL;

ADCONO = 0x91;

break;

case 6:

ADCONO = 0x95;

break;

case 7:

while(ADCONO&0x04);// will wait here but should be done anyway (about 100 micro-seconds) VBSenseH = ADRESH;

VBSenseL = ADRESL;

ADCONO = Ox0l;

updateAnalogInputsState=255;
// will be incremented to zero.

break;

updateAnaloglnputsState++;
/******************************************************
32 bit floating point functions.
void uintl6ToFloat32(unsigned int datal6, unsigned char float32Address);
int float32ToIntl6(unsigned char float32Address);
void floatMultiply(unsigned char addressA, unsigned char addressB, unsigned char addressAtimesB);
void floatDivide(unsigned char addressA, unsigned char addressB, unsigned char addressAoverB);
void floatAdd(unsigned char addressA, unsigned char addressB, unsigned char addressAplusB);
void cos128(unsigned int cosData, unsigned char float32Address);
void floatChangeSign(unsigned char addressA);
void putFIoatPager(unsigned char float32Address);
void FL01632U(void);
void FPD32(void);
void FPM32(void);
void FPA32(void);
void INT3216(void);
******************************************************/
void uintl6ToFloat32(unsigned int datal6, unsigned char float32Address);
AARGBO=datal6»8;
AARGB 1=datal6&Oxff;
FL01632U();
RamADDRESS = 0x5100+float32Address;
RamDATA = AARGBO;
RAM_WRITE;
RamDATA = AARGB 1;
RAM_WRITE;
RamDATA = AARGB2;
RAM_WRITE;
RamDATA = AEXP;

RAM WRITE;
int float32T0Int16(unsigned char float32Address);
unsigned int datal6;
RamADDRESS = 0x5100+float32Address;
RAM_READ;
AARGBO = RamDATA;
RAM_READ;
AARGB 1 = RamDATA;
RAM_READ;
AARGB2 = RamDATA;
RAM_READ;
AEXP = RamDATA;
INT3216Q;
datal6=AARGBO;
datal 6 = (datal6«8) + AARGB 1;
return(datal6);
void floatMultiply(unsigned char addressA, unsigned char addressB, unsigned char addressAtimesB);
RamADDRESS = 0x5100+addressA;
RAM_READ;
AARGBO = RamDATA;
RAM_READ;
AARGBI =RamDATA;
RAM_READ;
AARGB2 = RamDATA;
RAM_READ;
AEXP = RamDATA;
RamADDRESS = Ox~ 100+addressB;
RAM_READ;
BARGBO = RamDATA;
RAM_READ;
BARGBI =RamDATA;
RAM_READ;
BARGB2 = RamDATA;
RAM_READ;
BEXP = RamDATA;
FPM32Q;
RamADDRESS = 0x5100+addressAtimesB;
RamDATA = AARGBO;
RAM_WRITE;
RamDATA = AARGB 1;
RAM_WRITE;
RamDATA = AARGB2;
RAM_V~RITE;
RamDATA = AEXP;
RAM_WRITE;
;
void floatDivide(unsigned char addressA, unsigned char addressB, unsigned char addressAoverB);
RamADDRESS = 0x5100+addressA;
RAM_READ;
AARGBO = RamDATA;
RAM_READ;
AARGB 1 = RamDATA;
RAM_READ:
AARGB2 = RamDATA;
RAM READ;

AEXP = RamDATA;

RamADDRESS = 0x5100+addressB;

RAM_READ;

BARGBO = RamDATA;

RAM_READ;

BARGBI =RamDATA;

RAM_READ;

BARGB2 = RamDATA;

RAM_READ;

1 BEXP = RamDATA;
O

FPD32Q;

RamADDRESS = 0x5100+addressAoverB;

RamDATA = AARGBO;

RAM_W RITE;

RamDATA = AARGB 1;

RAM_WRITE;

RamDATA = AARGB2;

RAM_WRITE;

RamDATA = AEXP;

RAM WRITE;

void floatAdd(unsigned char addressA, unsigned char addressB, unsigned char addressAplusB){
RamADDRESS = 0x5100+addressA;

RAM_READ;

AARGBO = RamDATA;

RAM_READ;

AARGB 1 = RamDATA:

RAM_READ;

AARGB2 = RamDATA;

RAM_READ;

AEXP = RamDATA;

RamADDRESS = 0x5100+addressB;

RAM_READ;

BARGBO = RamDATA;

RAM_READ;

BARGB1 =RamDATA;

RAM_READ;

BARGB2 = RamDATA;

RAM_READ;

BEXP = RamDATA;

FPA32Q;

RamADDRESS = 0x5100+addressAplusB;

RamDATA = AARGBO;

RAM_WRITE;

RamDATA = AARGB 1;

RAM_WRITE;

RamDATA = AARGB2;

RAM_WRITE;

RamDATA = AEXP;

RAM WRITE;

void floatChangeSign(unsigned char addressA);
RamADDRESS = 0x5100+addressA;
RAM_READ;
RamDATA ~= 0x80;
RamADDRESS = 0x5100+addressA;
RAM_WRITE;
;

void cos128(unsigned int cosData, unsigned char float32Address){
// This function takes a value that is 128 times an angle between 0 and 360 degrees.
// 0 to 45 is approximated by cos(x) = 1 - (x*x/6890) // 45 to 90 is approximated by cos(x) = 1.192 - (x/122) - (x*x/17800) // 90 to 180 cos(x) =-cos(180-x) // 180 to 270 cos(x) =-cos(x-180) // 270 to 360 cos(x) = cos(360-x) unsigned char negativeFlag;
negativeFlag = 0;
if(eosData>34560){ // 270 to 360 degrees cosData = 46080 - cosData;
else if(cosData>23040){ // 180 to 270 degrees cosData -= 23040;
negativeFlag = 1;
else if(cosData>11520){ // 90 to 180 degrees cosData = 23040 - cosData;
negativeFlag= l;
i uintl6ToFloat32(cosData, 0x50);
floatMultiply(Ox50, Ox4C, 0x50);
floatMultiply(Ox50, 0x50, 0x54);
if(cosData<5760) { // 0 to 45 degrees floatMultiply(Ox54, Ox3C, 0x54);
floatAdd(Ox54, 0x58, float32Address);
i else { // 45 to 90 degrees floatMultiply(Ox50, 0x44, 0x50);
floatMultiply(Ox54, 0x48, 0x54);
floatAdd(Ox54, 0x40, 0x54);
floatAdd(Ox54, 0x50, float32Address);
if(negativeFlag) floatChangeSign(float32Address);
r void powerSaveSleep(void){
received byte = read_ext_eeprom(Ox02); // Check loader version for < 18 if (received byte<Ox30 ~~ received byte>Ox39) pqEvent=0;
else {
received byte -= 0x30;
received byte *= 10;
pqEvent = read_ext_eeprom(Ox03);
if (pqEvent<Ox30 ~~ pqEvent>Ox39) pqEvent=0;
else {
pqEvent -= 0x30;
pqEvent += received byte;
if(pqEvent < 18) pqEvent=0;
if(!sleepTest){
if(!(exceptionMask&0x02));
exceptionMask ~= 0x02;
eepromBuffer(Ox 1 1.exceptionMask);
saveParCheckSumQ;

pqPollState = 0;
while(saveParCheckSumState) saveParCheckSum();
pqLog(timeOfDay&OxFF);
pqLog(timeOfDay»8);
pqLog(OxFB);
buffToPromState = 0;
pqLogToPromState=0;
while(writingEeprom) bufferToEeprom();
buffToPromState = 0;
pqLogToPromState = 0;
while(pqBuffInPnt!=pqBuffOutPnt) pqLogToEeprom();
delay_ms( I 0);
if(killPower) KILL PWR OUTP= 1;
CPUSTAbits.GLINTD= 1;
TOSTA = Ob01100000;
InstalLINT(-INT);
INTSTAbits.INTE= l;
CPUSTAbits.GLINTD = 0;
PAGER_PWR_OUTP = 0;
nDCLED_OUTP = 0;
nSTILED_LATCHED_OP= I;
nSTATUS2_LED_OUTP= 1;
nCLEAR_SW_SET_DDRB = I; // Turn switch 2 off by setting it to an input CPUSTAbits.GLINTD= I;
while(nAC OK_INP){
rtc-get datetime(&alarm);
alarm.period=I;
alarm.sec += 4;
validateTime(&alarm);
if(alarm.hr>=24){ // Day roll over.
for(;;); // Cause a WDT reset.
i rtc_set_alarm_datetime(&alarm);
nLED_EN LATCHED_OP= l;
CPUSTAbits.GLINTD= l;
RWextendedIO();
if(pqEvent) remoteSleepQ;
else {
_asm sleep _endasm ;
nLED_EN_LATCHED_OP = 0;
RWextendedIO();
CPUSTAbits.GLINTD = 0; // To allow RTC alarm interrupt flag to be cleared.
for(;;); // Cause a WDT reset.
#pragma code testInFlash = 0x4020 void testInFlash(void){
putstring(sNewLine,echoSize); // echoSize is in the same memory location putstring(sBootBanner,echoSize);// as echoPort in the Loader.

sleepiest = !(rtc read(Oxff));
rtc_write(Oxff,sleepTest);
if(sleepTest) {
putstring(sKillTest,echoSize);
KILL PWR_OUTP= 1;
putstring(sFailed,echoSize);
}
sleepiest= 1;
putstring(sSleepTest,echoSize);
powerSaveSleep();
_asm movlw 0 movwf PCLATH
movwf PCL
endasm }
void putstring( static const rom char *data, char whichPort );
do{
if(whichPort---I ){
while(BusyUSART 1 ());
putcUSART 1 (* data);
}
else {
while(BusyUSART2Q);
putcUSART2(* data);
}
}while(*data++);

Appendix B
tlem.0uan(ityReferenceValw Descrip(ion -._ . ParlNo. Mfr.

1 Id Ct, C2. D.tuF Cap, Car, X7R, SMT-0805,ECJ-2vBtEt04KPanasonic C3, CI, C5. 25v, t0%
C7. C8. C9, C12.C11,Ct5.Ct7,Ct8.C19,C2D, C2t, C22.
C2J. C25, C26, C27, C30, CJ1, CJ2.
CJJ, C3d, C75, C76.
CJB, C40, C41, C42, C43, C44, C47, C49, C50, G52.
C57, C54, C55. C56, C60.

~

2 6 xt, C6, Nm Installed -. U9. C11,.
R20. R27 ~

_ 1 C10 lOpF Cap. Car, NPO, S0.1T-0805.ECU-V1N100DCNPanasonic 50v, 5% -4 2 Ct3, C28 t000uF Cep, Elytc, t05C. 10v,ECE-AIAFSt02Panasonic ~ 20%.2000nr, Radial, HFS

1 C16 IOuF Cap. TanL 16v, 20%. ECS-HICCtO6RPanasonic SMT C-Case 6 1 C24 68uF Cap, Tanl. 6.7v, 20%, ECS-HOJD686RPanasonro SMT D-Case 7 t C29 - Nd Installed . -8 t C37 t200uF Cap. Elytc, 105C, 16v,ECA-ICFOt22Panasonic 20%, 200Dbr, Radial, HFO
9 t C39 t.OF Cap. NF Senes Super EEC-FSRSU105Panasonic CaD. 5 5v, -25 - 70C

70 2 C46. C45 lBpF Gap. Car. NPO. SA1T-0605.ECU-V1Nt80JCNPanasonic 50v, 5 %

t1 1 Cd8 1uF Cap, Cer, %7R, SMT.1206,ECJ-3VB1Ct05KPanasonro 16v, t0%

t2 1 C51 O.OtuF Cap. Car, X7R, SMT-0805,ECU-VtHtOJKBGPanasonro 50v, 5%

1J 2 D1, D4 BA570 Onoda. Schotlky, 70v, BA570DI Diodes SOT.23 Inc.

14 d D2, 07. FMMD9t1Die, Switching, 75v. FMMD914 - 2elea D6, 07 SOT-23 1 DB 52D Orode,SmLRed.GP.200V/2A,S2D - - M~aosemi SMT, DO-2t4AA
-16 9 FBt, FB2. 1k Ferr4s BeaA, tK ~ 100Mnz,BLM-21Bt02SPTMurala F87. FBd. 0.4 OCR, 200mA. D805 F85, F86.
F87, FB9, fBtt t7 2 F010. F88 . Nd Installed . -t8 J W t, JPt, JMP1XJ Jumper, ts7, Tm tail, 51011-OJ Sullies JP2 Gold Conl., 0230m.
lg.

19 1 Jt - Nd Instaked - -t J2 JMPls7 Jumper, 1s7, Tin Uil, 57011-07 Sull~ns Gold CoM., 0.230~n.
lg.

21 1 J3 fi9747-1Fles Foil Cable. 2 69317-7 Caole s 6 Positron Connections 22 1 JI HDR Jumper, 2st 3. fn tail.5201 t-17 Sulkns 2 a Gold Cont., 0.270m.
13 lg.

23 1 J9 - No( Installed 2d 5 LEDt, LED2,REO . ~~LED RED WATER CLEARSSL-LxtSIC-TRLumea LED7. LED4, ~ SOT.23 LEDB

d LEDS, LE06,GRN LEO GREEN WATER CLEAR SSLL%tSGC-TRLumea LED7. LE09 SOT-23 -26 1 PCBt 700t PCB, Gen 2 Pager Inledace,3001 t-0050XPT
t-0050 Rev. C

27 t Pt 873Jt-2220Header, 22 Pos. Str.. 87331-2220Moles 2mm. M~n~-GrM

26 1 P2 - Nd In5lalled 29 3 Ot. 07. MMBT3906TRAMS PNP dOV SkID MMBT7906 MOtorda 15 02, OJ, MMBT3904TRAMS NPN 40V SMD SOT-2JMM8T39M Motorola Od, 05, O6.
08, OtO.
020.

021. 030.
071. 0J2.
0t00. 0101.

71 10 Rt, R2. 2.2k Res. CarOOn, 5%, SMT-0605ERJ-6GEVJ222Panasonic R7. Rd. R13.
R14, R15.
R16, Rt 7, 8209 -72 35 R5. R6, 10k Res. CdlbOn, 5.4. SMT.0805ERJ-6GEYJt03Panasonic RB, Rte, R19, R23, R24, ~

R25. R26, R29. R71, R31, R40, R41, Ra2. R4 J, R44, R45.
R17, R50, R51, R56. R57, R62. R6J.
R67, R70, R7t, R79, R61, 8207. 8204, 8205, R2D6.

37 t9 R7. R9, 100k Res. CsrOOn, 5%, SMT-0805ERJ-fiGEYJIOaPanasonic RtO. Rag, R52. RSJ.
R65.

R72. R77.
R90, R94, 8702. 8103, Rt04. R105, R20t, RJ(H1.
8301, 3d 2 R11, R12 56k Ras, CarDOn, 5%. 5M7.0805ERJ-6GEYJ567Pan ni s a 3 R21, R22, 1.7k Res, C8lDOn, 5%. SMT-0805ERJ-6GEYJ172o R28 c Panasmic J6 t R30 .. 665k Res,MF,t%,SMT.0805 ERJ-6ENF665tPanasonro ~

77 2 2.00k. Res.MF,t%,SMT-Ot105 ERJ-6ENF2DOt- Panasonic R32.R55 -38 3 RJ3, R97, 5 6k Res. Carbon, 5X, SMT-OB05~ERJ-6GEYJ562- Panasonic RlOt 39 1 RJ5 30.1k Ras. MF, 1%, SMT-0805 ERJ-6ENF7012Panason t 10 1 RJ6 95.7k Res. MF, 1%, SMT-0805 ERJ-6ENF95J2Pan aSMro a1 7 R77, R39. 220k Res. Carbon ERJ-6GEVJ224P
R58. R59. 5%
R60, R61. SMT-0805 , anasenro , a2 1 RJ8 22k Ras, Carbon. 5X. SMT-0805ERJ-6GEYJ22JPanasonro 47 t RSd 1.99k Res. MF, 1%, SMT.0805 ERJ-6ENF499tPanasonro 4a 1 R64 700 Res. Carbon, 5%. SMT.0805ERJ-6GEYJtOtPanasonro -2 R66. R78 Rx NOI Installed - -46 1 R6B 10k Ras, Carbon, 5%. SMT-0805ERJ-6GEVJtOJPanaSOnK

d7 1 R73 765k _ Rss. MF, 1%. SMT-0805ERJ-6ENF3652Panasonic - ~~

a8 1 25 5k Res. MF, 1%, SMT-0805 ERJ-6ENF2552Panasonro R7t ~ ~ ~~
-49 1 R75 97.6k Rsa, MF, 1%, SMT.p805 ERJ-6ENF9762Panasonro 1 R76 -__ 775 Res. MF. t%. SMT-OB05 ERJ-6ENF7150Panasonic _ _.

Appendix B continued Item Guantlty Retarence ~...-. _ Vatw DW cr;pGOn van No. - Mrr.
1 R80 1704 Res. DaIDOIi, 5%, SMT.0805 ERJ-6GEYJ471 Panasonic 50 R82. R9t,174 Rea, Carbon, 5%, $M1-0805ERJ-6GEVJa7JPanasonrc 1 R92, 51 R81 Ra Nd Instaued 52 R85 tOk Res,Carbon,5%,SMT-0805ERJ-6GEYJ103Panasonic t 53 R86 625 Res. MF, t%. 5M70805 ERJ-6ENF8250Panasonic t ~
~

t R8t tO.Ok Res. MF, t%. SMT-0805ERJ-6ENFt002Panasonic 51 ~ -~~~~~-~~

55 R88 t2.74 Res,MF,1%.SMT-0805 ERJ-6ENFt272Panason~c t ~ ~ -~
~
~

1 R95 tOk Res, CarbM, 5X, $MT.O805ERJ-6GEVJt03~ Panasonic 58 ~

57 Rt06, Rz Nd Inauned 2 Rt00 58 8200 Rz Ndhnlalled t 59 8202 Ra Nd Installed -t t R20B t4 Rea.CarDOn,S%.SMT-0805ERJ-6GEVJt02Panasonic 60 8209 224 Rea, CarDdl, 5%, SMT-0805ERJ-6GEYJ222Panasonic t 61 8210 tO.Ok Res, MF, t%. $MT-OB05ERJ-6ENFt002Panasonic t 62 R2tt 12.74 Res, MF, 1%, SMT-D805ERJ.6ENFt272Panasonrc t 6J R2t2 1M Res.CarDOn,s%,SMT-0805ERJ-6GEYJt05Panasonla t - . -.
--t $t ....FR22d9 M~,Swdcn. t5-25 A/TLt04.FR2249 CP
64 200v.0 SA. SMT Clare -65 TPt, TP2.TESTPOINT..PCB Testpomt Only No Pan 3 TP7 ..

66 U1 Nol Installed 67 U3, U2 Nol Installed 68 W ~AM29Fd00BT-t20-EIFlath Memory. IMBd AM29F400BT-120-EIAMD
1 z 16 (256 kbytesl.
SMT, TSOP-18 69 US 2dLC65-I15MEEprom memory, e4 2aLC65-USMMicrochip t s B Dyles, Serial.
$0L-08 70 U6 . Nd Irtstaced 1t U7 LH5t61AHN-10LSlakc RAM, Bk a 8 LH5t6dAHN.tOISharp 1 bytes, 100 n$ec, -72 U8 PCf8583TD RSet Tma Cbck - Calendar,PCF85B3TD Pnikpa t 12C. SOL-B

7J Ut0 "... MM71HC241WMIC,OctaIBuderlprner,$MT50-11MM74HC2ddWMFairchild t -7d Utt MM7dHC37JM IC.Odal'D' Transp MM7dHC373M~ ~Fauchild t - ...... Lalcn, SO-20 - -~ .. -.........

75 Ut2.UiJ SN74HC573DWIC.Odal'D'lransp.Latch.SO-20SN74NC57JOW......MOlorda Z ~ ~~

76 Ut1 LM291tS IC. 1 AMP low DropoutLM29d15 ~~~~~-Nalanal 1 Regulator, TO-263-5 ~~~ ~ -~

t) Ut5 LP295tACM IC.MlustabklowdropoulRegulator,$MT.50-8LP295tACM
Falrchd0 t 78 Ut7 TC71NC08AFNIC, Ouad 211p NarM TC7dHCO8AFNTosluDa t Gate, SMT SO-11 79 U18 MM7dHC27M IC, Tnple 3l/p NOR MM7dHC27M Fairchild t Gate, SMT, SO-t4 80 Ut9 PICt7C756-16IILIC.CMOSMnaoconlroller,Ind.Temp.PICt7C756-16/ILM~crocn~p t 8t U20 MM7dHCJ2M IC, Ouad 2 VD OR Gale,MM7dHCJ2M Fa~rchdd t SMT, SO-t1 82 U2t MM7dHCOdM IC. Hea tnvener, SMT,k1M71HC04MFairchild t SO-td 8J U22 LM2902M IC, Ouad OPAmp, La LM2902M National t - Power, SO-1d -84 U23 ~ Instaned -t - .
..

es U2d - -NInstaaed...... - .. .. ...
1 ~ -.

B6 U200 TCIINCt4AFN-IC, Hex Schm~ll Imener,TC7dHCtdAFN~ ~
t $MT, SO-11 ~ TosIYDa -87 VR1 - Ndlnstalled t BB VR1 - Nd Instaned 89 VR3 LM4JtBIM3 IC AOJ PRECISION ZENERLM4J1BIMJ Nalanal 1 REG $0T23 90 WR1 - Ndlnstalled 9t WR2 Notlnslalled t 92 WRI . Nil Installed 93 WR5 0 Rea,CarDOn.S%.SMT.0805ERJ-6GEYDRDOVPanasonK
t 9d WRfi - Nd Inalallpd t 95 WR7 - Nd Installed t 96 WR8 Ndlnalallad 97 WR9 . ....~ynslalled~ .
a -98 WRtO 0 Rea,Carpon,5%.SMT-OB05ERJ-6GEYOROOVPanasonK
t ~

99 WRt1 - Ndlnstalled -t t00 WRt2 0 Res,CarDOn.S%,SMT.pg05ERJ-6GEYORDOVPanasonrc t t01 WR73 - Nd Installed . -t 102 WRt4 0 Res. Carbon, 5%, SMT-Ofi05ERJ-6GEVOROOVPanasonic t t03 WRtS - NdIrvWled t 10a WR16 - Ndlnstafied t t05 WRt7 0 Ras. Dar00n.5%, $MT-OB05ERJ-6GEYOROOVPanasonic t t06 WR20 - Ndlnsislled -t t07 W2 JMPtX2 Jumper.la2.tnla4.GddCoM..017Qn$t0lt-0Z $uR~ns t g.

t08 %Ut9 Socket. Socket, PLCC-68 510-99-068-17-400000M~IIMaa t PLCC-6B

tOS x2 ~k~ xtaLTnc.Ortz.2oovm,t2.5oFECS3x6 ECS
t .. .. -tt0 _ x3 3.6861MNZ xtal. Sml _Orlz, 50ppm.20pFECS-3620-SPECS
t Appendix C
Item tiy RetarencaValw - eon Guan -- _ -..

P arl No.

t 4 Ct,C9,C13,C17O.tuF Cap,Cer,X7R,ShlT-0805 25v t0%

, ECJ.2VBtE104Kpanasonit .

2 7 C2. CJ, Ca, C~
CS. C6, C7, C30 Not used. _ Ct6 , 100uF Cap, Elytc, 105c, 4 S C11 25v, 1000hr, SMT, EEV-HAIEtOIP
Ct2 F-Case Cta Panasonic - , O.OtuF Cap. Cer, X7R, SMT-0805.ECUV1Ht03KBGP
2 , 50v. 10%
.

anasonic 8 - , 0luF Cap, Cer, X7R, SMT-OB05.25v,ECJ
2 C19 t0%
C

-2VBtEtOaKPanasonic 7 , 1nF Cap, Cer, X7R

16v 10%

, ECJ-3YBtC105KPanasonic 2 D2. 01 52D , .
0i d S
R

8 5 01, 02 - o S2D r OJ e, Mi D3 m1, 07 ecLGP,200V12A, SMT, DO-2taAA

c 9 1 , Do Nol Install - osemi .
, J.tSAm . -10t H5 D Fuse,5x20,SR216.F8,t25V

217-J15 Lrttetluse 1 - po Not Install 1t2 JPt.JP2 Jh1P1X2' J~~r 1z2 Tlnlad GoldCont 270inl , S1011-02 Sunirts t2- - , .
.
..
g.

t31 Jt HOR 2 x Bos Connector Strip. -1at J2 13 Horizontal Enly 2x13 Pos t5t hITSW-t , BCS-113-L-D-HESamlec 10-14G-D-470-RA., Header, 2210. R1 Angie, O.a70m. Post Height , MTSW-t JJ MTSW-103-09-G-S-350-RA. t0-10-G-D-a70-RA
. Samtec Header 1x3 Rt An le 350~n p t H

, MTS'N-103-C9-G-S-350-RA Samlec t6- , g , .
.
os eghL 0730OAL.

t71 PC81 30021.0050PCB. Gen 2 Relay-Charger Rev C

, 30021-0050XPT
1B3 O4, O5, 08 MMBT390d .

h1h18T390aMotorola t9t p6 MJD32C Trans PNP 100v 3A
O-Pak SMT

MJD32C ST Mncroelectromc5 20t Rt 20M Res. Carbon 5%

, ERJ-6GEYJ206VPanasonic , 21a R2, RiB, 100k Res. Carbon Ra3, Ra4 5%

, ERJ-6GEYJ10cyPanasons 22t9R3. Ra, R5, Ra , R6, R7, N
R8. R9, RiS.

o1 used.
R16, R20, - -R21, R22, R2J, R2a, R25, R26, R27, R28.

23t R70 0 Res, Carpon 5%

, ERJ-6GEYJORDOVPanasonic 2a4 Rt t, RJS, 100k .
R37, Ra2 Res. Carbon 5%

. ERJ-6GEY104VPanasonic 252 Rt3. R12 0 .
Res. Carbon 5%

, ERJ-6GEYJOROOVPanasonic 26t Rta iR8 , Res, Carbon 5%

, ERJ-6ROJ1R8VPanasonic 27 Rt7 22k , Res. Carbon 5%

, ERJ-6GEY22JVPanasonic 28t Rt9 39k , Res, Carbon 5%

, ERJ-6GEYJ393VPanasonic 291 R29 470 , Res, Carpon 5%

, ERJ-0GEYJa7IVPanasonic 30t RJO l9.ik , Res, MF
1%

, ERJ-6ENF1g12PanaSOnic 31a R3t, RJ2. 390 , R73, R34 R
C

J21 R38 69 e5, ERJ-0GEY39tVP
8 arbon, 5%. SMT-0805 anasonic 331 R39 . Res. MF, t%, SMT-OB05ERJ
k 6ENF
-- Panasonic 220 Res, CarDOn 6982 5%

, ERJ-6GEY22tVPanasonic 3at Re0 390k .
Res. Carbon 5%

, ERJ-0GEY39aVPanasonic 351 Ra1 43.2k , - Res MF
t%
SMT-OBOS

. ERJ-6ENFd322Panasonic 362 Ra6, Rd5 1k , .
Res. Carbon 5%
1IBW Leaded , 1 OkEBK-ND
372 Ra7, RaB 6 8k .
Res. Carbon 5 %

, ERJ-6GEY682VPanasonic 38t R50 Rs - , Nol used .
J91 SCHt 30020-0050Schematic, Gen 2 X
a0 Rev. C Relay-Char er Boara g 3002p-0050PT
a12 St. 52 FR22a9 Alag. Swilcn. 15-25 AITLlOa 200v O
SA
SMT

. FR22a9 CP Clare t S3 .
x , .
Do Not Install a21 TB1 31018103 3Pos.PWgonTertnmalBkxkHeader 434 TP t, TPJ. -TPa, TPS

a41 TP2 - -a5t Ut Uz po Notlnsia8 - -a61 U2 UC2906N IC. SealeO Lead Acid d7 Battery Charger UC2906N Unilrode 1 UJ LM2902M Quad OyArt,p Lo Power S(?14 . LM2902M National a8a Ua, U5, U6. LBAt t0 .
c9 U7 IC, ODto-MOS Relay, 120mA
35 ohm l , LBAt t0 CP Clare 2 U8, U9 PS2705-t , - , con aa IC. AC W Optcoupler, 3750 V(isol) Snr 1e Tran SOP
a , PS2705-t NEC
50t NJR1 0 g s , -R
C
b 5t1 lvR2 0 ar ERJ-6GEYJOROOVPa es, on, 5%. SMT-0805 nasonic 5Zt WR3 Res. Carbon, S%. ERJ-6GEYJOROOV

Panasonic t Oo Nd Install -3 R4 ' 542 XF1 po Nd Instaa . 111501 FuseNdr,2AG5a20.
55f XF3 111501 860620 Lrtlelfuse F;nern,2AG15x20 .
5fit xTBt 71079102 2POS.plug-InTermmalBbck31 . 211

Claims (10)

1. An interface board for connecting an electrical power usage meter to a pager board capable of transmitting data to a remote host computer, the interface board comprising:
an input port for connection to the meter for periodically reading load profile data from the meter; and an output port for connection to the pager board for outputting load profile data to the pager board, whereby the load profile data may be transmitted via the pager board and a pager system to a remote host computer.
2. An interface board for connecting an electrical power usage meter to a pager board capable of transmitting data to a remote host computer, the interface board comprising:
an input port for connection to the meter for periodically reading load profile data from the meter;
a data processor for compressing the load profile data read from the meter;
and an output port for connection to the pager board for outputting compressed load profile data to the pager board, whereby the compressed load profile data may be transmitted via the pager board and a pager system to a remote host computer.
3. The interface board of claim 2, wherein:
the load profile data is read serially from the meter; and the load profile data is compressed by a differential compression process in which each load profile datum is represented in the compressed load profile data by the difference between it and the preceding load profile datum if that difference can be represented by a selected number of bits that is less than the number of bits needed to represent the largest possible load profile datum and is represented in the compressed load profile data uncompressed if that difference cannot be so represented.
4. The interface board of claim 3, wherein a bit pattern that would otherwise be a possible representation of a difference is excluded from use as a representation of a difference and used to indicate the end of a sequence of differences.
5. A computer method for compressing load profile data for transmission from an electrical power usage meter via a pager system to a remote host computer, the method comprising:
reading the load profile data serially from the meter; and compressing the load profile data by a differential compression process in which each load profile datum is represented in the compressed load profile data by the difference between it and the preceding load profile datum if that difference can be represented by a selected number of bits that is less than the number of bits needed to represent the largest possible load profile datum and is represented in the compressed load profile data uncompressed if that difference cannot be so represented.
6. The method of claim 5, wherein a bit pattern that would otherwise be a possible representation of a difference is excluded from use as a representation of a difference and used to indicate the end of a sequence of differences.
7. An interface board for installation within the housing of an electrical power usage meter, the interface board comprising:
an input port for connection to the meter for reading data from the meter indicative of the quality of the power passing through the meter;
a data processor for processing the data read from the meter to determine whether a power quality event has occurred; and an output port for transmitting notification that a power quality event has occurred to a remote location.
8. The interface board of claim 7, wherein the data read from the meter includes average voltages supplied to the meter.
9. The interface board of claim 8, wherein the data read from the meter includes samples of the voltage supplied to the meter.
10. A method for monitoring quality power passing through an electrical power usage meter comprising:
reading data from the meter indicative of the quality of the power passing through the meter;
processing the data read from the meter to determine whether a power quality event has occurred; and transmitting notification that a power quality event has occurred to a remote location.
CA 2332297 2001-01-25 2001-01-25 Wireless electronic energy meter Abandoned CA2332297A1 (en)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CA 2332297 CA2332297A1 (en) 2001-01-25 2001-01-25 Wireless electronic energy meter

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CA 2332297 CA2332297A1 (en) 2001-01-25 2001-01-25 Wireless electronic energy meter

Publications (1)

Publication Number Publication Date
CA2332297A1 true CA2332297A1 (en) 2002-07-25

Family

ID=4168176

Family Applications (1)

Application Number Title Priority Date Filing Date
CA 2332297 Abandoned CA2332297A1 (en) 2001-01-25 2001-01-25 Wireless electronic energy meter

Country Status (1)

Country Link
CA (1) CA2332297A1 (en)

Cited By (2)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
WO2012021372A1 (en) * 2010-08-10 2012-02-16 Sensus Usa Inc. Electric utility meter comprising load identifying data processor
WO2018233865A1 (en) * 2017-06-23 2018-12-27 Diehl Metering S.A.S. Method and system for collecting sensor data

Cited By (6)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
WO2012021372A1 (en) * 2010-08-10 2012-02-16 Sensus Usa Inc. Electric utility meter comprising load identifying data processor
WO2018233865A1 (en) * 2017-06-23 2018-12-27 Diehl Metering S.A.S. Method and system for collecting sensor data
FR3068163A1 (en) * 2017-06-23 2018-12-28 Diehl Metering Sas METHOD AND SYSTEM FOR COLLECTING DATA PROVIDED BY SENSORS
CN110786021A (en) * 2017-06-23 2020-02-11 代傲表计简易股份公司 Method and system for collecting sensor data
US11399223B2 (en) 2017-06-23 2022-07-26 Diehl Metering S.A.S. Method and system for collecting data supplied by sensors
CN110786021B (en) * 2017-06-23 2022-08-12 代傲表计简易股份公司 Method and system for collecting sensor data

Similar Documents

Publication Publication Date Title
US20020145537A1 (en) Systems and methods for wirelessly transmitting data from a utility meter
CN101435831B (en) Wireless data and image double-collecting anti-electric theft monitoring system and anti-electric theft monitoring method
AU2005239409B2 (en) System and method for efficient configuration in a fixed network automated meter reading system
US7106044B1 (en) Systems, methods, and apparatuses for detecting residential electricity theft in firmware
CN201293803Y (en) Anti-electric larceny monitoring system for collection of wireless data and image
US20070211768A1 (en) Versatile radio packeting for automatic meter reading systems
CN101888408A (en) Wireless sensor network-based environment monitoring system
CN104200620A (en) Building healthy remote monitoring system and building healthy remote monitoring method
US10771360B1 (en) Endpoint data collection in battery and data constrained environments
US20020030604A1 (en) Telemetry system and method
Do et al. An early flood detection system using mobile networks
CN201725417U (en) Environment monitoring system based on wireless sensor network
CN108154663A (en) A kind of safety detection early warning system
CN201967003U (en) Alarming monitoring system
CN101645611B (en) Self-adaptive re-sampling method for digital relay protection device
CN101674466B (en) Multi-information fusion intelligent video monitoring fore-end system
CN201259514Y (en) Wireless image transmission anti-electricity-theft system based on mobile communication network
CN202748831U (en) Logistics bump monitoring forensics system
CN110166972B (en) Intelligent sensing system with block chain module
CA2332297A1 (en) Wireless electronic energy meter
CN101295178A (en) Intelligent remote wireless management control system
CN102158686A (en) Novel security management system for Internet of things residential district
CN2738241Y (en) Radio air quality monitor
CN201607806U (en) Distributed comprehensive earthquake situation and secondary disaster situation collecting system on site of earthquake
CN111092861A (en) Communication network safety prediction system

Legal Events

Date Code Title Description
FZDE Dead