• Steel Soldiers now has a few new forums, read more about it at: New Munitions Forums!

  • Microsoft MSN, Live, Hotmail, Outlook email users may not be receiving emails. We are working to resolve this issue. Please add support@steelsoldiers.com to your trusted contacts.

AMMPS Generator Series Remote Data

R1ckyb0nd

Member
47
11
8
Location
Ct
Starting dedicated thread.
I would like to discuss and share information on how to get the status data from the AMMPS generators through either RS485 ( Remote port) or tapping into the J1939 Canbus pins.
I believe the RS 485 coming from the Remote port is Modbus RTU, but I am unable to scan it with ModScan 64.

Has anyone been successful in getting generator data with a different application other then the Inpower software?
 

kloppk

Well-known member
Steel Soldiers Supporter
2,126
3,490
113
Location
Pepperell, Massachusetts
If your set included the "LOCAL CABLE" part number 30554-04-21227 you can use it to do the Remote Monitoring. The RS-232 to RS-485 adapter gets connected to the connector labeled "P2-MAIN". It looks like this with the RS232 to RS485 adapter on it.
Remote Monitor Cable.jpg

If you're missing the cable it's wired like this. The cable contains 4 twisted pairs, C&D, E&L, H&G, F&N and a shield.
For the Remote monitoring you can get away with just the top 4 red wires.
Remote Monitor Cable Wiring.jpg

If you're missing the RS-232 to RS485 there is one on Amazon that works. It worked good and was using 100' of CAT5 cable.
It's only about $11 instead of $125 for the one normally supplied with the cable.
Only minor hitch is the pinout is slightly different. ---> Amazon RS-232 to RS-485

Original adapter schematic
RS-232 to RS-485 Adapter That Came with DCS.jpg


Pinout of Amazon adapter.
RS-232 to RS-485 Adapter From Amazon.jpg
 
Last edited:

kloppk

Well-known member
Steel Soldiers Supporter
2,126
3,490
113
Location
Pepperell, Massachusetts
The back and forth data that the Remote Monitoring port are a series of bytes.
Once a second the Remote Monitor software sends over a series of 44 data requests and the AMMPS responds to each of the 44 requests as each request comes in with the requested data. For instance the request for Fuel Level is 93 2 28 28 91 and my 1030 sends back 93 2 FB 2 68 (the fuel level at that time)
Each transmission begins with 93. The next byte is the number of subsequent bytes minus one.
I captured the data using the UART feature of an Arduino connected to the TX data and RX data on the RS-232 side.

Here is a table of all 44 requests and responses that I captured.
Table of Data 44 Requests.jpg

I haven't poked at getting the data via another means.
 

R1ckyb0nd

Member
47
11
8
Location
Ct
So you have not tried to send the Inpower command to the Remote port from the Arduino yet, using a python serial module?
 

kloppk

Well-known member
Steel Soldiers Supporter
2,126
3,490
113
Location
Pepperell, Massachusetts
So you have not tried to send the Inpower command to the Remote port from the Arduino yet, using a python serial module?
The InPower program goes thru a rather unique long handshake routine to link up to the AMMPS before it starts requesting and receiving data. I was able to see on a scope the PC initially send some data at 4,800 baud(?), then another transmission at a higher baud rate, then more at an even higher baud rate and so on. At a point the AAMPS started sending handshaking back and forth with the PC until it finally started requesting and receiving status data.

I did try using the Arduino to simply send the "Fuel Level" request but didn't get any response. I'm sure that was due to the fact I didn't to the initial linkup handshaking before that.
 
Last edited:

R1ckyb0nd

Member
47
11
8
Location
Ct
That is not cool, this is will make it harder to get the data outside the Inpower application. Have you tried connecting to the Canbus?
 

kloppk

Well-known member
Steel Soldiers Supporter
2,126
3,490
113
Location
Pepperell, Massachusetts
That is not cool, this is will make it harder to get the data outside the Inpower application. Have you tried connecting to the Canbus?
Not yet. I'd need to add the needed pins and some wires to the P3 connector on the AAMPS wire harness. According to the TM there are no wire connected to the K & L locations in the P3 connector in a 1030 or 1040.
I did buy some CAN adapter boards to connect up to it when I get to that point. I'm not sure which CANBUS protocol the AAMPS may use though. It may be 2.0 B
 
Last edited:

Icesythe7

Active member
147
223
43
Location
Indiana, USA
The InPower program goes thru a rather unique long handshake routine to link up to the AMMPS before it starts requesting and receiving data. I was able to see on a scope the PC initially send some data at 4,800 baud(?), then another transmission at a higher baud rate, then more at an even higher baud rate and so on. At a point the AAMPS started sending handshaking back and forth with the PC until it finally started requesting and receiving status data.

I did try using the Arduino to simply send the "Fuel Level" request but didn't get any response. I'm sure that was due to the fact I didn't to the initial linkup handshaking before that.
I'm reversing that handshake atm but haven't had much time to fully go thru the disassembly, i first need to create a "fake" 1030 with my pico since I don't have a real one which makes it even more annoying.
 

R1ckyb0nd

