PGN221 - Display machine messages on AOG screen

I searched Discourse for PGN221 and did not find much discussion of it, so I thought I would post what I have found and how I am using it.


In AOG 6.8.6, PGN221 can be used by a machine/controller to send a hardware message that AOG displays as a single-line banner. I am using it in my sprayer controller for operator information and warnings such as low pressure, too fast/slow, invalid setup, and setup summaries.

The AgIO frame I am sending is:

Byte 0    0x80
Byte 1    0x81
Byte 2    0x7F        Source
Byte 3    0xDD        PGN 221
Byte 4    payload length

Payload:
Byte 5    display time in seconds
Byte 6    color/type
Byte 7+   UTF-8 message text

Last byte  checksum

Or looking only at the PGN221 payload:

Payload byte 0 = display time in seconds
Payload byte 1 = color/type
Payload byte 2+ = UTF-8 message text

For the display time, 0 leaves the message displayed until the operator clicks/touches the banner. A non-zero value displays it for that number of seconds.

For the color/type, what I see in AOG 6.8.6 is:

0        warning / salmon colored
non-zero information / bisque colored

For example, my controller sends messages such as:

D02:PRESSURE LOW - BOOM OFF
D04:TOO FAST
D05:TOO SLOW
D06:PRESSURE OK - WORK SWITCH TO RESTART

I also use a PGN221 message at startup/setup to show the operator the calculated sprayer setup.

A simple example packet for an 8-second warning would therefore be conceptually:

80 81 7F DD [length] 08 00 "message text" [checksum]

My implementation calculates payload length as message length + 2, then appends the normal AgIO additive checksum as the final frame byte.

This has been useful because it lets the implement/controller report something more descriptive than just an LED or section state. In my case the sprayer controller can tell AOG why it has inhibited spraying even though the underlying machine communication is still operating.

I would be interested to know whether anyone else is using PGN221, whether there are additional supported values for the color/type byte, or whether its intended use has changed in newer AOG versions.


1 Like

here is the way I used it on the Teensy (probably copied from someone)

// sendHardwareMessage("put your message here", 3, 1, ipDestination, 9999)

void sendHardwareMessage(const String& message, uint8_t seconds, uint8_t color, IPAddress ipDest, uint16_t port)
{
    uint8_t hardwareMessage[128] = { 0x80, 0x81, 0x7E, 221 };

    int msgLen = message.length();    // byte count (ASCII assumed)
    if (msgLen > 120) {
        Serial.println("Error: Message too long for hardware message buffer");
        return;
    }
    int totalLength = 7 + msgLen + 1; // header(7) + message + CRC(1)

    hardwareMessage[4] = msgLen + 2;  // message length + display config
    hardwareMessage[5] = seconds;           // seconds to display
    hardwareMessage[6] = color;           // color (0 = normal, 1 = alt)

    // Copy message into buffer
    message.getBytes(&hardwareMessage[7], msgLen + 1);

    // Copy the range we need for checksum into temp buffer
    uint8_t temp[128];
    int checksumLen = 7 + msgLen - 2; // from index 2 up to 6+msgLen
    memcpy(temp, hardwareMessage + 2, checksumLen);

    // Sum for checksum
    int16_t CK_A = 0;
    for (int i = 2; i < 7 + msgLen; i++)
    {
        CK_A += hardwareMessage[i];
    }
    hardwareMessage[7 + msgLen] = CK_A; // CRC

    SendHardwareMessage(hardwareMessage, totalLength, ipDest, port);
}

void SendHardwareMessage(uint8_t data[], uint8_t size, IPAddress ipDest, uint16_t port){
    Udp.beginPacket(ipDest, port);
	Udp.write(data, size);
	Udp.endPacket();
}

There are only 2 colors

That nice to know! I wasn’t aware

I tried to use for CANBUS autosteer disengagement cause message, but I had some wrong messages and no time to test further

I think @theGtknerd also used it, I think my code is from him originally

1 Like

I use it to send the Canbus state of Challenger tractors, and warnings to slow down to engage, and WAS plausibility errors.

1 Like