[Tutor] newbie bragging about code

Eur van Andel eur at fiwihex.nl
Wed Nov 12 19:05:39 EST 2003


Hi

I sent the prog hereunder to my father with comments. Maybe these comments will
help others or draw better comments.


The program sends data to and from a string of PIC microcontrollers that
measure 4 temperatures of a water to air heat exchanger. These messages leave
the PC as hex strings like ;0123FAFAFAFACC where ; is the delimiter, 01 the
address, 23 the command, FA the four data bytes and CC the checksum. They get
cnverted by a PIC converter board to pulse duration code msgs on a two signal
wire bus with hardware node costs of $2 (two RJ 6/6 amp connectors and 74HC14).
The bus carries power too. I need to connect several 1000's of these later. 

The PIC controllers (16F819) have a bootloader that allows reprogramming of the
application program in flash memory.  The program hereunder deals just with
receiving and sending temperatures. 


Output first:

>/home/eur # cd xwisp/
>/home/eur/xwisp # python almeria4.py
>Run
all PICs run (from bootloader to app)

>1 0x4E4D4E4E 2 0x4B464A49 3 0x4C4D4F4E 4 0x4C4A4B4C
>5 0x53534F53 6 0x4B4D544F 7 0x53515152 8 0x4F4E4E54
raw-output, nice debugging. Only data in recvd msgs shown

>
>1 15.6 15.4 15.6 15.6 2 15.0 14.0 14.8 14.6 3 15.2 15.4 15.8 15.6
>4 15.2 14.8 15.0 15.2 5 16.6 16.6 15.8 16.6 6 15.0 15.4 16.8 15.8
>7 16.6 16.2 16.2 16.4 8 15.8 15.6 15.6 16.8 15.6 15.4 15.7 15.7
8 PICs with board number and 4 temps each. last four are average temps that get
send to first PIC (which has no sensors, it controls the water flow)

>
>
>1 0x4E4D4E4E 2 0x4B464A49 3 0x4C4D4F4E 4 0x4C4A4B4C
>5 0x52535053 6 0x4B4D544F 7 0x53515152 8 0x4F4E4E54
>
>1 15.6 15.4 15.6 15.6 2 15.0 14.0 14.8 14.6 3 15.2 15.4 15.8 15.6
>4 15.2 14.8 15.0 15.2 5 16.4 16.6 16.0 16.6 6 15.0 15.4 16.8 15.8
>7 16.6 16.2 16.2 16.4 8 15.8 15.6 15.6 16.8 15.6 15.4 15.8 15.7
Quite cold in here. Netherlands in winter, Pythoneer to cheap to turn on
heating after office hours.
>
>
>1 0x4D4D4E4E 2 0x4B464A4A 3 0x4C4D4F4E 4 0x4C4A4B4C
>5 0x52534E53 6 0x4C4D544F 7 0x53515151 8 0x4E4D4D54
>
>1 15.4 15.4 15.6 15.6 2 15.0 14.0 14.8 14.8 3 15.2 15.4 15.8 15.6
>4 15.2 14.8 15.0 15.2 5 16.4 16.6 15.6 16.6 6 15.2 15.4 16.8 15.8
>7 16.6 16.2 16.2 16.2 8 15.6 15.4 15.4 16.8 15.6 15.4 15.7 15.7
>


Code follows, inline bragging after that:



####################################################################
#
#	program almeria3.py
#	makes logfile 
#	polls boards for temperatures
#	writes temperatures to screen
#	records temperatures in logfile
#	makes new logfile every midnight
#     can output raw packets
#
#	to do: config file, separate data routine for msgs 
#	fault-tolerant: no output (or the lack of output)
#	may NOT crash the program
#	re-boot boards if necessary
#     send average temps to pump board
#
###################################################################

from time import strftime, localtime, sleep
import random, fpformat
import sys
from xwisp_fwx3 import fwx
import string

def make_logfile():
   logfile = open(strftime("%d-%m-%Y_%H-%M.log", localtime()),'w')
   #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
   logfile.write('temperatures and pump duty cycles\n')
   logfile.write('from six fans and pump controller\n')
   return(logfile)

