Anurag S. Vasanwala
Published © GPL3+

Windows 10 IoT Core : Hydroflyer

Modify RC boat and maneuver using Windows Phone or Window Laptop.

IntermediateShowcase (no instructions)21,282
Windows 10 IoT Core : Hydroflyer

Things used in this project

Hardware components

RC Boat (Min 48 cm long)
×1
Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
×1
Arduino Nano R3
Arduino Nano R3
×1
ESP8266 ESP-01
Espressif ESP8266 ESP-01
×1
GPS Module (uBlox Neo 6m; 9600 baud)
×1
Digital Compass HMC5883L
×1
N-Channel MOSFET (75NF75)
×1
L298 (H-Bridge Motor Driver)
×1
Voltage Regulator 3.3V (LD1117V33)
×1
Voltage Regulator 5V (7805)
×1
Electrolytic Capasitor (35V; 100uF)
×6
Glass Fuse (5 Amp)
×1
Glass Fuse (10 - 15 Amp)
×1
Resistor 1K
×2
Resistor 2K
×1
Diode (1N5822)
×1

Software apps and online services

Arduino IDE
Arduino IDE
Visual Studio 2015
Microsoft Visual Studio 2015
Windows 10 IoT Core
Microsoft Windows 10 IoT Core

Story

Read more

Schematics

Flashing/Programming ESP8266-01

Hydroflyer - Circuit Reference

Hydroflyer - Circuit Reference (Fritzing)

Hydroflyer - Circuit Reference

Hydroflyer - Circuit Reference (Fritzing)

Code

ESP8266-01 (Arduino Sketch)

C/C++
/*
	ESP8266-01 sketch for Hydroflyer (Boat) (RPi2 + WinIoT)
	https://www.hackster.io/AnuragVasanwala/windows-10-iot-core-hydroflyer-f83190

	NOTE: This sketch is tested and meant for ESP8266-01.

	Objectives:
	+ Create Access Point (Act as WiFi)
	+ Listen for 'Maneuver' GET requests
	+ Fetch arguments from GET request and store them in global 'Arguments'
	+ Respond back plain-text from global buffer 'ReplyBuffer_100' which was received from gateway
	+ On root or index GET request, send back project description html page
	+ Initialize serial port and listen for incoming data
	+ When data is received from gateway and send it back global 'Arguments'
	+ Gateway will send character array to ESP which will be store in global buffer 'ReplyBuffer_100'
	+ ESP will send back character array 'ReplyBuffer_100' to requested remote device via 'Maneuver' GET request
	+ Auto-reset global Arguments if no device connects for more than 'AutoReset_Timeout'

	: WARNING :
		+ 'Argument_Size' AND 'SensorBuffer_Size' MUST BE IDENTICAL ON GATEWAY SKETCH.
		+ SERIAL BAUD RATE MUST BE SAME ON BOTH SIDE (GATEWAY & ESP).
		+ 'SEND_DATA_STRUCTURE' MUST BE IDENTICAL ON GATEWAY SIDE (WITH NAME OF 'RECEIVE_DATA_STRUCTURE').
		+ 'RECEIVE_DATA_STRUCTURE' MUST BE IDENTICAL ON GATEWAY SIDE (WITH NAME OF 'SEND_DATA_STRUCTURE').
		+ YOU MUST HAVE 'EasyTransfer' LIBRARY IN YOUR ARDUINO FOLDER.
		  TO INSTALL 'EasyTransfer', REFER: https://github.com/madsci1016/Arduino-EasyTransfer
		  THIS REPOSITORY CONTAINS:
			  + EasyTransfer
			  + EasyTransferI2C
			  + EasyTransferVirtualWire
			  + SoftEasyTransfer

			  BUT YOU ONLY HAVE TO COPY FIRST FOLDER 'EasyTransfer' TO YOUR ARDUINO's LIBRARY FOLDER
*/

/* Global Includes */
#include <ESP8266WiFi.h>
#include <EasyTransfer.h>
#include <ESP8266WebServer.h>
#include <WiFiClient.h> 


/* Arguments sizes and Timeout constants */
#define AutoReset_Timeout    500        // If no device connects for this milliseconds, ESP will reset 'Arguments'
#define Argument_Size        6          // Number of arguments to be fetch and store in global 'Arguments'
#define SensorBuffer_Size    100        // Max number of characters to be sent back to remote device

/* Create MyServer object that listens on port number 80 (which is default http port) */
ESP8266WebServer MyServer(80);

/* Access Point's SSID and Password */
const char *SsID = "WIoT - Hydroflyer";    // SsID should not be more than 15 chars
const char *Password = "12345678";

