Go Down

Topic: Arduino ESP8266 and NTP (Read 35273 times) previous topic - next topic

Joss

I thought it would be a good idea to try and get one of these new ESP8266 WiFi boards and try get it to connect and download the time from an NTP server.  I can see that there are examples in the IDE for Ethernet and WIFI shields but nothing for this new device (which isn't surprising) ;D

I'm using a Pro mini and I've managed to get everything connected and 'talking' but I'm stumped at the next step of how to get the public NTP server to send the information to me!  Ive googled around but the information I'm finding is very opaque to me as I am unfamiliar with network packets and protocols. The code posted below sets up a connection and sends a message to the NTP server.  The server replies with "No IP".  This suggests to me that I am connected to the server but have not provided the correct message.  Does anyone know the correct structure of the message (in an easy to understand way)!

Here is my code below as it might help others to get started but if you have any help you can offer, please do!

Code: [Select]
// // ESP8266 connection

/* Connect ESP8266 as follows
  I used an Arduino 3.3v Pro Mini with a 3.3V USB-FTDI converter
  The ESP8266 was communicating at 9600 when delivered
  Vcc - 3.3v
  gnd - gnd
  rx - pin 11
  tx - pin 10
  ch_PD - pin 6
*/

// Target Access Point
#define ssid         "your ssid"
#define pass         "your password"

int EnablePin = 6;
unsigned long time = 0;

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX


void setup() 
{
 
  pinMode(EnablePin, OUTPUT);
  digitalWrite(EnablePin, LOW);
  delay (500);
  digitalWrite(EnablePin, HIGH);
  delay (500);
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);
  delay (1000);

  connectWiFi();  // Start the WiFi module
 
  setPacket();

}

void loop() // run over and over
{
  if (mySerial.available())
    Serial.write(mySerial.read());
  if (Serial.available())
    mySerial.write(Serial.read());
}

void connectWiFi()
{
 
//Rest the module.
Serial.println("AT+RST");
mySerial.println("AT+RST");
time = millis();
  while ((time + 2000) >= millis()){
   if (mySerial.available() > 0) {
   Serial.write(mySerial.read());
   }
  }

delay(20);

//Set the wireless mode
Serial.println("AT+CWMODE=1");
mySerial.println("AT+CWMODE=1");
time = millis();
  while ((time + 500) >= millis()){
   if (mySerial.available() > 0) {
   Serial.write(mySerial.read());
   }
  }
 delay(20);

//disconnect  - it shouldn't be but just to make sure
Serial.println("AT+CWQAP");
mySerial.println("AT+CWQAP");
time = millis();
  while ((time + 1000) >= millis()){
   if (mySerial.available() > 0) {
   Serial.write(mySerial.read());
   }
  }
 
// connect to your wireless router 
String cmd="AT+CWJAP=\"";
cmd+=ssid;
cmd+="\",\"";
cmd+=pass;
cmd+="\"";
Serial.println(cmd);
mySerial.println(cmd);
//delay(2000);
time = millis();
  while ((time + 15000) >= millis()){
   if (mySerial.available() > 0) {
  Serial.write(mySerial.read());
  }
  }

//print the ip addr
  mySerial.println("AT+CIFSR");
  Serial.println("ip address:");
  time = millis();
  while ((time + 5000) >= millis()){
   if (mySerial.available() > 0) {
   Serial.write(mySerial.read());
   }
  }
 
//set the single connection mode
Serial.println("AT+CIPMUX=0");
mySerial.println("AT+CIPMUX=0");
time = millis();
  while ((time + 1000) >= millis()){
   if (mySerial.available() > 0) {
   Serial.write(mySerial.read());
   }
  }

//Connect to the NTP server

String cmd1 = "AT+CIPSTART=\"UDP\",\"";
cmd1 += "129.6.15.28";
cmd1 += "\",123";
mySerial.println(cmd1);
Serial.println(cmd1);
time = millis();
  while ((time + 5000) >= millis()){
   if (mySerial.available() > 0) {
   Serial.write(mySerial.read());
   }
  }
}