def make_rawfile():
   rawfile = open(strftime("%d-%m-%Y_%H-%M.raw", localtime()),'w')
   #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
   logfile.write('raw msgs from fwx boards\n')
   return(rawfile)

def data_send_X(B3,B2,B1, B0):        # makes integer that is a 8-char in hex
    data = '%02X' % B3 + '%02X' % B2 + '%02X' % B1 + '%02X' % B0
    data = long(data, 16)
    #print '%08X' % data 
    return data
    
    
def send_temps_to_pumpcontr(Tai, Tao, Twl, Twh):
   Response = f.Send_Receive(75, data_send_X(Tai*5, Tao*5, Twl*5, Twh*5), 1) #
pump_controller has address 1
   #print Response 

############################ START OF PROGRAM #######################


number_of_boards = 8		# should be in config file
raw_output = True

T = [ [None] * 5 for i in range(1,number_of_boards+2) ] # array of
[0..num][0..4]

f = fwx( '/dev/cuaa0' )
Errors = 0
f.CMD_RUN()

date = localtime()[2]

logfile = make_logfile()
if raw_output:
   rawfile = make_rawfile()


# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ START OF MAIN LOOP $$$$$$$$$$$$$$

while True:
   minutes, seconds =  localtime()[4], localtime()[5]

   if date != localtime()[2]:	# a new day has dawned, open a new logfile
      logfile = make_logfile()
      date = localtime()[2]

   if minutes % 1 == 0:
      if seconds % 10 == 0:
         if raw_output:
  	      rawfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
		
         for n in range(1, number_of_boards + 1):
            Response = f.Send_Receive(33, Address = n)
            if Response == None:
               if raw_output:
                  print '%1d None      ' % n,
                  rawfile.write('%1d None ')
                  if n % 4 == 0:   # after 4 raw packets
                     print '\n',
               Response = 825307441L
            else:
               if raw_output:
                  print '%1d 0x%08X' % (n,Response),
                  rawfile.write('%1d 0x%08X ' % (n, Response))
                  if n % 4 == 0:   # after 4 raw packets
                     print '\n',
          
            D = '%08X' % Response
            T[n][1] = int(D[0:2],16)/5.0	# T air in
            T[n][2] = int(D[2:4],16)/5.0	# T air out
            T[n][3] = int(D[4:6],16)/5.0	# T water high 
            T[n][4] = int(D[6:8],16)/5.0	# T water low
            # end of looping over boards
	    
         if raw_output:
            print '\n',
            rawfile.write('\n')

         logfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
         Tai = Tao = Twl = Twh = 0 
         print Tai, Tao, Twl, Twh
         for n in range(1, number_of_boards+1):
            print '%1d' % n,
            logfile.write('%1d' % n)
            if n > 1 and n < number_of_boards:  # 1 = pump, last= control group
               Tai = Tai + T[n][1]
               Tao = Tao + T[n][2]
               Twl = Twl + T[n][3]
               Twh = Twh + T[n][4]
            
            for i in range(1,5):
               print '%4.1f' % T[n][i],
	       logfile.write('%4.1f' % T[n][i])
            if n % 3 == 0:  # after three temperature quads
                print '\n',

         Tai = Tai / (number_of_boards - 2) # 1 pump board, 1 control group
board
         Tao = Tao / (number_of_boards - 2) 
         Twl = Twl / (number_of_boards - 2) 
         Twh = Twh / (number_of_boards - 2) 

         print '%4.1f %4.1f %4.1f %4.1f' % (Tai, Tao, Twl, Twh)

         print '\n'
         
         logfile.write('\n')
         logfile.flush()
      
         send_temps_to_pumpcontr(Tai, Tao, Twl, Twh)
      
         sleep(2)
#   if seconds < 55:
#      sleep(55 - seconds)
  






>####################################################################
>#
>#	program almeria3.py
>#	makes logfile 
>#	polls boards for temperatures
>#	writes temperatures to screen
>#	records temperatures in logfile
>#	makes new logfile every midnight
>#     can output raw packets
>#
>#	to do: config file, separate data routine for msgs 
needs work