/*
EasyTransfer library makes it much easy to send and receive composite data over Serial.
Create separate object for sending and receiving.
*/
EasyTransfer ETin, ETout;

/* 'SEND_DATA_STRUCTURE' MUST BE IDENTICAL ON GATEWAY SIDE (WITH NAME OF 'RECEIVE_DATA_STRUCTURE') */
struct SEND_DATA_STRUCTURE
{
	byte Arguments[Argument_Size];
};

/* 'RECEIVE_DATA_STRUCTURE' MUST BE IDENTICAL ON GATEWAY SIDE (WITH NAME OF 'SEND_DATA_STRUCTURE') */
struct RECEIVE_DATA_STRUCTURE
{
	char ReplyBuffer_100[SensorBuffer_Size];
};

SEND_DATA_STRUCTURE OutgoingData;
RECEIVE_DATA_STRUCTURE IncomingData;


/* Global Counters */
uint8_t C1;
unsigned long LastAccessTime;  // To reset 'Arguments' if no connection was made in last AutoReset_Timeout milliseconds.



/* Handles index or root requests */
void HandleRoot() {
	MyServer.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate", false);
	MyServer.sendHeader("Pragma", "no-cache", false);
	MyServer.sendHeader("Expires", "1000", false);
	MyServer.send(200, "text/html", "<html><head><title>Windows IoT - Hydroflyer</title></head><body>Hydrofler is online.<br/>Windows IoT is the heart of the boat.<br/>To maneuver boat, please refer: www.hackster.io/AnuragVasanwala/windows-10-iot-core-hydroflyer");
}

/* Handles maneuver requests */
void HandleManeuver() {
	/* Parse arguments and store it to Arguments byte array */
	for (C1 = 0; (C1 < Argument_Size) && (C1 < MyServer.args()); C1++)
	{
		/* Convert string argument value to equivalent byte */
		OutgoingData.Arguments[C1] = (byte)(MyServer.arg(C1).toInt());
	}

	/* Prepare html headers to instruct remote device to not to cache page */
	MyServer.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate", false);
	MyServer.sendHeader("Pragma", "no-cache", false);
	MyServer.sendHeader("Expires", "0", false);

	/* Send data to remote device */
	MyServer.send(200, "text/plain", IncomingData.ReplyBuffer_100);

	LastAccessTime = millis();
}

void setup()
{
	/* Wait for a one and half second to make ESP stable */
	delay(1500);

	/*
		Configure and start Serial Communication with gateway.
		SERIAL BAUD RATE MUST BE IDENTICAL GATEWAY, TOO.
	*/
	Serial.begin(115200);

	/*
		Setup ESP as AP with 'SsID' and 'Password'.
		Please make sure to do not give SSID more than 15
		and password length must also be less than 10 char
		in order to achieve quick response from ESP.
	*/
	WiFi.mode(WIFI_AP);
	WiFi.softAP(SsID, Password);

	/* Handle index or root GET request on function HandleRoot */
	MyServer.on("/", HandleRoot);

	/* Handle Maneuver GET request on function HandleManeuver */
	MyServer.on("/Maneuver", HandleManeuver);

	/* Start MyServer */
	MyServer.begin();

	/* Initialize ReplyBuffer to show initial message */
	IncomingData.ReplyBuffer_100[0] = 'B';
	IncomingData.ReplyBuffer_100[1] = 'O';
	IncomingData.ReplyBuffer_100[2] = 'O';
	IncomingData.ReplyBuffer_100[3] = 'T';
	IncomingData.ReplyBuffer_100[4] = 'I';
	IncomingData.ReplyBuffer_100[5] = 'N';
	IncomingData.ReplyBuffer_100[6] = 'G';
	IncomingData.ReplyBuffer_100[7] = '\0';

	/*
		Initialize ETin for incoming data while ETout for outgoing data
			+ Received data from gateway will be stored in 'IncomingData'
			+ 'OutgoingData' will holds data to be send to gateway on request
	*/
	ETin.begin(details(IncomingData), &Serial);
	ETout.begin(details(OutgoingData), &Serial);
}

void loop()
{
	/* Periodically check for request on Web Server */
	MyServer.handleClient();

	/* If gateway sends data to ESP, receive it and send back 'Arguments' */
	if (ETin.receiveData())
	{
		/* If no device has been connected for last 'AutoReset_Timeout' time, reset Arguments */
		if (millis() - LastAccessTime > AutoReset_Timeout)
		{
			for (C1 = 0; C1 < Argument_Size; C1++)
			{
				OutgoingData.Arguments[C1] = 0;
			}
		}

		/* Send back 'Arguments' to Gateway */
		ETout.sendData();
	}
}

