AXS Port Decoded

Post Reply
sloarpower
Forum Junior Member
Posts: 2
Joined: Tue Dec 31, 2013 4:43 pm

AXS Port Decoded

Post by sloarpower » Fri Jan 03, 2014 6:17 pm

Posting info for others that may be looking for this. There was hardly any specific information in the manual, in fact, the manual states that only the charge controller was supported by the AXS port at this time. I was able to find the starting addresses for my system's FlexMax80 charge controllers and FX inverters. There is also some outback system info in another group.
I have version 1.05 firmware in this AXS port, not sure if Outback will change registers around with a new firmware rev.

Here are the registers where I found stuff:
40069 - Starts out the OB system area
40356 - System control
40428 - Starts the device in hub port 1 FX inverter 1
40511 - Starts the device in hub port 2 FX inverter 2
40594 - port 3 MX charge controller 1
40685 - port 4 MX charge controller 2

This is how I found my devices, I don't know exactly how the registers will be laid out in a different setup, whether the registers numbers start with the port number or device type. Either way, this should provide a rough guide.

The manual did list what to expect for data in each group, that part is exactly as printed, but never said where to start looking.

Note that your Master device will have to request the correct format, like, register 40356, with the prefix. If your device asks for Function Code 3 with a starting address of 356(without including the implied 4xxxx) the AXbox will send back an illegal address request. Pretty annoying.

I haven't figured out how to access the thing via a webpage, it doesn't appear to have anything built in for that. If anyone has figured that part out, please post here. I also haven't tried any of the emailing functionality. It works good to serve data back into a PLC though.

jkerr336
Forum Member
Posts: 9
Joined: Tue Nov 20, 2012 12:51 pm
My RE system: Astronergy 290 watt panels, Outback FlexMax 80 Charge Controller, Magnum MS4448PAE Power Center, AIR X 48V 400 watts Wind Turbine, Surrette S-530 Batteries, AXS Port, MagWeb - Ethernet Web Monitoring Kit (ME-MW-E)
Location: Laramie, WY

Re: AXS Port Decoded

Post by jkerr336 » Tue Jan 14, 2014 4:34 pm

I use this device to remotely monitor my charge controller. Like you, the learning curve for using this was steep, but once I got the register calls figured out, it worked as expected. I agree that there is no web-based interface to communicate with the AXS, I have created my own. I wrote my interface in Java using the j2mod libraries. If you need help in this area, I'm happy to share/discuss my classes with you.

Once configured, the status email feature works like a champ; I get regular emails updating me on the charge progress. I haven't used the alarm emails.

Maybe, like you, I was surprised at the level of technical research I had to do to get this to work. Without a decent programming background, the AXS will quickly become a pretty expensive paper weight.

Rob.

User avatar
tallgirl
Forum Emperor
Posts: 4334
Joined: Wed Sep 26, 2007 9:59 am
My RE system: 16 Kyocera KC175GT (2,800 watts DC), MX-60, 2 OutBack GVFX3648, 8 GC2 batteries (6v @ 215AH),
Location: Austin, TX

Re: AXS Port Decoded

Post by tallgirl » Wed Jan 22, 2014 4:48 am

Greets,

The AXS Port device uses the SunSpec approach to detecting devices and their corresponding Modbus register maps. The SunSpec blocks all start at address 40000 and encode their types, sizes and other such information needed to find the next block. The following Java method shows how to do it using the j2mod library my company (gHg&e) maintains --

