Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 14:33 11 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 : Multi Page GUI Example

Author Message
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1116
Posted: 03:09am 13 Jun 2018
Copy link to clipboard 
Print this post

Hi folks,

I have been playing around with GUI controls for some months now and after quite a bit of head scratching have come up with a framework for a multi page GUI display example - it does nothing other than show how multiple pages may be created and used.

I hope it may be of use as a reference on one way in which multiple pages may be used and displayed (I make no claim to it being the best or only way - it is just an example).

It will run standalone on a 470 with an SSD 1963 7" display. If anyone is interested, I also have a similar version on a 2.8" ILI display.

Hope it is of interest and useful to someone.

panky


'**************************************************************
' Universal Function Controller Interface '
' Written by Doug Pankhurst May 2017, based on coding '
' ideas from Tom Pankhurst and '
' various other Back Shed Forum members - thanks. '
' Written for MMBasic V5.4.09 , copyright Geoff Graham 2012 '
' Program runs on PIC32MX470 with an SSD1963 7" touch screen '
' This program is free for anyone to use or modify as they '
' choose however, if you wish to re-distribute, please '
' acknowledge authors and contributors above. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' File Version TSetSSD006.bas 10 June 2018
' Notes:
' running on MMBasic V5.04.09B19
' - last known good version
' Initial Entry to Program
EntryPoint:
SetTick 0,0 ' Disable timer interrupt for now
Option explicit
' Option autorun on

' Define constants for gui controls
' ILI display is 320 x 240
const BGround_Box = 10
Const Head_Box = 11
const Back_Arrow_Box = 12 ' back arrow box
const Help_Button_Box = 13 ' question mark box
const Hint1_Box = 14
const Intro_Box = 15 ' initial introduction
const Info_Box= 16 ' used for help messages
const PTSwitch_Box = 17
const TimeSwitch_Box = 18
const TempSwitch_Box = 19
const RisingEdgeRB = 20
const FallingEdgeRB = 21
const HiLevelRB = 22
const LoLevelRB = 23
const TriggerSelRB = 24
const TimerOnTimeNB = 25 ' duration of "on" time
const TrigHoldOffNB = 26 ' debounce from edge
const DecHrsNB = 27
const OffTimeDelayNB = 28
const TimerOnTimeF = 29
const OffTimeDelayF = 30
const HoldOffF = 31
const HoldOffNB = 32
const RelaySelF = 33
const Relay1RB = 34
const Relay2RB = 35
const Relay3RB = 36
const TODStartF = 37
const TODEndF = 38
const TODStartDB = 39
const TODEndDB = 40
const HiTempF = 41
const LoTempF = 42
const THoldOffF = 43
const HiTempNB = 44
const LoTempNB = 45
const THoldOffNB = 46
const TempTrigF = 47
const TempALoRB = 48
const TempBLoRB = 49
const TempAHiRB = 50
const TempBHiRB = 51


' touch areas for display boxes - these must have a lower control
' value than any thing they overlay.
const Back_Arrow_Touch = 1
const Help_Button_Touch = 2
const Hint1_Touch = 3
const PTSwitch_Touch = 4
const TimeSwitch_Touch = 5
const TempSwitch_Touch = 6

' other constants
'Define variables
' Global Variables - initialised to default values
Dim F_Ver = 0.06 ' File Version TSetSSD006.bas 10 June 2018
' Voltage and current of caravan battery

' misc variables
dim Help$ ' string to display in help box
dim ActiveScreen = 1 'default to main GUI page
Dim Ax 'loop counter
dim S_Bright = 40 ' initial display brightness
dim delim$ = "," ' delimiter for field extract
dim n_field = 1 ' number of field in front end data stream
dim hrs,mins,secs,dom ' used in calculating time flags
dim Instruct$ ' use with Hint_1 display box
dim IntroMsg$ ' startup message
dim Trigger = 0 ' 1 rising edge, 2 falling edge, 3 HiLevel, 4 LoLevel
dim TimerOnTime ' duration of "on" time in decimal minutess
dim HoldOffDelay ' debounce the input edge in mSecs
dim OffTimeDelay ' delay before next cycle can begin
dim TODStart$ ' start time for TOD counter
dim TODEnd$ ' end time for TOD counter
dim TODStartSecs ' referenced to previous midnight
dim TODEndSecs ' referenced to previous midnight
dim LCDScale 2 ' SSD is 2 times ILI
dim LCDType = 2 ' 1 is ILI, 2 is SSD