Gateway Sketch (Arduino Sketch)

C/C++
/*
	Gateway sketch for Hydroflyer (Boat) (RPi2 + WinIoT)
	https://www.hackster.io/AnuragVasanwala/windows-10-iot-core-hydroflyer-f83190

	NOTE: This sketch is tested and meant for Arduino Nano.

	Gateway sketch for Arduino is still in early development. Contribution is most welcomed.

	Objectives:
		+ Provide support for ADC, PWM, Serial to I2C Master on request
		+ Periodically send ESP8266-01 'ReplyBuffer_100' received from I2C Master
		+ Once 'ReplyBuffer_100' is sent to ESP, ESP will send back 'Arguments' parsed from 'Maneuver' GET request so read them.
		+ Provide support for GPS (Device: uBlox New6mV2 with 9600 baud) to I2C Master on demand

	: WARNING :
		+ 'Argument_Size' AND 'SensorBuffer_Size' MUST BE IDENTICAL ON ESP8266-01 SKETCH.
		+ SERIAL BAUD RATE MUST BE SAME ON BOTH SIDE (GATEWAY & ESP).
		+ 'SEND_DATA_STRUCTURE' MUST BE IDENTICAL ON ESP8266 SIDE (WITH NAME OF 'RECEIVE_DATA_STRUCTURE').
		+ 'RECEIVE_DATA_STRUCTURE' MUST BE IDENTICAL ON ESP8266 SIDE (WITH NAME OF 'SEND_DATA_STRUCTURE').
		+ YOU MUST HAVE 'EasyTransfer' LIBRARY IN YOUR ARDUINO FOLDER.
		  TO INSTALL 'EasyTransfer', REFER: https://github.com/madsci1016/Arduino-EasyTransfer
		  THIS REPOSITORY CONTAINS:
			+ EasyTransfer
			+ EasyTransferI2C
			+ EasyTransferVirtualWire
			+ SoftEasyTransfer

			BUT YOU ONLY HAVE TO COPY FIRST FOLDER 'EasyTransfer' TO YOUR ARDUINO's LIBRARY FOLDER
*/

/* Global Include */
#include <Wire.h>
#include <TinyGPS++.h>
#include <EasyTransfer.h>
#include <SoftwareSerial.h>

/* Our pretty Gateway's name or let's say address */
#define MyGateway1 0x40

/* Arguments sizes and Timeout constants */
#define Argument_Size		6          // Number of arguments to be fetch and store in global 'Arguments'
#define SensorBuffer_Size   100        // Max number of characters to be sent back to remote device

/* GPS Pin defination & Baud Rate*/
#define GPS_RX_PIN			8
#define GPS_TX_PIN			9
#define GPS_BAUD			9600       // Please confirm your GPS's baud. If it is 115200, them use AltSoftSerial instead of SoftwareSerial

/*
	[ DO NOT MODIFY ]
	Following are the list of Gateway Protocol Mode
	They must be identical on both side (Windows IoT Sketch and Arduino Sketch)
	Please refer Windows IoT Hydroflyser's Header File: Library\Gateway (I2C)\Gateway.h
*/
#define GATEWAY_CTRL_pinMode								10
#define GATEWAY_CTRL_digitalWrite							11
#define GATEWAY_CTRL_digitalRead							12
#define GATEWAY_CTRL_analogWrite							13
#define GATEWAY_CTRL_analogRead								14
#define GATEWAY_CTRL_getManeuverBytes						15
#define GATEWAY_CTRL_getRequestedURL						16
#define GATEWAY_CTRL_getGPS									17
#define GATEWAY_CTRL_sendSensorData_ToRemoteDevice			18
#define GATEWAY_CTRL_sendString_ToRemoteDevice				19
#define GATEWAY_CTRL_sendString_ToRemoteDevice_Continued	20

/* Byte manipulation macro */
#define DoubleToByte(buf,doubleData) \
  *((double *)buf) = doubleData;

#define IntegerToByte(buf,intData) \
  *((int *)buf) = intData;

/* Global Variables */
byte CTRL, Pin, BoolValue, Temp_X[SensorBuffer_Size], Value;
uint16_t Index;
int IntValue;

/* Global Counters */
byte C1 = 0;

/* GPS Device object & Variable */
TinyGPSPlus GPD_Device;
byte GPS_SendBuffer[16], Buffer_4[4], Buffer_2[2];

