Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Update SRNE ML-2440 #39

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions solar_charge_controllers/srne_ml-2440/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# SRNE ML2440

This _Enapter Device Blueprint_ integrates **SRNE ML2440** - Solar Panel Regulator Charge Controller with [Modbus RTU](https://developers.enapter.com/docs/reference/ucm/modbus) over [RS-232 communication interface](https://developers.enapter.com/docs/reference/ucm/rs232).
This [Enapter Device Blueprint](https://github.com/Enapter/marketplace#blue_book-enapter-device-blueprints) integrates **SRNE ML2440** - Solar Panel Regulator Charge Controller with [Modbus RTU](https://developers.enapter.com/docs/reference/ucm/modbus) over [RS-232 communication interface](https://developers.enapter.com/docs/reference/ucm/rs232).

Use [Enapter ENP-RS232](https://handbook.enapter.com/modules/ENP-RS232/ENP-RS232.html) module for physical connection. See [connection instructions](https://handbook.enapter.com/modules/ENP-RS232/ENP-RS232.html#connection-example) in the module manual.
## Connect to Enapter

## RS-232 Communication Interface Parameters

- Baud rate: `9600` bps;
- Data bits: `8`;
- Parity: `N` (no parity);
- Stop bits: `1`.
- Sign up to Enapter Cloud using [Web](https://cloud.enapter.com/) or mobile app ([iOS](https://apps.apple.com/app/id1388329910), [Android](https://play.google.com/store/apps/details?id=com.enapter&hl=en)).
- Use [Enapter ENP-RS232](https://handbook.enapter.com/modules/ENP-RS232/ENP-RS232.html) module for physical connection. See [connection instructions](https://handbook.enapter.com/modules/ENP-RS232/ENP-RS232.html#connection-examples) in the module manual.
- [Add ENP-RS232 to your site](https://handbook.enapter.com/software/mobile/android_mobile_app.html#adding-sites-and-devices) using the mobile app.
- [Upload](https://developers.enapter.com/docs/tutorial/uploading-blueprint/) this blueprint to ENP-RS232.

## References

Expand Down
150 changes: 109 additions & 41 deletions solar_charge_controllers/srne_ml-2440/firmware.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
-- RS232 communication interface parameters
BAUD_RATE = 9600
DATA_BITS = 8
PARITY = 'N'
STOP_BITS = 1

-- Device Modbus address
ADDRESS = 1

function main()
local result = rs232.init(9600, 8, "N", 1)
local result = rs232.init(BAUD_RATE, DATA_BITS, PARITY, STOP_BITS)
if result ~= 0 then
enapter.log("RS-232 failed: "..result.." "..rs232.err_to_str(result), "error", true)
end

scheduler.add(30000, send_properties)
scheduler.add(1000, metrics)
scheduler.add(1000, send_telemetry)
end

function send_properties()
Expand All @@ -27,7 +34,7 @@ function send_properties()
enapter.send_properties(properties)
end

function metrics()
function send_telemetry()
local telemetry = {}
local alerts = {}
local status = "ok"
Expand Down Expand Up @@ -294,7 +301,7 @@ function metrics()
if data then
telemetry["load_status"] = is_on(data[1] >> 15)
telemetry["load_brightness"] = (data[1] >> 8) & 0x7F
telemetry["charging_state"] = data[1] >> 0xFF
telemetry["charging_state"] = get_charging_state(data[1] >> 0xFF)
else
enapter.log("Register 0x120 reading failed: "..modbus.err_to_str(result), "error")
alerts = {"communication_failed"}
Expand All @@ -304,7 +311,7 @@ function metrics()
--- TEST ---
local data, result = modbus.read_holdings(ADDRESS, 0xE01D, 1, 1000)
if data then
telemetry["load_working_mode"] = data[1]
telemetry["load_working_mode"] = get_load_working_mode(data[1])
else
enapter.log("Register 0xE01D reading failed: "..modbus.err_to_str(result), "error")
alerts = {"communication_failed"}
Expand Down Expand Up @@ -334,14 +341,12 @@ function metrics()
table.insert(alerts, "high_ambient_temp")
elseif (data[1] << 10) >> 15 == 1 then
table.insert(alerts, "high_controller_temp")
elseif (data[1] << 10) >> 15 == 1 then
table.insert(alerts, "high_controller_temp")
elseif (data[1] << 11) >> 15 == 1 then
table.insert(alerts, "load_overpower")
elseif (data[1] << 12) >> 15 == 1 then
table.insert(alerts, "load_short_circuit")
elseif (data[1] << 13) >> 15 == 1 then
table.insert(alerts, "battery_under_voltage") --warning
table.insert(alerts, "battery_under_voltage")
elseif (data[1] << 14) >> 15 == 1 then
table.insert(alerts, "battery_over_voltage")
elseif (data[1] << 15) >> 15 == 1 then
Expand All @@ -359,30 +364,7 @@ function metrics()
enapter.send_telemetry(telemetry)
end

function is_on(value)
if value == 1 then
return true
else
return false
end
end

function toint(register)
local raw_str = string.pack("BBBB", register[1]>>8, register[1]&0xff, register[2]>>8, register[2]&0xff)
return string.unpack(">I4", raw_str)
end

function tosignedint(value)
local byte7 = value >> 7
local result = value & 0x7F
if byte7 == 1 then
return result * (-1)
else
return result
end
end

function read_controller_parameters(ctx)
function read_configuration(ctx)
local parameters = {}

local data, result = modbus.read_holdings(ADDRESS, 0xE002, 13, 1000)
Expand Down Expand Up @@ -417,21 +399,107 @@ function read_controller_parameters(ctx)
return parameters
end

function write_controller_parameters(ctx, args)
function write_configuration(ctx, args)
local nominal_battery_capacity = {args["nominal_battery_capacity"]}
local result = modbus.write_multiple_holdings(ADDRESS, 0xE014, nominal_battery_capacity, 1000)
if result ~= 0 then
ctx.error("Register 0xE014 writing failed: "..modbus.err_to_str(result), "error")
end
end

enapter.register_command_handler("read_controller_parameters", read_controller_parameters)
enapter.register_command_handler("write_controller_parameters", write_controller_parameters)
enapter.register_command_handler("read_configuration", read_configuration)
enapter.register_command_handler("write_configuration", write_configuration)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to move to main()


main()
function is_on(value)
if value == 1 then
return true
elseif value == 0 then
return false
else
enapter.log("Unknown load status", "error")
return nil
end
end

--[[function from_float_to_int32(number)
local raw_str = string.pack(">f", number)
local byte1, byte0 = string.unpack(">I2", raw_str)
return {byte1, byte0}
end]]
function get_charging_state(value)
if value then
if value == 0x00 then
return "charging_deactivated"
elseif value == 0x01 then
return "charging_activated"
elseif value == 0x02 then
return "mppt_charging_mode"
elseif value == 0x03 then
return "equalizing_charging_mode"
elseif value == 0x04 then
return "boost_charging_mode"
elseif value == 0x05 then
return "floating_charging_mode"
elseif value == 0x06 then
return "current_limiting"
else
return "unknown"
end
end
end

function get_load_working_mode(value)
if value then
if value == 0x00 then
return "sole_light_control"
elseif value == 0x01 then
return "light_control_off_after_1h"
elseif value == 0x02 then
return "light_control_off_after_2h"
elseif value == 0x03 then
return "light_control_off_after_3h"
elseif value == 0x04 then
return "light_control_off_after_4h"
elseif value == 0x05 then
return "light_control_off_after_5h"
elseif value == 0x06 then
return "light_control_off_after_6h"
elseif value == 0x07 then
return "light_control_off_after_7h"
elseif value == 0x08 then
return "light_control_off_after_8h"
elseif value == 0x09 then
return "light_control_off_after_9h"
elseif value == 0x0A then
return "light_control_off_after_10h"
elseif value == 0x0B then
return "light_control_off_after_11h"
elseif value == 0x0C then
return "light_control_off_after_12h"
elseif value == 0x0D then
return "light_control_off_after_13h"
elseif value == 0x0E then
return "light_control_off_after_14h"
elseif value == 0x0F then
return "manual_mode"
elseif value == 0x10 then
return "debugging_mode"
elseif value == 0x11 then
return "normal_on_mode"
else
return "unknown"
end
end
end

function toint(register)
local raw_str = string.pack("BBBB", register[1]>>8, register[1]&0xff, register[2]>>8, register[2]&0xff)
return string.unpack(">I4", raw_str)
end

function tosignedint(value)
local byte7 = value >> 7
local result = value & 0x7F
if byte7 == 1 then
return result * (-1)
else
return result
end
end

main()
Loading