>#	fault-tolerant: no output (or the lack of output)
>#	may NOT crash the program
works. 

>#	re-boot boards if necessary
needs work

>#     send average temps to pump board
works

>#
>###################################################################
>
>from time import strftime, localtime, sleep
>import random, fpformat
>import sys
several libs, but I'll have to check if still needed

>from xwisp_fwx3 import fwx
wouter's msg routines

>import string
>
>def make_logfile():
def: define function
(): no arguments

>   logfile = open(strftime("%d-%m-%Y_%H-%M.log", localtime()),'w')
12-11-2003_23-59.log, open for writing, the 'w'


>   #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
>   logfile.write('temperatures and pump duty cycles\n')
>   logfile.write('from six fans and pump controller\n')
>   return(logfile)
so function returns logfile object. Call function with

logfile = make_logfile()

after that, you can do things like:

logfile.close()
logfile.flush()


>
>def make_rawfile():
>   rawfile = open(strftime("%d-%m-%Y_%H-%M.raw", localtime()),'w')
>   #logfile = open(strftime("%d_%H%M.log", localtime()),'w') # MSDOS
12_23-59.log, 8-char MSDOS compatible file name. This code snippet was
developed under DOS.

>   logfile.write('raw msgs from fwx boards\n')
>   return(rawfile)
>
>def data_send_X(B3,B2,B1, B0):        # makes integer that is a 8-char in hex
>    data = '%02X' % B3 + '%02X' % B2 + '%02X' % B1 + '%02X' % B0

'02X' % 255 means: write 255 as hexadecimal (X) string ('..'),  two chars wide
(2) with leading zeroes (0). Yes, Python is powerful. I took 3 days to
understand this.

>    data = long(data, 16)
>    #print '%08X' % data 
debugging :-)

>    return data
as 8-char hex string from 4 integers. If integers are 199498535 or other
useless number >255, hex string will always be 8 char wide. Very robust.

>    
>    
>def send_temps_to_pumpcontr(Tai, Tao, Twl, Twh):
>   Response = f.Send_Receive(75, data_send_X(Tai*5, Tao*5, Twl*5, Twh*5), 1) # pump_controller has address 1
>   #print Response 
remember: internal byte representation of temps in PIC is degree celsius * 5

75 is bad: needs to be replaced by cmd_set_av_temps constant. 

>
>############################ START OF PROGRAM #######################
>
>
>number_of_boards = 8		# should be in config file
indeed.

>raw_output = True
idem, in config file

>
>T = [ [None] * 5 for i in range(1,number_of_boards+2) ] # array of [0..num][0..4]
clumsy array of stupid newbie that wants arrays like T[1..n][1..4] instead of
Python way T[0..n-1][0..3]. Array should be replaced with nice set of fourfold
tuples. 

>
>f = fwx( '/dev/cuaa0' )
FreeBSD serial port 1

>Errors = 0
>f.CMD_RUN()
run boards
>
>date = localtime()[2]
>
>logfile = make_logfile()
>if raw_output:
>   rawfile = make_rawfile()
>
>
># $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ START OF MAIN LOOP $$$$$$$$$$$$$$
>
>while True:
>   minutes, seconds =  localtime()[4], localtime()[5]
powerful Python tuple (data set) value setting.

a,b,c,d = 1,2,3,4 

is normal Python. Typically T = [a,b,c,d] should be used

>
>   if date != localtime()[2]:	# a new day has dawned, open a new logfile
>      logfile = make_logfile()
>      date = localtime()[2]
>
>   if minutes % 1 == 0:
>      if seconds % 10 == 0:
for fast response

Note that Python has no begin...end, only indentation. One space off and the
compilers hurls abuse at you, rightly so. Even one tab and 8 spaces on
different lines are not accepted. ":" is "then"

>         if raw_output:
>  	      rawfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
Writing time to raw log, to match the raw packets later to the
temperatures.

>		
>         for n in range(1, number_of_boards + 1):
stupid newbie counting from 1 to n. Python way is 

for n in range(number_of_boards)

