Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 19:54 09 Nov 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Pi-comite: full webserver in MMBasic

     Page 1 of 2    
Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 12:54am 14 May 2017
Copy link to clipboard 
Print this post

Please find attached version 5.3.a29 of the Pi-cromite software

2017-05-14_113836_mmbasic.zip

This now includes the full capability to program fairly sophisticated webserver applications with stylesheets, embedded pictures, and favicons entirely in MMBasic



HTML pages can be written and stored as files. These can have MMBasic variable names included within the file in {} brackets and then these will be filled in from the current values in the MMBasic program when the page is requested.

To explain this an example is probably best. Here is the simple HTML file used to create the image above.

<body>
<h2 align='left'>
HTML DEMO</h2>
<link href="styles.css" rel="stylesheet" type="text/css">
<br>
<img src="tiger-3.jpg" alt="mypic">
<p>
<TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>
<TR>
<TD></TD>
<TD>Value</TD>
</TR>
<TR>
<TD>INTEGER</TD><TD>{tcurrent}</TD>
</TR>
<TR>
<TD>FLOAT (array)</TD><TD>{tmax(2)}</TD>
</TR>
<TR>
<TD>STRING</TD><TD>{tmin}</TD>
</TR>
<TR>
<TD>MISSING</TD><TD>{fred}</TD>
</TR>
</TABLE>
</p>
</body>
</html>


In it there are four MMBasic variables referenced: tcurrent, tmax(2), tmin, and fred

In my test program (full version below) these are defined as follows:

Option explicit
Option default none
Dim integer tcurrent=44
Dim float tmax(3)=(19.6, 22.4, 111.9,88.6)
Dim string tmin="A string"


Note that "fred" is not defined and I am using option explicit

to display the page from MMBasic we simply use

TRANSMIT PAGE "filename"

The MMBasic software will automatically create a correct HTTP 1.1 header and fill in the page with the current values of the variables. Missing variables will be ignored. Formatting of numeric variables is exactly the same as would be done by a PRINT statement. As usual STR$ can be used for more specific formatting.

Both the Pi 3 and Pi zero W have full wifi capability so no additional hardware is needed to run the code and connect to the web.

More details of the MMBasic support for building a webserver are given in this thread

The full MMBasic program to serve the example page, together with the image, css file, and favicon are included in the zip

2017-05-14_104243_html.zip

The Basic code is very simple, a single Basic interrupt routine is triggered by an HTTP request and this then transmits the relevant information to the client

Sub myint
'Load the request into a long string
Do While Not Eof(2)
LongString append a%(), Input$(10,2)
Loop
'Filter out the specific request
p%=LInStr(a%(),"GET",1)
t%=LInStr(a%(),"HTTP",1)
s$=LGetStr$(a%(),p%,t%-p%)
Print s$
LongString clear a%()
' Execute the relevant request
If Instr(s$,"favicon") Then
Transmit FILE "favicon.ico","image/vnd.microsoft.icon"
Else If Instr(s$,"styles") Then
Transmit FILE "styles.css","text/css"
Else If Instr(s$,"tiger-3.jpg") Then
Transmit file "tiger-3.jpg","image/jpeg"
Else
Transmit PAGE "index.html"
EndIf
End Sub




Edited by matherp 2017-05-15
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3464
Posted: 02:52am 14 May 2017
Copy link to clipboard 
Print this post

This is sweet. Many thanks for all your work.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 04:19am 14 May 2017
Copy link to clipboard 
Print this post

That is perfect.
Will this also work?

[code]
TRANSMIT FILE, "data.csv", "text/csv"
[/code]

data.csv file contents:
[code]
Current,Maximum,Minimum,Name
{tcurrent},{tmax(2)},{tmin},{fred}
[/code]


You sample page has some little formatting errors. It should be like this:
[code]
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2 align='left'>
HTML DEMO</h2>
<br />
<img src="tiger-3.jpg" alt="mypic" />
<br />
<TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>
<TR>
<TD> </TD>
<TD>Value</TD>
</TR>
<TR>
<TD>INTEGER</TD>
<TD>{tcurrent}</TD>
</TR>
<TR>
<TD>FLOAT (array)</TD>
<TD>{tmax(2)}</TD>
</TR>
<TR>
<TD>STRING</TD>
<TD>{tmin}</TD>
</TR>
<TR>
<TD>MISSING</TD>
<TD>{fred}</TD>
</TR>
</TABLE>
</body>
</html>
[/code]
It is not about the indentation, html does not care about that. There were a few items in the wrong place and some tags did not have the / closing tag.

