# Receiver code for Cawthron Institute's mussel farm accelerometer chain
# Author: Kevin Tang
from machine import I2C, Pin, SPI, UART, WDT
from lis3dh import LIS3DH, LIS3DH_I2C, RANGE_2_G, DATARATE_10_HZ, STANDARD_GRAVITY
from pyb import ADC, RTC
from ds3231_port import DS3231
import time
import os
import sys
import math

if sys.platform == 'pyboard':
  # Initialising pins
  thermistor = pyb.ADC('Y12')
  tx_enable = Pin('X20', Pin.OUT)
  
  # Initialising I2C
  i2c = I2C(1)
  rtc_i2c = I2C(2)
  ds3231 = DS3231(rtc_i2c)
  
  # Initialising UART
  uart = UART(4, 115200) # Pins X1 and X2
  uart.init(115200, bits=8, parity=None, stop=1, timeout=0) # Non-blocking UART
else:
  print('Incompatible board detected, please connect pyboard')

ID = 0 # Receiver ID (0-9). MAKE SURE TO CHANGE
accelerometer = LIS3DH_I2C(i2c, int1=None)

# Date initialisation
rtc = RTC()
timeCheck = ds3231.get_time() # Gets current time

rtc.datetime((timeCheck[0], timeCheck[1], timeCheck[2], timeCheck[6], timeCheck[3], timeCheck[4], timeCheck[5], 0)) # Syncs RTC clock to local time

"""
# Sets up RTC clock, uncomment to manually set the clock (NOTE: DAY OF WEEK AND TIME ZONE IS NOT WORKING)

rtc.datetime((2020, 1, 20, 1, 10, 3, 40, 0))  # Comment out if already programmed
ds3231.save_time()

# RTC Format: YY, MM, DD, Day of week (Mon = 1), hh, mm, ss, time zone
# Example: 16/12/2019 @ Monday 16:13:00
# = rtc.datetime((2019, 12, 16, 1, 16, 13, 0, 0))
"""

# Function to create and write headers for the accelerometer csv
def writeHeader():
  print('writing headers')
  log_header = open("/sd/accelerometer.csv", "a")
  log_header.write("ID, Date, Time, X Acceleration, Y Acceleration, Z Acceleration, Temperature \n")
  log_header.close()

# Retrieves date
def getDate():
  return str(rtc.datetime()[2]) + '/' + str(rtc.datetime()[1]) + '/' + str(rtc.datetime()[0])

# Retrieves time
def getTime():
  return str(rtc.datetime()[4]) + ':' + str(rtc.datetime()[5]) + ':' + str(rtc.datetime()[6])

LIS3DH.range = RANGE_2_G # Setting range to 2G
LIS3DH.datarate = DATARATE_10_HZ # Setting data rate to 10 Hz

# Buffer values for acceleration values
xBuffer = 0
yBuffer = 0
zBuffer = 0

dataBuffer = str()

# Enable Watchdog timer. UNCOMMENT WHEN IN USE. COMMENT IF CHANGES ARE NEEDED IN IDE
#wdt = WDT(timeout=30000) # enable with a timeout of 10 seconds

# Low power millisecond delay
""" rtc.wakeup and pyb.stop are untested. May remove """
def msTimer(ms):
  if pyb.Pin.board.USB_VBUS.value() == 1: #pyb.stop() will kill USB connection to pyboard
    time.sleep((ms/1000))
  else:
    rtc.wakeup(ms) # Allows the Pyboard to wake up every second

    pyb.stop() # Pyboard will enter low power mode to conserve battery life
    rtc.wakeup(None)
    
# Low power minute delay
def minTimer(minute):
  if pyb.Pin.board.USB_VBUS.value() == 1: #pyb.stop() will kill USB connection to pyboard
    time.sleep((minute*60))
  else:
    rtc.wakeup((minute*60*1000)) # Allows the Pyboard to wake up every second
    pyb.stop() # Pyboard will enter low power mode to conserve battery life
    rtc.wakeup(None)
    
