A basic socket server/client example from a beginner

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
Post Reply
NTL2009
Posts: 20
Joined: Wed Oct 26, 2016 10:07 pm

A basic socket server/client example from a beginner

Post by NTL2009 » Thu Dec 22, 2016 8:53 pm

I had a request to post this code. It might actually serve as an example of what NOT to do, but it is working for me. It is not 'pythonic' - I do use multiple statements per line and other things. Input is welcome, I have thick skin and would not put it up here if I weren't ready for (hopefully constructive) criticism. I am pre-embarrassed by it, so no harm can be done. :) I use this basic code to upload data ( ~ 1KB text file) once an hour from ~ 8 AM to 11PM, and it's been very reliable (though I've got a bit more condition testing in the full code).

For a beginner like me, I find I need examples. Some of the documentation I find assumes you know how to fill in the bits and pieces, and well, I don't! So maybe this will help others trying to get started.

In this stripped down example, the ESP just sends the contents of a data file to the server every 60 seconds. So include a small text file in a '/DATA' directory on your ESP8266.

So first, the 'Server' code, which I run in a terminal on my Linux (Xubuntu) laptop, which is my everyday machine. It was set up for Python 2.x, and I haven't changed it to 3 yet, I should to keep the syntax more similar between MicroPython and this, but I just haven't.

THE CODE: So this sets up the socket, and it listens for something from the ESP8266. When I get a connection, I send the text "ACK" to the ESP (that's just my text message, not a system 'ack' or anything - it could be anything you want) , that was initially for debug, but I left it in for some visual feedback that I have two-way comm.

Then I open a file (in my 'real code', I create a file name based on what was sent from the ESP in response to "ACK"), loop through the recv data from the ESP, and keep writing it to a file. When the ESP8266 reaches the end of the file it is reading, I send some added info/stats, and then send a text 'EOF\n' to tell the server that's all folks. Again, that 'EOF\n' was just my choice of a 'signal word', it's not a system level thing. You might want to do something else here.

So with all the data from the ESP, the server writes some of its own info/stats, closes the file, changes ownership, and renames it with a cycle count appended. It closes the socket, and loops back, opens a new socket to be ready for the next transmission (I hope I got that right, I think so, but I couldn't fully test it in this code segment). I'll put up the ESP code next.

SERVER SIDE:

Code: Select all

#!/usr/bin/python              
prg_n = 'ServerSocketDemo-from-v29'
#
# This needs to be running on 'Server' BEFORE starting ESP8266 routine
#
import time; import socket; import os; import glob
port = 1247
i_cnt=0
#
# **** While True Loop ****
while True:
  s = socket.socket()
  host = socket.gethostname()
  print host, port
  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # added allow to reuse when re-run
  s.bind(('',port)) # Needed (('',port)) or client never saw it
  s.listen(5)
  c, addr = s.accept()
  # end of setting socket
  #
  print 'c, addr: ', c, addr
  print 'Waiting for data from ESP8266...'
  # it printed... c, addr:  <socket._socketobject object at 0x7ff4d063c670> ('192.168.1.200', 3073)
  print("Connection accepted from " + repr(addr[1]) + "  Server ready for file transfer...\n")
  # it printed... Connection accepted from xxxx  Server ready for file transfer...
  #
  # ACK the client, get filename and open file for writes
  starttime = time.time()    # measure start of ET to get and write file
  c.send("ACK...\n")         # SEND MESSAGE TO CLIENT TO SEND DATA
  #
  tfile = "Server-WIP"
  fobj = open(tfile, "w")
  #
  # Incoming Data, loop until "FILE-EOF", then repeat for additional file(s)
  # get next line...
  client_sent = c.recv(1026)
  #
  while client_sent != 'EOF\n' :
    print client_sent,  # comma supresses a linefeed from printer (string already has a linefeed)
    fobj.write(client_sent)
    client_sent = c.recv(1026)
    #
  ######### HIT EOF\n
  localtime = time.asctime( time.localtime(time.time()) )
  print (localtime)
  E_Time = (time.time() - starttime)
  print "Elapsed Time:  %.2f Seconds" % E_Time
  fobj.write("Server:,,,,,Elapsed Time:  %.2f Seconds" %  E_Time + '\n' )
  fobj.write("Server:,,,,,Program Name:  %s " %prg_n + '\n' )
  fobj.close()
  os.chmod(tfile,0o666) # assign RW- to Owner, Group, Other
  os.rename(tfile, tfile[0:18] + '-' + str(i_cnt))
  i_cnt += 1
  print ('addr[1]: '+repr(addr[1])+' FILE XFER DONE for: '+tfile[0:18]+'-'+ str(i_cnt))
  #
  ### close the socket
  ### a new socket will be created for next upload;
  ### as the client closes its socket after uploads
  ###
  print ('ALL FILES RCVD this session')
  print ('close socket, open new socket and wait for next upload')
  print ('Back to top level # "**** While True Loop ****"\nServer Prog: ' + prg_n) 
  s.close()
:oops:

NTL2009
Posts: 20
Joined: Wed Oct 26, 2016 10:07 pm

Re: A basic socket server/client example from a beginner

Post by NTL2009 » Thu Dec 22, 2016 9:16 pm

Here's the ESP8266/MicroPython soide of things. NOTE - my ESP8266 is tied up on its main mission right now, so I wasn't able to test this stripped out code segment, hopefully I got all the extraneous variables pulled out, and it works this way, but I doubt it! Should be close though, I think.

FIRST: Manually create a "/DATA' dir on the ESP8266, and add a small test text file there. Fill in the code with your SSID/password. I've set my router to assign my server (just my main everyday computer) to '192.168.1.100', so make sure this matches the IP that your server is on, and remember it can change if devices are added or removed unless you give it an assignment (based on MAC of the device) on your router to keep it static.

The MAIN LOOP: Just loops until the time is even divide of 60 seconds, then it calls the upld_data routine, prints the status, waits one second and loops back until the next 60 seconds passes.

upld_data: Set up some vars, turn the RED LED OFF (high on my board), and get the list of files in "/DATA', and just pick the first one in the list. NOTE - no error checking here, I think it will crash if you don't have a file in the directory - this is very basic code! . Then turn on WIFI, make the socket connection, and wait for my server to send the text message "ACK" (just my choice of text). I then send the file name, and then send the file, line by line. I toggle the LED for some visual indication. When the file has been read to the end, I send some other info, and then the text 'EOF\n' to tell the server I'm done for now.

Yes, it's ugly, it needs more error checking (some of that is in my 'real' code, but it was integrated in my other routines, so I left it out for clarity). But I think it does serve as a framework for setting this up. Next, I need to see how to get multiples going, but I think I will take the KISS approach, a separate instance of the server program running with a separate port assignment?

Code: Select all

prg_n='ESP_SocketDemo.py'
#
# NOTE - Server socket must be up and running first
# This prog uploads the first file found in '/DATA' dir on the ESP8266
# Must first create '/DATA' and save a text file there
#
print(prg_n)
host='192.168.1.100';port=1247
my_ssid='ENTER-YOUR_SSID_HERE'
my_wifi_pw='ENTER_YOUR_NETWORK_PASSWORD_HERE'
#
import sys,time,utime,socket,network,machine
from ntptime import settime
from machine import Pin
#
uos.chdir('/DATA')# must be pre set up on ESP8266, add a text file demo here
#
# main code loop below, afer defs...
#
def set_WIFI(cntrl):
  i=0;sta_if=network.WLAN(network.STA_IF)
  if cntrl=='ON':
    if not sta_if.isconnected():
      print('conn WiFi')
      sta_if.active(True)
      sta_if.connect(my_ssid,my_wifi_pw)
      while not sta_if.isconnected():
        print(".",end="")
        time.sleep(1);i+=1
        if i>9:return#>>
  else:sta_if.active(False)
  print('NW CFG:',sta_if.ifconfig())
#
#
def upld_data():
  sd=.3 # delay between sends - experiment with this
  df='' # data file name
  p16.high();red_LED=False
  fl=uos.listdir()
  df=fl[0]
  print('fl: ',fl,'   dl:  ',dl)  
  #     
  set_WIFI('ON')
  s=socket.socket();time.sleep(sd)
  try:
    s.connect((host,port))
  except Exception as err:
    print('SockConn-ERR#: ',err)
    return('NOP')#>>
  #
  cmd=s.recv(1024)
  if cmd.startswith('ACK'): # I have Server send 'ACK' on connect
    print('SRV-ACK')
  else:#catch???
    print(cmd+' SRV-NO-ACK')
    s.close();return('NOP')#>>
  #
  print(df)
  s.send(df);time.sleep(sd)
  with open(df,'r') as fobj:
    for line in fobj: # will loop here until end-of-file
      print(line,end='')
      if red_LED == False:#toggle LED during sends
        p16.low();red_LED=True
      else:
        p16.high();red_LED=False
      s.send(line);time.sleep(sd)
  p16.low();red_LED=True
  #
  # following is extra info I send after the file has been read
  s.send('\nprg_n: '+prg_n+', Free MEM:,'+str(gc.mem_free())+'\n')
  time.sleep(sd)
  s.send('fl is: '+str(fl)+'\n')
  time.sleep(sd)
  fl=uos.statvfs('/')
  fl='Tot/Used/Free %d blocks: %d/%d/%d\n' %(fl[0],fl[2],fl[2]-fl[3],fl[3])
  print(fl)
  s.send(fl);time.sleep(sd)
  s.send('EOF\n') # Server will look for EOF message from ESP, finish,
                  # close/open its socket, loop back, wait for next upload
  p16.high()
  return('DONE')#>>
#
#END DEF
#
while True: # upload every 60 seconds
  if utime.time() % 60 == 0:
    df_status = upld_data()
    print (df_status)
    time.sleep(1)
    gc.collect()
  #
#

Post Reply