' Flags
Dim NewDataFlag = 0 ' Two second flag - set every
dim TouchDetected = 0 ' flag touch
dim StartAction = 1 ' used as initiator of timer action
dim InsideNB = 0 ' set when inside a NumberBox, cleared on Enter
''''''''''''''''''''''''''''''''''''''''''''''
' Carry out initialisation routines here - enable interrupt last
StartPoint:
cls
backlight 50
GUI interrupt TouchDown,TouchUp ' subroutine to handle touch int

InitGUI ' routine to set GUI controls
' preset some display boxes
CtrlVal(Head_Box) = "Function Controller"
ctrlval(Back_Arrow_Box) = "<" ' left arrow
ctrlval(Help_Button_Box) = "?" ' home
' preset some variables
IntroMsg$ = "Function Controller v"+str$(F_ver)+" "
IntroMsg$ = IntroMsg$ + "~Created by D.Pankhurst~"
IntroMsg$ = IntroMsg$ + "Select < to goto main menu~~"
IntroMsg$ = IntroMsg$ + "Tap header to increase brightness"
ctrlval(Intro_Box) = IntroMsg$
' ctrlval(Hint_1) = "Press for menu"
' Display all static data
ActiveScreen = 1
UpdateGUIScreen ' setup initial screen
UpdateGUIVals
TouchDetected = 0


''''''''''''''''''''''
' Initialise interrupts
''''''''''''''''''''''

Main:
Do 'Main program loop,
Do While TouchDetected = 0 ' wait until data request sent
Loop
' should only get here if touch detected
if InsideNB = 0 then
UpdateGUIScreen
UpdateGUIVals
TouchDetected = 0
else

endif

Loop
' end of main process loop

''''''''''''''''''''''''''''''''
' main data processing subroutines

''''''''''''''''''''''''''
' Interrupt handling subroutines
' - screen touch routines for GUI interface
Sub TouchDown
Select Case Touch(REF)
Case Help_Button_Touch ' ? Button
ActiveScreen = 9

Case Hint1_Touch ' Hint box
ActiveScreen = 2 ' info screen

Case Back_Arrow_Touch ' < Button
ActiveScreen = 3 ' Main menu screen

case PTSwitch_Touch ' select period timer
ActiveScreen = 4 ' Period Timer screen
' StartAction = 1

case TimeSwitch_Touch ' select time of day timer
ActiveScreen = 5 ' Period Timer screen
' StartAction = 1

case TempSwitch_Touch ' select temperature controller
ActiveScreen = 6 ' temperature controller screen
' StartAction = 1

case TimerOnTimeNB '
InsideNB = 1
' ActiveScreen = 6

case OffTimeDelayNB '
InsideNB = 1
' ActiveScreen = 6

case HoldOffNB '
InsideNB = 1
' ActiveScreen = 6

Case Else ' anywhere else on screen to change brightness
' .. may nedd to define a particular area
If S_Bright < 100 then
S_Bright = S_Bright + 2 ' adjust steps to suit
else
S_Bright = 0
endif
backlight S_Bright
End Select
TouchDetected = 1
End Sub ' end of touch down subroutine

sub TouchUp
InsideNB = 0 ' was inside a NumberBox
End sub
'End of Interrupt handling routines
'''''''''''''''''''''
sub UpdateGUIScreen ' control multiple page display
select case ActiveScreen
case 1 ' default base screen with intro message
page 1,2
CtrlVal(Head_Box) = "Universal Timer"
ctrlval(Hint1_Box) = "Press for Main Menu"
ctrlval(Intro_Box) = IntroMsg$

case 2 ' info screen
page 1,3
CtrlVal(Head_Box) = "Main Menu"
CTRLVAL(PTSwitch_Box) = "Period~Timer"
CtrlVal(TimeSwitch_Box) = "TOD~Timer"
CtrlVal(TempSwitch_Box) = "Temp~Timer"
ctrlval(Hint1_Box) = "Select Menu Option"

case 3
page 1,3
CtrlVal(Head_Box) = "Main Menu"
CTRLVAL(PTSwitch_Box) = "Period~Timer"
CtrlVal(TimeSwitch_Box) = "TOD~Timer"
CtrlVal(TempSwitch_Box) = "Temp~Timer"
ctrlval(Hint1_Box) = "Select Menu Option"

case 4
page 1,4,7,8
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "Period Timer"
TimerOnTime = ctrlval(TimerOnTimeNB)
OffTimeDelay = ctrlval(OffTimeDelayNB)
HoldOffDelay = ctrlval(HoldOffNB)

case 5
page 1,5,7,8
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "TOD Timer"

case 6
page 1,6,8,10
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "Temperature Timer"

case 7

case 8

case 9
CtrlVal(Info_Box) = "Help Help Help~~Help Help Help"
page 1,9

case else

end select
End sub

sub UpdateGUIVals
select case ActiveScreen
case 1 ' default base screen

case 2 '

case 3

case 4

case 5

case 6

case 7

case 8

case 9

case else

end select
end sub

'------------------------------------------------------
' GUI Setup routine - only called once on startup but safer in a subroutine
' values here are for an SSD1963 7" LCD
sub InitGUI ' initialise graphics
gui setup 1 ' same for all pages
Font 3,2
GUI displaybox Head_Box,0,0,800,80,RGB(white),RGB(25,100,215)
font 3,2
GUI Displaybox Back_Arrow_Box,0,400,80,80,RGB(black),RGB(220,220,255)
GUI Displaybox Help_Button_Box,720,400,80,80,RGB(black),RGB(220,220,255)
font 2,2
GUI displaybox Hint1_Box,90,400,620,80,RGB(black),RGB(white)
GUI area Back_Arrow_Touch,0,400,80,80 '
GUI area Help_Button_Touch,720,400,80,80 '
GUI area Hint1_Touch,90,400,620,80 '

GUI setup 2 ' intro box
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for below
font 2
gui displaybox Intro_Box,0,90,800,300,RGB(black),RGB(210,230,210) '

gui setup 3 ' main menu area
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 2,2
GUI DISPLAYBOX PTSwitch_Box,0,90,180,90,RGB(black),RGB(green)
GUI DISPLAYBOX TimeSwitch_Box,0,190,180,90,RGB(black),RGB(magenta)
GUI DISPLAYBOX TempSwitch_Box,0,290,180,90,RGB(black),RGB(cyan)
gui area PTSwitch_Touch,0,90,180,80
gui area TimeSwitch_Touch,0,190,180,80
gui area TempSwitch_Touch,0,290,180,80

gui setup 4 ' period timer
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame TimerOnTimeF,"On Time S",0,100,200,70,rgb(white)
gui frame OffTimeDelayF,"Off Time S",0,210,200,70,rgb(white)
gui frame HoldOffF,"HoldOff mS",0,320,200,70,rgb(white)
gui numberbox TimerOnTimeNB,10,120,180,40,rgb(red),rgb(Black)
gui numberbox OffTimeDelayNB,10,230,180,40,rgb(red),rgb(Black)
gui numberbox HoldOffNB,10,340,180,40,rgb(red),rgb(Black)

