diff --git a/HISTORY.rst b/HISTORY.rst
index afef05b8d83140bf260016ea63b0286578172ea9..a94f3900674ff45112c0d5377c4db9759781243d 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -6,3 +6,7 @@ History
 ------------------
 
 * First release on new build system.
+
+2018.228 (2018-08-16)
+------------------
+* Updated to work with python 2 and 3
diff --git a/data2passcal/__init__.py b/data2passcal/__init__.py
index 7bd3e8bba5df65833d52a5b1f5833dfd7539ddee..1636f688fe53d37abbe1eb0fb8f667675ef3b664 100644
--- a/data2passcal/__init__.py
+++ b/data2passcal/__init__.py
@@ -4,4 +4,4 @@
 
 __author__ = """IRIS PASSCAL"""
 __email__ = 'software-support@passcal.nmt.edu'
-__version__ = '2018.171'
+__version__ = '2018.228'
diff --git a/data2passcal/data2passcal.py b/data2passcal/data2passcal.py
index b1faa622d6d079a1d08e845245b63fedad2137da..7632678c2c3cef869cf1a7490bd7f69fbbd63b97 100644
--- a/data2passcal/data2passcal.py
+++ b/data2passcal/data2passcal.py
@@ -5,10 +5,11 @@ data2passcal
 Ship miniseed data to passcal via ftp for qc before archival at DMC DMS
 Lloyd Carothers IRIS/PASSCAL
 '''
-VERSION = '2018.171'
+from __future__ import print_function
+VERSION = '2018.228'
 
 import ftplib
-import sys, os, signal, struct, cPickle, logging
+import sys, os, signal, struct, pickle, logging
 from time import time, sleep
 import datetime
 
@@ -84,7 +85,7 @@ logger.info('FTP RECONNECT WAIT: %d' % FTP_RECONNECT_WAIT)
 def scan_dir(dir):
     '''Returns a list of absolute file names found below root dir'''
     rootdir = dir
-    print
+    print()
     logger.info('Scanning: %s' % os.path.abspath(dir))
     filelist = []
     filesize = 0
@@ -104,7 +105,7 @@ def scan_dir(dir):
     logger.info('Total Files = %s'% len(filelist) )
     logger.info('Total Dirs = %s'% foldercount)
     logger.info('Scan time = %0.2fs'% (time() - starttime))
-    print
+    print()
 
     return filelist
 
@@ -183,29 +184,30 @@ def ByteOrder(infile, seekval=20) :
                0 <= Min <= 59 and \
                0 <= Sec <= 59:
                 Order = "little"
-    except Exception, e:
+    except Exception as e:
         pass
 
     return Order
 #########################################################
 
 def get_sent_file_list(sentfile = SENTFILE):
+    sentlist = []
     if os.path.isfile(sentfile):
         logger.debug('Using sentfile %s' % sentfile)
-        with open(sentfile) as f:
-            sentlist = cPickle.load(f)
+        if os.path.getsize(sentfile) > 0:
+            with open(sentfile, 'rb') as f:
+                sentlist = pickle.load(f)
     elif os.path.isfile(SENTFILE_OLD):
         logger.debug('Using old sentfile %s' % SENTFILE_OLD)
-        with open(SENTFILE_OLD) as f:
-            sentlist = cPickle.load(f)
-    else:
-        sentlist = []
+        if os.path.getsize(SENTFILE_OLD) > 0:
+            with open(SENTFILE_OLD, 'rb') as f:
+                sentlist = pickle.load(f)
     return sentlist
 
 def write_sent_file_list(sentlist , sentfile = SENTFILE ):
     logger.info('Saving list of files sent to passcal')
-    with open(sentfile, 'w+') as f:
-        cPickle.dump(sentlist, f)
+    with open(sentfile, 'wb+') as f:
+        pickle.dump(sentlist, f, protocol=2)
 
 
 def get_FTP():
@@ -254,11 +256,11 @@ def passcal_ftp_reachable():
 
 def url_reachable(url = 'http://www.passcal.nmt.edu/'):
     '''fetches a url returns True or False'''
-    import urllib2
+    import urllib.request, urllib.error, urllib.parse
     start = time()
     try:
-        f = urllib2.urlopen(url)
-    except Exception, e:
+        f = urllib.request.urlopen(url)
+    except Exception as e:
         logger.error("Failed to open connection to %s" % url)
         logger.error(e)
         return False
@@ -275,10 +277,10 @@ def test_FTP(FTP):
     try:
         assert isinstance(FTP, ftplib.FTP)
         FTP.voidcmd('NOOP')
-    except ftplib.all_errors, e :
+    except ftplib.all_errors as e :
         logger.error(e)
         return False
-    except AssertionError, e:
+    except AssertionError as e:
         logger.error(e)
         return False
     else:
@@ -290,7 +292,7 @@ def send2passcal(mslist, sentlist=None):
 
     # Handle SIGINT while in this function: to gracefully close connection and save sentlist to disk file
     def signal_handler(signum, frame):
-        print
+        print()
         logger.info('Caught interrupt while FTPing. Aborting transfer %s' % current_file )
         logger.info('Sent %d of %d' % (num_sent, num_to_send) )
         logger.info('Sent %s of %s' % (format_size(size_sent), format_size(size_to_send)) )
@@ -309,7 +311,7 @@ def send2passcal(mslist, sentlist=None):
         '''Updates the terminal display.'''
         signal.signal(signal.SIGINT, signal_handler)
         update.bytes_sent += len(data)
-        print '\r' + str(PB) + ' %s /sec ' % ( format_size(size_sent / (time() - starttime )) ),
+        print('\r' + str(PB) + ' %s /sec ' % ( format_size(size_sent / (time() - starttime )) ), end=' ')
         '''
         print '%s %0.2f%%. %0.10d / %0.10d.' % ( current_file.center(20), 
                                                 (update.bytes_sent / update.file_size)*100, 
@@ -318,12 +320,12 @@ def send2passcal(mslist, sentlist=None):
                                                 ) , 
         '''
         ETA_sec = ((time() - starttime) / size_sent ) * (size_to_send - size_sent)
-        print 'ETA %s %s %s' % (  str(datetime.timedelta(seconds=(ETA_sec))), current_file.center(20), ' '*20 ) ,
+        print('ETA %s %s %s' % (  str(datetime.timedelta(seconds=(ETA_sec))), current_file.center(20), ' '*20 ), end=' ')
         sys.stdout.flush()
 
     if sentlist is None:
         sentlist = []
-    print
+    print()
     logger.info('Sending MSEED files to PASSCAL')
     num_to_send = len(mslist)
     num_sent = 0
@@ -376,7 +378,7 @@ def send2passcal(mslist, sentlist=None):
                 sentlist.append(f)
                 break
 
-    print
+    print()
     logger.info( 'Sent %d of %d' % (num_sent, num_to_send) )
     logger.info( 'Sent %s of %s' % (format_size(size_sent), format_size(size_to_send)) )
     logger.info( '%s /sec' % ( format_size(size_sent / (time() - starttime )) ) )
@@ -397,12 +399,12 @@ class ProgressBar:
     def animate(self):
         for i in range(self.duration):
             if sys.platform.lower().startswith('win'):
-                print self, '\r',
+                print(self, '\r', end=' ')
             else:
-                print self, chr(27) + '[A'
+                print(self, chr(27) + '[A')
             self.update_time(i + 1)
             time.sleep(1)
-        print self
+        print(self)
 
     def update_time(self, elapsed_secs):
         self.__update_amount((elapsed_secs / float(self.duration)) * 100.0)
@@ -413,7 +415,7 @@ class ProgressBar:
         all_full = self.width - 2
         num_hashes = int(round((percent_done / 100.0) * all_full))
         self.prog_bar = '[' + self.fill_char * num_hashes + ' ' * (all_full - num_hashes) + ']'
-        pct_place = (len(self.prog_bar) / 2) - len(str(percent_done))
+        pct_place = int((len(self.prog_bar) / 2) - len(str(percent_done)))
         pct_string = '%d%%' % percent_done
         self.prog_bar = self.prog_bar[0:pct_place] + \
             (pct_string + self.prog_bar[pct_place + len(pct_string):])
@@ -425,21 +427,21 @@ class ProgressBar:
 ########################################################
 def main():
     if len(sys.argv) < 2 or sys.argv[1] in [ '-h', '--help', '-?']:
-        print HELP
+        print(HELP)
         sys.exit()
     # find all files below dir
     filelist = scan_dir(sys.argv[1])
     # filter for files named as the qc system likes
     logger.info('Removing improperly named files.')
-    msnamedlist = filter(filename_qc_format, filelist)
+    msnamedlist = list(filter(filename_qc_format, filelist))
     logger.info('Properly named files files: %d' % len(msnamedlist))
     logger.info('Other files: %d'% (len(filelist) - len(msnamedlist)))
-    print
+    print()
     logger.info('Removing files that are not Miniseed.')
-    mslist = filter(sendable, msnamedlist)
+    mslist = list(filter(sendable, msnamedlist))
     logger.info('MiniSEED files: %d' % len(mslist))
     logger.info('Properly named but not miniseed files: %d'% (len(msnamedlist) - len(mslist)))
-    print
+    print()
     logger.info('Removing files already sent to PASSCAL')
     sentlist = get_sent_file_list()
     unsentms = [f for f in mslist if f not in sentlist]
diff --git a/setup.cfg b/setup.cfg
index 86190ffcd4bd6c7fcec3e17846801616bd4a59c2..bd5e695214d2220c9df4bb5f53418783cc3037b2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 2018.171
+current_version = 2018.228
 commit = True
 tag = True
 
diff --git a/setup.py b/setup.py
index afcfad780540cb996c961f69c70fc9e2e638764c..3c7b133727cf7718141b7ce8e55399c30d670526 100644
--- a/setup.py
+++ b/setup.py
@@ -21,6 +21,7 @@ setup(
         'License :: OSI Approved ::  GNU General Public License v3 (GPLv3)',
         'Natural Language :: English',
         'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3.6',
     ],
     description="Prepare SEED data for shipment to PASSCAL.",
     entry_points={
@@ -51,6 +52,6 @@ setup(
     packages=find_packages(include=['data2passcal']),
     test_suite='tests',
     url='https://git.passcal.nmt.edu/passoft/data2passcal',
-    version='2018.171',
+    version='2018.228',
     zip_safe=False,
 )