tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/examples/net/ntpclient/main.go (about)

     1  // This is an example of an NTP client.
     2  //
     3  // It creates a UDP connection to request the current time and parse the
     4  // response from a NTP server.  The system time is set to NTP time.
     5  
     6  //go:build ninafw || wioterminal || challenger_rp2040
     7  
     8  package main
     9  
    10  import (
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"machine"
    15  	"net"
    16  	"runtime"
    17  	"time"
    18  
    19  	"tinygo.org/x/drivers/netlink"
    20  	"tinygo.org/x/drivers/netlink/probe"
    21  )
    22  
    23  var (
    24  	ssid string
    25  	pass string
    26  	// IP address of the server aka "hub". Replace with your own info.
    27  	ntpHost string = "0.pool.ntp.org:123"
    28  )
    29  
    30  const NTP_PACKET_SIZE = 48
    31  
    32  var response = make([]byte, NTP_PACKET_SIZE)
    33  
    34  func main() {
    35  
    36  	waitSerial()
    37  
    38  	link, _ := probe.Probe()
    39  
    40  	err := link.NetConnect(&netlink.ConnectParams{
    41  		Ssid:       ssid,
    42  		Passphrase: pass,
    43  	})
    44  	if err != nil {
    45  		log.Fatal(err)
    46  	}
    47  
    48  	conn, err := net.Dial("udp", ntpHost)
    49  	if err != nil {
    50  		log.Fatal(err)
    51  	}
    52  
    53  	println("Requesting NTP time...")
    54  
    55  	t, err := getCurrentTime(conn)
    56  	if err != nil {
    57  		log.Fatal(fmt.Sprintf("Error getting current time: %v", err))
    58  	} else {
    59  		message("NTP time: %v", t)
    60  	}
    61  
    62  	conn.Close()
    63  	link.NetDisconnect()
    64  
    65  	runtime.AdjustTimeOffset(-1 * int64(time.Since(t)))
    66  
    67  	for {
    68  		message("Current time: %v", time.Now())
    69  		time.Sleep(time.Minute)
    70  	}
    71  }
    72  
    73  // Wait for user to open serial console
    74  func waitSerial() {
    75  	for !machine.Serial.DTR() {
    76  		time.Sleep(100 * time.Millisecond)
    77  	}
    78  }
    79  
    80  func getCurrentTime(conn net.Conn) (time.Time, error) {
    81  	if err := sendNTPpacket(conn); err != nil {
    82  		return time.Time{}, err
    83  	}
    84  
    85  	n, err := conn.Read(response)
    86  	if err != nil && err != io.EOF {
    87  		return time.Time{}, err
    88  	}
    89  	if n != NTP_PACKET_SIZE {
    90  		return time.Time{}, fmt.Errorf("expected NTP packet size of %d: %d", NTP_PACKET_SIZE, n)
    91  	}
    92  
    93  	return parseNTPpacket(response), nil
    94  }
    95  
    96  func sendNTPpacket(conn net.Conn) error {
    97  	var request = [48]byte{
    98  		0xe3,
    99  	}
   100  
   101  	_, err := conn.Write(request[:])
   102  	return err
   103  }
   104  
   105  func parseNTPpacket(r []byte) time.Time {
   106  	// the timestamp starts at byte 40 of the received packet and is four bytes,
   107  	// this is NTP time (seconds since Jan 1 1900):
   108  	t := uint32(r[40])<<24 | uint32(r[41])<<16 | uint32(r[42])<<8 | uint32(r[43])
   109  	const seventyYears = 2208988800
   110  	return time.Unix(int64(t-seventyYears), 0)
   111  }
   112  
   113  func message(format string, args ...interface{}) {
   114  	println(fmt.Sprintf(format, args...), "\r")
   115  }