Code: Select all

	public ArrayList<Device> scanBlocks(ModbusTransport transport) {
		ArrayList<Device> result = new ArrayList<Device>();
		
		ReadInputRegistersRequest readRequest =
				new ReadInputRegistersRequest();
		
		ReadInputRegistersResponse readResponse =
				new ReadInputRegistersResponse();
		
		int	lastBlockStart = 40000;
		Common commonBlock = null;
		boolean	lastWasAggregate = false;
		boolean	useAggregate = false;
		
		if (hub != null) {
			int	instance = modbus != null ? modbus.getInstance():0;
			
			useAggregate = hub.getValue("SunSpec" + instance +
					"_" + unitNumber + ".Aggregate", useAggregate);
		}
		while (true) {
			ModbusTransaction trans = transport.createTransaction();
			trans.setCheckingValidity(true);
			trans.setRetries(3);
			
			readRequest = new ReadInputRegistersRequest();
			readRequest.setUnitID(0);
			readRequest.setReference(lastBlockStart);
			readRequest.setWordCount(2);
			
			trans.setRequest(readRequest);
			try {
				trans.execute();
				ModbusResponse response = trans.getResponse();
				
				if (! (response instanceof ReadInputRegistersResponse))
					break;
				
				readResponse = (ReadInputRegistersResponse) response;
				InputRegister header[] = readResponse.getRegisters();
				
				int blockType = 0;
				int blockLength = 0;
				
				Device device = null;
				
				switch (header[0].toUnsignedShort()) {
				case Values.SUNSPEC_COMMON_BLOCK_1:
					readRequest = new ReadInputRegistersRequest();
					readRequest.setUnitID(0);
					readRequest.setReference(lastBlockStart);
					readRequest.setWordCount(4);
					
					trans = transport.createTransaction();
					trans.setCheckingValidity(true);
					trans.setRetries(3);
					
					trans.setRequest(readRequest);
					trans.execute();
					response = trans.getResponse();
					
					if (! (response instanceof ReadInputRegistersResponse))
						break;
					
					readResponse = (ReadInputRegistersResponse) response;
					header = readResponse.getRegisters();
					
					if (header[1].toUnsignedShort() == Values.SUNSPEC_COMMON_BLOCK_2) {
						blockType = Values.SUNSPEC_COMMON_BLOCK;
						blockLength = header[3].toUnsignedShort() + 4;
					}
					lastWasAggregate = false;
					break;
				case Values.SUNSPEC_AGGREGATION_BLOCK:
					blockType = header[0].toUnsignedShort();
					blockLength = header[1].toUnsignedShort() + 2;
					
					device = new Aggregator(commonBlock, lastBlockStart,
							blockLength);
					
					lastWasAggregate = true;
					
					break;
				case Values.SUNSPEC_INVERTER_1PH:
				case Values.SUNSPEC_INVERTER_3PH:
				case Values.SUNSPEC_INVERTER_1PH_SPLIT:
					blockType = header[0].toUnsignedShort();
					blockLength = header[1].toUnsignedShort() + 2;
					
					device = new Inverter(commonBlock, blockType,
							lastBlockStart, blockLength);
					
					if (lastWasAggregate)
						((Inverter) device).setAggregate(true);
					
					break;
				case Values.SUNSPEC_MODULE_INT:
				case Values.SUNSPEC_MODULE_FLOAT:
					blockType = header[0].toUnsignedShort();
					blockLength = header[1].toUnsignedShort() + 2;
					break;
				default:
					break;
				}
				
				if (blockType == 0)
					break;
				
				readRequest.setWordCount(blockLength);
				trans.setRequest(readRequest);
				trans.execute();
				
				response = trans.getResponse();
				if (! (response instanceof ReadInputRegistersResponse))
					break;
				
				readResponse = (ReadInputRegistersResponse) response;
				header = readResponse.getRegisters();
				
				/*
				 * Do stuff here.
				 */
				
				short registers[] = new short[header.length];
				for (int i = 0;i < header.length;i++)
					registers[i] = header[i].toShort();
				
				if (blockType == Values.SUNSPEC_COMMON_BLOCK) {
					commonBlock = new Common(registers);
				} else if (device != null) {
					device.update(registers, 0);
					
					/*
					 * Inverters can be physical or aggregated.  Adding all the
					 * physical inverters =and= the aggregated inverter will double
					 * the total, so don't do that.
					 */
					if (device instanceof Inverter) {
						Inverter inverter = (Inverter) device;
						
						if (inverter.isAggregate() == useAggregate)
							result.add(device);
						
						lastWasAggregate = false;
					} else if (device instanceof Aggregator) {
						if (useAggregate)
							result.add(device);
					} else {
						result.add(device);
					}
				}
				lastBlockStart += blockLength;
				
				exceptionCount = 0;
			} catch (ModbusException e) {
				totalExceptionCount++;
				exceptionCount++;
			}
		}
		
		return result;
	}
There are no charge controller or other such devices in the above sample code. Mostly because I've been working on inverters a lot more over the past several years than charge controllers, meters, weather equipment, etc. But you should get the idea -- each block encodes its type and size. Once you know the size of a block, add that to the previous offset in the Modbus map and read the next. Repeat until you reach the end.

SunSpec can be a bit challenging at first because there are no truly fixed addresses -- which is a key advantage, and a major pain. If you're used to writing Modbus code, instead of using a fixed offset, say, address 20 for a specific voltage, you'll have to add the base address of that device's block to the fixed address 20. Each of that particular type of device will have a unique base address, but all of the instances for that device value will be at that device's base address plus 20.
Julie in Texas

I ride bicycles. A lot.

Sasquatch
Forum Member
Posts: 17
Joined: Mon May 04, 2015 4:48 pm
My RE system: http://www.m2group.com/SolarPowerWeb/
Location: Green Bay, WI
Contact:

Re: AXS Port Decoded

Post by Sasquatch » Wed May 06, 2015 2:02 pm

If I have one FX with no hub, is it possible to communicate with it using the AXS?
I guess what I am asking is will a lone FX show up on the AXS as device 0 like it does on a MATE 2?

Sasquatch
Forum Member
Posts: 17
Joined: Mon May 04, 2015 4:48 pm
My RE system: http://www.m2group.com/SolarPowerWeb/
Location: Green Bay, WI
Contact:

Re: AXS Port Decoded

Post by Sasquatch » Sat May 09, 2015 12:12 am

I updated the firmware on the AXS to v3.007 and devices are showing up now.
With an FX connected directly to the AXS, I found the FX Real Time Block at 40555-40585
with the Configuration Block at 40586-40659.
I have all of the real time data live on a web page at:
http://www.m2group.com/SolarPowerWeb/

After the Crestron logo appears, it takes a couple minutes to download the graphics and plugins before it will stream data. :)

Post Reply