arduino-libraries / MKRGSM

GNU Lesser General Public License v2.1
54 stars 51 forks source link

udp.available() returning -1 x bytes available #105

Closed MrWFoulkes closed 4 years ago

MrWFoulkes commented 4 years ago

Calls to udp.available() appear to return -1 x number of bytes available.

My program sends a Network Time Protocol request over UDP and receives a 48 byte response.

Here's the serial output:

Attempting to connect to the modem

Attempting to connect to the GPRS Access Point Name

Packet sent
Awaiting response
udp.parsePacket 0
udp.parsePacket 0
udp.parsePacket 48
udp.available -48 <---expected 48 here

Code (excluding secrets.h) is below. The relevant serial output comes from void loop():

#define LEAP_INDICATOR_NO_WARNING 0
#define LEAP_INDICATOR_61_SECONDS 1
#define LEAP_INDICATOR_59_SECONDS 2
#define LEAP_INDICATOR_UNKNOWN 3
#define LEAP_INDICATOR_Pos 6

#define VERSION_NUMBER_Pos 3

#define MODE_CLIENT 3
#define MODE_Pos 0

#define STRATUM_UNSPECIFIED_OR_INVALID 0

#define NTP_DOMAIN "pool.ntp.org"

const unsigned int NTP_PORT = 123u;
const unsigned int LOCAL_PORT = 2390u;

#include "arduino_secrets.h"
#include <MKRGSM.h>
/*Create the objects needed for internet requests*/
GSM gsm;    //class to connect to the modem
GSMClient gsmClient;    
GPRS gprs;  //class to connect to the internet
GSMUDP udp;

struct NTPPacket {
        unsigned int leapIndicator;
        unsigned int versionNumber;
        unsigned int mode;
        unsigned int stratum;
        int poll;
        int precision;
        unsigned long rootDelay;
        unsigned long rootDispersion;
        unsigned long referenceID;
        unsigned long long referenceTimestamp;
        unsigned long long originTimestamp;
        unsigned long long receiveTimestamp;
        unsigned long long transmitTimestamp;
        //ignoring the remaining fields for now. The example implementation doesn't use them.
    };

void printLongLong(long long value) {
    Serial.print((long)(value >> 32), HEX);
    Serial.println((unsigned long)(value), HEX);
}

void bigEndian(long long input, int sizeOfOutput, unsigned char output[]) {
    for (int i = 0; i < sizeOfOutput; i++) {
        int rightShift = (sizeOfOutput - 1 - i) * 8;
        char nextByte = (char) (input >> rightShift);
        output[i] = nextByte;
    }
}

long long bigEndianToLongLong(unsigned char data[], int sizeOfData) {
    long long output = 0;
    for(int i = 0; i < sizeOfData; i++) {
        output = output << 8;
        output += data[i];
    }
    return output;
}

void sendNTPRequest(unsigned long long referenceTimestamp, unsigned long long originTimestamp) {
    struct NTPPacket packet;
    packet.leapIndicator = LEAP_INDICATOR_UNKNOWN;
    packet.versionNumber = 4;
    packet.mode = MODE_CLIENT;
    packet.stratum = STRATUM_UNSPECIFIED_OR_INVALID;
    packet.poll = 0; //presumably this is set by the server?
    packet.precision = 0; // ditto
    packet.rootDelay = 0;
    packet.rootDispersion = 0;
    packet.referenceID = 0;
    packet.referenceTimestamp = referenceTimestamp;
    packet.originTimestamp = originTimestamp;
    packet.receiveTimestamp = 0;
    packet.transmitTimestamp = 0;

    while(!udp.beginPacket(NTP_DOMAIN, NTP_PORT));
    udp.write(packet.leapIndicator << LEAP_INDICATOR_Pos
        | packet.versionNumber << VERSION_NUMBER_Pos
        | packet.mode << MODE_Pos);
    udp.write(packet.stratum);
    udp.write(packet.poll);
    udp.write(packet.precision);
    byte byteArray[8];  //byte array to be used for multi-byte fields in big endian format
    bigEndian((long long)packet.rootDelay, 4, byteArray);
    udp.write(byteArray, 4);
    bigEndian((long long)packet.rootDispersion, 4, byteArray);
    udp.write(byteArray, 4);
    bigEndian((long long)packet.referenceID, 4, byteArray);
    udp.write(byteArray, 4);
    bigEndian(packet.referenceTimestamp, 8, byteArray);
    udp.write(byteArray, 8);
    bigEndian(packet.originTimestamp, 8, byteArray);
    udp.write(byteArray, 8);
    bigEndian(packet.receiveTimestamp, 8, byteArray);
    udp.write(byteArray, 8);
    bigEndian(packet.transmitTimestamp, 8, byteArray);
    udp.write(byteArray, 8);
    while(!udp.endPacket());
    Serial.println("Packet sent");
}

