Blink-Ping: A WiPy LED blink with a simple TCP socket
The WiPy is first of all a wireless device, and resolutely so. It wasn’t for a couple of minutes after finishing the WiPy’s unboxing and blinky test (super easy) that I realized the USB cable to my laptop had contributed nothing but power for the WiPy. No data went over the cable. It was all about wireless internet from the first moment. Blinking an LED tells you things are alive and working. Pinging a server tells you that somebody's home and a bit about the speed of the network connection. Listening to the sound of connections being opened and closed give you a very different experience of how the system is behaving.
[click View Image for more detail] Blinky_Ping also makes sound. The speaker is from an old phone handset. Most speakers are around 8 ohms impdance. This one is 165 ohms because it's from a phone. 165 ohms is printed on the back of the speaker. The higher impedance is what allows an un-amplified connection to a 3.3V logic line. The sound output is a small number of milliwatts. The floating wire goes to GP10. Touching it to 3.3V signals the WiPy to exit its while loop.
Clients and servers are pieces of software. Servers sit and wait for a client request. The basic blinky of socket programming is to make a connection between a client socket and a server socket, then then do something like print Hello World on both ends of the connection to see that it’s alive, then close the connection. The Blinky_Ping_client.py script (WiPy) does a sort of ping by opening and closing a socket on Blinky_Ping_server.py (laptop). The "ping" time can be seen in how fast the LED blinks. Each time a socket is opened and closed the LED turns on and then off once. Each time the LED turns on or off, the speaker emits a click. The clicks bracket the time spent opening, then interacting, then closing the socket. In my case theinterval ranged from 7ms to 2500ms.
A 74HC573 chip buffers the WiPy up to around 20mA at 3.3V. The narrow black thing is the pull down for the '573 inputs. It's an R-pak resistor network that has eight 10k resistors tied to a common ground. Very compact. Only one of the eight channels is being used. The speaker and the LED share the same channel.In socket programming, "socket" is an electrical metaphor that represents an IP address and a port number taken together. Sockets are created by applications that want to communicate with other applications.
The green and orange jumpers are the speaker leads. The ground side of the speaker passes through a 1uF capacitor. Any value between 1uF and 250uF work fine. Watch for polarity on electrolytic caps!
By ancient custom some port numbers are associated with particular well known services or protocols. Port 80 is used by web servers. A web server socket looks like “192.168.1.1:80”. There are about 65000 possible port numbers. Port numbers are chosen by the program or handed out by the operating system. Port numbers must match expectations on both ends of the connection so that the client and server can find each other.The client repl is on the left. The server is on the right. The server is a Python script running on the laptop.
To make your own network software, you write either a server program or a client program or both. The server and client can talk to each other because bytes have the same meaning on both ends of the connection. The point is that you can make up your own scheme to describe your data and the method for passing it over a network connection. That is to say you can make up your own protocol. Python has extensive libraries for writing your own clients and servers.
The telnet session and the serial terminal show the same information, but the Cutecom serial terminal is easier to read than the telnet window, IMO. The serial connection often still works if the wireless connection fails.
In small embedded systems, we're used to seeing the server code running on the small device and the client, e.g. a web browser running on the laptop. Not so here. In this case, the WiPy is the client. Servers must listen at all times for a client request, and so must stay awake, thus draining their battery. If the embedded device is the client, it can wake up from hibernate whenever it pleases, then interact with the server, then go back to sleep. Blinky_Ping doesn't use a low-power configuration, but low power is the way of the future.
Here is the client:
# Blinky_Ping_client.py, alias main.py on wipy # Ed Bennett ''' This was helpful: http://www.bogotobogo.com/python/python_network_programming_server_client.php This script does a sort of ping by measuring the time of opening and closing a socket on a remote server. (Blinky_Ping_server.py) The "ping" time can be seen in how fast the LED blinks. When a socket is starting to be opened the LED turns on. When the socket has closed the LED turns off. ''' if __name__ == '__main__': import socket import time from machine import Pin host = '192.168.1.64' # laptop running server port = 9999 addr = socket.getaddrinfo(host, port)[0][-1] # The expansion board is designed so that the"user led" on G16 # is switched on the low side [google for low-side switch], # therefore the LED's logic level is inverted. 1 is off, 0 is on. # The GP16 pin itself is NOT inverted. LED1 = Pin('GP16', mode=Pin.OUT) LED1.value(1) # blink once to check wiring on startup time.sleep_ms(1000) LED1.value(0) time.sleep_ms(1000) w = 0 # while loop counter elapsed = 10 # initialize to 10ms ''' The stop_flag allows you to stop a broken, long, or infinite loop and interact with the filesystem by using commands in the os module, etc. Making GP10 high (touch a jumper between GP10 and +3.3V) causes the while loop to end and return you to the repl. ''' stop_pin = Pin('GP10', mode=Pin.IN, pull=Pin.PULL_DOWN) stop_flag = 0 stop_flag = stop_pin() # check here in case the while loop is broken while w < 1000 and stop_flag == 0: stop_flag = stop_pin() print("stop_flag = ", stop_flag) if stop_flag == 1: print("stopped") break ''' This pauses the loop for the same number of milliseconds as the previous message's round-trip. Otherwise it would be hard to see the LED blinking. The measured value of "elapsed" is not affected. ''' time.sleep_ms(elapsed) ''' Measure the overhead in sending data between my laptop and the WiPy through my home router: start a timer, then connect to the server, then don't send any data, then receive some data from the server, then close the socket, then stop the timer. ''' LED1.value(1) start = time.ticks_ms() # init the elapsed timer: start measuring # create a socket object s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(addr) # This just shows the idea of a small amount of data coming # back from the server. # Receive no more than 1024 bytes tm = s.recv(1024) s.close() elapsed = time.ticks_diff(start, time.ticks_ms()) #measure loop time LED1.value(0) print("Loop #", w, " round trip time was", elapsed, "ms") print("The time got from the server is %s" % tm.decode('ascii')) w += 1
Here is the server:
# Blinky_Ping_server.py # runs on laptop # http://www.bogotobogo.com/python/python_network_programming_server_client.php # This is pretty much the standard minimal server socket demo, but with the # added feature of sending the time to the client. import socket import time # create a socket object mytcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) mytcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # # get this computer's name # host = socket.gethostname() this was getting machine name not ipaddr host='192.168.1.64' # only the server's actual ip address works here (?!) port = 9999 # bind to the port mytcpsock.bind((host, port)) # queue up to 5 requests mytcpsock.listen(5) while True: # establish a connection clientsocket,addr = mytcpsock.accept() print("Got a connection from %s" % str(addr)) currentTime = time.ctime(time.time()) + "\r\n" clientsocket.send(currentTime.encode('ascii')) clientsocket.close()
Ed Bennett ed atsign kineticsandelectronics dotcom
Leave a comment