collapse

Author Topic: ESP8266 to C# -- WiFi Serial  (Read 10095 times)

Ladvien

  • Alabtu-ian Refugee
  • Member
  • *
  • Posts: 57
ESP8266 to C# -- WiFi Serial
« on: March 19, 2017, 09:39:49 AM »
ESPER

ESPER is a mini project to troubleshoot how the Lumi program will interact with a remote device.

There are two sets of code below, the first is the C# side of the interaction.  It sets up an HttpClient with POST and GET calls.  The one real variant which makes C# ESPER code a little bit different is the asynchronous polling POST request for data.  This is meant to imitate a serial communication RX line across a WiFi signal.

The other code is Arduino C and sets up the ESP8266 device as an HTTP WebServer.  It can then take data received from the UART and print it to the server.  This allows the C# polling POST call to pick up the data.  Of course, in the same manner, there is a the Arduino code is setup to receive data from the HTTP Client and transmit it across the UART.  Voila! Serial communication across WiFi.  Now all we need is the annoying autobauding sounds and we will be firmly back in the 1990s.

Update 2/7/2016


I've added a search method to the ESPER class.  Basically, this iterates over a range POSTing a name request for the ESPER.  When the C# code discovers an ESPER, then it adds it to an array.  I'm pretty happy with it.

I did run into an issue trying to use Windows.HttpClient, as there doesn't seem to be a way to adjust the timeout.  The default was like 3 seconds, which is way too long.  Therefore, the System.Net.HttpClient was used, since it has a Timeout property which takes a Timespan.

Windows C# Code

Code: [Select]
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Web.Http;

namespace ESPER
{
    class Esper
    {
        const int defaultPollingDelay=50;

        HttpClient httpClient=new HttpClient();
        CancellationTokenSource PollingForDataCancelToken=new CancellationTokenSource();

        private string WebServerUrl { get; set; }
        private int PollingDelay { get; set; }=defaultPollingDelay;
        private bool PollingActive { get; set; }=false;

        public Esper(string consumerUrl)
        {
            WebServerUrl=consumerUrl;
        }

        public async void PostByteArray(byt[/pre]e[] data)
        {

            var httpClient=new HttpClient();
            var webService=WebServerUrl + "data";
            var resourceUri=new Uri(WebServerUrl);
            try
            {
                IBuffer buffer=data.AsBuffer();
                using (HttpBufferContent content=new HttpBufferContent(buffer))
                {
                    content.Headers.Add("Content-Type", "text/html; charset=utf-8");
                    content.Headers.ContentLength=buffer.Length;
                    var response=await httpClient.PostAsync(resourceUri, content);
                    Debug.WriteLine(response);
                }
            }
            catch (TaskCanceledException ex)
            {
                // Handle request being canceled due to timeout.
            }
        }

        public async void PostString(string str)
        {
            var httpClient=new HttpClient();
            var webService=WebServerUrl + "string";
            var resourceUri=new Uri(webService);
            try
            {
                using (HttpStringContent content=new HttpStringContent(str, Windows.Storage.Streams.UnicodeEncoding.Utf8))
                {
                    content.Headers.ContentLength=(ulong)str.Length;
                    using (var response=await httpClient.PostAsync(resourceUri, content)) { };
                }
            }
            catch (TaskCanceledException ex)
            {
                // Handle request being canceled due to timeout.
            }
        }

        public void Start()
        {
            if(false == PollingActive)
            {
                PollingActive=true;
                PollingForDataCancelToken=new CancellationTokenSource();
                PollWebServerDataAvailability();
            }
        }

        public void End()
        {
            PollingActive=false;
            PollingForDataCancelToken.Cancel();
        }

        public void SetPollingDelay(int delayInMilliseconds) { PollingDelay=delayInMilliseconds; }

        private void PollWebServerDataAvailability()
        {   
            try
            {
                Task.Run(async () =>
                {
                    while (true)
                    {
                        if (PollingForDataCancelToken.IsCancellationRequested)
                        {
                            PollingForDataCancelToken.Token.ThrowIfCancellationRequested();
                        }
                        await GetData();
                        await Task.Delay(PollingDelay);
                    }
                }, PollingForDataCancelToken.Token);
            } catch (TaskCanceledException)
            {
                // TODO: Add cancelation callback here.
            }
        }

