1. Setup a TCP network server
    2. Setup a TCP client
    3. Modify ESP8266WiFi_0v25.js to do UDP and TCP
    4. Use UDP to allow clients to find the server IP address
    5. The UDP/TCP client.

    6. Setup a TCP network server
      The TCP server and client code are based on the examples listed under the Sockets section of
      http://www.espruino.com/Internet
      The following code sets up a TCP server on port 9988.
      You will need to edit:
      //var Hardware=0; //Espruino board
      var Hardware =1; //PICO
      var SSID="ssid";
      var key= "router passcode";

    When a client connects two commands are accepted “LED On” and “LED Off”

    //netServer3.js
    //2 Oct 2016
    //espruino board with ESP8266
    //PICO  with ESP8266
    var Sport=9988;
    //var Hardware=0; //Espruino board
    var Hardware =1; //PICO
    var SSID="ssid";
    var key= "router passcode";
    var Serial;
    var MyIP="";
    function test(){
    if(Hardware===1)Serial=Serial2;
    if(Hardware===0)Serial=Serial4;
    
    if(Hardware===1){
     digitalWrite(B9,1); // enable on Pico Shim V2
     Serial.setup(115200, { rx: A3, tx : A2 }); //Pico
    }
    if(Hardware===0)Serial.setup(115200, { rx: C11, tx : C10 }); 
    //espruino board
    
    console.log("Start connection process");
    var wifi = require("ESP8266WiFi_0v25").connect(Serial, function(err) {
      if (err)return 1;// throw err;
    console.log("Reset the ESP8266");
      wifi.reset(function(err) {
        if (err)return 1;// throw err;
        console.log("Connecting to WiFi");
        wifi.connect(SSID,key, function(err) {
          if (err)return 1;//throw err;
          wifi.getIP(function(l,ip){console.log("IP= ",ip,"\n\r"+l);MyIP=ip;
          console.log("Wi-Fi Connected");
    // Now you can do something, 
    serve();
    
    ////////////////////////////////////////////////////////////
         });
        });
      });
    });
    }//end test
    
    var x;
    console.log("Start");
    x=test();
    if(x!==0)console.log("Try again");//error seen
    
    if(x===0) console.log("Exit Seen");
    
    //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    function serve(){
     var data;
     var server = require("net").createServer(function(c) {
      // A new client has connected
    //  c.write("Hello");
    /*  c.on('data', function(data) {
        console.log(">"+JSON.stringify(data));
      });
    */
       c.on('close',function(d){
       console.log("Close "+d);
       data=c.read(c.available());
       console.log(data);
       c.write(data);
         if(data==="LED Off"){
          digitalWrite(B12,0); //B12 is green LED
         }
         if(data==="LED On"){
          digitalWrite(B12,1);
         }
      });
      c.end();
    });
    server.listen(Sport);
    
    }
    

    The TCP server output:

    >echo(0);
    Start
    Start connection process
    Try again
    =undefined
    Reset the ESP8266
    Connecting to WiFi
    IP=  192.168.1.3
    null
    Wi-Fi Connected
    >
    

    At this point I disconnect the WebIDE from the TCP server hardware and reconnect using putty.
    Make a note of the server IP addess.


    1 Attachment

    1. Setup a TCP client
      This code sets up the TCP client on port 9988.
      The console is redirected to the Loopback to allow the WebIDE to act as a simple menu system.
      You will need to edit the hardware flag , ssid and router passcode.
      Additionally you have to hardcode the IP address of the server. (we will fix this with UDP later)

      //netclient3.js
      //2 Oct 2016
      //espruino board with ESP8266
      //PICO  with ESP8266
      var IP="192.168.1.3";
      var Sport=9988;
      //var Hardware=0; //Espruino board
      var Hardware =1; //PICO
      var SSID="ssid";
      var key= "router passcode";
      var Serial;
      function test(){
      if(Hardware===1)Serial=Serial2;
      if(Hardware===0)Serial=Serial4;
      if(Hardware===1){
      digitalWrite(B9,1); // enable on Pico Shim V2
      Serial.setup(115200, { rx: A3, tx : A2 }); //Pico
      }
      if(Hardware===0)Serial.setup(115200, { rx: C11, tx : C10 }); 
      //espruino board
      console.log("Start connection process");
      var wifi = require("ESP8266WiFi_0v25").connect(Serial, function(err) {
      if (err)return 1;// throw err;
      console.log("Reset the ESP8266");
      wifi.reset(function(err) {
      if (err)return 1;// throw err;
      console.log("Connecting to WiFi");
      wifi.connect(SSID,key, function(err) {
        if (err)return 1;//throw err;
        wifi.getIP(function(l,ip){console.log("IP= ",ip,"\n\r"+l);});
        console.log("Wi-Fi Connected");
      // Now you can do something,
      menu();
      ////////////////////////////////////////////////////////////
      });
      });
      });
      }//end test
      //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      function sendit(msg){
      var reply="";
      var client = require("net").connect({host: IP, port: Sport}, function() {
      //  console.log('client connected');
      client.write(msg);
      client.on('data', function(data) {
      reply+=data;
      console.log(">"+JSON.stringify(data));
      });
      client.on('end', function() {
      //    console.log('client disconnected');
      return reply;
      });
      });
      }
      
      ////////////////////////////////////////////////////////////////////////
      var sst="";
      //Menus
      menu=function(){
      USB.print(" \n\r");
      USB.print("Select using digit and return key\n\r");
      USB.print("1 LED Off\n\r");
      USB.print("2 LED On\n\r");
      USB.print("0 Exit\n\r");
      };
      parsecmd=function(){
      USB.print("\n\r");
      switch(this.sst.charAt(0)){
      case "1"://LED off
       sst="";
       USB.print("LED Off\n\r");
       sendit("LED Off");
      break;
      case "2"://LED On
       USB.print("LED On\n\r");
       sst="";
       sendit("LED On");
      break;
      case"0"://Exit
       LoopbackA.print("USB.setConsole();\n\r");
       sst="";
       USB.print("Exit\n\r");
      break;
      default:
       menu();
      break;
      }//end switch sst
      };//end parsecmd
      //input cmd from terminal,send it to parsecmd()
      USB.on('data', function (data) {
      var i;
      sst+=data;
      USB.print(data);
      if(sst.length>0)
      if(sst.charAt(sst.length-1)==="\r")parsecmd();
      });
      //Espruino replies here
      LoopbackA.on('data',function(data){
      USB.print(data); //sending data to terminal
      });
      /////////////////////////
      setTimeout(function () {
      var x;
      console.log("Start");
      LoopbackB.setConsole();
      x=test();
      if(x!==0)console.log("Try again");//error seen
      if(x===0) console.log("Exit Seen");
      }, 1000);
      

    And the WebIde output:

    >echo(0);
    =undefined
    Start
    -> LoopbackB
    <- USB
    Start connection process
    Try again
    Reset the ESP8266
    Connecting to WiFi
    >
    Select using digit and return key
    1 LED Off
    2 LED On
    0 Exit
    Wi-Fi Connected
    IP=  192.168.1.4
    null
    >1
    LED Off
    2
    LED On
    1
    LED Off
    0
    Exit
    <- LoopbackB
    =undefined
    =undefined
    >USB.setConsole();
    -> USB
    

    Notice that the 0 Exit restores the console to the USB port.
    The Server output on the Putty terminal:

    Close false
    LED Off
    Close false
    LED On
    Close false
    LED Off
    >
    

    1 Attachment

    1. Modify ESP8266WiFi_0v25.js to do UDP and TCP
      Add UDPflag and CIPstartSTR.

      var at;
      var UDPflag=0;
      var CIPstartStr="";
      var socks = [];
      

    Modify the code to CIPstart a client in the create: function

      create : function(host, port) {
        /* Create a socket and return its index, host is a string, port is an integer.
        If host isn't defined, create a server socket */  
        if (host===undefined) {
    console.log("Server");
          sckt = MAXSOCKETS;
          socks[sckt] = "Wait";
          sockData[sckt] = "";
          at.cmd("AT+CIPSERVER=1,"+port+"\r\n", 10000, function(d) {
            if (d=="OK") {
              socks[sckt] = true;
            } else {
              socks[sckt] = undefined;
              setTimeout(function() {
                throw new Error("CIPSERVER failed ("+(d?d:"Timeout")+")");
              }, 0);
            }
          });
          return MAXSOCKETS;
        } else {  
    console.log("Client");
          var sckt = 0;
          while (socks[sckt]!==undefined) sckt++; // find free socket
          if (sckt>=MAXSOCKETS) throw new Error("No free sockets");
          socks[sckt] = "Wait";
          sockData[sckt] = "";
    
    if(UDPflag){//UDP client
     CIPstartStr='AT+CIPSTART='+sckt+',"UDP",'+JSON.stringify(host)+','+port+','+port+',0\r\n';
    
    }else{//TCP client
     CIPstartStr='AT+CIPSTART='+sckt+',"TCP",'+JSON.stringify(host)+','+port+'\r\n';
    }//end else
    at.cmd(CIPstartStr,10000, function cb(d) {
    

    Add code to set and reset the UDP flag

    "SetUDP":function(){ UDPflag=1;
    },
    "SetTCP" : function(){ UDPflag=0;
    },
    
      "getIP" : function(callback) {
    

    UDP.js placed in modules directory of WebIDE project.

    AT commands for UDP
    https://github.com/espressif/ESP8266_AT/wiki/at_example_0020000903
    https://gist.github.com/mokogobo/3f4d2f074305d4d84344

    https://cdn.sparkfun.com/assets/learn_tutorials/4/0/3/4A-ESP8266__AT_Instruction_Set__EN_v0.30.pdf
    AT+CIPSTART – Establish TCP connection or register UDP port, start connection
    Example
    AT+CIPSTART="TCP","192.168.101.110",1000
    Single connection
    (+CIPMUX=0)
    AT+CIPSTART=,,[,,]
    [,]
    Multiple connection
    (+CIPMUX=1)
    AT+CIPSTART=,,,[,,]
    [,]
    Response
    OK
    or
    ERROR
    If connection already exists, returns
    ALREAY CONNECT
    Parameters

    ID of the connection (0~4), for multi-connect

    string, "TCP" or "UDP"

    string, remote IP

    string, remote port
    []
    for UDP only
    [] In UDP transparent transmission, it has to be 0.
    [] 0
    : destination peer entity of UDP will not change.
    [] 1
    : destination peer entity of UDP can change once.
    [] 2
    : destination peer entity of UDP is allowed to change.
    Note: [] can only be used when [] is set.
    []
    default 0. unit: 500 milliseconds.
    [] 0
    : disable TCP keep-alive
    [] 1 ~ 7200
    : TCP keep-alive interval

    AT+CIPSTART – Function 2: R
    egister UDP port
    Example
    AT+CIPSTART="UDP", "192.168.101.110", 1000, 1002,
    2
    Single connection
    (AT
    +CIPMUX=0)
    AT+CIPSTART=, , [, ,
    ]
    Multiple connection
    (AT+CIPMUX=1
    )
    AT+CIPSTART=, , , [, , ]
    Response
    OK or
    ERROR
    If connection already exists, returns
    ALREADY CONNECT


    1 Attachment

    1. Use UDP to allow clients to find the server IP address
      The UDP is used with a broadcast IP to allow a named client to locate a named server’s IP address.
      The message sent via UDP:

      function UDPmsg(a,b,c,d){
      this.ClientName=a;
      this.ClientIP=b;
      this.ServerName=c;
      this.ServerIP=d;
      }
      var UDP1=new UDPmsg("Bob",0,"Bill",0);
      

    Client named Bob will ask server named Bill for Bill’s IP address

    The server code:

    //UDP_TCPserver1.js
    //2 Oct 2016
    //espruino board with ESP8266
    //PICO  with ESP8266
    //var BroadIP="255.255.255.255";
    var BroadIP="192.168.1.255";
    //var Hardware=0; //Espruino board
    var Hardware =1; //PICO
    var SSID="ssid";
    var key= "router passcode";
    var Serial;
    var Wifi;
    //var LocalIP;
    var UDPport=1234;
    var TCPport=9988;
    function UDPmsg(a,b,c,d){
     this.ClientName=a;
     this.ClientIP=b;
     this.ServerName=c;
     this.ServerIP=d;
    }
    var UDP1=new UDPmsg("Bob",0,"Bill",0);
    var UDP2=new UDPmsg("",0,"",0);
    var ddata="";
    function test(){
    if(Hardware===1)Serial=Serial2;
    if(Hardware===0)Serial=Serial4;
    
    if(Hardware===1){
     digitalWrite(B9,1); // enable on Pico Shim V2
     Serial.setup(115200, { rx: A3, tx : A2 }); //Pico
    }
    if(Hardware===0)Serial.setup(115200, { rx: C11, tx : C10 }); 
    //espruino board
    
    console.log("Start connection process");
    var wifi = require("UDP").connect(Serial, function(err) {
    //var wifi = require("ESP8266WiFi_0v25").connect(Serial, function(err) {
      if (err)return 1;// throw err;
      Wifi=wifi;
    console.log("Reset the ESP8266");
      wifi.reset(function(err) {
        if (err)return 1;// throw err;
    Wifi.at.cmd("AT+CWMODE_CUR=3\r\n", 1000, function(d){console.log(d+"xx1");});
    Wifi.at.cmd("AT+CIPMUX=1\r\n", 1000, function(d){console.log(d+"xx2");});
     console.log("Connecting to WiFi");
     wifi.connect(SSID,key, function(err) {
      if (err)return 1;//throw err;
      wifi.getIP(function(l,ip){
       console.log("IP= ",ip,"\n\r");
       UDP1.ServerIP=ip;
       console.log("LocalIP= ",UDP1.ServerIP);
       console.log("WiFi Connected ");
    
    //start the UDP listener
       setupUDP(); 
    //HTTP or Network Server goes here
       TCPserve();
    
    ////////////////////////////////////////////////////////////
         });//end wifi.getIP
        });//end wifi.connectSSID
      });//end wifi.reset
    });//end wifi.connect
    }//end test
    
    function setupUDP(){
        Wifi.SetUDP();
      var client = require("net").connect({host:BroadIP,port:UDPport ,protocolVersion: 17}, function() {
        console.log('client connected');
        client.write(JSON.stringify(UDP1));
        client.on('data', function(data){
         ddata+=data;
         if(ddata.charAt(ddata.length-1)==='}'){
          UDP2=JSON.parse(ddata);
          console.log(">"+ddata);
          if(UDP1.ServerName===UDP2.ServerName){
           UDP2.ServerIP=UDP1.ServerIP;
           client.write(JSON.stringify(UDP2));
           ddata="";
          }//endif
         }//endif
        });//end client on data
        client.on('close',function(){console.log("client Close");});
        client.on('end', function() {
         console.log('client disconnected');
        });//end client on end
        //client.end();
       });//end UDP client connect
    }//end setupUDP
    
    function TCPserve(){
     var data;
     Wifi.SetTCP();
     var server = require("net").createServer(function(c) {
      // A new client as connected
    //  c.write("Hello");
    /*  c.on('data', function(data) {
        console.log(">"+JSON.stringify(data));
      });
    */
       c.on('close',function(d){
       console.log("Close "+d);
       data=c.read(c.available());
       console.log(data);
       c.write(data);
         if(data==="LED Off"){
          digitalWrite(B12,0); //B12 is green LED
         }
         if(data==="LED On"){
          digitalWrite(B12,1);
         }
      });
      c.end();
    });
    server.listen(TCPport);
    }//end TCPserve
    
    var x;
    console.log("Start");
    x=test();
    if(x!==0)console.log("Try again");//error seen
    
    if(x===0) console.log("Exit Seen");
    

    The output as the server starts

    >echo(0);
    Start
    Start connection process
    Try again
    =undefined
    Reset the ESP8266
    Connecting to WiFi
    OKxx1
    OKxx2
    IP=  192.168.1.3
    LocalIP=  192.168.1.3
    WiFi Connected
    Client
    Server
    client connected
    Send 0 {"ClientName":"Bob","ClientIP":0,"ServerName":"Bill","ServerIP":"192.168.1.3"}
    >
    

    Again disconnect the server from WebIde and connect to Putty terminal application.


    1 Attachment

    1. The UDP/TCP client

      //UDP_TCPclient1.js
      //2 Oct 2016
      //espruino board with ESP8266
      //PICO  with ESP8266
      //var BroadIP="255.255.255.255";
      var BroadIP="192.168.1.255";
      //var LocalIP;
      var UDPport=1234;
      var TCPport=9988;
      //var Hardware=0; //Espruino board
      var Hardware =1; //PICO
      var SSID="ssid";
      var key= "router passcode";
      var Serial;
      var Wifi;
      function UDPmsg(a,b,c,d){
      this.ClientName=a;
      this.ClientIP=b;
      this.ServerName=c;
      this.ServerIP=d;
      }
      var UDP1=new UDPmsg("Bob",0,"Bill",0);
      var UDP2;//=new UDPmsg("",0,"",0);
      var ServerIP=0;
      var ddata="";
      function test(){
      if(Hardware===1)Serial=Serial2;
      if(Hardware===0)Serial=Serial4;
      if(Hardware===1){
      digitalWrite(B9,1); // enable on Pico Shim V2
      Serial.setup(115200, { rx: A3, tx : A2 }); //Pico
      }
      if(Hardware===0)Serial.setup(115200, { rx: C11, tx : C10 }); 
      //espruino board
      console.log("Start connection process");
      var wifi = require("UDP").connect(Serial, function(err) {
      if (err)return 1;// throw err;
      Wifi=wifi;
      console.log("Reset the ESP8266");
      wifi.reset(function(err) {
      if (err)return 1;// throw err;
      Wifi.at.cmd("AT+CWMODE_CUR=3\r\n", 1000, function(d){console.log(d+"xx1");});
      Wifi.at.cmd("AT+CIPMUX=1\r\n", 1000, function(d){console.log(d+"xx2");});
      console.log("Connecting to WiFi");
      wifi.connect(SSID,key, function(err) {
      if (err)return 1;//throw err;
      wifi.getIP(function(l,ip){console.log("IP= ",ip,"\n\r");
       UDP1.ClientIP=ip;
       console.log("Wi-Fi Connected");
      // Setup UDP Client 
       wifi.SetUDP();
       var client = require("net").connect({host: BroadIP, port: UDPport,protocolVersion: 17}, function() {
        console.log('client connected');
        console.log(UDP1.ClientIP);
        client.write(JSON.stringify(UDP1));
        client.on('data', function(data) {
         ddata+=data;
          if(ddata.charAt(ddata.length-1)==='}'){
           UDP2=JSON.parse(ddata);
           console.log(">"+ddata);
           ddata="";
            if((UDP1.ClientName===UDP2.ClientName)&&       (UDP1.ServerName===UDP2.ServerName)){
             ServerIP=UDP2.ServerIP;
      //HTTP or network client goes here
             menu();
            }else{
            }//endif
          }//endif ddata=}
        });//end client on data
        client.on('end', function() {
         console.log('client disconnected');
        });//client on end
        //client.end(");
       });//end client.net
      });//end wif.getIP
      ////////////////////////////////////////////////////////////
      });//end wifi.connect SSID
      });//end wifi.reset
      });//end wifi.connect serial
      }//end test
      //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      function sendit(msg){
      var reply="";
      Wifi.SetTCP();
      var client = require("net").connect({host: ServerIP, port: TCPport}, function() {
      //  console.log('client connected');
      client.write(msg);
      client.on('data', function(data) {
      reply+=data;
      console.log(">"+JSON.stringify(data));
      });
      client.on('end', function() {
      //    console.log('client disconnected');
      return reply;
      });
      });
      }
      ////////////////////////////////////////////////////////////////////////
      var sst="";
      //Menus
      menu=function(){
      USB.print(" \n\r");
      USB.print("Select using digit and return key\n\r");
      USB.print("1 LED Off\n\r");
      USB.print("2 LED On\n\r");
      USB.print("0 Exit\n\r");
      };
      parsecmd=function(){
      USB.print("\n\r");
      switch(this.sst.charAt(0)){
      case "1"://LED off
       sst="";
       USB.print("LED Off\n\r");
       sendit("LED Off");
      break;
      case "2"://LED On
       USB.print("LED On\n\r");
       sst="";
       sendit("LED On");
      break;
      case"0"://Exit
       LoopbackA.print("USB.setConsole();\n\r");
       sst="";
       USB.print("Exit\n\r");
      break;
      default:
       menu();
      break;
      }//end switch sst
      };//end parsecmd
      //input cmd from terminal,send it to parsecmd()
      USB.on('data', function (data) {
      var i;
      sst+=data;
      USB.print(data);
      if(sst.length>0)
      if(sst.charAt(sst.length-1)==="\r")parsecmd();
      });
      //Espruino replies here
      LoopbackA.on('data',function(data){
      USB.print(data); //sending data to terminal
      });
      /////////////////////////
      setTimeout(function () {
      var x;
      console.log("Start");
      LoopbackB.setConsole();
      x=test();
      if(x!==0)console.log("Try again");//error seen
      if(x===0) console.log("Exit Seen");
      }, 1000);
      

    The WebIde screen:

    >echo(0);
    =undefined
    Start
    -> LoopbackB
    <- USB
    Start connection process
    Try again
    Reset the ESP8266
    Connecting to WiFi
    OKxx1
    OKxx2
    IP=  192.168.1.4
    Wi-Fi Connected
    Client
    client connected
    192.168.1.4
    Send 0 {"ClientName":"Bob","ClientIP":"192.168.1.4","ServerName":"Bill","ServerIP":0}
    >
    Select using digit and return key
    1 LED Off
    2 LED On
    0 Exit
    >{"ClientName":"Bob","ClientIP":"192.168.1.4","ServerName":"Bill","ServerIP":"192.168.1.3"}
    >1
    LED Off
    Client
    Send 1 LED Off
    >2
    LED On
    Client
    Send 1 LED On
    >0
    Exit
    <- LoopbackB
    =undefined
    =undefined
    >USB.setConsole();
    -> USB
    

    And the server screen:

    >{"ClientName":"Bob","ClientIP":"192.168.1.4","ServerName":"Bill","ServerIP":0}
    Send 0 {"ClientName":"Bob","ClientIP":"192.168.1.4","ServerName":"Bill","ServerIP":"192.168.1.3"}
    Socket accept 1 "LED O" undefined
    Close false
    LED O
    Socket accept 1 "ff" undefined
    Close false
    ff
    Socket accept 1 "LED On" undefined
    Close false
    LED On
    >
    
    

    1 Attachment

  • I was trying your code, but the net module isn't available anymore on http://www.espruino.com/modules/. Is there anything I'm missing here?

  • The code you are trying to load is for either an espruino board or Pico connected via serial port to an ESP8266. It doesn't currently work on an ESP8266 flashed with the Espruino firmware. That would require modification to the Espruino source code and a recompile. As a consumer of Espruino I'm not currently setup to do such a task. Perhaps some of the ESP8266 folks on the Forum who are so equipped can comment further.

    Some modules are built into the firmware and aren't listed in the modules on the Espruino site.

    Which hardware combination are you trying to use?

  • Espruino looks at different places for modules at code upload time when using just the name as string literal (single or double quoted, but not as variable):

    1. built in - no upload needed. For access, you still need to require() the module
    2. http://www.espruino.com/modules/ - from Espruino's official modules repository
    3. modules folder from project sandbox folder specified

    Web IDE code upload can also handle a url in (as string literal): the code will be pulled from the given url and uploaded to Espruino board (board with Espruino firmware / standalone operating system).

    Espruino executes the uploaded code - it is JavaScript after all - and stores the result in the modules cache, from where it is then pulled at runtime when the require() is actually executed. Again, at that moment it checks wether it is a (binary / firmware / operating system) built-in module, a cached module, or it can be found on a mounted SD card / file system. At runtime / on execution, require() does not care if the provided string argument is a literal or a variable. This allows to dynamically require a module at runtime by a variable, but as said, the module has to found, otherwise an error / exception is thrown.

    To make the code example to work, it may be good enough to define the sandbox project modules folder in the Web IDE and save the UDP module as UDP.js (or minified as UDP.min.js) in that folder. Minifying can be done on googles (JS) closure compiler Web page.

    For a bit more what is going on uploading code, saving code on Espruino board, repowering Espruino boards, etc. can found in the conversation titled simple explanation how to save code that espruino run on start?.

    I hope this helps getting you going...

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Using UPD to find IP addresses for a TCP network in 5 parts.

Posted by Avatar for ClearMemory041063 @ClearMemory041063

Actions