--------------------------------------------------------------------------------
-- DS3231 I2C module for NODEMCU
-- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT
-- Tobie Booth <tbooth@hindbra.in>
--------------------------------------------------------------------------------

local moduleName = ...
local M = {}
_G[moduleName] = M

-- Constants:
M.EVERYSECOND = 6
M.EVERYMINUTE = 7
M.SECOND = 1
M.MINUTE = 2
M.HOUR = 3
M.DAY = 4
M.DATE = 5
M.DISABLE = 0

-- Default value for i2c communication
local id = 0

--device address
local dev_addr = 0x68

local function decToBcd(val)
  if val == nil then return 0 end
  return((((val/10) - ((val/10)%1)) *16) + (val%10))
end

local function bcdToDec(val)
  return((((val/16) - ((val/16)%1)) *10) + (val%16))
end

local function addAlarmBit(val,day)
  if day == 1 then return bit.bor(val,64) end
  return bit.bor(val,128)
end

-- initialize i2c
--parameters:
--d: sda
--l: scl
function M.init(d, l)
  if (d ~= nil) and (l ~= nil) and (d >= 0) and (d <= 11) and (l >= 0) and ( l <= 11) and (d ~= l) then
    sda = d
    scl = l
  else
    print("[ERROR] i2c config failed!") return nil
  end
  print("[LOG] DS3231 init done")
  i2c.setup(id, sda, scl, i2c.SLOW)
end

--get time from DS3231
function M.getTime()
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x00)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local c=i2c.read(id, 7)
  i2c.stop(id)
  return bcdToDec(tonumber(string.byte(c, 1))),
  bcdToDec(tonumber(string.byte(c, 2))),
  bcdToDec(tonumber(string.byte(c, 3))),
  bcdToDec(tonumber(string.byte(c, 4))),
  bcdToDec(tonumber(string.byte(c, 5))),
  bcdToDec(tonumber(string.byte(c, 6))),
  bcdToDec(tonumber(string.byte(c, 7)))
end

--set time for DS3231
-- enosc setted to 1 disables oscilation on battery, stopping time
function M.setTime(second, minute, hour, day, date, month, year, disOsc)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x00)
  i2c.write(id, decToBcd(second))
  i2c.write(id, decToBcd(minute))
  i2c.write(id, decToBcd(hour))
  i2c.write(id, decToBcd(day))
  i2c.write(id, decToBcd(date))
  i2c.write(id, decToBcd(month))
  i2c.write(id, decToBcd(year))
  i2c.stop(id)

  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local c = string.byte(i2c.read(id, 1), 1)
  i2c.stop(id)
  if disOsc == 1 then c = bit.bor(c,128)
  else c = bit.band(c,127) end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.write(id, c)
  i2c.stop(id)
end

-- Reset alarmId flag to let alarm to be triggered again
function M.reloadAlarms ()
  if bit == nil or bit.band == nil or bit.bor == nil then
    print("[ERROR] Module bit is required to use alarm function")
    return nil
  end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0F)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local d = string.byte(i2c.read(id, 1), 1)
  i2c.stop(id)
  -- Both flag needs to be 0 to let alarms trigger
  d = bit.band(d,252)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0F)
  i2c.write(id, d)
  i2c.stop(id)
  print('[LOG] Alarm '..almId..' reloaded')
end

-- Enable alarmId bit. Let it to be triggered
function M.enableAlarm (almId)
  if bit == nil or bit.band == nil or bit.bor == nil then
    print("[ERROR] Module bit is required to use alarm function")
    return nil
  end
  if almId ~= 1 and almId ~= 2 then print('[ERROR] Wrong alarm id (1 or 2): '..almId) return end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local c = string.byte(i2c.read(id, 1), 1)
  i2c.stop(id)
  c = bit.bor(c,4)
  if almId == 1 then c = bit.bor(c,1)
  else c = bit.bor(c,2) end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.write(id, c)
  i2c.stop(id)
  M.reloadAlarms()
  print('[LOG] Alarm '..almId..' enabled')
end
-- If almID equals 1 or 2 disable that alarm, otherwise disables both.
function M.disableAlarm (almId)
  if bit == nil or bit.band == nil or bit.bor == nil then
    print("[ERROR] Module bit is required to use alarm function")
    return nil
  end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local c = string.byte(i2c.read(id, 1), 1)
  i2c.stop(id)
  if almId == 1 then c = bit.band(c, 254)
  elseif almId == 2 then c = bit.band(c, 253)
  else
    almId = '1 and 2'
    c = bit.band(c, 252)
  end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.write(id, c)
  i2c.stop(id)
  print('[LOG] Alarm '..almId..' disabled')
end

-- almId can be 1 or 2;
-- almType should be taken from constants
function M.setAlarm (almId, almType, second, minute, hour, date)
  if bit == nil or bit.band == nil or bit.bor == nil then
    print("[ERROR] Module bit is required to use alarm function")
    return nil
  end
  if almId ~= 1 and almId ~= 2 then print('[ERROR] Wrong alarm id (1 or 2): '..almId) return end
  M.enableAlarm(almId)
  second = decToBcd(second)
  minute = decToBcd(minute)
  hour = decToBcd(hour)
  date = decToBcd(date)
  if almType == M.EVERYSECOND or almType == M.EVERYMINUTE then
    second = addAlarmBit(second)
    minute = addAlarmBit(minute)
    hour = addAlarmBit(hour)
    date = addAlarmBit(date)
  elseif almType == M.SECOND then
    minute = addAlarmBit(minute)
    hour = addAlarmBit(hour)
    date = addAlarmBit(date)
  elseif almType == M.MINUTE then
    hour = addAlarmBit(hour)
    date = addAlarmBit(date)
  elseif almType == M.HOUR then
    date = addAlarmBit(date)
  elseif almType == M.DAY then
    date = addAlarmBit(date,1)
  end
  local almStart = 0x07
  if almId == 2 then almStart = 0x0B end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, almStart)
  if almId == 1 then i2c.write(id, second) end
  i2c.write(id, minute)
  i2c.write(id, hour)
  i2c.write(id, date)
  i2c.stop(id)
  print('[LOG] Alarm '..almId..' setted')
end

-- Get Control and Status bytes
function M.getBytes ()
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0E)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local c = i2c.read(id, 2)
  i2c.stop(id)
  return tonumber(string.byte(c, 1)), tonumber(string.byte(c, 2))
end

-- Resetting RTC Stop Flag
function M.resetStopFlag ()
  if bit == nil or bit.band == nil or bit.bor == nil then
    print("[ERROR] Module bit is required to reset stop flag")
    return nil
  end
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0F)
  i2c.stop(id)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.RECEIVER)
  local s = string.byte(i2c.read(id, 1))
  i2c.stop(id)
  s = bit.band(s,127)
  i2c.start(id)
  i2c.address(id, dev_addr, i2c.TRANSMITTER)
  i2c.write(id, 0x0F)
  i2c.write(id, s)
  i2c.stop(id)
  print('[LOG] RTC stop flag resetted')
end

return M
