Yes those PGNs (not sure about the last one) just need to be broadcast on the bus to the 255 j1939 destination address.
Remember in j1939 a CAN ID is broken up into sub fields. PGNs only make sense in this context (in other words a CAN ID can be different with the same PGN, depending on the destination address). Inside a j1939 message is payload (up to 8 bytes), divided into fields according to a formal SPN specification, see below for a pdf.
Here’s a couple of functions to compose and decompose CAN IDs:
void j1939_decode(long ID, unsigned long* PGN, byte* priority, byte* src_addr, byte *dest_addr)
{
/* decode j1939 fields from 29-bit CAN id */
*src_addr = 255;
*dest_addr = 255;
*priority = (int)((ID & 0x1C000000) >> 26);
*PGN = ID & 0x00FFFF00;
*PGN = *PGN >> 8;
ID = ID & 0x000000FF;
*src_addr = (int)ID;
/* decode dest_addr if a peer to peer message */
if( (*PGN > 0 && *PGN <= 0xEFFF) ||
(*PGN > 0x10000 && *PGN <= 0x1EFFF) ) {
*dest_addr = (int)(*PGN & 0xFF);
*PGN = *PGN & 0x01FF00;
}
}
long j1939_encode(unsigned long pgn, byte priority, byte src_addr, byte dest_addr)
{
long id;
id = (priority & 0x07) << 26; //three bits only
/* if a peer to peer message, encode dest_addr */
if ((pgn > 0 && pgn <= 0xEFFF) ||
(pgn > 0x10000 && pgn <= 0x1efff)) {
pgn = pgn & 0x01ff00;
pgn = pgn | dest_addr;
}
id = id | (pgn << 8);
id = id | src_addr;
return id;
}
Here’s some code from my project (which uses my custom CAN frame wrapper, so it would need adaptation) that creates some of those CAN messages. I’m not sure if the sender’s address matters that much. For the most part the multi-byte fields used in j1939 are little endian, which is why my CAN frame wrapper can deal with uint64s, uint32s, uint16s, and individual bytes, depending on what’s needed. I can post my CAN Frame wrapper if needed (makes it nice for accessing the individual j1939 SPN fields). My code uses 28 because that’s what John Deere receivers use:
//gps position
//create pgn 65267, priority 3, source 28, dest 2555
msg.set_id(j1939_encode(65267, 3, 28, 255));
msg.get_data()->uint32[0] = (latitude + 210.0) * 10000000;
msg.get_data()->uint32[1] = (longitude + 210.0) * 10000000;
can0.write(msg);
//PGN 65254, priority 3, src 28, dest 255
//date and time
datetime = (uint64_t)(seconds * 4);
datetime |= ((uint64_t)minutes << 8);
datetime |= ((uint64_t)hours << 16);
datetime |= ((uint64_t)month << 24)
datetime |= (((uint64_t)day * 4) << 32);
datetime |= (((uint64_t)year - 1985) << 40);
datetime |= ((uint64_t)125 << 48); //zero
datetime |= ((uint64_t)125 << 56); //zero
msg.set_id(j1939_encode(65254,3,28,255));
msg.get_data()->uint64 = datetime;
can0.write(msg);
// vehicle direction and speed
// heading is uint16[0] / 128.0
// speed is uint16[1] / 256.0 for km/h
// pitch is uint16[2] / 128.0 - 200.0 for angle, pos is climbing, neg is sinking
// altitude is uint16[3] / 0.125 - 2500 for metres.
//vehicle direction and speed
//pgn 65256, priority 3, src 28, dest 255
msg.set_id(j1939_encode(65256,3,28,255));
msg.get_data()->uint16[0] = heading * 128; //degrees
msg.get_data()->uint16[1] = speed * 256; //kph
msg.get_data()->uint16[2] = 200 * 128; //not sure how critical vehicle pitch is 200 is zero
msg.get_data()->uint16[3] = (altitude + 2500) * 8; //metres
can0.write(msg);
Here’s a good resource https://gurtam.com/files/ftp/CAN/j1939-71.pdf
My CAN frame wrapper object is at: gps_j1939/canframe.h at master · torriem/gps_j1939 · GitHub