/*
	GPS Device UART Link

	: WARNING :
		IF YOUR GPS DEVICE SUPPORTS 115200 BAUD, PLEASE USE 'AltSoftSerial' LIBRARY INSTEAD OF 'SoftwareSerial'
*/
SoftwareSerial GPS_UART(GPS_RX_PIN, GPS_TX_PIN);

/*
	EasyTransfer library makes it much easy to send and receive composite data over Serial.
	Create separate object for sending and receiving.
*/
EasyTransfer ETin, ETout;

/* 'RECEIVE_DATA_STRUCTURE' MUST BE IDENTICAL ON GATEWAY SIDE (WITH NAME OF 'SEND_DATA_STRUCTURE') */
struct RECEIVE_DATA_STRUCTURE
{
	byte Arguments[Argument_Size];
};

/* 'SEND_DATA_STRUCTURE' MUST BE IDENTICAL ON GATEWAY SIDE (WITH NAME OF 'RECEIVE_DATA_STRUCTURE') */
struct SEND_DATA_STRUCTURE
{
	char ReplyBuffer_100[SensorBuffer_Size];
};

SEND_DATA_STRUCTURE mydata_out;
RECEIVE_DATA_STRUCTURE mydata_in;

void setup()
{
	/* Initialize Gateway as I2C slave and register Request and Response handler */
	Wire.begin(MyGateway1);
	Wire.onRequest(onRequest);
	Wire.onReceive(onReceive);

	/* Initialize Hardware Serial to communicate with ESP8266-01 with 115200 baud. */
	Serial.begin(115200);

	/* Initialize Software Serial to communicate with GPS (uBlox Neo6mV2) with GPS_BAUD baud. */
	GPS_UART.begin(GPS_BAUD);

	/*
		Initialize ETin for incoming data while ETout for outgoing data
			+ 'OutgoingData' will holds data to be send to ESP on request
			+ Received data from ESP will be stored in 'IncomingData'
			
	*/
	ETin.begin(details(mydata_in), &Serial);
	ETout.begin(details(mydata_out), &Serial);
	
	/* Initial response to Remote Device (WP8.1/10) */
	mydata_out.ReplyBuffer_100[0] = 'B';
	mydata_out.ReplyBuffer_100[1] = 'O';
	mydata_out.ReplyBuffer_100[2] = 'O';
	mydata_out.ReplyBuffer_100[3] = 'T';
	mydata_out.ReplyBuffer_100[4] = 'I';
	mydata_out.ReplyBuffer_100[5] = 'N';
	mydata_out.ReplyBuffer_100[6] = 'G';
	mydata_out.ReplyBuffer_100[7] = ':';
	mydata_out.ReplyBuffer_100[8] = 'G';
	mydata_out.ReplyBuffer_100[9] = 'A';
	mydata_out.ReplyBuffer_100[10] = 'T';
	mydata_out.ReplyBuffer_100[11] = 'E';
	mydata_out.ReplyBuffer_100[12] = 'W';
	mydata_out.ReplyBuffer_100[13] = 'A';
	mydata_out.ReplyBuffer_100[14] = 'Y';
	mydata_out.ReplyBuffer_100[15] = '\0';

}
unsigned long LastUpdateTime;

void loop() {
	/* Periodically send response string to ESP (received from RPi2) and take back 'Arguments' from ESP */
	if (millis() - LastUpdateTime > 30)
	{
		/* Send string received from RPi2 */
		ETout.sendData();
		
		/* Wait for a while before receiving 'Arguments' from ESP */
		delay(30);

		/* Receive 'Arguments' from ESP */
		if (ETin.receiveData()){}

		/* Update time to avoid frequent calls */
		LastUpdateTime = millis();
	}

	/* Read and parse GPS Data if ready */
	while (GPS_UART.available() > 0)
		if (GPD_Device.encode(GPS_UART.read()))
			FetchGPSData();
}

/* Executes when RPi2 requests number of bytes */
void onRequest()
{
	/* CTRL will be set by onReceived */
	switch (CTRL)
	{
	case GATEWAY_CTRL_digitalRead:
		Wire.write(BoolValue);
		break;
	case GATEWAY_CTRL_analogRead:
		IntegerToByte(Buffer_2, IntValue);
		Wire.write(Buffer_2, 2);
		break;
	case GATEWAY_CTRL_getManeuverBytes:
		Wire.write(mydata_in.Arguments, Argument_Size);
		break;
	case GATEWAY_CTRL_getRequestedURL:
		/* NOT IMPLEMENTED YET; CONTRIBUTION IS MOST WELCOMED */
		break;
	case GATEWAY_CTRL_getGPS:
		Wire.write(GPS_SendBuffer, 16);
		break;
	case GATEWAY_CTRL_sendSensorData_ToRemoteDevice:
	case GATEWAY_CTRL_sendString_ToRemoteDevice:
	case GATEWAY_CTRL_sendString_ToRemoteDevice_Continued:
		break;
	default:
		break;
	}
}

