This project is an Automotive Network Integration and Control Module designed to add functionality to a vehicle by interpreting digital communication signals.
The core function is to passively sniff the vehicle's Local Interconnect Network (LIN) bus using an Arduino Nano and a TLIN1029 transceiver. The system is built to monitor specific LIN messages and use that data to trigger an external accessory. The accessory is controlled by a high-side PMOS in which the output signal — a digital pin on the nano — is connected to the PMOS's gate.
Since I want to be able to access the serial monitor, I have not tied the RX from the transceiver to the D0/D1 (RX/TX) as I would not be able to use the hardware serial monitor. That being said, I am using the SoftwareSerial library to establish a separate port for reading the LIN bus. The code employs protocol parsing by first reading and discarding the Sync Byte (0x55), and then using a bitwise mask (& 0x3F) to accurately extract the 6-bit Message ID from the PID. This ensures reliable message identification before a command is sent to activate the PMOS switch.
I have already created a protoboard, preceding with a schematic drawn. I have verified that the circuit should function properly. However, I am not able to receive any data bytes from the LIN signal. Does anyone have any recommendations for code as I feel that my code logically makes sense but I think it is the culprit as to why I can't obtain the LIN frame.
Thanks to anyone that helps out with this! It means a ton
Is this a Nano classic, or clone, or some other Nano? It really matters, there's a lot of products now with that 4-letter name
have you considered, for example, a Nano V4? I ask, because the Serial for Serial Monitor is separate from the device serial in that case, which will allow you to avoid any form of software Serial.
Schematic, please; it's important, as it helps us assess whether you're also fighting hardware miswiring or inadequacies, including power provisions.
Thanks, and again, Welcome to the forum. You'll find we're eager to help with software, hardware, and integration issues, but we're obsessed with knowing enough about your project to help usefully.
Choose an Arduino (Mega...) with enough hardware Serials to develop your software. Move to the unreliable SoftwareSerial only after you verified your code.
That is a wonderful point you make. Attached below is the code. Please note that there is no output for the code at the moment as I was going to tackle that challenge after I successfully decoded the LIN frame.
#include <SoftwareSerial.h>
// ***PIN DEFINITIONS***
// LIN RX pin (Where the TLIN1029 RXD is connected to the Nano)
#define LIN_RX_PIN 9 //input signal pin
// LIN TX pin (This pin is technically not used for transmission since TXD is tied to 5V, but SoftwareSerial requires a TX pin so I chose to use a random pin available)
#define LIN_TX_PIN 6 //random pin
// PMOS Control Pin
#define PMOS_CONTROL_PIN 3 //output signal pin
// Baud Rate (May change depending on application: either 19200 or 9600)
#define LIN_BAUD_RATE 19200
// Serial Monitor Baud Rate
#define MONITOR_BAUD_RATE 115200
// ***OBJECT INITIALIZATION***
// Create SoftwareSerial object for LIN communication
SoftwareSerial linSerial(LIN_RX_PIN, LIN_TX_PIN);
void setup() {
// 1. Initializing Hardware Serial for the Serial Monitor (via CH340 chipset)
Serial.begin(MONITOR_BAUD_RATE);
// Waiting for Serial Monitor to open
while (!Serial);
Serial.println("--- LIN Bus Sniffer Initialized ---");
Serial.println("Baud Rate: 19200 bps");
// 2. Configuring PMOS Control Pin
pinMode(PMOS_CONTROL_PIN, OUTPUT);
// Initialize PMOS to OFF (P-MOS OFF = Gate HIGH)
digitalWrite(PMOS_CONTROL_PIN, HIGH);
Serial.println("PMOS/Relay initialized to OFF.");
// 3. Initializing SoftwareSerial for LIN
linSerial.begin(LIN_BAUD_RATE);
Serial.println("Waiting for LIN traffic...");
Serial.println("---------------------------------");
Serial.println("ID | D0 D1 D2 D3 D4 D5 D6 D7 | CHKSUM | Frame Length");
Serial.println("---------------------------------");
}
void loop() {
// 1. Waiting for the START of a frame
if (linSerial.available() > 0) {
// 2. Reading the first byte, which should be the Sync Byte (0x55)
byte syncByte = linSerial.read();
// 3. Checking if the read byte is the required Sync Byte (0x55).
if (syncByte == 0x55) {
// For context, the max buffer size: PID (1) + Data (8) + Checksum (1) = 10 bytes
// linFrame[0] will now hold the PID, linFrame[1] the first Data Byte, etc.
byte linFrame[10];
int frameIndex = 0;
// Setting a short timeout (5ms) to define the end of a LIN frame
unsigned long timeout = millis() + 5;
// 4. Reading the rest of the frame (PID, Data, Checksum)
while (linSerial.available() || millis() < timeout) {
if (linSerial.available()) {
linFrame[frameIndex] = linSerial.read();
frameIndex++;
timeout = millis() + 5; // Reset timeout on every new byte
if (frameIndex >= 10) break;
}
}
// 5. Processing the received frame
// Valids the frame so that it must contain at least the PID and Checksum (2 bytes)
if (frameIndex >= 2) {
// linFrame[0] is now the PID
byte protectedId = linFrame[0];
byte linId = protectedId & 0x3F;
// Printing the ID
Serial.print("0x");
if (linId < 0x10) Serial.print("0");
Serial.print(linId, HEX);
Serial.print(" |");
// Calculating Data Length (Total bytes - PID & Checksum)
int dataLength = frameIndex - 2;
// Printing the Data Bytes
for (int i = 1; i <= 8; i++) {
if (i <= dataLength) {
byte dataByte = linFrame[i];
Serial.print(" 0x");
if (dataByte < 0x10) Serial.print("0");
Serial.print(dataByte, HEX);
} else {
Serial.print(" --"); // Using placeholder for unused data bytes
}
}
Serial.print(" |");
// Printing the Checksum (the last byte)
byte checksum = linFrame[frameIndex - 1];
Serial.print(" 0x");
if (checksum < 0x10) Serial.print("0");
Serial.print(checksum, HEX);
// Printing the total received byte count (PID + Data + Checksum)
Serial.print(" | ");
Serial.print(frameIndex);
Serial.println(" bytes");
}
} else {
// If the first byte wasn't 0x55, it was noise or it missed the Sync Break.
// I think it would be necessary to flush the buffer to discard the bad data and wait for the next frame.
while (linSerial.available()) {
linSerial.read();
}
}
}
}
// If the first byte wasn't 0x55, it was noise or it missed the Sync Break.
// I think it would be necessary to flush the buffer to discard the bad data and wait for the next frame.
while (linSerial.available()) {
linSerial.read();
actually peeked ahead, and broke out immediately before 0x55? wouldn't that sync you with the next frame?
This is not a Nano classic. It is a third-party that uses the CH340 chip set so it took some trial and error to figure out what settings needed to be adjusted.
I have not considered the Nano V4. You're saying that I would be able to hook up to the RX/TX pins on the Nano V4 while still being able to use the hardware serial monitor? I feel that's a big caveat I face as I am stuck to using Software Serial since I cannot use the RX/TX supplied pins while simultaneously recording live feed from the hardware monitor that is hooked up to my laptop via USB.
Thanks! I think @camsysca made a valid point with going with an alternative arduino for this project. I am not to familiar with the differences between the arduinos so I just ignorantly picked one at random.
Yes. That's what I'm saying. A V4 has the USB(which appears to be Serial, to the user), and it has RX/TX on a separate UART. Check carefully through the information here:
By the way, what I think you're describing is a Nano Classic clone, which has all the characteristics of the Nano Classic, except a different USB <-> Serial chip. For your purposes, use just like a nano Classic, but yeah, go with the Nano 4 if you want Serial Monitor and a separate hardware UART for your LIN interface. I think it's essential, to avoid getting bogged down in SS-related issues.
That's a valid point about the potential to lose the synchronization byte of the next frame. You're right that a more sophisticated sniffer could try to peek ahead to find the next 0x55 and continue. However, for the limited resources that I have currently, I think a simple and robust sniffer needs that flush. It guarantees that the sniffer completely resets its state and waits for a clean and confirmed start of a new frame. This helps prevent trying to process garbled data.
Now, I still may be misunderstanding this but that is my take. I think regardless, I should obtain a more reliable arduino such as the one you and @DrDiettrich recommended.
I will obtain the Nano V4 and connect the received LIN signal to the RX UART pin on the nano. Everything else in the schematic I will keep the same. Hopefully I will have more accurate and reliable readings and will come back to this post to give an update.
Please let me know if you have any other helpful advice and that goes to anyone else that sees this as well. If you think that changes could be made to the software or hardware schematic feel free to leave a comment!
I still have concerns about your hardware implementation, but that can be discussed if you come back; be sure to add to this thread, not sparking up a new one, just for context.
Good luck!
You cannot connect the gate of a PMOS with 12V on it's source to a 5V or 3.3V Arduino output pin. If the source voltage is 12V, the gate voltage must also be 12V (or greater) to turn the transistor OFF.
Reach out by PM if you'd prefer, though you'll get more useful help here on the open forum. We all have different areas of experience (I shy away from "expertise).