Der Webserver ist eine einfache Möglichkeit, HTML Seiten zu senden. Es sei gleich darauf hingewiesen, dass zum Seitenaufbau viel RAM benötigt wird, was sehr schnell an die Grenzen dieser Methode stossen lässt - prinzipiell funktioniert dies aber :-)
Das vorliegende Programm ist eine äußerst primitive Website, die einfache Daten zurückgibt. An das Limit kommt man eigentlich nur, wenn das HTML File mehr oder weniger im RAM des µC gehalten werden muss. Es gibt aber einige Ansatze (siehe dazu auch unter Links [4])
Der ESP8266 wird dabei als Station initialisiert und über Port 8080 angesprochen. In dieser Lauerstellung wartet der Webserver auf eine "GET"-Anforderung und baut darauf hin die Website zusammen.
Im Beispiel verwende ich einen TMP100 am IC-Bus - stattdessen kann natürlich auch ein (interner) ADC oder eine beliebige andere Größe verwendet werden.
' ------------------------------------------------------------------------------
' µC misst über TMP100 Temperatur und stellt diese dem Webserver bereit
'
' HW:tr045-R06
' - ESP8266 am seriellen Port
' - LCD für Statusausgabe (optional)
' - Baudratenclock
'
' V1.0: funktionierende Version, erlaubt aber nur einen Client(SendeKanal) 03/05/15
' V1.1: MultiClient läuft 14/5/15
'
' ------------------------------------------------------------------------------
$Regfile="m8def.dat"
$Crystal=14745600 ' externer Baudratenclock
$hwstack=40
$swstack=16
$framesize=32
$baud=57600
$version 1,1,1
' ------------------ Konstanten
const Adr_TMP101w = 144
const mySSID = "meinSSID"
const myPW = "meinPW"
const myPort ="8080"
const NotausgangZeit = 10000
const CrLf ="{013}{010}"
const LCD_Ausgabe = 1
' ------------------ Subroutinen deklarieren
declare Function Temp_tmp100(byval Adresse As Byte) As Single
declare Sub Init_tmp100(byval Adresse As Byte)
declare sub Warte_OK_Error(byval Suchstring as string*10,byval NoDelete as byte)
' ------------------ Deklarationen
' Variablen
dim i as byte, j as byte
dim Lbyte as byte, Hbyte as byte,Big_Byte as integer at lbyte overlay
dim Temperature as single
dim Empfangs_Ticker as word
dim Sendestring as string * 250, headerstring as string * 100, SendeKanal as string*1
dim myIPAdress as string *15
dim strdummy as string *10
dim Flagbyte as byte
Flag_ok alias Flagbyte.0
Flag_Error alias Flagbyte.1
' UART ISR
const Uart_string_laenge=45
Dim Uart_get_char As Byte , Uart_string_len As Byte , Uart_char As String * 1 , Uart_string As String * Uart_string_laenge ', Str_laenge As Byte
' Timer
Config Timer0 = Timer , Prescale = 64
Const Timer0_preload = 131' Timer1 tickt im Sekundentakt
On Timer0 Timer0_isr
'I2C
Config Scl = Portc.5
Config Sda = Portc.4
#if LCD_Ausgabe=1
' LCD
Config Lcdpin = Pin , Db4 = Portd.7 , Db5 = Portd.6 , Db6 = Portd.5 , Db7 = Portd.4 , E = Portb.1 , Rs = Portb.0
Config Lcd = 16 * 2
initlcd
Cls
Cursor Off
LCDLight alias portb.2:config LCDLight = output:set lcdlight ' Pin für LCD Beleuchtung
#endif
' Interrupts
On Urxc Uart_isr
' ------------------ Hauptschleife ---------------------------------------------
call Init_tmp100(Adr_TMP101w)
#if LCD_Ausgabe=1
lcd Version(1)
lowerline:lcd version(2)
#endif
wait 1
Enable Urxc
enable timer0
enable interrupts
' ESP8266 initialisieren
#if LCD_Ausgabe=1
cls
LCD "ESP8266 Init"
#endif
wait 3 ' warte ab, bis nach Kaltstart der ESP bereit ist
print"AT+RST"
#if LCD_Ausgabe=1
lowerline:lcd "."
#endif
call Warte_OK_Error("",0)
wait 3 ' und jetzt warte die Bereitschaft nach dem Softstart ab
print "AT+CIPMUX=1"
#if LCD_Ausgabe=1
lcd "."
#endif
call Warte_OK_Error("",0)
print "AT+CIPSERVER=1,";myPort
#if LCD_Ausgabe=1
lcd "."
#endif
call Warte_OK_Error("",0)
print "AT+CIPSTO=0"
#if LCD_Ausgabe=1
lcd "."
#endif
call Warte_OK_Error("",0)
print "AT+CWJAP={034}";mySSID;"{034},{034}";myPW;"{034}"
#if LCD_Ausgabe=1
lcd "."
#endif
call Warte_OK_Error("",0)
print "AT+CIFSR"
#if LCD_Ausgabe=1
lcd "."
#endif
call Warte_OK_Error("",1)
' isoliere IP Adresse
i = instr(uart_string,"STAIP,")
i = i + 7
j = instr(i,uart_string,"{034}")
j = j-i
myIPAdress = mid(uart_string,i,j)
Uart_string=""
#if LCD_Ausgabe=1
cls
lcd myIPAdress
wait 2
#endif
Empfangs_Ticker = 0
#if LCD_Ausgabe=1
cls
#endif
do
Temperature = Temp_tmp100(Adr_TMP101w)
if instr(uart_string,"GET")<>0 then ' GET wurde angefordert
' isoliere Sendekanal
i=instr(uart_string,"+IPD,")
i=i+5
SendeKanal = mid(uart_string,i,1)
#if LCD_Ausgabe=1
upperline
lcd "HTML requested"
locate 2,1:lcd "Kanal ";SendeKanal
#endif
Uart_string="" 'löschen um Mehrfachaufruf zu verhindern
strdummy = fusing(temperature,"#.#")
' HTML File zusammensetzten
Sendestring = "<html><body bgcolor="#CCFFFF"><h1>Temperatur=" + strdummy
Sendestring =Sendestring + "</h1></html>"
' Header zusammensetzten
headerstring = "HTTP/1.1 200 OK{013}{010}"
i=len(Sendestring) ' länge des HTML Files bestimmen und übergeben
headerstring = headerstring + "Content-Length:" + str(i) + "{013}{010}{013}{010}"
'Header ausgeben
print "AT+CIPSEND="; SendeKanal ; ",";
print len(HeaderString)
call Warte_OK_Error(">",0)
print HeaderString
call Warte_OK_Error("SEND OK",0)
'HTML ausgeben
print "AT+CIPSEND="; SendeKanal ; ",";
print len(SendeString)
call Warte_OK_Error(">",0)
print SendeString;
call Warte_OK_Error("SEND OK",0)
else
#if LCD_Ausgabe=1
upperline
'lcd spc(16)
lcd myIPAdress
#endif
end if
#if LCD_Ausgabe=1
lowerline
lcd Temperature;" "
#endif
loop
end
' ------------------ Subroutinen -----------------------------------------------
Function Temp_tmp100(byval Adresse As Byte) As Single
Local Tmp100_w As Byte , Tmp100_r As Byte
Tmp100_w = Adresse
Tmp100_r = Tmp100_w + 1
I2cstart 'repeated start
I2cwbyte Tmp100_r 'slave address (read)
I2crbyte Hbyte , Ack 'read byte
I2crbyte Lbyte , Ack
I2cstop
shift big_byte,right,4
Temp_tmp100 = big_byte*0.0625
End Function
Sub Init_tmp100(byval Adresse As Byte)
Local Tmp100_w As Byte
Tmp100_w = Adresse
' Temperatursensor initialisieren
I2cstart
I2cwbyte Tmp100_w 'Addressw 'slave adsress
I2cwbyte 1
I2cwbyte &H20 'vorher &H60
I2cstop
I2cstart 'generate start
I2cwbyte Tmp100_w 'Addressw 'slave adsress
I2cwbyte 0
I2cstop
End Sub
Sub Warte_OK_Error(byval Suchstring as string*10,byval NoDelete as byte)
Empfangs_Ticker=0
if Suchstring="" then
' warte bis OK oder ERROR empfangen wird
while instr(Uart_string,"OK")=0 AND instr(uart_string,"ERROR")=0
if Empfangs_Ticker > NotausgangZeit then
exit while 'Notausgang
end if
wend
else
while instr(Uart_string,Suchstring)=0
if Empfangs_Ticker > NotausgangZeit then
exit while 'Notausgang
end if
wend
end if
' lösche nur, wenn NoDelete-Byte =0
if NoDelete <>1 then
Uart_string = ""
end if
Flag_error = 0
Flag_ok = 0
Empfangs_Ticker = 0
end sub
' ISR Routinene -------------------------------------------------------------
Uart_isr:
Uart_get_char = Udr
Uart_char = Chr(uart_get_char)
Uart_string =Uart_string + Uart_char
' warte auf Schlüsselwörter
Empfangs_Ticker=0 ' Wartewatchdog zurücksetzten, da Daten reinkamen
if instr(Uart_string,"OK")>0 then
Flag_ok=1
elseif instr(uart_string,"ERROR")>0 then
Flag_Error=1
else
Flag_ok=0
Flag_Error=0
end if
Return
' Diese Routine wird jede mSekunde aufgerufen wenn Timer0 aktiviert ist
Timer0_isr:
' Timer0 wieder neu vorladen
Timer0 = Timer0_preload
incr Empfangs_Ticker
return
Timing
Das Timing - sprich Wartezeiten - nach den AT Befehlen kann einen zur Verzweiflung bringen. Das Beste ist es, auf 'OK', 'ERROR' usw auszuwerten. In der obigen SW wird dies mit der Subroutine
Sub Warte_OK_Error(byval Suchstring as string*10)
realisiert und erlaubt auch noch individuelle Suchbegriffe abzuwarten
Header Aufbau
Dieser MUSS aus der Zeile HTTP/1.1 200 OK
bestehen, gefolgt von einem weiteren CrLf (Carrige Return + Line Feed oder "\r\n
"). Selbstverständlich muss die Anzahl der zu sendenden Daten in der "AT+CIPSEND
" auch die beiden zusätzlichen Zeichen gezählt haben! Dieser Aufbau und die Richtigkeit hat mich trotz seiner beschaulichen Größe Stunden an Fehlersuche gekostet
Spezielle Header-Daten
Der Eintrag von "Content-Length
" ist wohl nicht zwingend, beschleunigt aber deutlich die Übergabe an den Browser, von daher empfehle ich die Verwendung.