void receiveNTPPacket() {
    struct NTPPacket packet;
    byte byteArray[8];  //byte array to be used for multi-byte fields in big endian format
    Serial.print((int)udp.available());
    Serial.println(" bytes available");
    Serial.print("First byte: ");
    Serial.println((int)udp.peek(), DEC);
    packet.leapIndicator = (udp.peek() >> LEAP_INDICATOR_Pos) & 0b11;
    packet.versionNumber = (udp.peek() >> VERSION_NUMBER_Pos) & 0b111;
    packet.mode = (udp.read() >> MODE_Pos) & 0b111;
    packet.stratum = udp.read();
    packet.poll = udp.read();
    packet.precision = udp.read();
    udp.read(byteArray, 4);
    packet.rootDelay = bigEndianToLongLong(byteArray, 4);
    udp.read(byteArray, 4);
    packet.rootDispersion = bigEndianToLongLong(byteArray, 4);
    udp.read(byteArray, 4);
    packet.referenceID = bigEndianToLongLong(byteArray, 4);
    udp.read(byteArray, 8);
    packet.referenceTimestamp = bigEndianToLongLong(byteArray, 8);
    udp.read(byteArray, 8);
    packet.originTimestamp = bigEndianToLongLong(byteArray, 8);
    udp.read(byteArray, 8);
    packet.receiveTimestamp = bigEndianToLongLong(byteArray, 8);
    udp.read(byteArray, 8);
    packet.transmitTimestamp = bigEndianToLongLong(byteArray, 8);

    Serial.print("Leap indicator ");
    Serial.println(packet.leapIndicator);
    Serial.print("Version number ");
    Serial.println(packet.versionNumber);
    Serial.print("Mode ");
    Serial.println(packet.mode);
    Serial.print("Stratum ");
    Serial.println(packet.stratum);
    Serial.print("Poll ");
    Serial.println(packet.poll);
    Serial.print("Precision ");
    Serial.println(packet.precision);
    Serial.print("Root delay ");
    Serial.println(packet.rootDelay);
    Serial.print("Root Dispersion ");
    Serial.println(packet.rootDispersion);
    Serial.print("Reference ID ");
    Serial.println(packet.referenceID);
    Serial.print("Reference Timestamp 0x");
    printLongLong(packet.referenceTimestamp);
    Serial.print("Origin Timestamp 0x");
    printLongLong(packet.referenceTimestamp);
    Serial.print("Receive Timestamp 0x");
    printLongLong(packet.receiveTimestamp);
    Serial.print("Transmit Timestamp 0x");
    printLongLong(packet.transmitTimestamp);
}

void setup() {
    // put your setup code here, to run once:
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);
    Serial.begin(9600);
    while(!Serial);
    Serial.println("Attempting to connect to the modem");
    while(gsm.begin() != GSM_READY) {
        Serial.print('.');
        delay(100);
    }
    Serial.println();
    Serial.println("Attempting to connect to the GPRS Access Point Name");
    while(gprs.attachGPRS(SECRET_GPRS_APN, SECRET_GPRS_LOGIN, SECRET_GPRS_PASSWORD) != GPRS_READY) {
        Serial.print('.');
        delay(100);
    }
    Serial.println();

    while(!udp.begin(LOCAL_PORT));
    sendNTPRequest(0,0);
    Serial.println("Awaiting response");

    int parsePacket;
    do {
        parsePacket = udp.parsePacket();
        Serial.print("udp.parsePacket ");
        Serial.println(parsePacket);
        delay(100);
    }   while (parsePacket < 1);

//  Serial.println("Response received");
//  receiveNTPPacket();
//  udp.stop();

}

void loop() {
    Serial.print("udp.available ");
    Serial.println(udp.available());
    Serial.print("udp.peek ");
    Serial.println(udp.peek());
    Serial.print("udp.available ");
    Serial.println(udp.available());
    Serial.print("udp.read ");
    Serial.println(udp.read());
    delay(2000);
}