gui setup 5 ' time of day switch
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame TODStartF,"Start Time~HH:MM:SS",0,120,210,100,rgb(white)
gui frame TODEndF,"End Time~HH:MM:SS",0,280,210,100,rgb(white)
gui textbox TODStartDB,10,160,190,50,rgb(red),rgb(Black)
gui textbox TODEndDB,10,320,190,50,rgb(red),rgb(Black)

gui setup 6 ' temperature switch
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame LoTempF,"Lo Temp C",0,100,200,70,rgb(white)
gui frame HiTempF,"Hi Temp C",0,210,200,70,rgb(white)
gui frame THoldOffF,"HoldOff C",0,320,200,70,rgb(white)
gui numberbox LoTempNB,10,120,180,40,rgb(red),rgb(Black)
gui numberbox HiTempNB,10,230,180,40,rgb(red),rgb(Black)
gui numberbox THoldOffNB,10,340,180,40,rgb(red),rgb(Black)

gui setup 7 '
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame TriggerSelRB,"Trigger",220,100,210,290,rgb(white)
GUI RADIO RisingEdgeRB, "Rising~Edge",260,150,15,rgb(Blue)
GUI RADIO FallingEdgeRB, "Falling~Edge",260,220,15,rgb(Blue)
GUI RADIO HiLevelRB, "Hi~Level",260,290,15,rgb(Blue)
GUI RADIO LoLevelRB, "Lo~Level",260,360,15,rgb(Blue)

gui setup 8 '
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame RelaySelF,"Relays",440,100,200,290,rgb(white)
GUI RADIO Relay1RB, "RLY 1",480,150,15,rgb(Blue)
GUI RADIO Relay2RB, "RLY 2",480,220,15,rgb(Blue)
GUI RADIO Relay3RB, "RLY~1,2,3",480,290,15,rgb(Blue)

gui setup 9 ' help screen
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for below
font 2
GUI Displaybox Info_Box,0,90,800,300,RGB(black),RGB(210,230,210)

gui setup 10 '
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for below
font 3
gui frame TempTrigF,"Trigger",220,100,210,290,rgb(white)
GUI RADIO TempALoRB, "Temp~> Lo",260,150,15,rgb(Blue)
GUI RADIO TempBLoRB, "Temp~< Lo",260,220,15,rgb(Blue)
GUI RADIO TempAHiRB, "Temp~> Hi",260,290,15,rgb(Blue)
GUI RADIO TempBHiRB, "Temp~< Hi",260,360,15,rgb(Blue)

' end of GUI setups
end sub
'-------------------------------------------------------------
function HMS2Secs(arg1$,arg2)
local HMSTime$,SecsTime,hh,mm,ss)
HMTime$ = arg1$
hh = val(Field$(HMTime$,1,":"))
mm = val(Field$(HMTime$,2,":"))
ss = val(Field$(HMTime$,3,":"))
SecsTime
HMS2Secs = (hh*3600)+(mm*60)+ss
end function

