Advertisement
Guest User

Untitled

a guest
Jan 9th, 2012
254
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.53 KB | None | 0 0
  1. sean@mbs ~/ted/v2 % cat ted.py
  2. #!/usr/bin/env python
  3. #
  4. # This is a Python module for The Energy Detective: A low-cost whole
  5. # house energy monitoring system. For more information on TED, see
  6. # http://theenergydetective.com
  7. #
  8. # This module was not created by Energy, Inc. nor is it supported by
  9. # them in any way. It was created using information from two sources:
  10. # David Satterfield's TED module for Misterhouse, and my own reverse
  11. # engineering from examining the serial traffic between TED Footprints
  12. # and my RDU.
  13. #
  14. # I have only tested this module with the model 1001 RDU, with
  15. # firmware version 9.01U. The USB port is uses the very common FTDI
  16. # USB-to-serial chip, so the RDU will show up as a serial device on
  17. # Windows, Mac OS, or Linux.
  18. #
  19. # The most recent version of this module can be obtained at:
  20. # http://svn.navi.cx/misc/trunk/python/ted.py
  21. #
  22. # Copyright (c) 2008 Micah Dowty <micah@navi.cx>
  23. #
  24. # Permission is hereby granted, free of charge, to any person obtaining a copy
  25. # of this software and associated documentation files (the "Software"), to deal
  26. # in the Software without restriction, including without limitation the rights
  27. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  28. # copies of the Software, and to permit persons to whom the Software is
  29. # furnished to do so, subject to the following conditions:
  30. #
  31. # The above copyright notice and this permission notice shall be included in
  32. # all copies or substantial portions of the Software.
  33. #
  34. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  35. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  36. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  37. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  38. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  39. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  40. # THE SOFTWARE.
  41. #
  42.  
  43. import serial
  44. import time
  45. import binascii
  46. import sys
  47. import struct
  48. import pycurl, json
  49.  
  50.  
  51. # Special bytes
  52.  
  53. PKT_REQUEST = "\xAA"
  54. ESCAPE = "\x10"
  55. PKT_BEGIN = "\x04"
  56. PKT_END = "\x03"
  57.  
  58. class ProtocolError(Exception):
  59. pass
  60.  
  61.  
  62. class TED(object):
  63. def __init__(self, device):
  64. self.port = serial.Serial(device, 19200, timeout=0)
  65. self.escape_flag = False
  66.  
  67. # None indicates that the packet buffer is invalid:
  68. # we are not receiving any valid packet at the moment.
  69. self.packet_buffer = None
  70.  
  71. def poll(self):
  72. """Request a packet from the RDU, and flush the operating
  73. system's receive buffer. Any complete packets we've
  74. received will be decoded. Returns a list of Packet
  75. instances.
  76.  
  77. Raises ProtocolError if we see anything from the RDU that
  78. we don't expect.
  79. """
  80.  
  81. # Request a packet. The RDU will ignore this request if no
  82. # data is available, and there doesn't seem to be any harm in
  83. # sending the request more frequently than once per second.
  84. self.port.write(PKT_REQUEST)
  85.  
  86. return self.decode(self.port.read(4096))
  87.  
  88. def decode(self, raw):
  89. """Decode some raw data from the RDU. Updates internal
  90. state, and returns a list of any valid Packet() instances
  91. we were able to extract from the raw data stream.
  92. """
  93.  
  94. packets = []
  95.  
  96. # The raw data from the RDU is framed and escaped. The byte
  97. # 0x10 is the escape byte: It takes on different meanings,
  98. # depending on the byte that follows it. These are the
  99. # escape sequence I know about:
  100. #
  101. # 10 10: Encodes a literal 0x10 byte.
  102. # 10 04: Beginning of packet
  103. # 10 03: End of packet
  104. #
  105. # This code illustrates the most straightforward way to
  106. # decode the packets. It's best in a low-level language like C
  107. # or Assembly. In Python we'd get better performance by using
  108. # string operations like split() or replace()- but that would
  109. # make this code much harder to understand.
  110.  
  111. for byte in raw:
  112. if self.escape_flag:
  113. self.escape_flag = False
  114. if byte == ESCAPE:
  115. if self.packet_buffer is not None:
  116. self.packet_buffer += ESCAPE
  117. elif byte == PKT_BEGIN:
  118. self.packet_buffer = ''
  119. elif byte == PKT_END:
  120. if self.packet_buffer is not None:
  121. packets.append(Packet(self.packet_buffer))
  122. self.packet_buffer = None
  123. else:
  124. raise ProtocolError("Unknown escape byte %r" % byte)
  125.  
  126. elif byte == ESCAPE:
  127. self.escape_flag = True
  128. elif self.packet_buffer is not None:
  129. self.packet_buffer += byte
  130.  
  131. return packets
  132.  
  133.  
  134. class Packet(object):
  135. """Decoder for TED packets. We use a lookup table to find individual
  136. fields in the packet, convert them using the 'struct' module,
  137. and scale them. The results are available in the 'fields'
  138. dictionary, or as attributes of this object.
  139. """
  140.  
  141. # We only support one packet length. Any other is a protocol error.
  142. _protocol_len = 276
  143.  
  144. _protocol_table = (
  145. # TODO: Fill in the rest of this table.
  146. #
  147. # It needs verification on my firmware version, but so far the
  148. # offsets in David Satterfield's code match mine. Since his
  149. # code does not handle packet framing, his offsets are 2 bytes
  150. # higher than mine. These offsets start counting at the
  151. # beginning of the packet body. Packet start and packet end
  152. # codes are omitted.
  153.  
  154. # Offset, name, fmt, scale
  155. (82, 'kw_rate', "<H", 0.0001),
  156. (108, 'house_code', "<B", 1),
  157. (247, 'kw', "<H", 0.01),
  158. (251, 'volts', "<H", 0.1),
  159. )
  160.  
  161. def __init__(self, data):
  162. self.data = data
  163. self.fields = {}
  164. if len(data) != self._protocol_len:
  165. raise ProtocolError("Unsupported packet length %r" % len(data))
  166.  
  167. for offset, name, fmt, scale in self._protocol_table:
  168. size = struct.calcsize(fmt)
  169. field = data[offset:offset+size]
  170. value = struct.unpack(fmt, field)[0] * scale
  171.  
  172. setattr(self, name, value)
  173. self.fields[name] = value
  174.  
  175. def on_receive(data):
  176. print "Got response!!"
  177.  
  178. def main():
  179. t = TED(sys.argv[1])
  180. c = pycurl.Curl()
  181. v = 0.0
  182. w = 0.0
  183.  
  184.  
  185. while True:
  186. for packet in t.poll():
  187. print
  188. # print "%d byte packet: %r" % (
  189. # len(packet.data), binascii.b2a_hex(packet.data))
  190. # print
  191.  
  192.  
  193.  
  194.  
  195. # c.setopt (c.URL, "={w:%.2f,vrms:%.2f,kwpeaktdy:%.2f,kwpeekmtd:%.2f,watttdysum:%.2f}");
  196. c.setopt (c.WRITEFUNCTION, on_receive);
  197.  
  198.  
  199. v = packet.fields['volts'];
  200. w = packet.fields['kw'] * 1000;
  201. s = '{v:%f,w:%f}' % (v, w)
  202.  
  203. u = "http://X.X.X.X/emoncms3/api/post?apikey=KEY&json="
  204.  
  205. u = u + s
  206.  
  207. # print u
  208.  
  209. c.setopt (c.URL, u)
  210. c.perform();
  211.  
  212.  
  213. # for name, value in packet.fields.items():
  214. # print "%s = %s" % (name, value)
  215.  
  216. time.sleep(1.0)
  217.  
  218. if __name__ == "__main__":
  219. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement