ESP8266 Module - Issues

Coding and general discussion relating to user created compiler modules

Moderators: David Barker, Jerry Messina

Post Reply
User avatar
RangerBob
Posts: 152
Joined: Thu May 31, 2007 8:52 am
Location: Beds, UK

ESP8266 Module - Issues

Post by RangerBob » Tue Dec 09, 2014 1:26 pm

Hello All,

I'm releasing a module for the low cost ESP8266 Uart to Wifi Bridge.

EDIT:*** POSSIBLY FIXED ***
{
Please be aware that this is still a work in progress; there is a major bug in the module somewhere, but I have spent far too long on this module already and cannot seem to get to the bottom of the bug.

If anyone else wants to take a look at this I would be most grateful. It appears to be a memory leak/trashing issue that occurs somewhere *i think* around the IP address checking. The module will work for a while, then jam up somewhere. There is a lot of string manipulation going on, it could be around there.
}

The demo program posts values to the Sparkfun data service (Phant), and would be a valuable core to an Internet Of Things type device. I have chosen sparkfun over my previous Pachube/Cosm/Xively services as it is much simpler to interact with, and they provide the source to run your own instance. My Demo stream is here.

In the code, you'll need to add your own PUBLICKEY & PRIVATEKEY, from the sparkfun instructions, plus your own Wifi SSID and PASSWORD.

This is tested with an ESP8266 module with Version: 00160901 firmware. I have not tested against anything else.

Module Code:

Code: Select all

Module ESP

#option RX_BUFFER_SIZE = 256 			// Need to have a good sized buffer, 64 was too short.
#option RX_PRIORITY = ipLow
'#option RX_PRIORITY = ipHigh

Include "String.bas"
Include "Convert.bas"
Include "USART.bas"
Include "ISRRX.bas"

Public Dim
	// Pass through some ISRRX routines
   DataAvailable As ISRRX.DataAvailable,
   ISRReset As ISRRX.Reset,
   ISRStart As ISRRX.Start,
   ISRStop As ISRRX.Stop,
   ReadByte As ISRRX.ReadByte,
   ReadWord As ISRRX.ReadWord,
   ReadLongWord As ISRRX.ReadLongWord,
   ReadFloat As ISRRX.ReadFloat,
   ReadStr As ISRRX.ReadStr

Public Const 
	CR = #13,
	LF = #10,
	QU = #34

Public Structure TIPAddr
    Val(4) As Byte
    IP As Val(0).AsLongWord
End Structure
	
Public Dim
	IPAddr As TIPAddr 

{
****************************************************************************
* Name    : ISRRXFind  (BLOCKING W T/O)                                    *
* Purpose : Finds pStr in the ISRRX buffer (blocking with timeout)         *
****************************************************************************
}
Public Function ISRRXFind(pStr As String, pTimeout As Word) As Boolean
   Dim Counter, StrIndex As Byte
   Dim Timeout As Word
   Dim Ch As Byte

   Result = False
   StrIndex = 0
   Counter = 10
   Repeat
      Timeout = pTimeout
      While Timeout > 0
         Ch = pStr(StrIndex)
         If Ch = 0 Then
            result = True
            Exit
         ElseIf ISRRX.DataAvailable Then
            If Ch = ISRRX.ReadByte() Then
               Inc(StrIndex)
            Else
               StrIndex = 0
            EndIf   
         EndIf
         DelayUS(100)
         Dec(Timeout)
      Wend
      Dec(Counter)
   Until Counter = 0
   
End Function

{
****************************************************************************
* Name    : ReadString (BLOCKING W T/O)                                    *
* Purpose : Reads pText string from the ISRRX buffer (blocking with timeout)*
****************************************************************************
}
Function ReadString(ByRef pText As String, pTimeout As Word, count As Word, pTerminator As Char = null) As Byte
	Dim Ch As Char
	Dim tTimeout As LongWord
			
	tTimeout = pTimeout * 4000
   
	Result = 0
	Repeat
		Repeat
			DelayUS(25)
         	Dec(tTimeout)
         	If tTimeout = 0 Then
         		result = 0
         		pText(0) = 0	// Null terminate whatever we have
         		Exit
 			EndIf         	
		Until ISRRX.DataAvailable
		Ch = ISRRX.ReadByte()
		If Ch <> pTerminator Then
			pText(result) = Ch
			Inc(Result)
			If result = Count - 2 Then 
         		pText(result) = 0	// Null terminate whatever we have
         		Inc(Result)
         		Exit
			EndIf				
		EndIf   
	Until Ch = pTerminator
	pText(result) = 0
	Inc(Result)
End Function
{
****************************************************************************
* Name    : StrToIP                                                        *
* Purpose : Checks whether pString is an IP address, and formats it        *
****************************************************************************
}
Public Function StrToIP(pString As String, ByRef IPaddress As TIPAddr) As Boolean

	Dim 
	octno As Byte,
	oct As String(10),
	Pos As Byte,
	split As Byte
		
	result = false	
	Pos = 0
	
	// Sensible size?  
   	FSR0 = AddressOf(pString)
   	While POSTINC0 <> 0 And pos < 18
      	Inc(pos)
   	Wend
   	
	// Carry on...
	If pos < 18 And pos > 0 Then
		// Split into octlets
		For octno = 3 To 0 Step -1
			split = Position(".", pString)
			If split > -1 Then 
				oct = Left(pString, split)			// Get octlet
				If IsDecValid(oct) Then				// valid decimal number?
					IPaddress.Val(octno) = Byte(StrToDec(oct))	
				Else
					IPaddress.IP = $00000000
					Exit        					// Nope. Exit false
				EndIf
				pString = Mid(pString, split + 1)	// Get remainder
			Else
				IPaddress.IP = $00000000
				Exit 								// Couldn't find split (.)
			EndIf
		Next
		
		result = true
	EndIf
	
End Function
{
****************************************************************************
* Name    : Setup (PUBLIC) (BLOCKING W T/O)                                *
* Purpose : Sets ESP8266 to known state and configures peripherals         *
****************************************************************************
}
Public Function Setup(Timeout As Word = 5000) As Boolean	

	USART.SetBaudrate(br115200)
   	ISRRX.Initialize()
	
	DelayMS(100)

	ISRRX.Reset()

	USART.Write("AT+RST", CR, LF)

	If ISRRXFind("ready", Timeout) Then
		Result = true
	Else
		Result = false
	EndIf
	
End Function

{
****************************************************************************
* Name    : ConnectWifi (PUBLIC) (BLOCKING W T/O)                          *
* Purpose : Sets ESP to connect to an AP with the SSID and PASS credentials*
****************************************************************************
}
Public Function ConnectWifi(SSID As String, PASS As String, Timeout As Word = 5000) As Boolean
	
	USART.Write("AT+CWMODE=1", CR, LF)
	USART.Write("AT+CWJAP=") 
	USART.Write(QU, SSID, QU) 
	USART.Write(",", QU, PASS, QU, CR, LF)
	If ISRRXFind("OK", Timeout) Then
		result = true
	Else
		result = false
	EndIf
	
End Function 

{
****************************************************************************
* Name    : CheckWifi (PUBLIC) (BLOCKING W T/O)                            *
* Purpose : Checks ESP for a valid IP (indicating connected)               *
****************************************************************************
}
Public Function CheckWifi(Timeout As Word = 100) As TIPAddr
	Dim IPstring As String
	result.IP = $00000000

	USART.Write("AT+CIFSR", CR, LF)
	
	If ISRRXFind("AT+CIFSR" + CR + CR + LF, 100) Then
		ReadString(IPstring, 500, 25, CR)
		StrToIP(IPString, result)
	EndIf
	DelayMS(500)