        public async Task<string> GetData()
        {
            var cts=new CancellationTokenSource();
            cts.CancelAfter(TimeSpan.FromSeconds(30));

            var webService=WebServerUrl + "buffer";
            var resourceUri=new Uri(webService);
            try
            {
                HttpResponseMessage response=await httpClient.PostAsync(resourceUri, null);
                var message=await response.Content.ReadAsStringAsync();
                if (message != "") { Debug.WriteLine(message); }
                response.Dispose();
                cts.Dispose();
                return message;
            }
            catch (TaskCanceledException ex)
            {
                // Handle request being canceled due to timeout.
                return "";
            }
            return "";
        }
    }
}



Arduino ESPER​ WebServer
Code: [Select]

/*
 * This code has been adapted from:
 *    "SDWebServer - Example WebServer with SD Card backend for esp8266
 *    Copyright (c) 2015 Hristo Gochkov. All rights reserved.
 *    This file is part of the ESP8266WebServer library for Arduino environment."
 *
*/

const char* ssid = "SSID";
const char* password = "password";

// Gross.  Global variables.  These are used for collecting Serial Data.
String inputBuffer = "";         
 
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const int ledPin = 2;
const char* host = "esp8266sd";

ESP8266WebServer server(80);

void returnOK() {
  server.send(200, "text/plain", "");
}

void returnFail(String msg) {
  server.send(500, "text/plain", msg + "\r\n");
}

void debugWebRequest(){
  String message = "";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  Serial.print(message);
}

void getSerialBuffer(){
  Serial.print("Sent data: ");
  Serial.println(inputBuffer);
  server.send(200, "text/plain", inputBuffer);
  inputBuffer = "";
}

void handleUnknownPost(){
  returnOK();
  //debugWebRequest(); 
  Serial.print("Unknown POST.");
}

void handleStringPost(){
  returnOK();
  //debugWebRequest();
  Serial.print("Got data: ");
  Serial.print(server.arg(0));
}

void handleDataPost(){
  returnOK();
  //debugWebRequest();
  Serial.print("Data POST.");
}

void handleNotFound(){
  returnOK();
  //debugWebRequest();
  Serial.print("Resource not found POST.");
}

void setup(void){
  pinMode(ledPin, OUTPUT);
 
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.print("\n");
  WiFi.begin(ssid, password);
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  digitalWrite(ledPin, HIGH);
  bool isLedPinOn = true;
 
  // Wait for connection
  uint8_t i = 0;
  while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
    delay(500);
    Serial.print(".");
    isLedPinOn = !isLedPinOn; 
   
  }
  Serial.println("");
  if(i == 21){
    digitalWrite(ledPin, HIGH);
    Serial.print("Could not connect to");
    Serial.println(ssid);
    while(1) {
      digitalWrite(ledPin, isLedPinOn ? HIGH : LOW);
      delay(200);
    }
  }
 
  digitalWrite(ledPin, LOW); 
 
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
 
  if (MDNS.begin(host)) {
    MDNS.addService("http", "tcp", 80);
    Serial.println("MDNS responder started");
    Serial.print("You can now connect to http://");
    Serial.print(host);
    Serial.println(".local");
  }

  server.on("/", HTTP_POST, handleUnknownPost);
  server.on("/string", HTTP_POST, handleStringPost);
  server.on("/data", HTTP_POST, handleDataPost);
  server.on("/buffer", HTTP_POST, getSerialBuffer);
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputBuffer += inChar;
  }
}

mogul

  • Hot glue gunslinger
  • Member
  • *
  • Posts: 151
  • This is a good day!
Re: ESP8266 to C# -- WiFi Serial
« Reply #1 on: March 19, 2017, 01:24:36 PM »
Interesting. Still trying to understand what you have made, how it can be used. Is it a general purpose setup or very specialized for the debugging task you had at hand?

One thing springs in mind, wouldn't a TCP socket have been more ideal for the serial emulation?

Ladvien

  • Alabtu-ian Refugee
  • Member
  • *
  • Posts: 57
Re: ESP8266 to C# -- WiFi Serial
« Reply #2 on: March 20, 2017, 08:09:04 AM »
Well, it's not complete yet, but the idea is to have C# framework which can search for ESP8266's on the network and easily pass data between the computer and microcontroller.