Next challenge? :), posting data to a webserver?
Lots of IOT device send data to cloud services to generate graphs, and historical data.
Edited by MicroBlocks 2017-05-15
Microblocks. Build with logic.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 05:28am 14 May 2017
Copy link to clipboard 
Print this post

  Quote  Will this also work?


No but this will in version 5.3.a30

2017-05-14_152718_mmbasic.zip

  Quote  Transmit page "text.csv","text/csv"


The second parameter is optional on TRANSMIT PAGE, if omitted then a html header will be sent

TRANSMIT FILE sends a file without local buffering so no length restrictions
TRANSMIT PAGE constructs a file in memory with embedded variables, currently maximum length 65535 bytes

You should be able to look at the demo on a Pi Zero W at this address. Let me know if it worksEdited by matherp 2017-05-15
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3464
Posted: 11:16am 14 May 2017
Copy link to clipboard 
Print this post

  matherp said  Let me know if it works

That page works for me. When I "view source", this is what I see.
[code]
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<link href="styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2 align='left'>
HTML DEMO</h2>
<br />
<img src="tiger-3.jpg" alt="mypic" />
<br />
<TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>
<TR>
<TD> </TD>
<TD>Value</TD>
</TR>
<TR>
<TD>INTEGER</TD>
<TD>714396</TD>
</TR>
<TR>
<TD>FLOAT (array)</TD>
<TD>0.139801</TD>
</TR>
<TR>
<TD>STRING</TD>
<TD>22:13:44</TD>
</TR>
<TR>
<TD>MISSING</TD>
<TD></TD>
</TR>
</TABLE>
</body>
</html>
[/code]

Edited by lizby 2017-05-15
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1116
Posted: 06:46pm 14 May 2017
Copy link to clipboard 
Print this post

Terrific work Peter, you continue to amaze!

Do you have any plans to impliment this functionality in your Extreme or an MM plus using an 8266?

Doug.
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 09:28pm 14 May 2017
Copy link to clipboard 
Print this post

The page works.
However when i try to use an URL that refers to the image directly it does not.
Seems to hang. Also the page that previously worked does then not work anymore.
I suspect the program stopped with an error.
The url i used was the one you posted with /tiger-3.jpg added.

I like the way it is done. With an interrupt to handle the request the main program does not have to care about it. Very sleek!
Edited by MicroBlocks 2017-05-16
Microblocks. Build with logic.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 09:47pm 14 May 2017
Copy link to clipboard 
Print this post

  Quote  I suspect the program stopped with an error.


The program need to have proper error checking - it was just put together to show the sysntax

QUESTION:

I could allow MMBasic expressions or function calls to be used in the html

e.g.

{str$(avar * bvar,10,4)}

The downside is that if "avar" doesn't exist you will get an error and the program will stop. By using simple variables I am able to trap this and prevent an error.

Thoughts anyone?
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 10:15pm 14 May 2017
Copy link to clipboard 
Print this post

I would use only a variable name not expressions and functions, with maybe a formatting string as used in the printf statements in C. This will allow great control about visualization at the place you need it (in the HTML).
[code]
{avr:%.2f}
[/code]

Please don't forget the ability to add HTTP response headers to the TRANSMIT command. These are of great value.

Microblocks. Build with logic.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 05:49am 15 May 2017
Copy link to clipboard 
Print this post

Attached is 5.3.a31

2017-05-15_161857_mmbasic.zip

This has the following changes:

ADDED:

LONGSTRING TRIM nnnn


This removes nnnn characters from the left of a long string. This is very useful for processing incoming datastreams such as net traffic or GPS. The attached code snippet is for finding the next HTTP request but the concept can be used for any datastream


Do While Not Eof(2) 'if anything to process append it to the long string
LongString append a%(), Input$(10,2)
Loop
p%=LInStr(a%(),"GET",1) 'identify the start of what we are interested in
t%=LInStr(a%(),"HTTP",1) 'identify the end of what we are interested in
s$=LGetStr$(a%(),p%,t%-p%+4) 'get the interesting data as a normal string
LongString trim a%(),t%+4 'strip this from the front of the long string


CHANGED:

Fixed another bug in TIMER to avoid it reporting negative numbers

Processing mode changed to "real-time" in one-wire execution. This fixes a bug seen on the Pi-zero where one-wire processing (including DS18B20) would occasionally return invalid values.

My webserver test program incorporating these changes, some better error checking, and checking for invalid page requests is now:

Dim float tmax(3)=(19.6, 22.4, 111.9,88.6)
Dim string tmin="A string"
Open "socket,myint,100" As #2
Do
TEMPR START 11
tcurrent=Timer
tmin=Time$
Pause 500
tmax(2)=TEMPR(11)
Loop
Sub myint
'print the request - normally we would parse it
Do While Not Eof(2)
LongString append a%(), Input$(10,2)
Loop
p%=LInStr(a%(),"GET",1)
t%=LInStr(a%(),"HTTP",1)
if p% and t% then
s$=LGetStr$(a%(),p%,t%-p%+4)
LongString trim a%(),t%+4
Print LLen(a%()),s$

'
If Instr(s$,"favicon") Then
Transmit FILE "favicon.ico","image/vnd.microsoft.icon"
Else If Instr(s$,"styles") Then
Transmit FILE "styles.css","text/css"
Else If Instr(s$,"tiger-3.jpg") Then
Transmit file "tiger-3.jpg","image/jpeg"
Else If Instr(s$,"GET / HTTP") Then
Transmit page "index.html"
Else
Transmit code 404
EndIf
endif
end sub


Edited by matherp 2017-05-16
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 04:20am 16 May 2017
Copy link to clipboard 
Print this post

That webserver functionality now works a lot better. I get the 404 when a unknown file is requested and it does not hang anymore.
Good work!

Microblocks. Build with logic.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 08:31am 17 May 2017
Copy link to clipboard 
Print this post

There is a logic bug in the above program and it is one which has caught me before and yet I still wasted an hour on it this afternoon.

if p% and t% then


I wanted this to say: "if p% isn't zero and t% isn't zero then do something.

BUT IT DOESN'T

What it actually says is: "if the bitwise AND of p% and t% isn't zero then do something"

Consider p%=1 and t%=2, the bitwise AND is zero and therefore it fails the test whereas I wanted it to pass and with a logical AND it would.

In C "&" is a bitwise AND. A logical AND is "&&" so the two are differentiated.

The moral of the tale is always use = and <> if you want a logical AND

so:

if p%<>0 and t%<>0 then


works perfectly

Once this is solved attached is a simple program for a remote controlled thermostat to run on a Pi in MMBasic.

You can have a look at the application here for the next few hours. The security code is "123456" which is needed to reset the max/min or change the thermostat setting

[code]
option explicit
option default none
Const starttemp = 18
Const maxargs = 32
Const relaypin=7
Const off=0
Const on=1
Dim a%(1000),i%
Dim integer sp(11)
Dim s$,pg$
Dim security$="123456"
Dim string cp(11)
Dim float tcurrent,tnew
Dim float tmax=-1000
Dim float tmin=1000
Dim arg$(1,maxargs-1)
Dim integer checked=0
Const check$="checked='checked'" 'string to set a readio button pressed
Dim string heating="off"
Const hon$="#ff0000" 'red
Const hoff$="#00ff00" 'green
Dim string hcol=hoff$
'
SetPin relaypin,dout
For i%=0 To 10
sp(i%)=i%+starttemp-1 'set up the temperature values
cp(i%)="" 'set up the radio buttons as not pressed
Next i%
tcurrent=TEMPR(11) 'get the current temperature
cp(checked)=check$
Open "socket,myint,100" As #2
Do
TEMPR START 11
Pause 2000
tnew=TEMPR(11)
If Abs(tnew-tcurrent)<10 Then tcurrent=tnew
updateheater
If tcurrent>tmax Then tmax=tcurrent
If tcurrent<tmin Then tmin=tcurrent
Loop
'
Sub myint
Local p%=0, t%=0
Local g$
Do While Not Eof(2)
LongString append a%(),Input$(10,2)
Loop
p%=LInStr(a%(),"GET",1)
t%=LInStr(a%(),"HTTP",1)
If p%<>0 And t%<>0 Then 'full request received
s$=LGetStr$(a%(),p%,t%-p%+4)
LongString trim a%(),t%+4
pg$= parserequest$(s$,i%)
If i% Then
If arg$(0,0)="Security" And arg$(1,0)=security$ Then 'valid update
If arg$(0,1)="R" Or arg$(0,2)="R" Then
cp(checked)=""
If arg$(0,1)="R" Then checked=Asc(arg$(1,1))-Asc("A")
If arg$(0,2)="R" Then checked=Asc(arg$(1,2))-Asc("A")
cp(checked)=check$
updateheater
EndIf
If arg$(0,1)="Check" Or arg$(0,2)="Check" Then
tmin=tcurrent
tmax=tcurrent
EndIf
EndIf
EndIf
'
If pg$="index.html" Then
Transmit page "index.html"
Else
Transmit code 404
EndIf
EndIf
End Sub

'Function to parse an HTML GET request'
' Assumes that the request starts with "GET /"
' and ends with "HTTP"
'
Function parserequest$(req$, paramcount As integer)
Local a$,b$
Local integer inpos,startparam,processargs
For inpos=0 To maxargs-1
arg$(0,inpos)=""
arg$(1,inpos)=""
Next inpos
paramcount=0
a$=Mid$(req$,6,Len(req$)-10)
inpos=Instr(a$,"?")
If inpos<>0 Then 'parameters found
processargs=1
parserequest$=Left$(a$,inpos-1)
a$=Mid$(a$,inpos+1)
Do
arg$(0,paramcount)=""
arg$(1,paramcount)=""
inpos=Instr(a$,"=")
startparam=1
arg$(0,paramcount)=Mid$(a$,startparam,inpos-startparam)
startparam=inpos+1
inpos=Instr(a$,"&")
If inpos<>0 Then
arg$(1,paramcount)=Mid$(a$,startparam,inpos-startparam)
a$=Mid$(a$,inpos+1)
paramcount=paramcount+1
Else
arg$(1,paramcount)=Mid$(a$,startparam)
paramcount=paramcount+1
processargs=0
EndIf
Loop While processargs
Else
parserequest$=a$
EndIf
If a$="" Then
parserequest$="index"
EndIf
If Instr(parserequest$,".html")=0 And Instr(parserequest$,".HTML")=0 Then
parserequest$=parserequest$+".html"
End Function

Sub updateheater
Local float hcalc
If checked=11 Then
Pin(relaypin)=1
heating="On"
hcol=hon$
EndIf
If checked=0 Then
Pin(relaypin)=0
heating="Off"
hcol=hoff$
EndIf
If checked>=1 And checked<=10 Then
hcalc=checked+starttemp-1 'setpoint temperature
If tcurrent>hcalc Then 'turn heating off
Pin(relaypin)=0
heating="Off"
hcol=hoff$
EndIf
If tcurrent<hcalc Then 'turn heating on
Pin(relaypin)=1
heating="On"
hcol=hon$
EndIf
EndIf
End Sub


and the HTML including the variable substitution strings

<html>
<head>
<title>Remote Thermostat</title>
</head>
<body>
<form name='f1' method='get' action='index'>
<h2 align='left'>Remote Thermostat V4.0</h2>
Update Code: <input type='text' name='Security' size='6' value='000000'>
<br>
<p>
<TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>
<TR>
<TD>Heating</TD>
<TD BGCOLOR='{HCOL}'>{heating}</TD>
</TR>
</TABLE>
</p>
<p>
<TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>
<TR>
<TD></TD>
<TD>Temperature</TD>
</TR>
<TR>
<TD>Current</TD>
<TD>{TCURRENT}°C</TD>
</TR>
<TR>
<TD>Max</TD>
<TD>{TMAX}°C</TD>
</TR>
<TR>
<TD>Min</TD>
<TD>{TMIN}°C</TD>
</TR>
</TABLE>
</p>
<input name='Check' type='checkbox' value='Reset' onClick='this.form.submit()'>
Reset Max/Min<p>
<TABLE BORDER='1' CELLSPACING='0' CELLPADDING='5'>
<TR>
<TD>Thermostat</TD>
<TD><input name='R' type='radio' {CP(0)} value='A' onClick='this.form.submit()'> Off<br></TD>
<TD><input name='R' type='radio' {CP(1)} value='B' onClick='this.form.submit()'> {SP(1)}°C<br></TD>
<TD><input name='R' type='radio' {CP(2)} value='C' onClick='this.form.submit()'> {SP(2)}°C<br></TD>
<TD><input name='R' type='radio' {CP(3)} value='D' onClick='this.form.submit()'> {SP(3)}°C<br></TD>
<TD><input name='R' type='radio' {CP(4)} value='E' onClick='this.form.submit()'> {SP(4)}°C<br></TD>
<TD><input name='R' type='radio' {CP(5)} value='F' onClick='this.form.submit()'> {SP(5)}°C<br></TD>
<TD><input name='R' type='radio' {CP(6)} value='G' onClick='this.form.submit()'> {SP(6)}°C<br></TD>
<TD><input name='R' type='radio' {CP(7)} value='H' onClick='this.form.submit()'> {SP(7)}°C<br></TD>
<TD><input name='R' type='radio' {CP(8)} value='I' onClick='this.form.submit()'> {SP(8)}°C<br></TD>
<TD><input name='R' type='radio' {CP(9)} value='J' onClick='this.form.submit()'> {SP(9)}°C<br></TD>
<TD><input name='R' type='radio' {CP(10)} value='K' onClick='this.form.submit()'> {SP(10)}°C<br></TD>
<TD><input name='R' type='radio' {CP(11)} value='L' onClick='this.form.submit()'> On<br></TD>
</TR>
</TABLE>
</p>
</form>
</body>
</html>