End Function

{
****************************************************************************
* Name    : GetVersion (PUBLIC) (BLOCKING W T/O)                           *
* Purpose : Checks ESP for version string                                  *
****************************************************************************
}
Public Function GetVersion(ByRef pString As String, Timeout As Word = 100) As Boolean

	result = false
	USART.Write("AT+GMR", CR, LF)
	
	If ISRRXFind("AT+GMR" + CR + CR + LF, 100) Then
		ReadString(pString, 500, 20, CR)
		result = true
	EndIf
End Function

{
****************************************************************************
* Name    : ConnectTCP (PUBLIC) (BLOCKING W T/O)                           *
* Purpose : ESP connects to given desthost; returns true if successful     *
****************************************************************************
}
Public Function ConnectTCP(desthost As String, pPort As Word = 80, Timeout As Word = 1000) As Boolean
	result = false

   	// Set single connection mode
	USART.Write("AT+CIPMUX=0", CR, LF)
	If Not ISRRXFind("OK", Timeout) Then
		Exit
	EndIf

	USART.Write("AT+CIPSTART=", QU, "TCP", QU)
	USART.Write(",", QU, desthost, QU, ",", DecToStr(pPort), CR, LF)

	If ISRRXFind("Linked", Timeout) Then
		result = true
	EndIf
	
End Function

{
****************************************************************************
* Name    : SendTCPData (PUBLIC) (BLOCKING W T/O)                          *
* Purpose : Sends tcpdata to previously connected host; returns true if successful*
****************************************************************************
}
Public Function SendTCPData(ByRef tcpdata As String, Timeout As Word = 5000) As Boolean
	Dim strlen, i As Word
	
	result = false

	// Get length of tcpdata
	strlen = 0
   	FSR0 = AddressOf(tcpdata)
	While POSTINC0 <> 0
		Inc(Strlen)
	Wend

	USART.Write("AT+CIPSEND=", DecToStr(strlen), CR, LF)
	 		
 	If ISRRXFind(">", Timeout) Then
		For i = 0 To (strlen - 1)		// Usart.write(tcpdata) as string seemed to have an issue.
 			USART.WriteByte(tcpdata(i))
		Next 
		If ISRRXFind("SEND OK", timeout) Then
			result = true 	
		EndIf
	EndIf

End Function 

{
****************************************************************************
* Name    : GetTCPDataMode (PUBLIC) (BLOCKING W T/O)                       *
* Purpose : Parses +IPD message and returns TCP data length                *
****************************************************************************
}
Public Function GetTCPDataMode(Timeout As Word = 1000) As Integer
	Dim CountString As String
	Dim ChCount As Word
	
	result = -1
	 				
	If ISRRXFind("+IPD,", 500) Then 			// Look for IPD and Count
		ReadString(CountString, 500, 10, ":")				// Pull out Counter
		If IsDecValid(CountString) Then  		// Turn into Word
			ChCount = StrToDec(CountString)
			Result = Integer(ChCount)
		EndIf
	EndIf
End Function

{
****************************************************************************
* Name    : Disconnect (PUBLIC)                                            *
* Purpose : Closes the TCP Connection                                      *
****************************************************************************
}
Public Sub Disconnect() 
	USART.Write("AT+CIPCLOSE", CR, LF)
End Sub
Demo Code:

Code: Select all

Device = 18F27J53
Clock = 48

Public Config
   OSC = INTOSCPLL,                  ' internal osc 8MHz - No output on RA6
   PLLDIV = 2,                        ' usb 48MHz
   CPUDIV = OSC1,                     ' 48MHz cpu clock
   FCMEN = Off,
   IESO = Off,
   WDTEN = Off,
   WDTPS = 2048,
   STVREN = On,
   XINST = Off
   'DEBUG = OFF

Include "SUART.bas"
Include "ESP8266.bas"
Include "String.bas"
Include "Convert.bas"
Include "setdigitalio.bas"

{
****************************************************************************
* Purpose : Enter your own keys here!!                                     *
****************************************************************************
}
Const
	// URL = http://data.sparkfun.com/streams/PUBLICKEY
	PUBLICURL	= "data.sparkfun.com",
	PUBLICKEY 	= "ENTERYOURPUBLICKEYHERE",
	PRIVATEKEY	= "ENTERYOURPRIVATEKEYHERE",
	
	SSID 		= "ENTERYOURWIFISSIDHERE",
	PASSWORD 	= "ENTERYOURWIFIPASSWORDHERE" 
	


{
****************************************************************************
* Name    : Main                                                           *
* Purpose : Main loop                                                      *
****************************************************************************
}
Sub Main()
	Dim lastIP As TIPAddr
	Dim count As Integer
	Dim DataString As String(150)
	Dim Timer As Word
	Dim UpCount As Word	
	
	SetAllDigital()
	
	UART.SetTX(PORTB.7)
	UART.SetRX(PORTB.6)
	UART.SetBaudrate(sbr19200)
	UART.SetMode(umTrue)

	DelayMS(5000)	
	
	UART.Write("ESP8266 Sparkfun Demo ready!", CR , LF)
	UART.Write("Once connected, Data will be posted to data.sparkfun.com", CR , LF)
	UART.Write("at one minute intervals.", CR , LF)
	UART.Write("========================================================", CR , LF)

    // Run setup stuff on the ESP8266
	ESP.Setup
	
	// Get Module version number
	If ESP.GetVersion(DataString) Then
		UART.Write("Version: " + DataString + CR + LF)
	EndIf
	
	DelayMS(500)
		
	// Clear IP addresses
	IPAddr.IP = $00000000
	lastIP.IP = $00000000
	Timer = 0
	Upcount = 0
	
	// Loop forever		
	While true
	
		// Are we connected to the AP? We have an IP address if we are.
		If IPAddr.IP = $00000000 Then
			// Nope, connect using these credentials 
			ISRReset ' for safety
			ESP.ConnectWifi(SSID, PASSWORD)
			UART.Write("Connecting Wifi...")
		EndIf
		
		// Wait for a connection (indicated by valid IP address, not $00000000)
		Count = 100 
		Repeat
			IPAddr = CheckWifi()
			Dec(count)
		Until IPAddr.IP <> $00000000 Or count = 0
		
		// Display IP if its new
		If IPAddr.IP <> lastIP.IP Then
			UART.Write("New IP: ")
			For count = 3 To 0 Step -1
				UART.Write(DecToStr(IPAddr.Val(count)))
		     	If count > 0 Then
        			UART.Write(".")
			    EndIf
			Next
			UART.Write(CR, LF)				
			lastIP = IPAddr
		EndIf	

		// Don't go round the loop too quickly! Note: CheckWifi has 500mS delay too 
		DelayMS(500)		
		
		If Timer = 0 Then
			Inc(upcount)
			// Connect to data.sparkfun.com at port 80
			UART.Write("Connecting...")
			If ConnectTCP(PUBLICURL, 80) Then
				UART.Write("Connected!", CR , LF)
				UART.Write("Sending Data...")
				// Format our data
				DataString = "GET " + "/input/" + PUBLICKEY 
				DataString = DataString + "?private_key=" + PRIVATEKEY 
				
				// Heres where the Vars are - check Datastring is long enough and the var names match up to the webpage (vars are lowercase)
				// These are jsut examples, you could use the ADC or read another sensor
				DataString = DataString +"&ip=" + DecToStr(IPAddr.Val(3)) + "." + DecToStr(IPAddr.Val(2)) + "." + DecToStr(IPAddr.Val(1)) + "." + DecToStr(IPAddr.Val(0))
				DataString = DataString +"&count=" + DecToStr(Upcount)
				DataString = DataString +"&porta=" + BinToStr(PORTA,8)	 				
				
				// Finish the string
				DataString = DataString + CR + LF + CR + LF + CR + LF
				
				// Send it off
				If SendTCPData(datastring) Then
					Count = GetTCPDataMode()		// If it worked, did we get something back?
					If Count > -1 Then   			// yup
						'' This is an Example to spew out what we receive on usart2 - caution: CDC or software serial are too slow bytewise.
						'' CDC can be done as an array
						'Repeat              				// Receive x no of chars
						'	Repeat
						'	Until ESP.DataAvailable
						'	USART2.Write(ESP.ReadByte)		
						' 	Dec(Count)
						'Until Count = 0
						
						// For this example we search the return for the  message "1 success", waiting 1 second
					 	If ISRRXFind("1 success", 1000) Then
					 		UART.Write("Data posted OK.", CR , LF)
						Else
							UART.Write("Failed to post data!", CR , LF)
						EndIf
					EndIf
				Else
					UART.Write("Failed to send data.", CR , LF)
				EndIf
				// disconnect 
				ESP.Disconnect()
			Else
			 	UART.Write("Timeout.", CR , LF)
			EndIf
			
			// Don't come back for a minute
			Timer = 60
		EndIf
		
		Dec(Timer)
	Wend	