void setPacket(){
  // 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;
 
  //mySerial.write("192.168.0.13", 123);
  mySerial.write(packetBuffer,NTP_PACKET_SIZE);
 // Serial.println(packetBuffer,NTP_PACKET_SIZE);
  //Udp.endPacket();
}

westfw

I am pretty sure that the no ip message is from the esp8266, and means that the step where you set up the udp connection did not work.

Erni

#2
Dec 12, 2014, 02:46 pm Last Edit: Dec 12, 2014, 02:52 pm by Erni

Joss

I am pretty sure that the no ip message is from the esp8266, and means that the step where you set up the udp connection did not work.

When I ran the code again today I got a different result!!! :smiley-roll:

This time the response to the command

AT+CIPSTART="UDP","129.6.15.28",123

is "OK" suggesting the command was accepted.

However, after I send the packet I only get some garbage returned......as below....

===================================================================

AT+RST
AT+RST


OK
.ªЃÿP2ýPfhLÈ 4¥_PFˆ
[Vendor:www.ai-thinker.com Version:0.9.2.4]

ready
AT+CWMODE=1
AT+CWMODE=1

no change
AT+CWQAP
AT+CWQAP


OK
AT+CWJAP="xxxxxxxxxxxxx","xxxxxxxxxxxxx"
AT+CWJAP="xxxxxxxxxxxxx","xxxxxxxxxxxxx"


OK
ip address:
AT+CIFSR

192.168.0.13

OK
AT+CIPMUX=0
AT+CIPMUX=0


OK
AT+CIPSTART="UDP","129.6.15.28",123
AT+CIPSTART="UDP","129.6.15.28",123


OK
ã   ì        1N14


=================================================================

So I think the connection today was good but I'm still stumped by what the format of the message to the server should be.



Joss

There are some info here:
http://playground.arduino.cc/Code/NTPclient

Maybe it is easyer to use google:
http://www.esp8266.com/viewtopic.php?f=19&t=809
I looked at these.  The 2nd one may be the simplest and I'll try it in the next few days.  As for the 1st link I couldnt make it work with this ESP8266  (I assume it needs the WIFI or Ethernet shield connected.  Anyway I find the presentation of the page too complicated for my current competance.  I tried extracting this portion of the code and just writing t to the serial port

  // Send an NTP request
  if (! (udp.beginPacket(timeServer, 123) // 123 is the NTP port
    && udp.write((byte *)&ntpFirstFourBytes, 48) == 48
    && udp.endPacket()))

Again I just seem to get garbage back, I wonder if the return info is not ASCII?

countrypaul

Assuming ASCII return is likely to be wrong.

THis may help you understand: http://www.ntp.org/ntpfaq/NTP-s-algo.htm

Paul

westfw


Joss

#7
Dec 14, 2014, 03:30 am Last Edit: Dec 14, 2014, 03:31 am by Joss
Not at all ascii: http://www.meinbergglobal.com/english/info/ntp-packet.htm


Thanks - I found that page during my searches and it does provice me a bit of information but as I am unfamiliar with the syntax of datagrams and how to handle them it is still only part of the information.  I'm really looking for the c code to drop into my program.

Joss

Assuming ASCII return is likely to be wrong.

THis may help you understand: http://www.ntp.org/ntpfaq/NTP-s-algo.htm

Paul
Sorry, this link is to the main NTP org people who are all about setting up and running servers.  There is no easy to read documentation here that I can find to show how to make a simple request to a server and to receive the appropriate response. 

I am not interested in accuracy yet I thought it would be easy to just get the number of seconds since 1900,1,1 and just perform a simple calculation and hey presto get the correct date and time. 

All I seem to be learning is the detail, ie how server work and how they calculate accuracy yet these websites and documentation assume a level of competancy that I do not have.  I'm all up for doing the hard yards to learn but the sources I'm finding are all diving in way too deep.