Edited by matherp 2017-05-18
 
RonnS
Senior Member

Joined: 16/07/2015
Location: Germany
Posts: 121
Posted: 07:21pm 17 May 2017
Copy link to clipboard 
Print this post

Hi Peter,
everything works.
I use WINSCP for convenient editing of the files and a wireless keyboard to control the PI so I can see changes and results immediately on TV

Thanks for the examples, which are good for beginners with HTML

regards Ron

 
led-bloon

Senior Member

Joined: 21/12/2014
Location: Australia
Posts: 208
Posted: 09:15pm 22 May 2017
Copy link to clipboard 
Print this post

Peter
Could the following modification be made in your code:

<code snippet>
Sub MyInt
Local f$, i%

s$= ..... get the GET request ....
'
i% = Instr(s$, "/")+1
f$ = Mid$(s$,i%,Len(s$)-5) ' extract the file plus extension string
'
If Instr(f$,".ico") Then ' Icon file?
Transmit FILE f$,"image/vnd.microsoft.icon"
'------------^^^
Else If Instr(f$,".css") Then ' style sheet file?
Transmit FILE f$,"text/css" ' Use f$ for the file name
'------------^^^
Else If Instr(f$,".jpg") Then
Transmit FILE f$,"image/jpeg"
Else If Instr(f$,".htm") Then
Transmit page f$
Else
Transmit CODE 404
EndIf

Multiple jpgs or gifs or pngs could appear on one page without having
to name each file in this code.
Also:
Has Transmit CODE been deleted as it doesn't appear to work with FireFox?
I just get a blank screen
Finally:
Great work and thanks!
don

Miss you George
 
WhiteWizzard
Guru

Joined: 05/04/2013
Location: United Kingdom
Posts: 2959
Posted: 11:30am 27 May 2017
Copy link to clipboard 
Print this post

People across the planet - please help me test something!

I am trying to do something based on Peter's demo Thermostat. To ensure I have things set-up correctly can anyone out there with a couple of spare minutes log onto the following URL:

whitewizzard.ddns.net:1234

Hopefully you will see the familiar Thermostat webpage. If so, can you then simply click between 27'C and ON (no need to enter an Update code - just click/toggle between 27'c and ON)

Thanks


WW
 
OA47

Guru

Joined: 11/04/2012
Location: Australia
Posts: 1013
Posted: 11:43am 27 May 2017
Copy link to clipboard 
Print this post

WW tried to logon using Msoft Edge on W10 but timed out.
Graeme
 
WhiteWizzard
Guru

Joined: 05/04/2013
Location: United Kingdom
Posts: 2959
Posted: 11:46am 27 May 2017
Copy link to clipboard 
Print this post

Thanks OA47

Please, if you have time, can you try my WAN IP instead as follows:

151.228.246.21:1234

Thanks . . .
 
OA47

Guru

Joined: 11/04/2012
Location: Australia
Posts: 1013
Posted: 11:51am 27 May 2017
Copy link to clipboard 
Print this post

ww, I can ping the address ok but still no response from browser.
Graeme
 
WhiteWizzard
Guru

Joined: 05/04/2013
Location: United Kingdom
Posts: 2959
Posted: 12:00pm 27 May 2017
Copy link to clipboard 
Print this post

I just done the 'traditional' switch-it-off-and-back-on-again trick!

Getting a faster response now here - any different there now?

EDIT: Do you have port blocking on your router? Port 1234 will need to be 'open'Edited by WhiteWizzard 2017-05-28
 
WhiteWizzard
Guru

Joined: 05/04/2013
Location: United Kingdom
Posts: 2959
Posted: 12:10pm 27 May 2017
Copy link to clipboard 
Print this post

Someone is out there I can see you!
 
     Page 1 of 2    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025