Member
47
11
8
Location
Ct
I'm reversing that handshake atm but haven't had much time to fully go thru the disassembly, i first need to create a "fake" 1030 with my pico since I don't have a real one which makes it even more annoying.
Do you have a capture of the handshake that you can share?
 

Icesythe7

Active member
147
223
43
Location
Indiana, USA
Do you have a capture of the handshake that you can share?
Still going thru the code and renaming everything to make sense of it, it takes awhile lol

C++:
int __stdcall IsComPortValid(int port_number)
{
  const char *port_number_as_string; // eax
  int port_number_as_decimal; // eax
  const CHAR *v3; // eax
  const char *v4; // eax
  int v5; // eax
  const char *v6; // eax
  int v8; // [esp+8h] [ebp-18h]
  HANDLE hObject; // [esp+Ch] [ebp-14h]
  char v10[4]; // [esp+10h] [ebp-10h] BYREF
  int v11; // [esp+1Ch] [ebp-4h]

  v8 = 1;
  if ( !(unsigned __int8)ATL::CSimpleStringT<char,1>::IsEmpty(port_number) )
  {
    ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>(v10);
    v11 = 0;
    port_number_as_string = (const char *)ATL::CSimpleStringT<char,1>::operator char const *(port_number);
    port_number_as_decimal = atoi(port_number_as_string);
    ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::Format(
      v10,
      "\\\\.\\COM%d",
      port_number_as_decimal);
    v3 = (const CHAR *)ATL::CSimpleStringT<char,1>::operator char const *(v10);
    hObject = CreateFileA(v3, 0x80000000, 0, 0, 3u, 0, 0);// attempt to open com port
    CloseHandle(hObject);
    if ( !hObject || hObject == (HANDLE)-1 )
    {
      v4 = (const char *)ATL::CSimpleStringT<char,1>::operator char const *(port_number);
      v5 = atoi(v4);
      ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::Format(
        v10,
        "COM Port %d is not available or is busy. \nPlease select another COM Port.",
        v5);
      v6 = (const char *)ATL::CSimpleStringT<char,1>::operator char const *(v10);
      AfxMessageBox(v6, 0x10u, 0);
      v8 = 0;
    }
    v11 = -1;
    ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::~CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>(v10);
  }
  return v8;
}
Have to start at the lowest level to actually find where it all takes place, I'm trying to find where it actually sends the request so I can attach a debugger to the program atm...

They have some pretty bad anti debugging code that is mildly annoying and a waste of 30 mins of my time bypassing...sigh


C++:
int __cdecl sub_E189A4(int a1)
{
  dword_E3B4E4 = IsDebuggerPresent();
  crt_debugger_hook(1);
  _crtUnhandledException(a1);
  if ( !dword_E3B4E4 )
    crt_debugger_hook(1);
  return _crtTerminateProcess(-1073740791);
}
 
Last edited:

Icesythe7

Active member
147
223
43
Location
Indiana, USA
Well before I go to bed I can get the software to respond to my virtual com ports finally...it seems to send "83 03 00 00 01 81" and if i echo that back it will then attempt to "negotiate a baud rate" and if i echo it back yet again a few time it will set a baud rate (depending on the speed i echo it back) and connect...it then seems to request a bunch of data that I can't respond with and gives a connection error (because I can't type in data that fast and will need to actually code some logic, will maybe do that this weekend)
 

Icesythe7

Active member
147
223
43
Location
Indiana, USA
Made some progress out of boredum, I made a small c++ app to emulate a genset and am able to connect to the software and get some data but without a real machine to have to extract data from I am unsure what data to respond back with...from kloppk's data table above I may have enough to slowly figure this out as the applications disassembly is quite convoluted with all the external dll imports and this may be faster. Below is the data that my emulator recieves from a fresh connection, i am simply echoing back "83 03 00 00 01 81" currently after every full request sent from the software until the software gives an error and disconnects due to an "unsupported device" surely from invalid responses after connect when it requests genset data.


Code:
Command = 0x83, data size = 3
Data = 0x00, 0x00, 0x01
Checksum = 0x81

Command = 0x86, data size = 2
Data = 0x04, 0x00
Checksum = 0x80

Command = 0x93, data size = 2
Data = 0x62, 0x29
Checksum = 0xDA

Command = 0x93, data size = 2
Data = 0x08, 0x00
Checksum = 0x99

Command = 0x86, data size = 2
Data = 0x1A, 0x00
Checksum = 0x9E

Command = 0x86, data size = 2
Data = 0x1A, 0x00
Checksum = 0x9E

Command = 0x83, data size = 3
Data = 0x00, 0x00, 0x01
Checksum = 0x81

Command = 0x86, data size = 2
Data = 0x04, 0x00
Checksum = 0x80

Command = 0x93, data size = 2
Data = 0x62, 0x29
Checksum = 0xDA

Command = 0x93, data size = 2
Data = 0x08, 0x00
Checksum = 0x99

Command = 0x86, data size = 2
Data = 0x1A, 0x00
Checksum = 0x9E

Command = 0x86, data size = 2
Data = 0x1A, 0x00
Checksum = 0x9E
C++:
#include <iostream>
#include <Windows.h>