'-----------------------------------------------------
' Geoff's Message Field$ Routine (with one amendment
' by CaptainBoing)
'-----------------------------------------------------
Function Field$(DataVals$,n_field,delim$)
Local Integer i, StartIdx = 1, EndIdx
local dv$ length 64
' enter with DataVals$ with 9 fields in the form
' BVolts,SVolts,CVolts,SAmps,CAmps,ACAmps,DCAmps,LAmps,"zz.zz"
' note always 9 fields but field length may vary
' with 9th field a pseudo checksum as "zz.zz"
dv$ = DataVals$
' get the start of the field in StartIdx
For i = 2 To n_field
StartIdx = Instr(StartIdx, dv$, delim$) + 1
If StartIdx = 1 Or StartIdx > Len(dv$) Then
Exit Function
endif
Next i
' get the end of the field in EndIdx and extract the field
EndIdx = Instr(StartIdx, dv$, delim$)
If EndIdx = 0 Then
EndIdx = Len(dv$)+1 ' makes it smaller than 255
endif
Field$ = Mid$(dv$, StartIdx, EndIdx - StartIdx)
End Function

'''''''''

' End program.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
RonnS
Senior Member

Joined: 16/07/2015
Location: Germany
Posts: 121
Posted: 08:15am 13 Jun 2018
Copy link to clipboard 
Print this post

Panky
Many thanks for sharing your work, i will test it with the picromite and a 3.5 or 4 inch display.

I have my problems with coding the pages

Did you take pictures ?

thanks Ron
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1116
Posted: 09:11am 13 Jun 2018
Copy link to clipboard 
Print this post

Hi Ron,

The positioning for all the elements is for an 800 x 480 display so the are a lot of changes needed for smaller displays. I will post a version for 320 x 240 dimension display later tonight (tested on a 2.4" ILI display). It should run on any larger size display.

I will also post a couple of pics. Again, this is just a framework for anyone to use for there own project - it doesn't actually do anything other than show a multi page menu system.

panky.
Edited by panky 2018-06-14
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1116
Posted: 10:02am 13 Jun 2018
Copy link to clipboard 
Print this post

The following code will run on a 470 with an 2.8" ILI display (320 x 240)

It doesn't have the code for processing the Temperature Timer option but the code from the first post could be plugged in (or any other functions in fact).

Cheers,
panky

'**************************************************************
' Universal Function Controller Interface '
' Written by Doug Pankhurst May 2017, based on coding '
' ideas from Tom Pankhurst and '
' various other Back Shed Forum members - thanks. '
' Written for MMBasic V5.4.09 , copyright Geoff Graham 2012 '
' Program runs on PIC32MX470 with an ILI 2.8" touch screen '
' This program is free for anyone to use or modify as they '
' choose however, if you wish to re-distribute, please '
' acknowledge authors and contributors above. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' File Version TSetSSD006.bas 10 June 2018
' Notes:
' running on MMBasic V5.04.09B19
' - last known good version
' Initial Entry to Program
EntryPoint:
SetTick 0,0 ' Disable timer interrupt for now
Option explicit
' Option autorun on

' Define constants for gui controls
' ILI display is 320 x 240
const BGround_Box = 10
Const Head_Box = 11
const Back_Arrow_Box = 12 ' back arrow box
const Help_Button_Box = 13 ' question mark box
const Hint1_Box = 14
const Intro_Box = 15 ' initial introduction
const Info_Box= 16 ' used for help messages
const PTSwitch_Box = 17
const TimeSwitch_Box = 18
const TempSwitch_Box = 19
const RisingEdgeRB = 20
const FallingEdgeRB = 21
const HiLevelRB = 22
const LoLevelRB = 23
const TriggerSelRB = 24
const TimerOnTimeNB = 25 ' duration of "on" time
const TrigHoldOffNB = 26 ' debounce from edge
const DecHrsNB = 27
const OffTimeDelayNB = 28
const TimerOnTimeF = 29
const OffTimeDelayF = 30
const HoldOffF = 31
const HoldOffNB = 32
const RelaySelF = 33
const Relay1RB = 34
const Relay2RB = 35
const Relay3RB = 36
const TODStartF = 37
const TODEndF = 38
const TODStartDB = 39
const TODEndDB = 40
const HiTempF = 41
const LoTempF = 42
const THoldOffF = 43
const HiTempNB = 44
const LoTempNB = 45
const THoldOffNB = 46
const TempTrigF = 47
const TempALoRB = 48
const TempBLoRB = 49
const TempAHiRB = 50
const TempBHiRB = 51


' touch areas for display boxes - these must have a lower control
' value than any thing they overlay.
const Back_Arrow_Touch = 1
const Help_Button_Touch = 2
const Hint1_Touch = 3
const PTSwitch_Touch = 4
const TimeSwitch_Touch = 5
const TempSwitch_Touch = 6

' other constants
'Define variables
' Global Variables - initialised to default values
Dim F_Ver = 0.06 ' File Version TSetSSD006.bas 10 June 2018
' Voltage and current of caravan battery

' misc variables
dim Help$ ' string to display in help box
dim ActiveScreen = 1 'default to main GUI page
Dim Ax 'loop counter
dim S_Bright = 40 ' initial display brightness
dim delim$ = "," ' delimiter for field extract
dim n_field = 1 ' number of field in front end data stream
dim hrs,mins,secs,dom ' used in calculating time flags
dim Instruct$ ' use with Hint_1 display box
dim IntroMsg$ ' startup message
dim Trigger = 0 ' 1 rising edge, 2 falling edge, 3 HiLevel, 4 LoLevel
dim TimerOnTime ' duration of "on" time in decimal minutess
dim HoldOffDelay ' debounce the input edge in mSecs
dim OffTimeDelay ' delay before next cycle can begin
dim TODStart$ ' start time for TOD counter
dim TODEnd$ ' end time for TOD counter
dim TODStartSecs ' referenced to previous midnight
dim TODEndSecs ' referenced to previous midnight
dim LCDScale 2 ' SSD is 2 times ILI
dim LCDType = 2 ' 1 is ILI, 2 is SSD

' Flags
Dim NewDataFlag = 0 ' Two second flag - set every
dim TouchDetected = 0 ' flag touch
dim StartAction = 1 ' used as initiator of timer action
dim InsideNB = 0 ' set when inside a NumberBox, cleared on Enter
''''''''''''''''''''''''''''''''''''''''''''''
' Carry out initialisation routines here - enable interrupt last
StartPoint:
backlight 50
GUI interrupt TouchDown,TouchUp ' subroutine to handle touch int

InitGUI ' routine to set GUI controls
' preset some display boxes
CtrlVal(Head_Box) = "Function Controller"
ctrlval(Back_Arrow_Box) = "<" ' left arrow
ctrlval(Help_Button_Box) = "?" ' home
' preset some variables
IntroMsg$ = "Function Controller v"+str$(F_ver)+" "
IntroMsg$ = IntroMsg$ + "~Created by D.Pankhurst~"
IntroMsg$ = IntroMsg$ + "Select < to goto main menu~~"
IntroMsg$ = IntroMsg$ + "Tap header to increase brightness"
ctrlval(Intro_Box) = IntroMsg$
' ctrlval(Hint_1) = "Press for menu"
' Display all static data
ActiveScreen = 1
UpdateGUIScreen ' setup initial screen
UpdateGUIVals
TouchDetected = 0


''''''''''''''''''''''
' Initialise interrupts
''''''''''''''''''''''

Main:
Do 'Main program loop,
Do While TouchDetected = 0 ' wait until data request sent
Loop
' should only get here if touch detected
if InsideNB = 0 then
UpdateGUIScreen
UpdateGUIVals
TouchDetected = 0
else

endif

Loop
' end of main process loop

''''''''''''''''''''''''''''''''
' main data processing subroutines

''''''''''''''''''''''''''
' Interrupt handling subroutines
' - screen touch routines for GUI interface
Sub TouchDown
Select Case Touch(REF)
Case Help_Button_Touch ' ? Button
ActiveScreen = 9

Case Hint1_Touch ' Hint box
ActiveScreen = 2 ' info screen

Case Back_Arrow_Touch ' < Button
ActiveScreen = 3 ' Main menu screen

case PTSwitch_Touch ' select period timer
ActiveScreen = 4 ' Period Timer screen
' StartAction = 1

case TimeSwitch_Touch ' select time of day timer
ActiveScreen = 5 ' Period Timer screen
' StartAction = 1

case TempSwitch_Touch ' select temperature controller
ActiveScreen = 6 ' temperature controller screen
' StartAction = 1

case TimerOnTimeNB '
InsideNB = 1
' ActiveScreen = 6

case OffTimeDelayNB '
InsideNB = 1
' ActiveScreen = 6

case HoldOffNB '
InsideNB = 1
' ActiveScreen = 6

Case Else ' anywhere else on screen to change brightness
' .. may nedd to define a particular area
If S_Bright < 100 then
S_Bright = S_Bright + 2 ' adjust steps to suit
else
S_Bright = 0
endif
backlight S_Bright
End Select
TouchDetected = 1
End Sub ' end of touch down subroutine

sub TouchUp
InsideNB = 0 ' was inside a NumberBox
End sub
'End of Interrupt handling routines
'''''''''''''''''''''
sub UpdateGUIScreen ' control multiple page display
select case ActiveScreen
case 1 ' default base screen with intro message
page 1,2
CtrlVal(Head_Box) = "Universal Timer"
ctrlval(Hint1_Box) = "Press for Main Menu"
ctrlval(Intro_Box) = IntroMsg$

case 2 ' info screen
page 1,3
CtrlVal(Head_Box) = "Main Menu"
CTRLVAL(PTSwitch_Box) = "Period~Timer"
CtrlVal(TimeSwitch_Box) = "TOD~Timer"
CtrlVal(TempSwitch_Box) = "Temp~Timer"
ctrlval(Hint1_Box) = "Select Menu Option"

case 3
page 1,3
CtrlVal(Head_Box) = "Main Menu"
CTRLVAL(PTSwitch_Box) = "Period~Timer"
CtrlVal(TimeSwitch_Box) = "TOD~Timer"
CtrlVal(TempSwitch_Box) = "Temp~Timer"
ctrlval(Hint1_Box) = "Select Menu Option"

case 4
page 1,4,7,8
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "Period Timer"
TimerOnTime = ctrlval(TimerOnTimeNB)
OffTimeDelay = ctrlval(OffTimeDelayNB)
HoldOffDelay = ctrlval(HoldOffNB)

case 5
page 1,5,7,8
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "TOD Timer"

case 6
page 1,6,8,10
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "Temperature Timer"

case 7

case 8

case 9
CtrlVal(Info_Box) = "Help Help Help~~Help Help Help"
page 1,9

case else

end select
End sub

sub UpdateGUIVals
select case ActiveScreen
case 1 ' default base screen

case 2 '

case 3

case 4

case 5

case 6

case 7

case 8

case 9

case else

end select
end sub

'------------------------------------------------------
' GUI Setup routine - only called once on startup but safer in a subroutine
sub InitGUI ' initialise graphics
gui setup 1 ' same for all pages
Font 3,2
GUI displaybox Head_Box,0,0,800,80,RGB(white),RGB(25,100,215)
font 3,2
GUI Displaybox Back_Arrow_Box,0,400,80,80,RGB(black),RGB(220,220,255)
GUI Displaybox Help_Button_Box,720,400,80,80,RGB(black),RGB(220,220,255)
font 2,2
GUI displaybox Hint1_Box,90,400,620,80,RGB(black),RGB(white)
GUI area Back_Arrow_Touch,0,400,80,80 '
GUI area Help_Button_Touch,720,400,80,80 '
GUI area Hint1_Touch,90,400,620,80 '

GUI setup 2 ' intro box
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for below
font 2
gui displaybox Intro_Box,0,90,800,300,RGB(black),RGB(210,230,210) '

gui setup 3 ' main menu area
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 2,2
GUI DISPLAYBOX PTSwitch_Box,0,90,180,90,RGB(black),RGB(green)
GUI DISPLAYBOX TimeSwitch_Box,0,190,180,90,RGB(black),RGB(magenta)
GUI DISPLAYBOX TempSwitch_Box,0,290,180,90,RGB(black),RGB(cyan)
gui area PTSwitch_Touch,0,90,180,80
gui area TimeSwitch_Touch,0,190,180,80
gui area TempSwitch_Touch,0,290,180,80

gui setup 4 ' period timer
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame TimerOnTimeF,"On Time S",0,100,200,70,rgb(white)
gui frame OffTimeDelayF,"Off Time S",0,210,200,70,rgb(white)
gui frame HoldOffF,"HoldOff mS",0,320,200,70,rgb(white)
gui numberbox TimerOnTimeNB,10,120,180,40,rgb(red),rgb(Black)
gui numberbox OffTimeDelayNB,10,230,180,40,rgb(red),rgb(Black)
gui numberbox HoldOffNB,10,340,180,40,rgb(red),rgb(Black)

gui setup 5 ' time of day switch
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame TODStartF,"Start Time~HH:MM:SS",0,120,210,100,rgb(white)
gui frame TODEndF,"End Time~HH:MM:SS",0,280,210,100,rgb(white)
gui textbox TODStartDB,10,160,190,50,rgb(red),rgb(Black)
gui textbox TODEndDB,10,320,190,50,rgb(red),rgb(Black)

gui setup 6 ' temperature switch
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame LoTempF,"Lo Temp C",0,100,200,70,rgb(white)
gui frame HiTempF,"Hi Temp C",0,210,200,70,rgb(white)
gui frame THoldOffF,"HoldOff C",0,320,200,70,rgb(white)
gui numberbox LoTempNB,10,120,180,40,rgb(red),rgb(Black)
gui numberbox HiTempNB,10,230,180,40,rgb(red),rgb(Black)
gui numberbox THoldOffNB,10,340,180,40,rgb(red),rgb(Black)

gui setup 7 '
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame TriggerSelRB,"Trigger",220,100,210,290,rgb(white)
GUI RADIO RisingEdgeRB, "Rising~Edge",260,150,15,rgb(Blue)
GUI RADIO FallingEdgeRB, "Falling~Edge",260,220,15,rgb(Blue)
GUI RADIO HiLevelRB, "Hi~Level",260,290,15,rgb(Blue)
GUI RADIO LoLevelRB, "Lo~Level",260,360,15,rgb(Blue)

gui setup 8 '
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 3
gui frame RelaySelF,"Relays",440,100,200,290,rgb(white)
GUI RADIO Relay1RB, "RLY 1",480,150,15,rgb(Blue)
GUI RADIO Relay2RB, "RLY 2",480,220,15,rgb(Blue)
GUI RADIO Relay3RB, "RLY~1,2,3",480,290,15,rgb(Blue)

gui setup 9 ' help screen
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for below
font 2
GUI Displaybox Info_Box,0,90,800,300,RGB(black),RGB(210,230,210)

gui setup 10 '
rbox 0,90,800,300,,RGB(black),RGB(210,230,210) ' empty area for below
font 3
gui frame TempTrigF,"Trigger",220,100,210,290,rgb(white)
GUI RADIO TempALoRB, "Temp~> Lo",260,150,15,rgb(Blue)
GUI RADIO TempBLoRB, "Temp~< Lo",260,220,15,rgb(Blue)
GUI RADIO TempAHiRB, "Temp~> Hi",260,290,15,rgb(Blue)
GUI RADIO TempBHiRB, "Temp~< Hi",260,360,15,rgb(Blue)

' end of GUI setups
end sub
'-------------------------------------------------------------
function HMS2Secs(arg1$,arg2)
local HMSTime$,SecsTime,hh,mm,ss)
HMTime$ = arg1$
hh = val(Field$(HMTime$,1,":"))
mm = val(Field$(HMTime$,2,":"))
ss = val(Field$(HMTime$,3,":"))
SecsTime
HMS2Secs = (hh*3600)+(mm*60)+ss
end function

'-----------------------------------------------------
' Geoff's Message Field$ Routine (with one amendment
' by CaptainBoing)
'-----------------------------------------------------
Function Field$(DataVals$,n_field,delim$)
Local Integer i, StartIdx = 1, EndIdx
local dv$ length 64
' enter with DataVals$ with 9 fields in the form
' BVolts,SVolts,CVolts,SAmps,CAmps,ACAmps,DCAmps,LAmps,"zz.zz"
' note always 9 fields but field length may vary
' with 9th field a pseudo checksum as "zz.zz"
dv$ = DataVals$
' get the start of the field in StartIdx
For i = 2 To n_field
StartIdx = Instr(StartIdx, dv$, delim$) + 1
If StartIdx = 1 Or StartIdx > Len(dv$) Then
Exit Function
endif
Next i
' get the end of the field in EndIdx and extract the field
EndIdx = Instr(StartIdx, dv$, delim$)
If EndIdx = 0 Then
EndIdx = Len(dv$)+1 ' makes it smaller than 255
endif
Field$ = Mid$(dv$, StartIdx, EndIdx - StartIdx)
End Function

'''''''''

' End program.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
SteveA
Regular Member

Joined: 10/03/2017
Location: United Kingdom
Posts: 48
Posted: 08:09am 14 Jun 2018
Copy link to clipboard 
Print this post

  Quote  The following code will run on a 470 with an 2.8" ILI display (320 x 240)


Sorry, it doesn't.

There are references to dimensions greater than 320 x 240 in the code, I think you may have inadvertently uploaded the wrong file?

Rgds,

Steve
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1116
Posted: 09:24am 14 Jun 2018
Copy link to clipboard 
Print this post

My apologies to all - the code was for an SSD display as SteveA pointed out.

The following code was tested on a Sil Chip MM+ Backpack with a 2.8" display and it did run. Copied from here to MMEdit and downloaded to the MM+.

NOTE: This is a 'proof of concept' program and may have errors - I was merely trying to offer some suggestions and an example of how a multi page menu based GUI program might be implimented. The Temp Timer does nothing at present - it is just a frame to do whatever you want to with.

Regards,
panky

'**************************************************************
' Universal Function Controller '
' Written by Doug Pankhurst May 2017, based on coding '
' ideas from Tom Pankhurst and '
' various other Back Shed Forum members - thanks. '
' Written for MMBasic V5.4.09 , copyright Geoff Graham 2012 '
' Program runs on PIC32MX470 with an ILI9361 2.8" touch screen'
' This program is free for anyone to use or modify as they '
' choose however, if you wish to re-distribute, please '
' acknowledge authors and contributors above. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' File Version TSetILI004.bas 6 June 2018
' Notes:
' running on MMBasic V5.04.09B19
' - last known good version
' Initial Entry to Program
EntryPoint:
SetTick 0,0 ' Disable timer interrupt for now
Option explicit
' Option autorun on

' Define constants for gui controls
' ILI display is 320 x 240
const BGround_Box = 10
Const Head_Box = 11
const Back_Arrow_Box = 12 ' back arrow box
const Help_Button_Box = 13 ' question mark box
const Hint1_Box = 14
const Intro_Box = 15 ' initial introduction
const Info_Box= 16 ' used for help messages
const PTSwitch_Box = 17
const TimeSwitch_Box = 18
const TempSwitch_Box = 19
const RisingEdgeRB = 20
const FallingEdgeRB = 21
const HiLevelRB = 22
const LoLevelRB = 23
const TriggerSelRB = 24
const TimerOnTimeNB = 25 ' duration of "on" time
const TrigHoldOffNB = 26 ' debounce from edge
const DecHrsNB = 27
const OffTimeDelayNB = 28
const TimerOnTimeF = 29
const OffTimeDelayF = 30
const HoldOffF = 31
const HoldOffNB = 32
const RelaySelF = 33
const Relay1RB = 34
const Relay2RB = 35
const Relay3RB = 36
const TODStartF = 37
const TODEndF = 38
const TODStartDB = 39
const TODEndDB = 40

' touch areas for display boxes - these must have a lower control
' value than any thing they overlay.
const Back_Arrow_Touch = 1
const Help_Button_Touch = 2
const Hint1_Touch = 3
const PTSwitch_Touch = 4
const TimeSwitch_Touch = 5
const TempSwitch_Touch = 6
const TODStart_Touch = 7
const TODEnd_Touch = 8

' other constants
'Define variables
' Global Variables - initialised to default values
Dim F_Ver = 0.04 ' File Version TSetILI004.bas 6 June 2018
' Voltage and current of caravan battery

' misc variables
dim Help$ ' string to display in help box
dim ActiveScreen = 1 'default to main GUI page
Dim Ax 'loop counter
dim S_Bright = 40 ' initial display brightness
dim delim$ = "," ' delimiter for field extract
dim n_field = 1 ' number of field in front end data stream
dim hrs,mins,secs,dom ' used in calculating time flags
dim Instruct$ ' use with Hint_1 display box
dim IntroMsg$ ' startup message
dim Trigger = 0 ' 1 rising edge, 2 falling edge, 3 HiLevel, 4 LoLevel
dim TimerOnTime ' duration of "on" time in decimal minutess
dim HoldOffDelay ' debounce the input edge in mSecs
dim OffTimeDelay ' delay before next cycle can begin
dim TODStart$ ' start time for TOD counter
dim TODEnd$ ' end time for TOD counter
dim TODStartSecs ' referenced to previous midnight
dim TODEndSecs ' referenced to previous midnight

' Flags
Dim NewDataFlag = 0 ' Two second flag - set every
dim TouchDetected = 0 ' flag touch
dim StartAction = 1 ' used as initiator of timer action
dim InsideNB = 0 ' set when inside a NumberBox, cleared on Enter
''''''''''''''''''''''''''''''''''''''''''''''
' Carry out initialisation routines here - enable interrupt last
StartPoint:
pwm 2,1000,S_Bright
' backlight 50
GUI interrupt TouchDown,TouchUp ' subroutine to handle touch int

InitGUI ' routine to set GUI controls
' preset some display boxes
CtrlVal(Head_Box) = "Function Controller"
ctrlval(Back_Arrow_Box) = "<" ' left arrow
ctrlval(Help_Button_Box) = "?" ' home
' preset some variables
IntroMsg$ = "Function Controller v"+str$(F_ver)+" "
IntroMsg$ = IntroMsg$ + "~Created by D.Pankhurst~"
IntroMsg$ = IntroMsg$ + "Select < to goto main menu~~"
IntroMsg$ = IntroMsg$ + "Tap header to increase brightness"
ctrlval(Intro_Box) = IntroMsg$
' ctrlval(Hint_1) = "Press for menu"
' Display all static data
ActiveScreen = 1
UpdateGUIScreen ' setup initial screen
UpdateGUIVals
TouchDetected = 0


''''''''''''''''''''''
' Initialise interrupts
''''''''''''''''''''''

Main:
Do 'Main program loop,
Do While TouchDetected = 0 ' wait until data request sent
Loop
' should only get here if touch detected
if InsideNB = 0 then
UpdateGUIScreen
UpdateGUIVals
TouchDetected = 0
else

endif

Loop
' end of main process loop

''''''''''''''''''''''''''''''''
' main data processing subroutines

''''''''''''''''''''''''''
' Interrupt handling subroutines
' - screen touch routines for GUI interface
Sub TouchDown
Select Case Touch(REF)
Case Help_Button_Touch ' ? Button
ActiveScreen = 9

Case Hint1_Touch ' Hint box
ActiveScreen = 2 ' info screen

Case Back_Arrow_Touch ' < Button
ActiveScreen = 3 ' Main menu screen

case PTSwitch_Touch ' select period timer
ActiveScreen = 4 ' Period Timer screen
' StartAction = 1

case TimeSwitch_Touch ' select time of day timer
ActiveScreen = 5 ' Period Timer screen
' StartAction = 1

case TempSwitch_Touch ' select temperature controller
ActiveScreen = 6 ' temperature controller screen
' StartAction = 1

case TimerOnTimeNB '
InsideNB = 1
' ActiveScreen = 6

case OffTimeDelayNB '
InsideNB = 1
' ActiveScreen = 6

case HoldOffNB '
InsideNB = 1
' ActiveScreen = 6

Case Else ' anywhere else on screen to change brightness
If S_Bright < 100 then
S_Bright = S_Bright + 10
else
S_Bright = 0
endif
pwm 2,1000,S_Bright
' backlight = S_Bright
End Select
TouchDetected = 1
End Sub ' end of touch down subroutine

sub TouchUp
InsideNB = 0 ' was inside a NumberBox
End sub
'End of Interrupt handling routines
'''''''''''''''''''''
sub UpdateGUIScreen ' control multiple page display
select case ActiveScreen
case 1 ' default base screen with intro message
page 1,2
CtrlVal(Head_Box) = "Universal Timer"
ctrlval(Hint1_Box) = "Press for Main Menu"
ctrlval(Intro_Box) = IntroMsg$

case 2 ' info screen
page 1,3
CtrlVal(Head_Box) = "Main Menu"
CTRLVAL(PTSwitch_Box) = "Period~Timer"
CtrlVal(TimeSwitch_Box) = "TOD~Timer"
CtrlVal(TempSwitch_Box) = "Temp~Timer"
ctrlval(Hint1_Box) = "Select Menu Option"

case 3
page 1,3
CtrlVal(Head_Box) = "Main Menu"
CTRLVAL(PTSwitch_Box) = "Period~Timer"
CtrlVal(TimeSwitch_Box) = "TOD~Timer"
CtrlVal(TempSwitch_Box) = "Temp~Timer"
ctrlval(Hint1_Box) = "Select Menu Option"

case 4
page 1,4,7,8
ctrlval(Hint1_Box) = "Press to Start"
CtrlVal(Head_Box) = "Period Timer"
TimerOnTime = ctrlval(TimerOnTimeNB)
OffTimeDelay = ctrlval(OffTimeDelayNB)
HoldOffDelay = ctrlval(HoldOffNB)

case 5
page 1,5,7,8
CtrlVal(Head_Box) = "TOD Timer"

case 6
page 1,6
CtrlVal(Head_Box) = "Temperature Timer"

case 7

case 8

case 9
CtrlVal(Info_Box) = "Help Help Help~~Help Help Help"
page 1,9

case else

end select
End sub

sub UpdateGUIVals
select case ActiveScreen
case 1 ' default base screen

case 2 '

case 3

case 4

case 5

case 6

case 7

case 8

case 9

case else

end select
end sub

'------------------------------------------------------
' GUI Setup routine - only called once on startup but safer in a subroutine
sub InitGUI ' initialise graphics
gui setup 1 ' same for all pages
Font 2
GUI displaybox Head_Box,0,0,320,40,RGB(white),RGB(25,100,215)
font 3
GUI Displaybox Back_Arrow_Box,0,200,40,40,RGB(black),RGB(220,220,255)
GUI Displaybox Help_Button_Box,280,200,40,40,RGB(black),RGB(220,220,255)
font 2
GUI displaybox Hint1_Box,45,200,230,40,RGB(black),RGB(white)
GUI area Back_Arrow_Touch,0,200,40,40 '
GUI area Help_Button_Touch,280,200,40,40 '
GUI area Hint1_Touch,50,200,220,40 '

GUI setup 2 ' intro box
rbox 0,45,320,150,,RGB(black),RGB(210,230,210) ' empty area for below
font 1
gui displaybox Intro_Box,0,45,320,150,RGB(black),RGB(210,230,210) '

gui setup 3 ' main menu area
rbox 0,45,320,150,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 1
GUI DISPLAYBOX PTSwitch_Box,0,45,90,45,RGB(black),RGB(green)
GUI DISPLAYBOX TimeSwitch_Box,0,95,90,45,RGB(black),RGB(magenta)
GUI DISPLAYBOX TempSwitch_Box,0,145,90,45,RGB(black),RGB(cyan)
gui area PTSwitch_Touch,0,45,90,40
gui area TimeSwitch_Touch,0,95,90,40
gui area TempSwitch_Touch,0,145,90,40

gui setup 4 ' period timer
rbox 0,45,320,150,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 1
gui frame TimerOnTimeF,"On Time S",0,50,100,35,rgb(white)
gui frame OffTimeDelayF,"Off Time S",0,105,100,35,rgb(white)
gui frame HoldOffF,"HoldOff mS",0,160,100,35,rgb(white)
gui numberbox TimerOnTimeNB,5,60,90,20,rgb(red),rgb(Black)
gui numberbox OffTimeDelayNB,5,115,90,20,rgb(red),rgb(Black)
gui numberbox HoldOffNB,5,170,90,20,rgb(red),rgb(Black)

gui setup 5 ' time of day switch
rbox 0,45,320,150,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 1
gui frame TODStartF,"Start Time~HH:MM:SS",0,60,105,50,rgb(white)
gui frame TODEndF,"End Time~HH:MM:SS",0,140,105,50,rgb(white)
gui TEXTbox TODStartDB,5,80,95,25,rgb(red),rgb(Black)
gui TEXTbox TODEndDB,5,160,95,25,rgb(red),rgb(Black)

gui setup 6 ' temperature switch
rbox 0,45,320,150,,RGB(black),RGB(210,230,210) ' empty area for menu switches below
font 1

gui setup 7 '
gui frame TriggerSelRB,"Trigger",110,50,105,145,rgb(white)
GUI RADIO RisingEdgeRB, "Rising~Edge",130,75,15,rgb(Blue)
GUI RADIO FallingEdgeRB, "Falling~Edge",130,110,15,rgb(Blue)
GUI RADIO HiLevelRB, "Hi~Level",130,145,15,rgb(Blue)
GUI RADIO LoLevelRB, "Lo~Level",130,180,15,rgb(Blue)

gui setup 8 '
gui frame RelaySelF,"Relay",220,50,100,145,rgb(white)
GUI RADIO Relay1RB, "TB1~NC",240,75,15,rgb(Blue)
GUI RADIO Relay2RB, "TB2~NO",240,110,15,rgb(Blue)
GUI RADIO Relay3RB, "TB3~NO",240,145,15,rgb(Blue)

gui setup 9 ' help screen
rbox 0,45,320,150,,RGB(black),RGB(210,230,210) ' empty area for below
font 1
GUI Displaybox Info_Box,0,45,320,150,RGB(black),RGB(210,230,210)


' end of GUI setups
end sub
'-------------------------------------------------------------
function HMS2Secs(arg1$,arg2)
local HMSTime$,SecsTime,hh,mm,ss)
HMTime$ = arg1$
hh = val(Field$(HMTime$,1,":"))
mm = val(Field$(HMTime$,2,":"))
ss = val(Field$(HMTime$,3,":"))
SecsTime
HMS2Secs = (hh*3600)+(mm*60)+ss
end function

'-----------------------------------------------------
' Geoff's Message Field$ Routine (with one amendment
' by CaptainBoing)
'-----------------------------------------------------
Function Field$(DataVals$,n_field,delim$)
Local Integer i, StartIdx = 1, EndIdx
local dv$ length 64
' enter with DataVals$ with 9 fields in the form
' BVolts,SVolts,CVolts,SAmps,CAmps,ACAmps,DCAmps,LAmps,"zz.zz"
' note always 9 fields but field length may vary
' with 9th field a pseudo checksum as "zz.zz"
dv$ = DataVals$
' get the start of the field in StartIdx
For i = 2 To n_field
StartIdx = Instr(StartIdx, dv$, delim$) + 1
If StartIdx = 1 Or StartIdx > Len(dv$) Then
Exit Function
endif
Next i
' get the end of the field in EndIdx and extract the field
EndIdx = Instr(StartIdx, dv$, delim$)
If EndIdx = 0 Then
EndIdx = Len(dv$)+1 ' makes it smaller than 255
endif
Field$ = Mid$(dv$, StartIdx, EndIdx - StartIdx)
End Function

'''''''''

' End program.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025