I guess I'm remembering back o when I did work with GPS's and for that type of device you send a message and you receive a string back.  I assumed NTP servers would be similar in their operation.  If they simple, I have not yet uncovered the secret!

Maybe I need NTP for dummies!   :smiley-confuse:

westfw

Don't you get a "+ IPD, <len>:" prefix, followed by a bunch of bytes? <len> should be >= 48
typically, you'd search for that prefix, and then do a "serial.readbytes(packetbuffer, 48);" and look at the values in packetbuffer as binary.  (actually, you'd normally make up a nice structure, like:

Code: [Select]
struct ntp_pkt_t {
       u_char li_vn_mode;   /* leap indicator, version and mode */
       u_char stratum;      /* peer stratum */
       u_char ppoll;        /* peer poll interval */
       s_char precision;    /* peer clock precision */
       u_fp   rootdelay;    /* distance to primary clock */
       u_fp   rootdispersion;      /* clock dispersion */
       u_int32       refid;        /* reference clock ID */
       l_fp   reftime;      /* time peer clock was last updated */
       l_fp   org;          /* originate time stamp */
       l_fp   rec;          /* receive time stamp */
       l_fp   xmt;          /* transmit time stamp */
       u_int32       exten[1];     /* misused */
       u_char mac[MAX_MAC_LEN]; /* mac */
} ntppkt;

and use that for both transmit/receive of the packet.
(ie "Serial.write(&ntppkt, sizeof(ntppkt));")


westfw

NTP is not a simple protocol.  It has things in it to synchronize time across networks to within very small times - much less than a network transmission delay.  Which is a really neat trick, if you think about it.
You might want to look into SNTP ("Simple Network Time Protocol"), Time Protocols (port 37), and unix "rdate", which I think are all more of the "tell me what time it is and I won't worry about how much time has passed since I first asked."
(OTOH, I don't know if they are as widely implemented...)

Joss

Thanks for your help westfw.  I've added a command to prepare the port for sending data (and now I get the IPD message)

AT+CIPSEND=48

The result/response is:

===================================================================

OK
AT+CIPSTART="UDP","129.6.15.28",123
AT+CIPSTART="UDP","129.6.15.28",123


OK
AT+CIPSEND=48
AT+CIPSEND=48

> ãì    1N14

SEND OK

+IPD,48:$ãACTSØ7×0Æêä

OK

=================================================================

I tried using your code   "mySerial.readBytes(packetBuffer, 48);"  but it returns a error:

"invalid conversion from 'byte*' to 'char*'"   


sketch_dec14a.ino: In function 'void setPacket()':
sketch_dec14a:177: error: invalid conversion from 'byte*' to 'char*'
sketch_dec14a:177: error: initializing argument 1 of 'size_t Stream::readBytes(char*, size_t)'


the packetBuffer was declared as a byte at the top of the program but is clearly not happy with this way of reading the data

hmmm one step forward.   You may be right about using the SNTP.  maybe i should investigate that option!

Joss

btw  I am using a structure similar to the one you suggested (copied from an arduino example).  It is at the end of my program thus:

Code: [Select]
void setPacket(){
  // 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;
 
  //mySerial.write("192.168.0.13", 123);
  mySerial.write(packetBuffer,NTP_PACKET_SIZE);
 
  delay (2000);
 
  mySerial.readBytes(packetBuffer, 48);
 // Serial.println(packetBuffer,NTP_PACKET_SIZE);
  //Udp.endPacket();
}




Joss

Well I did not get my own code to work :-*

but I did find an update to the firmware which includes a direct call to a SNTP server. 

If anyone is interested it can be found here:

http://www.esp8266.com/viewtopic.php?f=11&t=705&p=4881#p4881

Hope this helps somebody!

tgibbs99

Can someone please show us what an NTP request packet looks like? I see its a 48 byte thing. How can this sent via esp8266? Can it be converted to a string and back to hex bytes?

Thanks.

Tim

Go Up