Update dumperwithfilter.ino
This commit is contained in:
@@ -9,21 +9,51 @@ MCP_CAN CAN(CAN_CS_PIN);
|
||||
const byte CAN_SPEED = CAN_500KBPS;
|
||||
const byte OSC = MCP_8MHZ;
|
||||
|
||||
const size_t MAX_IDS = 256;
|
||||
// REDUCED: 64 IDs (saves memory)
|
||||
const size_t MAX_IDS = 64;
|
||||
uint32_t seenIds[MAX_IDS];
|
||||
uint16_t seenCounts[MAX_IDS];
|
||||
uint16_t changeCounts[MAX_IDS]; // NEW: Track number of changes
|
||||
size_t idsUsed = 0;
|
||||
|
||||
// Store last seen data for change detection
|
||||
byte lastData[MAX_IDS][8];
|
||||
byte lastDataLen[MAX_IDS];
|
||||
|
||||
// REDUCED: 16 filter IDs (saves 64 bytes)
|
||||
const size_t MAX_FILTER_IDS = 16;
|
||||
unsigned long filterIds[MAX_FILTER_IDS];
|
||||
size_t filterCount = 0;
|
||||
bool filterEnabled = false; // set true to enable filtering at startup
|
||||
bool filterEnabled = false;
|
||||
bool filterReverse = false;
|
||||
|
||||
char lineBuf[64];
|
||||
// REDUCED: 48 bytes linebuf (saves 16 bytes)
|
||||
char lineBuf[48];
|
||||
size_t lineLen = 0;
|
||||
|
||||
bool showTxOutput = true;
|
||||
bool showChangeOnly = false;
|
||||
uint8_t frameTypeFilter = 0;
|
||||
|
||||
// ========== Send Task Structure ==========
|
||||
struct SendTask {
|
||||
unsigned long id;
|
||||
unsigned long interval_ms;
|
||||
unsigned long last_send_ms;
|
||||
byte data[8];
|
||||
byte len;
|
||||
bool active;
|
||||
bool extended;
|
||||
};
|
||||
|
||||
const size_t MAX_SEND_TASKS = 4;
|
||||
SendTask sendTasks[MAX_SEND_TASKS];
|
||||
bool canSendMode = false;
|
||||
|
||||
// ========== Functions ==========
|
||||
|
||||
inline uint32_t packId(unsigned long id, bool extended) {
|
||||
return (extended ? 0x80000000UL : 0) | (id & 0x1FFFFFFFUL);
|
||||
return (extended ? 0x80000000UL : 0) | (id & 0x1FFFFFFFUL);
|
||||
}
|
||||
inline bool isExtendedPacked(uint32_t packed) { return (packed & 0x80000000UL) != 0; }
|
||||
inline unsigned long unpackId(uint32_t packed) { return packed & 0x1FFFFFFFUL; }
|
||||
@@ -35,44 +65,110 @@ int16_t findSeenIndex(uint32_t packed) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool rememberId(uint32_t packed) {
|
||||
int16_t idx = findSeenIndex(packed);
|
||||
// UPDATED: Store OLD data before updating, return change status
|
||||
// Returns: 0=new ID, 1=no change, 2=data changed
|
||||
uint8_t checkAndUpdateData(uint32_t packed, byte* newData, byte newLen, int16_t &idx, byte* oldData, byte &oldLen) {
|
||||
idx = findSeenIndex(packed);
|
||||
|
||||
if (idx >= 0) {
|
||||
if (seenCounts[idx] != 0xFFFF) seenCounts[idx]++;
|
||||
return false;
|
||||
|
||||
// Store old data
|
||||
oldLen = lastDataLen[idx];
|
||||
for (byte i = 0; i < oldLen; i++) {
|
||||
oldData[i] = lastData[idx][i];
|
||||
}
|
||||
|
||||
// Check if data changed
|
||||
bool changed = false;
|
||||
if (lastDataLen[idx] != newLen) {
|
||||
changed = true;
|
||||
} else {
|
||||
for (byte i = 0; i < newLen; i++) {
|
||||
if (lastData[idx][i] != newData[i]) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Increment change counter if data changed
|
||||
if (changed && changeCounts[idx] != 0xFFFF) {
|
||||
changeCounts[idx]++;
|
||||
}
|
||||
|
||||
// Update stored data
|
||||
lastDataLen[idx] = newLen;
|
||||
for (byte i = 0; i < newLen; i++) {
|
||||
lastData[idx][i] = newData[i];
|
||||
}
|
||||
|
||||
return changed ? 2 : 1;
|
||||
}
|
||||
|
||||
// New ID
|
||||
if (idsUsed < MAX_IDS) {
|
||||
idx = idsUsed;
|
||||
seenIds[idsUsed] = packed;
|
||||
seenCounts[idsUsed] = 1;
|
||||
changeCounts[idsUsed] = 0; // NEW: Initialize change count to 0
|
||||
lastDataLen[idsUsed] = newLen;
|
||||
for (byte i = 0; i < newLen; i++) {
|
||||
lastData[idsUsed][i] = newData[i];
|
||||
}
|
||||
idsUsed++;
|
||||
return 0; // New ID
|
||||
}
|
||||
return true;
|
||||
|
||||
return 1; // Array full
|
||||
}
|
||||
|
||||
void printSummary() {
|
||||
Serial.print(F("\nSummary: "));
|
||||
Serial.print(F("\nSum: "));
|
||||
Serial.print(idsUsed);
|
||||
Serial.println(F(" unique IDs seen."));
|
||||
Serial.println(F(" IDs"));
|
||||
for (size_t i = 0; i < idsUsed; i++) {
|
||||
bool ext = isExtendedPacked(seenIds[i]);
|
||||
unsigned long id = unpackId(seenIds[i]);
|
||||
Serial.print(F(" ID:0x"));
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.print(id, HEX);
|
||||
if (ext) Serial.print(F(" (ext)"));
|
||||
Serial.print(F(" count:"));
|
||||
Serial.println(seenCounts[i]);
|
||||
if (ext) Serial.print(F("(E)"));
|
||||
Serial.print(F(" n: "));
|
||||
Serial.print(seenCounts[i]);
|
||||
Serial.print(F(" chg:")); // NEW: Show change count
|
||||
Serial.print(changeCounts[i]);
|
||||
Serial.print(F(" ["));
|
||||
for (byte j = 0; j < lastDataLen[i]; j++) {
|
||||
if (lastData[i][j] < 16) Serial.print('0');
|
||||
Serial.print(lastData[i][j], HEX);
|
||||
if (j < lastDataLen[i] - 1) Serial.print(' ');
|
||||
}
|
||||
Serial.println(F("]"));
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
bool idInFilter(unsigned long id) {
|
||||
if (!filterEnabled || filterCount == 0) return true;
|
||||
bool isIdInList(unsigned long id) {
|
||||
for (size_t i = 0; i < filterCount; i++) {
|
||||
if (filterIds[i] == id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool shouldDisplayId(unsigned long id) {
|
||||
if (! filterEnabled) return true;
|
||||
if (filterCount == 0) return false;
|
||||
bool inList = isIdInList(id);
|
||||
return filterReverse ? !inList : inList;
|
||||
}
|
||||
|
||||
bool shouldDisplayFrameType(bool extended) {
|
||||
if (frameTypeFilter == 0) return true;
|
||||
if (frameTypeFilter == 1) return extended;
|
||||
if (frameTypeFilter == 2) return !extended;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addFilterId(unsigned long id) {
|
||||
for (size_t i = 0; i < filterCount; i++) {
|
||||
if (filterIds[i] == id) return false;
|
||||
@@ -87,16 +183,26 @@ void clearFilter() {
|
||||
}
|
||||
|
||||
void listFilter() {
|
||||
Serial.print(F("Filter "));
|
||||
Serial.print(filterEnabled ? F("ENABLED") : F("DISABLED"));
|
||||
Serial.print(F(". IDs ("));
|
||||
Serial.print(F("Filt "));
|
||||
Serial.print(filterEnabled ? F("ON") : F("OFF"));
|
||||
Serial.print(F(" "));
|
||||
Serial.print(filterReverse ? F("REV") : F("NOR"));
|
||||
Serial.print(F(" ("));
|
||||
Serial.print(filterCount);
|
||||
Serial.println(F("):"));
|
||||
Serial.println(F(")"));
|
||||
for (size_t i = 0; i < filterCount; i++) {
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.println(filterIds[i], HEX);
|
||||
}
|
||||
if (filterCount == 0) Serial.println(F(" <none>"));
|
||||
if (filterCount == 0 && filterEnabled) Serial.println(F(" (no output)"));
|
||||
|
||||
Serial.print(F("Frame: "));
|
||||
if (frameTypeFilter == 0) Serial.println(F("BOTH"));
|
||||
else if (frameTypeFilter == 1) Serial.println(F("EXT"));
|
||||
else Serial.println(F("NORM"));
|
||||
|
||||
Serial.print(F("Change: "));
|
||||
Serial.println(showChangeOnly ? F("ON") : F("OFF"));
|
||||
}
|
||||
|
||||
static void trimInPlace(char* s) {
|
||||
@@ -115,8 +221,7 @@ static char* nextToken(char* &p) {
|
||||
}
|
||||
|
||||
bool parseHexToken(const char* tok, unsigned long &out) {
|
||||
if (!tok || !*tok) return false;
|
||||
// optional 0x/0X
|
||||
if (! tok || ! *tok) return false;
|
||||
if ((tok[0] == '0') && (tok[1] == 'x' || tok[1] == 'X')) tok += 2;
|
||||
char* endp = nullptr;
|
||||
unsigned long val = strtoul(tok, &endp, 16);
|
||||
@@ -125,19 +230,239 @@ bool parseHexToken(const char* tok, unsigned long &out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void printHelp() {
|
||||
Serial.println(F("\nCommands:"));
|
||||
Serial.println(F(" s -> summary of all IDs seen"));
|
||||
Serial.println(F(" c -> clear remembered IDs"));
|
||||
Serial.println(F(" fe -> enable show-only filter"));
|
||||
Serial.println(F(" fd -> disable show-only filter"));
|
||||
Serial.println(F(" fl -> list filter IDs"));
|
||||
Serial.println(F(" fx -> clear filter IDs"));
|
||||
Serial.println(F(" f <hex> -> set single filter to <hex> (replaces list)"));
|
||||
Serial.println(F(" a <hex> -> add <hex> to filter list"));
|
||||
Serial.println(F("Examples: f 100 (only show 0x100), a 203 (also show 0x203), fe"));
|
||||
bool parseDecToken(const char* tok, unsigned long &out) {
|
||||
if (! tok || !*tok) return false;
|
||||
char* endp = nullptr;
|
||||
unsigned long val = strtoul(tok, &endp, 10);
|
||||
if (endp == tok) return false;
|
||||
out = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ========== Send Task Functions ==========
|
||||
|
||||
void initSendTasks() {
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
sendTasks[i].active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void switchToSendMode() {
|
||||
if (! canSendMode) {
|
||||
CAN.setMode(MCP_NORMAL);
|
||||
canSendMode = true;
|
||||
Serial.println(F("Mode: NORM"));
|
||||
}
|
||||
}
|
||||
|
||||
void switchToListenMode() {
|
||||
if (canSendMode) {
|
||||
CAN.setMode(MCP_LISTENONLY);
|
||||
canSendMode = false;
|
||||
Serial. println(F("Mode: LIST"));
|
||||
}
|
||||
}
|
||||
|
||||
int8_t findFreeSendTaskSlot() {
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
if (!sendTasks[i].active) return (int8_t)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int8_t findTaskByIdIdx(unsigned long id) {
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
if (sendTasks[i].active && sendTasks[i].id == id) {
|
||||
return (int8_t)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool addSendTask(unsigned long id, bool extended, unsigned long interval_ms, byte* data, byte len) {
|
||||
int8_t existingSlot = findTaskByIdIdx(id);
|
||||
int8_t slot;
|
||||
|
||||
if (existingSlot >= 0) {
|
||||
slot = existingSlot;
|
||||
Serial.print(F("Upd #"));
|
||||
Serial.print(slot);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.println(id, HEX);
|
||||
} else {
|
||||
slot = findFreeSendTaskSlot();
|
||||
if (slot < 0) {
|
||||
Serial.println(F("Full"));
|
||||
return false;
|
||||
}
|
||||
Serial.print(F("Task #"));
|
||||
Serial.print(slot);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.print(id, HEX);
|
||||
}
|
||||
|
||||
if (len > 8) len = 8;
|
||||
|
||||
sendTasks[slot].id = id;
|
||||
sendTasks[slot].extended = extended;
|
||||
sendTasks[slot].interval_ms = interval_ms;
|
||||
sendTasks[slot].len = len;
|
||||
for (byte i = 0; i < len; i++) {
|
||||
sendTasks[slot].data[i] = data[i];
|
||||
}
|
||||
sendTasks[slot]. last_send_ms = millis();
|
||||
sendTasks[slot]. active = true;
|
||||
|
||||
switchToSendMode();
|
||||
|
||||
if (extended) Serial.print(F("(E)"));
|
||||
Serial.print(F(" @"));
|
||||
Serial.print(interval_ms);
|
||||
Serial.println(F("ms"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void stopSendTask(size_t index) {
|
||||
if (index >= MAX_SEND_TASKS || ! sendTasks[index].active) {
|
||||
Serial.println(F("Invalid"));
|
||||
return;
|
||||
}
|
||||
sendTasks[index].active = false;
|
||||
Serial.print(F("Stop #"));
|
||||
Serial.println(index);
|
||||
|
||||
bool anyActive = false;
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
if (sendTasks[i].active) {
|
||||
anyActive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! anyActive) switchToListenMode();
|
||||
}
|
||||
|
||||
void stopAllSendTasks() {
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
sendTasks[i].active = false;
|
||||
}
|
||||
Serial.println(F("Stop all"));
|
||||
switchToListenMode();
|
||||
}
|
||||
|
||||
void listSendTasks() {
|
||||
Serial.println(F("\n=== Tasks ==="));
|
||||
bool anyActive = false;
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
if (sendTasks[i]. active) {
|
||||
anyActive = true;
|
||||
Serial.print(F("#"));
|
||||
Serial.print(i);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.print(sendTasks[i].id, HEX);
|
||||
if (sendTasks[i].extended) Serial.print(F("(E)"));
|
||||
Serial.print(F(" @"));
|
||||
Serial.print(sendTasks[i].interval_ms);
|
||||
Serial.print(F("ms ["));
|
||||
for (byte j = 0; j < sendTasks[i].len; j++) {
|
||||
if (sendTasks[i].data[j] < 16) Serial.print('0');
|
||||
Serial.print(sendTasks[i].data[j], HEX);
|
||||
if (j < sendTasks[i]. len - 1) Serial.print(' ');
|
||||
}
|
||||
Serial.println(F("]"));
|
||||
}
|
||||
}
|
||||
if (!anyActive) Serial.println(F("<none>"));
|
||||
Serial.print(F("TX: "));
|
||||
Serial.println(showTxOutput ? F("ON") : F("OFF"));
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void processSendTasks() {
|
||||
unsigned long now = millis();
|
||||
for (size_t i = 0; i < MAX_SEND_TASKS; i++) {
|
||||
if (sendTasks[i]. active) {
|
||||
if (now - sendTasks[i].last_send_ms >= sendTasks[i].interval_ms) {
|
||||
byte sndStat = sendTasks[i].extended ?
|
||||
CAN.sendMsgBuf(sendTasks[i]. id, 1, sendTasks[i].len, sendTasks[i].data) :
|
||||
CAN.sendMsgBuf(sendTasks[i]. id, 0, sendTasks[i].len, sendTasks[i].data);
|
||||
|
||||
if (showTxOutput) {
|
||||
if (sndStat == CAN_OK) {
|
||||
Serial.print(F("[TX#"));
|
||||
Serial.print(i);
|
||||
Serial.print(F("] 0x"));
|
||||
Serial.println(sendTasks[i].id, HEX);
|
||||
} else {
|
||||
Serial.print(F("[ERR#"));
|
||||
Serial.print(i);
|
||||
Serial.print(F(": "));
|
||||
Serial.print(sndStat);
|
||||
Serial. println(F("]"));
|
||||
}
|
||||
}
|
||||
|
||||
sendTasks[i].last_send_ms = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sendSinglePacket(unsigned long id, bool extended, byte* data, byte len) {
|
||||
if (! canSendMode) switchToSendMode();
|
||||
|
||||
byte sndStat = extended ?
|
||||
CAN.sendMsgBuf(id, 1, len, data) :
|
||||
CAN.sendMsgBuf(id, 0, len, data);
|
||||
|
||||
if (showTxOutput) {
|
||||
if (sndStat == CAN_OK) {
|
||||
Serial.print(F("[TX] 0x"));
|
||||
Serial.print(id, HEX);
|
||||
if (extended) Serial.print(F("(E)"));
|
||||
Serial.print(F(" ["));
|
||||
for (byte i = 0; i < len; i++) {
|
||||
if (data[i] < 16) Serial.print('0');
|
||||
Serial.print(data[i], HEX);
|
||||
if (i < len - 1) Serial.print(' ');
|
||||
}
|
||||
Serial.println(F("]"));
|
||||
return true;
|
||||
} else {
|
||||
Serial.print(F("[TX ERR: "));
|
||||
Serial.print(sndStat);
|
||||
Serial.println(F("]"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (sndStat == CAN_OK);
|
||||
}
|
||||
|
||||
// ========== Help ==========
|
||||
|
||||
void printHelp() {
|
||||
Serial.println(F("\n=== Log ==="));
|
||||
Serial.println(F("s summary"));
|
||||
Serial.println(F("c clear"));
|
||||
Serial.println(F("fe/fd filt on/off"));
|
||||
Serial.println(F("fr toggle rev"));
|
||||
Serial.println(F("fl list filt"));
|
||||
Serial.println(F("fx clear filt"));
|
||||
Serial.println(F("f/a <id> set/add"));
|
||||
Serial.println(F("fb/fext/fnorm"));
|
||||
Serial.println(F("ce/cd change on/off"));
|
||||
|
||||
Serial.println(F("\n=== Send ==="));
|
||||
Serial.println(F("w <id> <data>"));
|
||||
Serial.println(F("t <id> <ms> <data>"));
|
||||
Serial.println(F("tl/ts/tx"));
|
||||
Serial.println(F("txe/txd"));
|
||||
Serial.println(F("mn/ml"));
|
||||
}
|
||||
|
||||
// ========== Command Handler ==========
|
||||
|
||||
void handleCommandLine(char* line) {
|
||||
trimInPlace(line);
|
||||
if (line[0] == 0) return;
|
||||
@@ -148,52 +473,85 @@ void handleCommandLine(char* line) {
|
||||
char* p = line;
|
||||
char* cmd = nextToken(p);
|
||||
|
||||
// Single-letter quick commands
|
||||
if (strcmp(cmd, "s") == 0 || strcmp(cmd, "S") == 0) {
|
||||
printSummary();
|
||||
return;
|
||||
}
|
||||
if (strcmp(cmd, "c") == 0 || strcmp(cmd, "C") == 0) {
|
||||
idsUsed = 0;
|
||||
Serial.println(F("Cleared remembered IDs."));
|
||||
Serial.println(F("Cleared"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "ce") == 0) {
|
||||
showChangeOnly = true;
|
||||
Serial.println(F("Change ON"));
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "cd") == 0) {
|
||||
showChangeOnly = false;
|
||||
Serial.println(F("Change OFF"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "fb") == 0) {
|
||||
frameTypeFilter = 0;
|
||||
Serial.println(F("Frame: BOTH"));
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "fext") == 0) {
|
||||
frameTypeFilter = 1;
|
||||
Serial.println(F("Frame: EXT"));
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "fnorm") == 0) {
|
||||
frameTypeFilter = 2;
|
||||
Serial.println(F("Frame: NORM"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "fe") == 0) {
|
||||
filterEnabled = true;
|
||||
Serial.println(F("Filter ENABLED."));
|
||||
Serial.println(F("Filter ON"));
|
||||
listFilter();
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "fd") == 0) {
|
||||
filterEnabled = false;
|
||||
Serial.println(F("Filter DISABLED. All IDs will be shown."));
|
||||
Serial.println(F("Filter OFF"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "fr") == 0) {
|
||||
filterReverse = ! filterReverse;
|
||||
Serial. print(F("Mode: "));
|
||||
Serial.println(filterReverse ? F("REV") : F("NORM"));
|
||||
listFilter();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "fl") == 0) {
|
||||
listFilter();
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "fx") == 0) {
|
||||
clearFilter();
|
||||
Serial.println(F("Filter list cleared."));
|
||||
Serial.println(F("Filt clear"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Commands with arguments
|
||||
if (strcasecmp(cmd, "f") == 0) {
|
||||
// set single filter ID
|
||||
char* tok = nextToken(p);
|
||||
unsigned long id;
|
||||
if (parseHexToken(tok, id)) {
|
||||
clearFilter();
|
||||
addFilterId(id);
|
||||
filterEnabled = true;
|
||||
Serial.print(F("Filter set to only show ID 0x"));
|
||||
Serial. print(F("Filt: 0x"));
|
||||
Serial.println(id, HEX);
|
||||
return;
|
||||
}
|
||||
Serial.println(F("Invalid hex after 'f'. Example: f 100"));
|
||||
Serial.println(F("Bad ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -202,39 +560,142 @@ void handleCommandLine(char* line) {
|
||||
unsigned long id;
|
||||
if (parseHexToken(tok, id)) {
|
||||
if (addFilterId(id)) {
|
||||
Serial.print(F("Added 0x"));
|
||||
Serial.print(id, HEX);
|
||||
Serial.println(F(" to filter list."));
|
||||
Serial.print(F("Add 0x"));
|
||||
Serial.println(id, HEX);
|
||||
} else {
|
||||
Serial.println(F("Failed to add (duplicate or list full)."));
|
||||
Serial. println(F("Fail"));
|
||||
}
|
||||
filterEnabled = true;
|
||||
return;
|
||||
}
|
||||
Serial.println(F("Invalid hex after 'a'. Example: a 203"));
|
||||
Serial.println(F("Bad ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "mn") == 0) {
|
||||
switchToSendMode();
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "ml") == 0) {
|
||||
stopAllSendTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "txe") == 0) {
|
||||
showTxOutput = true;
|
||||
Serial.println(F("TX ON"));
|
||||
return;
|
||||
}
|
||||
if (strcasecmp(cmd, "txd") == 0) {
|
||||
showTxOutput = false;
|
||||
Serial.println(F("TX OFF"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "w") == 0) {
|
||||
char* idTok = nextToken(p);
|
||||
unsigned long id;
|
||||
if (! parseHexToken(idTok, id)) {
|
||||
Serial.println(F("Bad ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
byte data[8];
|
||||
byte len = 0;
|
||||
char* dataTok;
|
||||
while ((dataTok = nextToken(p)) != nullptr && len < 8) {
|
||||
unsigned long byteVal;
|
||||
if (parseHexToken(dataTok, byteVal)) {
|
||||
data[len++] = (byte)byteVal;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
Serial.println(F("No data"));
|
||||
return;
|
||||
}
|
||||
|
||||
sendSinglePacket(id, false, data, len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "t") == 0) {
|
||||
char* idTok = nextToken(p);
|
||||
char* intervalTok = nextToken(p);
|
||||
|
||||
unsigned long id, interval;
|
||||
if (! parseHexToken(idTok, id) || !parseDecToken(intervalTok, interval)) {
|
||||
Serial.println(F("Bad args"));
|
||||
return;
|
||||
}
|
||||
|
||||
byte data[8];
|
||||
byte len = 0;
|
||||
char* dataTok;
|
||||
while ((dataTok = nextToken(p)) != nullptr && len < 8) {
|
||||
unsigned long byteVal;
|
||||
if (parseHexToken(dataTok, byteVal)) {
|
||||
data[len++] = (byte)byteVal;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
Serial.println(F("No data"));
|
||||
return;
|
||||
}
|
||||
|
||||
addSendTask(id, false, interval, data, len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "tl") == 0) {
|
||||
listSendTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "ts") == 0) {
|
||||
char* indexTok = nextToken(p);
|
||||
unsigned long index;
|
||||
if (!parseDecToken(indexTok, index)) {
|
||||
Serial.println(F("Bad idx"));
|
||||
return;
|
||||
}
|
||||
stopSendTask((size_t)index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(cmd, "tx") == 0) {
|
||||
stopAllSendTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
printHelp();
|
||||
}
|
||||
|
||||
// ========== Setup ==========
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(50);
|
||||
|
||||
pinMode(CAN_INT_PIN, INPUT);
|
||||
initSendTasks();
|
||||
|
||||
if (CAN.begin(MCP_ANY, CAN_SPEED, OSC) != CAN_OK) {
|
||||
Serial.println(F("CAN init failed. Check wiring, bitrate, and 8 MHz oscillator."));
|
||||
if (CAN. begin(MCP_ANY, CAN_SPEED, OSC) != CAN_OK) {
|
||||
Serial.println(F("CAN fail"));
|
||||
while (1) { delay(1000); }
|
||||
}
|
||||
|
||||
CAN.setMode(MCP_LISTENONLY); // sniffer mode
|
||||
Serial.println(F("CAN listen-only started."));
|
||||
CAN.setMode(MCP_LISTENONLY);
|
||||
Serial.println(F("CAN Ready"));
|
||||
printHelp();
|
||||
listFilter();
|
||||
}
|
||||
|
||||
// ========== Loop ==========
|
||||
|
||||
void loop() {
|
||||
// Serial commands
|
||||
while (Serial.available() > 0) {
|
||||
char ch = (char)Serial.read();
|
||||
if (ch == '\r') {
|
||||
@@ -258,46 +719,72 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Process send tasks
|
||||
processSendTasks();
|
||||
|
||||
// Receive CAN
|
||||
while (CAN_MSGAVAIL == CAN.checkReceive()) {
|
||||
unsigned long id = 0;
|
||||
byte ext = 0;
|
||||
byte len = 0;
|
||||
byte buf[8];
|
||||
if (CAN.readMsgBuf(&id, &ext, &len, buf) != CAN_OK) {
|
||||
continue;
|
||||
}
|
||||
if (CAN.readMsgBuf(&id, &ext, &len, buf) != CAN_OK) continue;
|
||||
|
||||
bool extended = (ext != 0);
|
||||
|
||||
uint32_t packed = packId(id, extended);
|
||||
bool isNew = rememberId(packed);
|
||||
|
||||
if (!idInFilter(id)) {
|
||||
continue;
|
||||
|
||||
// Check and update data, store old data
|
||||
byte oldData[8];
|
||||
byte oldLen;
|
||||
int16_t idx;
|
||||
uint8_t changeStatus = checkAndUpdateData(packed, buf, len, idx, oldData, oldLen);
|
||||
|
||||
// Check if should display based on filters
|
||||
bool passesFilters = shouldDisplayId(id) && shouldDisplayFrameType(extended);
|
||||
|
||||
// If change detection is ON, only show if data changed
|
||||
if (showChangeOnly && changeStatus != 2) {
|
||||
continue; // Skip if not changed (status 0=new, 1=no change, 2=changed)
|
||||
}
|
||||
|
||||
// Dump the frame
|
||||
Serial.print(millis());
|
||||
Serial.print(F("ms "));
|
||||
Serial.print(F("ID:0x"));
|
||||
Serial.print(id, HEX);
|
||||
if (extended) Serial.print(F(" (ext)"));
|
||||
Serial.print(F(" DLC:"));
|
||||
Serial.print(len);
|
||||
Serial.print(F(" DATA:"));
|
||||
for (byte i = 0; i < len; i++) {
|
||||
if (buf[i] < 16) Serial.print('0');
|
||||
Serial.print(buf[i], HEX);
|
||||
Serial.print(' ');
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Optional: notify when an ID is seen for the first time
|
||||
if (isNew) {
|
||||
//Serial.print(F(" New ID observed -> 0x"));
|
||||
//Serial.print(id, HEX);
|
||||
//if (extended) Serial.print(F(" (ext)"));
|
||||
//Serial.print(F(" | total unique: "));
|
||||
//Serial.println(idsUsed);
|
||||
|
||||
if (passesFilters) {
|
||||
// Show change indicator and old->new
|
||||
if (showChangeOnly && changeStatus == 2) {
|
||||
Serial.print(F("[CHG] 0x"));
|
||||
Serial.print(id, HEX);
|
||||
if (extended) Serial.print(F("(E)"));
|
||||
Serial.print(F(" ["));
|
||||
|
||||
// Show OLD data
|
||||
for (byte i = 0; i < oldLen; i++) {
|
||||
if (oldData[i] < 16) Serial.print('0');
|
||||
Serial.print(oldData[i], HEX);
|
||||
if (i < oldLen - 1) Serial.print(' ');
|
||||
}
|
||||
|
||||
Serial.print(F("]->["));
|
||||
|
||||
// Show NEW data
|
||||
for (byte i = 0; i < len; i++) {
|
||||
if (buf[i] < 16) Serial.print('0');
|
||||
Serial.print(buf[i], HEX);
|
||||
if (i < len - 1) Serial.print(' ');
|
||||
}
|
||||
Serial.println(F("]"));
|
||||
} else {
|
||||
// Normal display
|
||||
Serial.print(millis());
|
||||
Serial.print(F("ms 0x"));
|
||||
Serial.print(id, HEX);
|
||||
if (extended) Serial.print(F("(E)"));
|
||||
Serial.print(F(" ["));
|
||||
for (byte i = 0; i < len; i++) {
|
||||
if (buf[i] < 16) Serial.print('0');
|
||||
Serial.print(buf[i], HEX);
|
||||
if (i < len - 1) Serial.print(' ');
|
||||
}
|
||||
Serial.println(F("]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user