The main entry file is:
-- Main run script for ChiliPeppr's Cayenn protocol
-- This is for the Texas Instrument's INA219 current sensor
-- which reports data to ChiliPeppr via the Cayenn protocol
-- so ChiliPeppr can visually show a current sensor graph
--cayenn.init()
--cayenn.sendBroadcas()
ina219.init()
setreport = '{"sampleEveryms": 100, "reportEverySampleCntOf": 10}'
ctr = 0
function onReport(val)
print("got report:" .. cjson.encode(val))
ctr = ctr + 1
if ctr >= 4 then
print("Got 4 reports so stopping")
ina219.onPublishReportOff()
end
end
ina219.onPublishReportSet(setreport, onReport)
--ina219.onPublishReportOn()
--ina219.onPublishReportOff()
Then, the cayenn.lua file.
-- UDP Hello Announce v3
--local M = {}
M = {}
M.port = 8988
M.myip = nil
M.sock = nil
--M.announce =
M.isInitted = false
function M.init(jsonTagTable)
print("Initting...")
-- figure out if i have an IP
M.myip = wifi.sta.getip()
if M.myip == nil then
print("You need to connect to wifi. Unable to init.")
else
print("My IP: " .. M.myip)
M.isInitted = true
-- create socket for outbound UDP sending
M.sock = net.createConnection(net.UDP, 0)
-- create server to listen to incoming udp
M.initUdpServer()
-- create server to listen to incoming tcp
M.initTcpServer()
-- send our announce
M.sendBroadcast(jsonTagTable)
end
end
function M.createAnnounce(jsonTagTable)
-- see if there is a jsontagtable passed in as extra meta
local jsontag = ""
if jsonTagTable ~= nil then
ok, jsontag = pcall(cjson.encode, jsonTagTable)
if ok then
--print("Adding jsontagtable" .. jsontag)
else
print("failed to encode jsontag!")
end
end
local a = {}
a.Announce = "i-am-a-client"
a.Widget = "com-chilipeppr-widget-ina219"
a.MyDeviceId = "chip:" .. node.chipid() .. "-flash:" .. node.flashid() .. "-mac:" .. wifi.sta.getmac()
a.JsonTag = jsontag
local ok, json = pcall(cjson.encode, a)
if ok then
--print("Encoded json for announce: " .. json)
else
print("failed to encode json!")
end
return json
end
-- send announce to broadcast addr so spjs
-- knows of our existence
function M.sendBroadcast(jsonTagTable)
if M.isInitted == false then
print("You must init first.")
return
end
local bip = wifi.sta.getbroadcast()
--print("Broadcast addr:" .. bip)
print("Sending announce to ip: " .. bip)
M.sock:connect(M.port, bip)
M.sock:send(M.createAnnounce(jsonTagTable))
M.sock:close()
end
function M.setupWifi()
-- setwifi
wifi.setmode(wifi.STATION)
-- longest range is b
wifi.setphymode(wifi.PHYMODE_B)
--Connect to access point automatically when in range
wifi.sta.config("NETGEAR-main", "blah")
wifi.sta.connect()
--register callback
wifi.sta.eventMonReg(wifi.STA_IDLE, function() print("STATION_IDLE") end)
--wifi.sta.eventMonReg(wifi.STA_CONNECTING, function() print("STATION_CONNECTING") end)
wifi.sta.eventMonReg(wifi.STA_WRONGPWD, function() print("STATION_WRONG_PASSWORD") end)
wifi.sta.eventMonReg(wifi.STA_APNOTFOUND, function() print("STATION_NO_AP_FOUND") end)
wifi.sta.eventMonReg(wifi.STA_FAIL, function() print("STATION_CONNECT_FAIL") end)
wifi.sta.eventMonReg(wifi.STA_GOTIP, M.gotip)
--register callback: use previous state
wifi.sta.eventMonReg(wifi.STA_CONNECTING, function(previous_State)
if(previous_State==wifi.STA_GOTIP) then
print("Station lost connection with access point\n\tAttempting to reconnect...")
else
print("STATION_CONNECTING")
end
end)
--start WiFi event monitor with default interval
wifi.sta.eventMonStart(1000)
end
function M.gotip()
print("STATION_GOT_IP")
M.myip = wifi.sta.getip()
print("My IP: " .. M.myip)
end
function M.sendinit()
-- we need to send a UDP announce packet to every IP
-- on the subnet to let any possible SPJS know we are
-- available
-- iterate thru entire subnet
M.ipctr = 1
tmr.alarm(6, 200, tmr.ALARM_SINGLE, M.sendNextHello)
end
function M.sendNextHello()
local i, j = string.find(M.myip, "%.%d+$")
local prefix = string.sub(M.myip, 0, i)
local ip = prefix .. M.ipctr
print("Sending announce to ip: " .. ip)
M.sock:connect(M.port, ip)
M.sock:send(M.announce)
M.sock:close()
M.ipctr = M.ipctr + 1
if M.ipctr < 25 then
tmr.alarm(6, 50, tmr.ALARM_SINGLE, M.sendNextHello)
end
end
function M.initUdpServer()
M.udpServer = net.createServer(net.UDP)
--M.udpServer:on("connection", M.onUdpConnection)
M.udpServer:on("receive", M.onUdpRecv)
M.udpServer:listen(8988)
print("UDP Server started on port 8988")
end
function M.onUdpConnection(sck)
print("UDP connection.")
--ip, port = sck:getpeer()
--print("UDP connection. from: " .. ip)
end
function M.onUdpRecv(sck, data)
print("UDP Recvd. data: " .. data)
end
function M.initTcpServer()
M.tcpServer = net.createServer(net.TCP)
M.tcpServer:listen(8988, M.onTcpListen)
print("TCP Server started on port 8988")
end
function M.onTcpListen(conn)
conn:on("receive", M.onTcpRecv)
end
function M.onTcpConnection(sck)
print("TCP connection.")
--ip, port = sck:getpeer()
--print("UDP connection. from: " .. ip)
end
function M.onTcpRecv(sck, data)
local peer = sck:getpeer()
print("TCP Recvd. data: " .. data .. ", Peer:" .. peer)
end
function M.subscribe(cmd, callback)
-- we need to inject this cmd to a table so
-- we can see if any incoming data matches this cmd
-- and if so we call the callback with the payload
if M.subscriptionCmds == nil then
M.subscriptionCmds = {}
end
if M.subscriptionCmds[cmd] ~= nil then
print("Warning. Subscription already existed. Overwriting. cmd:" .. cmd)
end
M.subscriptionCmds[cmd] = callback
-- debug. comment out for production
M.printKeys(M.subscriptionCmds)
end
-- We will be passed something like /getVals {timeStart:0, timeEnd:100}
-- Or something like /getVals
-- Or something like /setReports {"samples":10, "intervalms", 100}
function M.parseAndPublish(cmd)
-- get the signal name which is the first part with the / (slash)
local i, j = string.find(cmd, " ")
local signal = string.sub(cmd, 0, i - 1)
local payload = string.sub(cmd, j + 1)
print("signal got:\"" .. signal .. "\"")
print("payload:\"" .. payload .. "\"")
-- see if this cmd is subscribed to
if M.subscriptionCmds[signal] ~= nil then
M.subscriptionCmds[signal](payload)
else
print("Nobody is subscribed to signal:" .. signal)
end
end
function M.printKeys(table)
--local keyset={}
local n=0
print("Subscriptions:")
for k,v in pairs(table) do
n=n+1
--keyset[n]=k
if k ~= nil then
print(" Signal:" .. k .. ", Callback:good")
else
print(" Signal:" .. k .. ", Callback:empty")
end
end
--print(cjson.encode(keyset))
end
--return M
--M.setupWifi()
--M.init()
--M.initServer()
--M.sendBroadcast()
cayenn = M
And then the ina219.lua file...
-- ChiliPeppr INA219 Module ina219.lua v2
--local ina219 = {}
ina219 = {}
ina219.timerid = 0
ina219.id = 0 -- i2c id
ina219.sda = 1
ina219.scl = 2
ina219.devaddr = 0x40 -- 1000000 (A0+A1=GND)
-- Set multipliers to convert raw current/power values
ina219.maxVoltage = 0 -- configured for max 32volts by default after init
ina219.maxCurrentmA = 0 -- configured for max 2A by default after init
ina219.currentDivider_mA = 0 -- e.g. Current LSB = 50uA per bit (1000/50 = 20)
ina219.powerDivider_mW = 0 -- e.g. Power LSB = 1mW per bit
ina219.currentLsb = 0 -- uA per bit
ina219.powerLsb = 1 -- mW per bit
function ina219.init()
ina219.begin()
ina219.setCalibration_32V_2A()
reg = ina219.read_reg_str(0x00)
print("Config:" .. ina219.getHex(reg))
end
function ina219.getConfig()
local c = {}
c.gpiosda = ina219.sda
c.gpioscl = ina219.scl
c.i2cdevaddr = ina219.devaddr
c.maxVoltage = ina219.maxVoltage
c.maxCurrentmA = ina219.maxCurrentmA
c.currentDivider_mA = ina219.currentDivider_mA
c.powerDivider_mW = ina219.powerDivider_mW
c.currentLsb = ina219.currentLsb
c.powerLsb = ina219.powerLsb
return c
end
-- user defined function: read from reg_addr content of dev_addr
function ina219.read_reg_str(reg_addr)
i2c.start(ina219.id)
i2c.address(ina219.id, ina219.devaddr, i2c.TRANSMITTER)
i2c.write(ina219.id,reg_addr)
i2c.stop(ina219.id)
tmr.delay(1)
i2c.start(ina219.id)
i2c.address(ina219.id, ina219.devaddr, i2c.RECEIVER)
c=i2c.read(ina219.id, 16) -- read 16bit val
i2c.stop(ina219.id)
return c
end
-- returns 16 bit int
function ina219.read_reg_int(reg_addr)
i2c.start(ina219.id)
i2c.address(ina219.id, ina219.devaddr, i2c.TRANSMITTER)
i2c.write(ina219.id,reg_addr)
i2c.stop(ina219.id)
tmr.delay(1)
i2c.start(ina219.id)
i2c.address(ina219.id, ina219.devaddr, i2c.RECEIVER)
local c = i2c.read(ina219.id, 16) -- read 16bit val
i2c.stop(ina219.id)
-- convert to 16 bit int
local val = bit.lshift(string.byte(c, 1), 8)
local val2 = bit.bor(val, string.byte(c, 2))
return val2
end
function ina219.write_reg(reg_addr, reg_val)
print("writing reg:" .. reg_addr .. ", reg_val:" .. reg_val)
i2c.start(ina219.id)
i2c.address(ina219.id, ina219.devaddr, i2c.TRANSMITTER)
local bw = i2c.write(ina219.id, reg_addr)
--print("Bytes written: " .. bw)
-- upper 8 bits
local bw2 = i2c.write(ina219.id, bit.rshift(reg_val, 8))
--print("Bytes written: " .. bw2)
-- lower 8 bits
local bw3 = i2c.write(ina219.id, bit.band(reg_val, 0xFF))
--print("Bytes written: " .. bw3)
i2c.stop(ina219.id)
end
function ina219.begin()
-- initialize i2c, set pin1 as sda, set pin2 as scl
i2c.setup(ina219.id, ina219.sda, ina219.scl, i2c.SLOW)
end
function ina219.reset()
ina219.write_reg(0x00, 0xFFFF)
end
function ina219.setCalibration_16V_400mA()
ina219.maxVoltage = 16
ina219.maxCurrentmA = 400
ina219.currentDivider_mA = 20 -- Current LSB = 50uA per bit (1000/50 = 20)
ina219.powerDivider_mW = 1 -- Power LSB = 1mW per bit
ina219.currentLsb = 50 -- uA per bit
ina219.powerLsb = 1 -- mW per bit
ina219.write_reg(0x05, 8192)
-- INA219_CONFIG_BVOLTAGERANGE_16V |
-- INA219_CONFIG_GAIN_1_40MV |
-- INA219_CONFIG_BADCRES_12BIT |
-- INA219_CONFIG_SADCRES_12BIT_1S_532US |
-- INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
-- write_reg(0x05, 0x0000 | 0x0000 | 0x0400 | 0x0018 | 0x0007)
ina219.write_reg(0x00, 0x41F)
end
function ina219.setCalibration_32V_1A()
ina219.maxVoltage = 32
ina219.maxCurrentmA = 1000
-- Compute the calibration register
-- Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
-- Cal = 10240 (0x2800)
ina219.write_reg(0x05, 10240)
-- Set multipliers to convert raw current/power values
ina219.currentDivider_mA = 25 -- Current LSB = 40uA per bit (1000/40 = 25)
ina219.powerDivider_mW = 1 -- Power LSB = 800uW per bit
ina219.currentLsb = 40 -- uA per bit
ina219.powerLsb = 0.8 -- mW per bit
-- INA219_CONFIG_BVOLTAGERANGE_32V |
-- INA219_CONFIG_GAIN_8_320MV |
-- INA219_CONFIG_BADCRES_12BIT |
-- INA219_CONFIG_SADCRES_12BIT_1S_532US |
-- INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
local config = bit.bor(0x2000, 0x1800, 0x0400, 0x0018, 0x0007)
ina219.write_reg(0x00, config)
end
function ina219.setCalibration_32V_2A()
ina219.maxVoltage = 32
ina219.maxCurrentmA = 2000
-- Compute the calibration register
-- Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
-- Cal = 4096 (0x1000)
ina219.write_reg(0x05, 4096)
-- Set multipliers to convert raw current/power values
ina219.currentDivider_mA = 10 -- Current LSB = 100uA per bit (1000/100 = 10)
ina219.powerDivider_mW = 1 --Power LSB = 1mW per bit (2/1)
ina219.currentLsb = 100 -- uA per bit
ina219.powerLsb = 1 -- mW per bit
-- INA219_CONFIG_BVOLTAGERANGE_32V |
-- INA219_CONFIG_GAIN_8_320MV |
-- INA219_CONFIG_BADCRES_12BIT |
-- INA219_CONFIG_SADCRES_12BIT_1S_532US |
-- INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
local config = bit.bor(0x2000, 0x1800, 0x0400, 0x0018, 0x0007)
ina219.write_reg(0x00, config)
end
function ina219.getCurrent_mA()
-- Gets the raw current value (16-bit signed integer, so +-32767)
local valueInt = ina219.read_reg_int(0x04)
return valueInt / ina219.currentDivider_mA
end
function ina219.getBusVoltage_V()
-- Gets the raw bus voltage (16-bit signed integer, so +-32767)
local valueInt = ina219.read_reg_int(0x02)
-- Shift to the right 3 to drop CNVR and OVF and multiply by LSB
local val2 = bit.rshift(valueInt, 3) * 4
return val2 * 0.001
end
function ina219.getShuntVoltage_mV()
-- Gets the raw shunt voltage (16-bit signed integer, so +-32767)
local valueInt = ina219.read_reg_int(0x01)
return valueInt * 0.01
end
-- returns the bus power in watts
function ina219.getBusPowerWatts()
local valueInt = ina219.read_reg_int(0x03)
return valueInt * ina219.powerLsb
end
function ina219.checkVals()
reg = ina219.read_reg_str(0x00)
print("Config: " .. ina219.getHex(reg))
-- get Shunt Voltage
--reg = read_reg_int(0x01)
--print("Shunt Voltage")
--print(reg)
--printHex(reg)
print("Shunt Voltage mV: " .. ina219.getShuntVoltage_mV())
-- get Bus Voltage
--reg = read_reg_int(0x02)
--print("Bus Voltage")
--print(reg)
--printHex(reg)
print("Bus Voltage V: " .. ina219.getBusVoltage_V())
-- get Power
reg = ina219.read_reg_int(0x03)
print("Power: " .. reg)
--print(reg)
--printHex(reg)
print("Power watts: " .. ina219.getBusPowerWatts())
-- get Current
--reg = read_reg_int(0x04)
--print("Current")
--print(reg)
--printHex(reg)
print("Current mA:" .. ina219.getCurrent_mA())
print("")
end
-- returns an object of vals
function ina219.getVals()
local val = {}
val.voltageV = ina219.getBusVoltage_V()
val.shuntmV = ina219.getShuntVoltage_mV()
val.powerW = ina219.getBusPowerWatts()
-- sometimes the ina219 returns false current data
-- where the value is pegged at max so toss it if the
-- powerW is at 0 because that is usually when it happens
if val.powerW == 0 then
val.currentmA = 0
else
val.currentmA = ina219.getCurrent_mA()
end
return val
end
function ina219.getHex(val)
--print("len of val:" .. string.len(val))
local s = ""
for i = 1, string.len(val) do
if string.byte(val, i - 1) then
s = s .. string.format("%2X", string.byte(val, i - 1)) .. " "
end
end
--print(s)
return s
end
-- pass in a payload json string of
-- {"sampleEveryms": 100, "reportEverySampleCntOf": 10}
function ina219.onPublishReportSet(json, callbackOnReport)
tmr.stop(ina219.timerid)
local t = cjson.decode(json)
-- set the callback that is called when the report is complete
if callbackOnReport == nil then
print("You did not provide a callback to call when the report is complete. Returning.")
return
end
ina219.callbackOnReport = callbackOnReport
if t["sampleEveryms"] ~= nil then
ina219.sampleEveryms = t["sampleEveryms"]
print("Setting sample every ms:" .. ina219.sampleEveryms)
end
if t["reportEverySampleCntOf"] ~= nil then
ina219.reportEverySampleCntOf = t["reportEverySampleCntOf"]
print("Setting reportEverySampleCntOf:" .. ina219.reportEverySampleCntOf)
end
-- reset the sample data
ina219.sampleCtr = 0
ina219.sampleData = {}
-- set the timer
if tmr.alarm(ina219.timerid, ina219.sampleEveryms, tmr.ALARM_AUTO, ina219.onSample) then
print("Started sample reporting")
else
print("Error starting sample reporting.")
end
end
ina219.sampleCtr = 0
ina219.sampleData = {}
function ina219.onSample()
--print("Doing onSample for ctr:" .. ina219.sampleCtr)
local val = ina219.getVals()
--print("Sample:" .. cjson.encode(val))
-- get a sample
ina219.sampleData[ina219.sampleCtr] = val
ina219.sampleCtr = ina219.sampleCtr + 1
-- see if we are out of reports to grab
if ina219.sampleCtr >= ina219.reportEverySampleCntOf then
--print("At max of samples so sending report")
-- pause reporting just in case it is so fast it interrupts
-- us while we send the report off to spjs
tmr.stop(ina219.timerid)
ina219.callbackOnReport(ina219.sampleData)
-- reset sample data
ina219.sampleCtr = 0
ina219.sampleData = {}
-- see if we were asked to turn off
if ina219.isStopping == false then
tmr.start(ina219.timerid)
else
ina219.isStopping = false
end
end
--print("Done with onSample")
end
function ina219.onPublishReportOn()
tmr.stop(ina219.timerid)
end
ina219.isStopping = false
function ina219.onPublishReportOff()
ina219.isStopping = true
tmr.stop(ina219.timerid)
end
--ina219.init()
--return ina219
No comments:
Post a Comment