DCB Dcb;
COMMTIMEOUTS Cto;
HANDLE hComm;
uint8_t sBuffer[6] = {0x83, 0x03, 0x00, 0x00, 0x01, 0x81};

int main() {
    //Create file Handle
    hComm = CreateFile(L"\\\\.\\COM6",
        GENERIC_READ | GENERIC_WRITE,
        0,
        nullptr,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        nullptr);
    if (hComm == INVALID_HANDLE_VALUE) {
        printf("CreateFile handle failed ERROR: %lu.\n", GetLastError());
    }

    //Data Center Bridging
    Dcb.DCBlength = sizeof(Dcb);
    GetCommState(hComm, &Dcb);
    Dcb.BaudRate = CBR_115200;
    Dcb.fParity = false;
    Dcb.fNull = false;
    Dcb.StopBits = ONESTOPBIT;
    Dcb.Parity = NOPARITY;
    Dcb.ByteSize = 8;
    SetCommState(hComm, &Dcb);

    //Timeouts
    Cto.ReadIntervalTimeout = 0;
    Cto.ReadTotalTimeoutMultiplier = 0;
    Cto.ReadTotalTimeoutConstant = 0;
    Cto.WriteTotalTimeoutMultiplier = 0;
    Cto.WriteTotalTimeoutConstant = 0;
    SetCommTimeouts(hComm, &Cto);
    std::cout << "Hello World!\n";

    while (true) {
        DWORD dwBytesRead = 0;
        uint8_t command = 0;

        // wait for a command
        if (ReadFile(hComm, &command, 1, &dwBytesRead, nullptr)) {
            uint8_t command_size = 0;
            printf("Command = 0x%02X, ", command);
            // get data size
            if (dwBytesRead && ReadFile(hComm, &command_size, 1, &dwBytesRead, nullptr)) {
                uint8_t data[8];
                printf("data size = %d\n", command_size);
                if (dwBytesRead && ReadFile(hComm, data, command_size + 1, &dwBytesRead, nullptr)) {
                    printf("Data = ");
                    for (int i = 0; i < command_size - 1; ++i) {
                        printf("0x%02X, ", data[i]);
                    }
                    printf("0x%02X\n", data[command_size - 1]);
                    printf("Checksum = 0x%02X\n", data[command_size]);
                    WriteFile(hComm, sBuffer, 6, nullptr, nullptr);
                }
            }
        }
    }
}
The checksum is simply the command ^(XOR) the data (ie 83 ^ 0 ^ 0 ^ 1 = 81 for the first request sent on connection)


C++:
uint8_t CalcChecksum(uint8_t command, const size_t size, const uint8_t* data) {
    for (size_t i = 0; i < size; ++i)
        command ^= data[i];
    return command;
}
If anyone wants to try this btw you will need a driver like "com0com" to create a pair of virtual com ports and passthru 1 virtual com port to the other, you can then connect the software to port 1 which will pass its data to port 2 which is where the emulator connects to.
 
Last edited:

R1ckyb0nd

Member
47
11
8
Location
Ct
Made some progress out of boredum, I made a small c++ app to emulate a genset and am able to connect to the software and get some data but without a real machine to have to extract data from I am unsure what data to respond back with...from kloppk's data table above I may have enough to slowly figure this out as the applications disassembly is quite convoluted with all the external dll imports and this may be faster. Below is the data that my emulator recieves from a fresh connection, i am simply echoing back "83 03 00 00 01 81" currently after every full request sent from the software until the software gives an error and disconnects due to an "unsupported device" surely from invalid responses after connect when it requests genset data.
so are you creating a simulation with the Inpower DLLs? Or what are you basing the generator Sim on?
 

Icesythe7

Active member
147
223
43
Location
Indiana, USA
so are you creating a simulation with the Inpower DLLs? Or what are you basing the generator Sim on?
Yes using the remote monitoring tool and my "emulator" to emulate it connecting to a genset, if we can figure out all the communication properly can make something cool like an stm32 or esp MCU connected to the genset and forward all data wirelessly to phone etc to monitor and do things from anywhere
 

Digger556

Well-known member
Steel Soldiers Supporter
267
606
93
Location
Denver CO
Tag for more information.

I'm curious what can be extracted from the J1939 data, since it is very standardized.
 

R1ckyb0nd

Member
47
11
8
Location
Ct
Yes using the remote monitoring tool and my "emulator" to emulate it connecting to a genset, if we can figure out all the communication properly can make something cool like an stm32 or esp MCU connected to the genset and forward all data wirelessly to phone etc to monitor and do things from anywhere
What are you using to make the emulator?
 

Icesythe7

Active member
147
223
43
Location
Indiana, USA
What are you using to make the emulator?
C++ just on my computer using a virtual com port at the moment, once I understand how it works I can port it to an stm32 (Arduinos will not be able to communicate since they have some lines tied high[unless you make a new pcb and reroute them but why use an arduino anyway when better MCU's exist like esp's stm32's and pico's XD]), I posted above how you can do the same with com0com and the code
 
Top