void onReceive(int NOB)
{
	CTRL = Wire.read();

	switch (CTRL)
	{
	case GATEWAY_CTRL_pinMode:
		Pin = Wire.read();
		Value = Wire.read();
		pinMode(Pin, Value);
		break;
	case GATEWAY_CTRL_digitalWrite:
		Pin = Wire.read();
		Value = Wire.read();
		digitalWrite(Pin, Value);
		break;
	case GATEWAY_CTRL_digitalRead:
		Pin = Wire.read();
		BoolValue = digitalRead(Pin);
		break;
	case GATEWAY_CTRL_analogWrite:
		analogWrite(Wire.read(), Wire.read());
		break;
	case GATEWAY_CTRL_analogRead:
		IntValue = analogRead(Wire.read());
		break;
	case GATEWAY_CTRL_getManeuverBytes:
	case GATEWAY_CTRL_getRequestedURL:
	case GATEWAY_CTRL_getGPS:
		break;
	case GATEWAY_CTRL_sendSensorData_ToRemoteDevice:
	case GATEWAY_CTRL_sendString_ToRemoteDevice:
		Index = 0;
		for (C1 = 0; C1 < 31; C1++)
		{
			mydata_out.ReplyBuffer_100[Index++] = Wire.read();
		}
		mydata_out.ReplyBuffer_100[Index] = '\0';
		break;
	case GATEWAY_CTRL_sendString_ToRemoteDevice_Continued:
		for (C1 = 0; C1 < 31; C1++)
		{
			mydata_out.ReplyBuffer_100[Index++] = Wire.read();
		}
		if (Index < 99)
		{
			mydata_out.ReplyBuffer_100[Index] = '\0';
		}
		break;
	default:
		break;
	}
	while (Wire.available())
	{
		Wire.read();
	}
}

void FetchGPSData()
{
	//Validation Byte
	bitWrite(GPS_SendBuffer[0], 0, GPD_Device.location.isValid());
	bitWrite(GPS_SendBuffer[0], 1, GPD_Device.altitude.isValid());
	bitWrite(GPS_SendBuffer[0], 2, GPD_Device.date.isValid());
	bitWrite(GPS_SendBuffer[0], 3, GPD_Device.time.isValid());
	bitWrite(GPS_SendBuffer[0], 4, GPD_Device.speed.isValid());
	//Future Use
	bitClear(GPS_SendBuffer[0], 5);
	bitClear(GPS_SendBuffer[0], 6);
	bitClear(GPS_SendBuffer[0], 7);

	//Location : Latitude
	DoubleToByte(Buffer_4, GPD_Device.location.lat());
	GPS_SendBuffer[1] = Buffer_4[0];
	GPS_SendBuffer[2] = Buffer_4[1];
	GPS_SendBuffer[3] = Buffer_4[2];
	GPS_SendBuffer[4] = Buffer_4[3];

	//Location : Longitude
	DoubleToByte(Buffer_4, GPD_Device.location.lng());
	GPS_SendBuffer[5] = Buffer_4[0];
	GPS_SendBuffer[6] = Buffer_4[1];
	GPS_SendBuffer[7] = Buffer_4[2];
	GPS_SendBuffer[8] = Buffer_4[3];

	//Date : Day
	GPS_SendBuffer[9] = (byte)GPD_Device.date.day();
	//Date : Month
	GPS_SendBuffer[10] = (byte)GPD_Device.date.month();
	//Date : Year
	IntegerToByte(Buffer_2, GPD_Device.date.year());
	GPS_SendBuffer[11] = Buffer_2[0];
	GPS_SendBuffer[12] = Buffer_2[1];

	//Time : Hour
	GPS_SendBuffer[13] = (byte)GPD_Device.time.hour();
	//Time : Minute
	GPS_SendBuffer[14] = (byte)GPD_Device.time.minute();
	//Time : Second
	GPS_SendBuffer[15] = (byte)GPD_Device.time.second();
}

Windows IoT + ESP + Gateway

Credits

Anurag S. Vasanwala

Anurag S. Vasanwala

8 projects • 506 followers

Comments