Another goal is to have the ability to upload AVR programs wirelessly using the TinySafeBoot bootloader.  I've written several iterations of the TSB uploader.  The last iteration I demonstrated the proof-of-concept using Bluetooth LE.

Wireless Upload to BoB Brain Board (Atmega328P) using BLE

As for the TCP socket; I've no experience at network programming, however, I think you are right.  TCP socket's would make the process much smoother and flexible.  But, the goal was to consume framework / packages to accomplish a task quickly.  The ESP8266's Arduino WebServer made it extremely quick to setup.

This may not seem logical, but I've been reading the The Mythical Man Month and a pitfall outlined is trying to re-write base level code--as it is consumes more time and is prone to error.

 

* Search


* Recent Topics

The unnamed (yet) quatruped spider project by tinhead
[July 01, 2020, 04:22:11 PM]


"1984 Nixie Time" by 1 what
[May 08, 2020, 01:04:18 AM]


2D Side Scroller Cyberpunk themed by Killer Angel
[February 06, 2020, 06:39:40 AM]


A new wing design for model aircraft / drones by OddBot
[February 06, 2020, 04:42:06 AM]


SDR (Software Defined Radio) by Gareth
[February 02, 2020, 06:15:42 AM]


Circuit Math by ZeroMax
[January 31, 2020, 01:50:18 PM]


NanOMeter by Protowrxs
[January 01, 2020, 12:59:44 PM]


Investigating the VL53L0X Laser Rangefinder by erco
[December 30, 2019, 10:45:44 PM]


PS4 Single Handed Controller Deployed (part 7 of 7) by Gareth
[December 30, 2019, 09:52:29 AM]


"D" -Pad Workio just like Magic (Will Merlin stay or Go) (part 6 of 7) by Gareth
[December 30, 2019, 09:51:27 AM]


PS4 Joystick Digitals 4,5,6,7,10 - Analog's Lx,Ly,Rx,Ry Workio (part 5 of 7) by Gareth
[December 30, 2019, 09:50:37 AM]


Menu Workio ! (part 4 of 7) by Gareth
[December 30, 2019, 09:49:49 AM]


L1 trigger design Workio (Hori controller) (part 3 of 7) by Gareth
[December 30, 2019, 09:48:50 AM]


Hori aka PS4 Joystick Mappings (part 2 of 7) by Gareth
[December 30, 2019, 09:47:17 AM]


PS4 Single Left-Handed Controller (part 1 of 7) by Gareth
[December 30, 2019, 09:44:58 AM]

* Recent Posts

Re: The unnamed (yet) quatruped spider project by tinhead
[July 01, 2020, 04:22:11 PM]


Re: The unnamed (yet) quatruped spider project by jinx
[July 01, 2020, 04:06:19 PM]


Re: "1984 Nixie Time" by 1 what
[May 08, 2020, 01:04:18 AM]


Re: "1984 Nixie Time" by tomasp
[April 13, 2020, 06:03:28 PM]


Re: 2D Side Scroller Cyberpunk themed by Killer Angel
[February 06, 2020, 06:39:40 AM]


A new wing design for model aircraft / drones by OddBot
[February 06, 2020, 04:42:06 AM]


Re: "1984 Nixie Time" by Gareth
[February 02, 2020, 06:23:01 AM]


Re: SDR (Software Defined Radio) by Gareth
[February 02, 2020, 06:15:42 AM]


Re: SDR (Software Defined Radio) by ZeroMax
[January 31, 2020, 01:54:21 PM]


Re: "1984 Nixie Time" by ZeroMax
[January 31, 2020, 01:52:29 PM]


Circuit Math by ZeroMax
[January 31, 2020, 01:50:18 PM]


Re: 2D Side Scroller Cyberpunk themed by ZeroMax
[January 31, 2020, 01:45:33 PM]


NanOMeter by Protowrxs
[January 01, 2020, 12:59:44 PM]


Re: Investigating the VL53L0X Laser Rangefinder by erco
[December 30, 2019, 10:45:44 PM]


PS4 Single Handed Controller Deployed (part 7 of 7) by Gareth
[December 30, 2019, 09:52:29 AM]