End Sub

// No libraries need init, so can call this here, if not, put into a module and call first
'Config osc = INTIO2
OSCCON = %01110100
// 18F27J53 internal oscillator mode, set pll on.
OSCTUNE.bits(6) = 1
DelayMS(100)

Main()

User avatar
RangerBob
Posts: 152
Joined: Thu May 31, 2007 8:52 am
Location: Beds, UK

Re: ESP8266 Module - Issues

Post by RangerBob » Tue Dec 09, 2014 4:36 pm

OK, this is annoying. I fixed up something I noticed whilst putting it up on this page, and having tested for a while here it appears to have fixed it. It was a unterminated string condition sometimes occurring in the ReadString function. Tested now with SUART and USBCDC test terminal, and things appear to be stable now.

In the meantime, the code here appears to work fine and I'll tidy things up a bit and put the module on the wiki.

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Re: ESP8266 Module - Issues

Post by David Barker » Sun Dec 28, 2014 4:51 pm

Thanks for posting...

I've been playing around with the ESP8266 over the holidays and they are pretty amazing devices, given the price point. I have written a Firewing module which is now pretty robust, which uses a interrupt based timed state machine. This means it is non-blocking - really useful if you want you main program to do other stuff. I'll try and post when I get some more spare time. However, anyone who hasn't tried one of these devices and is looking at IoT (or any wifi project) should really check them out...

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: ESP8266 Module - Issues

Post by Coccoliso » Sun Dec 28, 2014 7:33 pm

Hello David,
I just bought and I will arrive in mid January ..
you've already put module in FW wiki? or when you think you do?

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Re: ESP8266 Module - Issues

Post by David Barker » Sun Dec 28, 2014 7:45 pm

Hopefully I will post soon, remind me with a PM if I don't...

AndrF
Posts: 8
Joined: Fri Feb 06, 2015 5:50 am

Re: ESP8266 Module - Issues

Post by AndrF » Tue Feb 10, 2015 8:18 am

Need an example:

PIC with ESP8266 <> WiFi <> Remote PC with DDNS (VB6 Or VB.Net)

...

User avatar
rocketbob
Registered User
Registered User
Posts: 51
Joined: Sun Apr 01, 2007 1:36 am
Location: Indiana USA

Re: ESP8266 Module - Issues

Post by rocketbob » Wed Jun 15, 2016 9:08 pm

Hi David,

I know its been a year and a half...but would you mind posting your ESP8266 code?

Thanks,
Bob

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Re: ESP8266 Module - Issues

Post by David Barker » Thu Jun 16, 2016 7:11 am


Post Reply