n will be 0...8

>            Response = f.Send_Receive(33, Address = n)
get 4 temperatures

>            if Response == None:
>               if raw_output:
>                  print '%1d None      ' % n,
>                  rawfile.write('%1d None ')
>                  if n % 4 == 0:   # after 4 raw packets
>                     print '\n',
>               Response = 825307441L
is 0x31313131 is 49 49 49 49 is 4 times 9.8*5 
which will thoroughly confuse pump_controller. Bad.

>            else:
>               if raw_output:
>                  print '%1d 0x%08X' % (n,Response),
print n as 1 digit integer, Response as 0xXXXXXXXX 8-digit hex number
trailing comma means no newline after print

print '\n' 

with no trailing comma means TWO newlines 

>                  rawfile.write('%1d 0x%08X ' % (n, Response))
note middle and trailing space in format string: chunk.write() will not spaced
numbers like print does

>                  if n % 4 == 0:   # after 4 raw packets
>                     print '\n',
to the screen, not to the file
>          
>            D = '%08X' % Response
D now 8 (hex) char long string

>            T[n][1] = int(D[0:2],16)/5.0	# T air in
T[n][1] is integer of first two chars of string, with base number 16, divided
by 5. 5.0 is written so Python will understand T[n][1] is a float

>            T[n][2] = int(D[2:4],16)/5.0	# T air out
>            T[n][3] = int(D[4:6],16)/5.0	# T water high 
>            T[n][4] = int(D[6:8],16)/5.0	# T water low
>            # end of looping over boards
>	    
>         if raw_output:
>            print '\n',
>            rawfile.write('\n')
Now a newline to the raw file

>
>         logfile.write(strftime('"%d-%m-%Y %H:%M:%S" ', localtime()))
>         Tai = Tao = Twl = Twh = 0 
resetting the values for averaging

>         print Tai, Tao, Twl, Twh
debugging

>         for n in range(1, number_of_boards+1):
clueless newbie loops again over boards, sigh

>            print '%1d' % n,
>            logfile.write('%1d' % n)

>            if n > 1 and n < number_of_boards:  # 1 = pump, last= control group
first board is pump_contr, last board is control_group, both should not be used
for calculating average

>               Tai = Tai + T[n][1]
>               Tao = Tao + T[n][2]
>               Twl = Twl + T[n][3]
>               Twh = Twh + T[n][4]
Real Pythoneers would combine these statements:

            T[n][1] = int(D[0:2],16)/5.0	# T air in
            Tai = Tai + T[n][1]

to 

            Tai = Tai + T[n][1] = int(D[0:2],16)/5.0	# T air in

in a single loop
>            
>            for i in range(1,5):
>               print '%4.1f' % T[n][i],
>	       logfile.write('%4.1f' % T[n][i])
no space, bad

>            if n % 3 == 0:  # after three temperature quads
>                print '\n',
print board number and temperatures in line of three
>
>         Tai = Tai / (number_of_boards - 2) # 1 pump board, 1 control group board
>         Tao = Tao / (number_of_boards - 2) 
>         Twl = Twl / (number_of_boards - 2) 
>         Twh = Twh / (number_of_boards - 2) 
averaging
>
>         print '%4.1f %4.1f %4.1f %4.1f' % (Tai, Tao, Twl, Twh)
debugging
>
>         print '\n'
>         
>         logfile.write('\n')
>         logfile.flush()
flush data stream to logfile so no data loss when power failure
>      
>         send_temps_to_pumpcontr(Tai, Tao, Twl, Twh)
ha! This took me a full day to get this single statement working!

>      
>         sleep(2)
so not 6 times in the same second. Yes, Python is that fast.

>#   if seconds < 55:
>#      sleep(55 - seconds)
To minimize system load?  


--
Ir. E.E. van Andel, Fine Wire Heat Exchangers, Fiwihex B.V. www.fiwihex.com
Wierdensestraat 74, NL-7604 BK  Almelo, The Netherlands   eur at fiwihex.nl
phone +31-546-491106  fax +31-546-491107  mobile +31-653-286573  



More information about the Tutor mailing list