# Checks to see if the csv file is empty, then writes the file if it is empty
try:
  with open("/sd/accelerometer.csv") as fileEmptyTest:
    emptyTest = fileEmptyTest.read(1)
    if not emptyTest:
      writeHeader()
    else:
      print('csv file is not empty')
except OSError: # File does not exist, make the files and headers
  writeHeader()
  
while True:
  #wdt.feed() # Turns off watchdog timer
  tx_enable.value(0)
  read = uart.read()
  idCheck = str(read)[2]
  #print(idCheck)
  
  print(getDate()) # Date
  print(getTime()) # Time
  
  # Print X,Y,Z acceleration values in g forces
  x, y, z = [value for value in accelerometer.acceleration]
  
  xAccel = round(x, 2)
  yAccel = round(y, 2)
  zAccel = round(z, 2)
  
  x = x / STANDARD_GRAVITY
  y = y / STANDARD_GRAVITY
  z = z / STANDARD_GRAVITY
  
  xRounded = round(x, 2)
  xRounded = round(x, 2)
  yRounded = round(y, 2)
  zRounded = round(z, 2)
  #print(xRounded, yRounded, zRounded)
  
  # Thermistor readings
  thermistorTest = thermistor.read() / 4095
  thermistorTest *= 100
  thermistorRounded = round(thermistorTest, 2)
  #print(thermistorRounded)
  
  # Write data to csv file
  log_header = open("/sd/accelerometer.csv", "a")
  log_header.write(str(ID) + ", " + getDate() + ", " + getTime() + ", " + str(xRounded) + ", " + str(yRounded) + ", " + \
  str(zRounded) + ", " + str(thermistorRounded) + " \n")
  log_header.close()
  
  dataString = str(ID) + ',' + getDate() + ',' + getTime() + ',' + str(xRounded) + ',' + str(yRounded) + ',' + str(zRounded) + ',' + str(thermistorRounded) + "..."
  
  #dataBuffer.append(dataString)
  dataBuffer += dataString
  print(len(dataBuffer))
  
  if(idCheck == str(ID)):
    tx_enable.value(1)
    print(uart.write(str(dataBuffer)))
    pyb.LED(2).on() # Debug LED
    tx_enable.value(0)
    dataBuffer = str() # Clear buffer once it sends the data values
    idCheck = str(99) # Arbitary reset number
    
  """
  if(idCheck == str(ID)):
    print('transmit')
    tx_enable.value(1)
    print(uart.write(str(ID) + ',' + str(xRounded) + ',' + str(yRounded) + ',' + str(zRounded) + ',' + str(thermistorRounded)))
    tx_enable.value(0)
    log_header = open("/sd/accelerometer.csv", "a")
    log_header.write(str(ID) + ", " + getDate() + ", " + getTime() + ", " + str(xRounded) + ", " + str(yRounded) + ", " + \
    str(zRounded) + ", " + str(thermistorRounded) + " \n")
    log_header.close()
    idCheck = str(99) # Arbitary reset number
    
    # Convert acceleration to m/s^2 and sleep for a set period amount of time
    try:
      if((abs(abs(xAccel - xBuffer) / xBuffer)) < 0.1) and ((abs(abs(yAccel - yBuffer) / yBuffer)) < 0.1) \
      and ((abs(abs(zAccel - zBuffer) / zBuffer)) < 0.1):
        print('Going to sleep')
        pyb.LED(1).on()
        for x in range((15*60000)/5000): # Kicks the watchdog every 5 seconds up to 15 minutes
          msTimer(5000)
          wdt.feed()
        pyb.LED(1).off()
        xBuffer = 0
        yBuffer = 0
        zBuffer = 0
    except ZeroDivisionError:
      continue
  """
    
  print()
  pyb.LED(2).off()
  
  xBuffer = xAccel
  yBuffer = yAccel
  zBuffer = zAccel
  
  msTimer(1000) 