From 845a0407efeaf62226961d580b9162fa0627d1d1 Mon Sep 17 00:00:00 2001 From: Dr-Daily <56745553+Dr-Daily@users.noreply.github.com> Date: Sun, 10 Oct 2021 10:24:48 -0600 Subject: [PATCH] Working UDP example --- .gitignore | 1 + CAN_to_UDP/CAN_to_UDP.ino | 327 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 .gitignore create mode 100644 CAN_to_UDP/CAN_to_UDP.ino diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3578f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*/.vs/* diff --git a/CAN_to_UDP/CAN_to_UDP.ino b/CAN_to_UDP/CAN_to_UDP.ino new file mode 100644 index 0000000..a44d57b --- /dev/null +++ b/CAN_to_UDP/CAN_to_UDP.ino @@ -0,0 +1,327 @@ + +#include +#include +#include +#include +#include // be able to keep realtime. +#include + +FlexCAN_T4 Can0; +FlexCAN_T4 Can1; + +#define CAN0_BAUD_RATE 250000 +#define CAN1_BAUD_RATE 250000 + +#define DHCP_RENEW_THRESHOLD_MS 86400000 //86,400,000 milliseconds in a day +#define NTP_RENEW_INTERVAL_MS 300000 // 300,000 milliseconds in 5 minutes + +EthernetUDP Udp; +#define WIZNET_CHIP_SELECT 10 + + + +uint32_t UDP_TX_Count; +uint32_t CAN0_RX_Count; +uint32_t CAN1_RX_Count; +uint32_t Total_CAN_RX_Count; + +bool LED_state; + +elapsedMillis DHCPLeaseRenewTimer; +elapsedMillis NTPRenewTimer; +elapsedMillis CANmessageTimer; + +elapsedMicros microsecondsPerSecond; + + + +// For a Maximum Transmission Unit (MTU) of 1500, 20 bytes are use for IP headers and 8 bytes are used for UDP headers +// This leaves 1472 bytes for the CAN data. However, if the last CAN frame spilled over the MTU boundary, +// Then the UDP packet will be fragmented and the header data will be sent twice to carry along the extra data. +// Therefore, we want to trigger a send command before the MTU is reached, so our threshold needs to be less than 1472 +// by an amount equal to a can frame. There are 19 bytes per CAN frame. +// See https://wiki.networksecuritytoolkit.org/nstwiki/index.php/LAN_Ethernet_Maximum_Rates,_Generation,_Capturing_&_Monitoring +#define UDP_TX_PACKET_MAX_SIZE 1472 //increase UDP size +#define CAN_FRAME_SIZE 19 +#define CAN_BUFFER_THRESHOLD UDP_TX_PACKET_MAX_SIZE-CAN_FRAME_SIZE +#define UDP_TX_PERIOD_MS 40 + +byte CANBuffer[UDP_TX_PACKET_MAX_SIZE]; +byte packetBuffer[UDP_TX_PACKET_MAX_SIZE]; +uint16_t CANBufferPosition = 0; + +#define PACKED_CAN_FRAMES_NO_REAL_TIME 0x01 + +// We'll determine the MAC based on the TeensyID library. +// https://github.com/sstaub/TeensyID +uint8_t mac[6]; + +// Use this as a placeholder. We'll change it when we get the local IP. +IPAddress broadcast_address(192,168,1,255); +unsigned int localPort = 8899; // local port to listen for UDP packets + +#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes +const char timeServer[] = "time.nist.gov"; // time.nist.gov NTP serverof the message + + +void setup(void) { + pinMode(13, OUTPUT); digitalWrite(13, HIGH); + pinMode(14, OUTPUT); digitalWrite(14, LOW); /* optional tranceiver enable pin */ + pinMode(35, OUTPUT); digitalWrite(35, LOW); /* optional tranceiver enable pin */ + setSyncProvider(getTeensy3Time); + setSyncInterval(1); + + // start Ethernet and UDP + Ethernet.init(WIZNET_CHIP_SELECT); // Based on the Schematic for the CAN-2 Ethernet board. + teensyMAC(mac); // Assign a unique MAC address. This uses the processors MAC (not the Wiznet) + while (true) { + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + } else if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + // no point in carrying on, so do nothing forevermore: + delay(1); + } + else { // Connection is good + Serial.print("The MAC address is: "); + Serial.println(teensyMAC()); + Serial.print("The DNS server IP address is: "); + Serial.println(Ethernet.dnsServerIP()); + Serial.print("The gateway IP address is: "); + Serial.println(Ethernet.gatewayIP()); + // print your local IP address: + Serial.print("The Local IP address is: "); + Serial.println(Ethernet.localIP()); + broadcast_address = Ethernet.localIP(); + broadcast_address[3] = 255; + Serial.print("The Broadcast IP address is: "); + Serial.println(broadcast_address); + Serial.print("The subnet mask is: "); + Serial.println(Ethernet.subnetMask()); + + break; + } + } + + Ethernet.setRetransmissionCount(3); + + // Start the UDP client + if(Udp.begin(localPort)) Serial.println("UDP Started"); + + //Start the CAN channels + Can0.begin(); + Serial.print("CAN0 Bitrate: "); + Serial.print(CAN0_BAUD_RATE); + Can0.setBaudRate(CAN0_BAUD_RATE); + Can0.setMaxMB(16); + Can0.enableFIFO(); + Can0.enableFIFOInterrupt(); + Can0.onReceive(canSniff0); + Serial.println("CAN0 mailbox Status:"); + Can0.mailboxStatus(); //This prints out the status + + Can1.begin(); + Serial.print("CAN1 Bitrate: "); + Serial.print(CAN1_BAUD_RATE); + Can1.setBaudRate(CAN1_BAUD_RATE); + Can1.setMaxMB(16); + Can1.enableFIFO(); + Can1.enableFIFOInterrupt(); + Can1.onReceive(canSniff1); + Serial.println("CAN1 mailbox Status:"); + Can1.mailboxStatus(); //This prints out the status + + NTPRenewTimer = NTP_RENEW_INTERVAL_MS + 1; +} + +// send an NTP request to the time server at the given address +void sendpacket(const char * address) { + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + Udp.beginPacket(address, 123); // NTP requests are to port 123 + Udp.write(packetBuffer, NTP_PACKET_SIZE); + Udp.endPacket(); +} + +void canSniff0(const CAN_message_t &msg) { + CAN0_RX_Count++; + uint8_t channel = 0; + canSniff(msg,channel,CAN0_RX_Count); +} + +void canSniff1(const CAN_message_t &msg) { + CAN1_RX_Count++; + uint8_t channel = 1; + canSniff(msg,channel,CAN1_RX_Count); +} + +void canSniff(const CAN_message_t &msg, uint8_t channel, uint32_t CAN_RX_Count) { + // Format defined by Subhohjeet in the RFC proposal!!! + Total_CAN_RX_Count++; + + // Format 1 + uint32_t micro_t = microsecondsPerSecond; + memcpy(&CANBuffer[CANBufferPosition],&channel,1); + CANBufferPosition += 1; + memcpy(&CANBuffer[CANBufferPosition],µ_t,3); + CANBufferPosition += 3; + memcpy(&CANBuffer[CANBufferPosition],&msg.timestamp,2); + CANBufferPosition += 2; + memcpy(&CANBuffer[CANBufferPosition],&msg.id,4); + CANBufferPosition += 4; + memcpy(&CANBuffer[CANBufferPosition],&msg.len,1); + CANBufferPosition += 1; + memcpy(&CANBuffer[CANBufferPosition],&msg.buf,8); + CANBufferPosition += 8; +} + +/* + * Reset the microsecond counter and return the realtime clock + * This function requires the sync interval to be exactly 1 second. + */ +time_t getTeensy3Time(){ + microsecondsPerSecond = 0; + return Teensy3Clock.get(); +} +void loop() { + Can0.events(); + //Periodically Renew the DHCP Lease + //This may be blocking an affect the logging. Uncomment if needed +// if (DHCPLeaseRenewTimer > DHCP_RENEW_THRESHOLD_MS){ +// DHCPLeaseRenewTimer = 0; +// Serial.print("DHCP Lease Renewal Returned : "); +// Serial.println(Ethernet.maintain()); // Renew DHCP Lease +// } + + // Send a request to get NTP time. + if (NTPRenewTimer >= NTP_RENEW_INTERVAL_MS){ + NTPRenewTimer = 0; + sendpacket(timeServer); + } + + if (CANBufferPosition > CAN_BUFFER_THRESHOLD || CANmessageTimer > UDP_TX_PERIOD_MS){ + CANmessageTimer = 0; + Udp.beginPacket(broadcast_address, 9101); + Udp.write(CANBuffer, CANBufferPosition); + Udp.endPacket(); + Serial.print("Sent UDP Message: "); + for (int i=0; i < CANBufferPosition; i++){ + Serial.printf("%02X ",CANBuffer[i]); + } + Serial.println(); + memset(CANBuffer,0,sizeof(CANBuffer)); + CANBuffer[0] = PACKED_CAN_FRAMES_NO_REAL_TIME; + CANBufferPosition = 1; + + UDP_TX_Count++; + memcpy(&CANBuffer[CANBufferPosition], &UDP_TX_Count, 4); + CANBufferPosition += 4; + + + memcpy(&CANBuffer[CANBufferPosition], &Total_CAN_RX_Count, 4); + CANBufferPosition += 4; + + time_t timeStamp = now(); + memcpy(&CANBuffer[CANBufferPosition], &timeStamp, 4); + CANBufferPosition += 4; + } + + int packetSize = Udp.parsePacket(); + if(Udp.available()){ + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i =0; i < 4; i++) + { + Serial.print(remote[i], DEC); + if (i < 3) + { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBufffer + Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE); + Serial.print("Contents: "); + for (int i=0; i< packetSize; i++){ + Serial.printf("%02X ",packetBuffer[i]); + } + Serial.println(); + + if (Udp.remotePort() == 123){ + // Now, read the data from the UDP packet + + // the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, extract the two words: + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + Serial.print("Seconds since Jan 1 1900 = "); + Serial.println(secsSince1900); + + // now convert NTP time into everyday time: + Serial.print("Unix time = "); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + // print Unix time: + Serial.println(epoch); + + + // print the hour, minute and second: + Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) + Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) + Serial.print(':'); + if (((epoch % 3600) / 60) < 10) { + // In the first 10 minutes of each hour, we'll want a leading '0' + Serial.print('0'); + } + Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) + Serial.print(':'); + if ((epoch % 60) < 10) { + // In the first 10 seconds of each minute, we'll want a leading '0' + Serial.print('0'); + } + Serial.println(epoch % 60); // print the second + + Teensy3Clock.set(epoch); // set the RTC + setTime(epoch); + } + } + + static uint32_t timeout = millis(); + if ( millis() - timeout > 500 ) { + CAN_message_t msg; + msg.id = 0x7DD; + for ( uint8_t i = 0; i < 8; i++ ) msg.buf[i] = i + 1; + Can0.write(msg); + timeout = millis(); + LED_state = !LED_state; + digitalWrite(13, LED_state); + } + +}