Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 16:22 13 May 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 : Newie got a few questions

     Page 20 of 25    
Author Message
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 04:03pm 05 Dec 2017
Copy link to clipboard 
Print this post

Line 240 is in that code, it's this section the last ENDIF
Else
If Char <> 34 Or KeepQuotes Then
FieldArray$(Count) = FieldArray$(Count) + Chr$(char)
EndIf
EndIf 'Line 240
 
Azure

Guru

Joined: 09/11/2017
Location: Australia
Posts: 446
Posted: 09:42pm 05 Dec 2017
Copy link to clipboard 
Print this post

Add a conditional print statement within that if statement to check what is going on when it is out of bounds
If Count >= MAXFIELDS-1 Then
Print "Count: "; Counr
Print "Record$: :; Record$
Print "Index: "; Index
EndIf


MAXFIELDS is whatever size you declare your FieldArray$(MAXFIELDS) to be, minus 1 since it starts at 0.
 
Azure

Guru

Joined: 09/11/2017
Location: Australia
Posts: 446
Posted: 09:44pm 05 Dec 2017
Copy link to clipboard 
Print this post

  Azure said   You cannot run the module if the USB port is connected to a computer. The unit must be powered from a power module only or via the power pins to be in GPIO mode (run using the play inputs).


You have not indicated if you were running the sound module from the PC USB connection, as indicated above it won't work using GPIO pins when it is plugged into anything other than a power source.
 
panky

Guru

Joined: 02/10/2012
Location: Australia
Posts: 1111
Posted: 05:58am 06 Dec 2017
Copy link to clipboard 
Print this post

Lewis,

Re your code example a couple of posts ago, there are a few problems there - firstly you have the function name as GetFieldArray but you also use GetFieldArray$() as the array inside the function - this is a no no and throws an error.

Also the count variable ended up outside the array bounds as you found.

I have taken the liberty of massageing your code a bit to get it working and included a bit of test code to demonstrate.


' Test program to demonstrate record splitting into a
' multi field array
option explicit
option base 1
dim record$ = "Field 1,"
record$ = record$+chr$(34)+"Field 2, in quotes and embedded delimiter"+chr$(34)+","
record$ = record$+"Field 3"
dim x
dim KeepQuotes = 0
dim Delimiter$ = ","
dim GetFieldArray$(3)
dim fields = 0
' call GFieldArray function which returns number of fields
' - it also fills the array GetFieldArray$()
' - we have KeepQotes = 0 here so no quotes in field 2
fields = GFieldArray(Record$,Delimiter$,KeepQuotes)
print "Field count = ",fields
print
Print "Pass 1 with no quotes passed to output"
for x = 1 to fields
print "Field ",x," = ",GetFieldArray$(x)
next x
print
' empty out array
for x = 1 to fields
GetFieldArray$(x) = ""
next x

' do it again with KeepQuotes set to 1
print "Pass 2 with quotes to be included in output"
KeepQuotes = 1
fields = GFieldArray(Record$,Delimiter$,KeepQuotes)
for x = 1 to fields
print "Field ",x," = ",GetFieldArray$(x)
next x
end

' Subroutines
Function GFieldArray(Record$, Delimiter$, KeepQuotes)
LOCAL Index, Char, InQuote, FieldCount
InQuote = 0
FieldCount = 1
' If no delimiter is provided use a comma
IF Delimiter$ = "" then Delimiter$ = ","
' Loop through all the characters in Record$
FOR Index = 1 to LEN(Record$)
Char = asc(MID$(Record$, Index, 1))
IF Char = 34 THEN 'check for quote character
' Flip InQuote between 0 and 1.
if KeepQuotes = 1 then 'only add quotes when KeepQuotes is 1
GetFieldArray$(FieldCount) = GetFieldArray$(FieldCount) + chr$(char)
endif
InQuote = NOT InQuote
elseIF char = ASC(Delimiter$) then ' check for delimiter
if InQuote then ' if in quotes, plug delimiter into array
GetFieldArray$(FieldCount) = GetFieldArray$(FieldCount) + chr$(char)
else ' next field
FieldCount = FieldCount + 1
endif
else
' Only collect characters for the requested field.
'plug in the character
GetFieldArray$(FieldCount) = GetFieldArray$(FieldCount) + chr$(char)
ENDIF
NEXT
GFieldArray = FieldCount
End Function



Hope this assists you,
Regards,
panky
... almost all of the Maximites, the MicromMites, the MM Extremes, the ArmMites, the PicoMite and loving it!
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 01:26pm 06 Dec 2017
Copy link to clipboard 
Print this post

Thanks Panky

On a different subject again

I have a picture displayed on the lcd screen showing various information
What I would LIKE to do is
IF my outdoor unit lost the GPS lock
or was faulty - not sending information for a set period of time

display a message on the Explore 100 lcd
similar to
Text 150,150,"GPS LOCK LOST" , LM, 2, 3, RGB(BLACK), RGB(WHITE) 'Print the weather description
or
Text 150,150,"Outdoor unit FAULTY" , LM, 2, 3, RGB(BLACK), RGB(WHITE) 'Print the weather description
BUT
I'd like to remove the text after a set period of time and have the original picture show
Is there a way to remove text without having to load the picture fully again?
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 01:56pm 06 Dec 2017
Copy link to clipboard 
Print this post

I sorted the problem partly by using led's on the screen, its easy to turn them on and off

Is there a way to set a timer on a serial port so that if nothing is received within a set period of time it then does something such as change a ctrlval
 
goc30

Guru

Joined: 12/04/2017
Location: France
Posts: 435
Posted: 02:10pm 06 Dec 2017
Copy link to clipboard 
Print this post

you must use a time-out couter


'------------------
'init var
dim tcpt1 as integer 'value of time out counter
dim tcpt2 as integer 'value of display counter
dim tcptmax as integer 'value max of time out
dim tcptmax2 as integer 'value of display time
SETTICK 100, gpsto (or settick 100, gpsto,x 'x=n° couter if use more than 1 coutner

'---------------
'in setup
tcpt1=0
tcptmax=30 'wait 3 sec time out
tcptmax2=50 'display msg during 5 sec

'-------------------
'after gps on
tcpt1=1 to begin couting
'-------------------
'in main loop
'...
'... others tasks
'...
' if not use COM time-out you must put this
tcpt1=1 'for begin couting time out "no response"
'gps read 'and goto read COM gps
'
'if gps value is correct
tcpt1=1 'raz time-out
tcpt2=0 'raz display
'...
'...
'----------------
'in interrupt sp
gpsto:
if tcpt2=0 then
tcpt2=1 'flag display is done
Text 150,150," " , LM, 2, 3, RGB(BLACK), RGB(WHITE) 'Print the
end if

if tcpt1<tcptmax then
if tcpt1>0 then tcpt1=tcpt1+1
else
if tcpt1=tcptmax then
tcpt1=tcpt1+1 'to indicat timeout is on and no display next time
Text 150,150,"GPS LOCK LOST" , LM, 2, 3, RGB(BLACK), RGB(WHITE) 'Print
tcpt2=1 'begin counting display time
'and add other instruct if necessary
else
if tcpt2<tcptmax2 then
if tcpt2>0 then tcpt2=tcpt2+1
else
tcpt2=0 'for raz display in next loop
end if
end if
end if

ireturn


if you work in interrupt mode for gps, it is more easy
you need to put a flag "gps ok", or put cpt1 to 1 (and cpt2 to zero if not) after decoding ans control string gps

Edited by goc30 2017-12-08
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 12:30pm 29 Dec 2017
Copy link to clipboard 
Print this post

Could my soldering be the reason this board isn't working?
I "attempted" to solder SMD the other day to save someone having to do it yet again for me but I failed, it doesn't work!
Guess my eyesight and hand/eye co-ordination isn't what it used to be - I "used" to be a tv tech years ago and was able to solder smd back then!

Good examples of how "NOT" to solder




 
Azure

Guru

Joined: 09/11/2017
Location: Australia
Posts: 446
Posted: 08:34am 30 Dec 2017
Copy link to clipboard 
Print this post

Some of those SMD joints look a bit suspect but may be OK, just becuase they are not square doens't make them bad joints. Check both side are soldered and they are not bridged under the compement to short it out.

You should check the 5V rail at the processor socket (chip removed) preferably with a scope to see if it is clean with minimal noise but a meter will suffice.

You should also check that cap under the processor, if it is the one connected to pin 20, it is supposed to be a tantalum 47uf or usually a 10uF X5F non polarised ceramic. It looks more like an electrolytic which won't work properly.

Other things to try, does the MM work in another board (shows it is programmed and working correctly).

After those thing then normal checks like reset line and any stuck pins will depend on your experience and available tools.
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 07:54pm 31 Dec 2017
Copy link to clipboard 
Print this post

It's alive - no idea how but it is
I resoldered the tantalum cap connected to the MM and put 3mm led's on the board instead of the smd ones and it now works
Miracles do happen!
Edited by lew247 2018-01-02
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 07:06pm 03 Jan 2018
Copy link to clipboard 
Print this post

Can anyone help with a time routine please?

On an Explore 100 board I have an audio board connected and I want it to chime on the hour and at 15 min intervals

I have an RTC connected
and in the code I have
hrs = Val(left$(Time$,2)) ' FOR INFO ONLY
mins = Val(Mid$(Time$,4,2)) ' FOR INFO ONLY
secs = Val(right$(Time$,2)) ' FOR INFO ONLY

I tried this

[code]IF Val(left$(Time$,2)) = 19 and Val(Mid$(Time$,4,2)) = 00 and Val(right$(Time$,2) = 01
print "19:00:01 and I work"
END IF
[/code]
However that just printed "19:00:01 and I work" continuously

I then tried
[code]
IF Val(left$(Time$,2)) = 19 THEN
print "19:00:01 and I work"
END IF[/code]

But this doesn't work either, it prints it continuously again

What I want to do is trigger a pin on each hour like this

[code]
IF Val(left$(Time$,2)) = 14 then
PIN(91) = 0 'Chime on
print "2PM Chime on" 'print so I know it's working
pause 150 'trigger delay for audio board
PIN(91) = 1 'Audio board off
pause 4250 'pause for the 1st hour chime to play
PIN(91) = 0 'Chime 2nd time
pause 150 'trigger delay for audio board
PIN(91) = 1 'Audio board off[/code]

Edited by lew247 2018-01-05
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3999
Posted: 09:42pm 03 Jan 2018
Copy link to clipboard 
Print this post

If it's in a loop the time won't count up a second for many loops.
(You'd want something like to store the time and check if it's changed from the old value and only then check if it's the time when you want to print.)

Similar idea if in a tick routine.

John
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 06:05pm 04 Jan 2018
Copy link to clipboard 
Print this post

I've got it sorted apart from the time display freezes while the chimes sound

I have the following code for the time and clock routines
[code]
SetTick 1000,UpDateTime ' Call every second, print date/time
do
main program
IF NewDataFlag = 1 then
TEXT 330,238, TIME$, LM, 1, 2, RGB(BLACK),RGB(white) 'Print the Time
Text 285,275, DOW$(DayOfWeek(Year, Month, Day))+" " +date$, LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the Day of week
NewDataFlag = 0
End IF

Sub UpDateTime ' called by 1 second interrupt from SetTick
Local hrs,mins,secs
hrs = Val(left$(Time$,2))
mins = Val(Mid$(Time$,4,2))
secs = Val(right$(Time$,2))
DOW$(0)="Sunday":DOW$(1)="Monday":DOW$(2)="Tuesday":DOW$(3)= "Wednesday":DOW$(4)="Thursday":DOW$(5)="Friday":DOW$(6)="Saturday" ' Day of the week
' Get the current time, extract the minutes component
If secs = 0 Then
MinFlag = 1
Else
MinFlag = 0
EndIf
If hrs = 23 And mins = 59 And secs = 59 Then 'check for gmt/bst - this comes from GPS unit
Text 285,275, " ", LM, 2, 1, RGB(BLACK), RGB(WHITE)
RTC GETTIME
EODFlag = 1
Else
EODFlag = 0
EndIf
Year=Val(Mid$(Date$,7,4)):Month=Val(Mid$(Date$,4,2)):Day=Val(Mid$(Date$,1,2))
NewDataFlag = 1 ' set to indicate fresh data
End Sub[/code]

That all works fine

It's the hour chimes I'm having problems with
I'm using the Westminster Chimes on an Adafruit board and I have 6 chimes I can use
0 - 3 = 1/4, 2/4. 3/4 and 4/4 chimes
output 4 is the 1 hour Chime which repeats depending on what hour it is, ie for 7 PM it would play and repeat 6 times
Chime 5 is the Midday Chime - 12 chimes in one file so that isn't a problem

My code for playing the hour chime is as follows

[code]
IF Val(left$(Time$,2)) = 07 and Val(Mid$(Time$,4,2)) = 00 and Val(right$(Time$,2)) = 00 then
PIN(91) = 0 'PIN(91)ON
print "7AM Chime ON" 'So I can see it's working if the sound stops
pause 150 'Delay to ensure sound board plays
PIN(91) = 1 'Turn PIN(91) OFF
pause 4250 'Delay while the chime plays
PIN(91) = 0 'Turn PIN(91) ON again
pause 150
PIN(91) = 1
pause 4250
PIN(91) = 0
pause 150
PIN(91) = 1
pause 4250
PIN(91) = 0
pause 150
PIN(91) = 1
pause 4250
PIN(91) = 0
pause 150
PIN(91) = 1
pause 4250
PIN(91) = 0
pause 150
PIN(91) = 1
pause 4250
PIN(91) = 0
pause 150
PIN(91) = 1
END IF[/code]

Thats the code for 7AM and also 7PM but the hour changed to 19 instead of 07

I need the pause of 150 after I make PIN(91) = 0 to make sure the sound board will play
Then I need a pause of 4250 because thats how long in total each chime takes to play.


IS there any way to do with while keeping the clock updating on the display or will I just have to accept the clock pausing while playing the chimes?
Edited by lew247 2018-01-06
 
Azure

Guru

Joined: 09/11/2017
Location: Australia
Posts: 446
Posted: 02:34am 05 Jan 2018
Copy link to clipboard 
Print this post

You chime routine can be compacted and more efficient and also get rid of the pauses.

It needs to ignore 12/24 hour by taking the hour mod 12 so you are only ever dealing with 0-11, with zero being 12:00/00:00). We offset it by 1 to stop multiple triggering for the midday chime.

If it is 1 (on the hour of midday/midnight) play chime 05.

If it is 2-12 (on the hour other than midday/midnight) set a flag/counter to play the 04 chime 'hour' times.

In the HourChime routine decrement the counter and if needed play the 04 Chime.

Use the 'PULSE 91, 150' command to generate the pulse on the pin to remove the PAUSE 150.

Use a SETTICK timer instead of PAUSE 4250 (which will need to be 4250+150 = 4400).

Here is something along those lines (not tested), hope it helps:


PS This Weather station is a big proect
'Generic words used for hours, minutes and seconds, these need to be properly coded

'Define the pins used for the midday and hourly chimes
CONST middayPin xx
CONST hourPin 91

'HourFlag > 0 play chimes, value is number of hourly chimes to play + 1
DIM INTEGER HourFlag = 0

'Check if we are on the hour and have not started the chimes
IF (minutes = 0) AND (seconds = 0) AND (HourFlag = 0) THEN
HourFlag = (hours mod 12) + 1 '+1 used to stop midday from multiple triggering
SETTICK 4400, HourChime, 2 'Using tick 2 as hourly chime delay
IF HourFlag = 1 THEN
PULSE middayPin, 150 'Play the midday chime
ELSE
HourChime 'Play first hourly chime
ENDIF
ENDIF

'Play the hourly chime stop when all done
SUB HourChime
HourFlag = HourFlag - 1 'count them down
IF HourFlag = 0 THEN 'If we just played the last one needed
SETTICK 0,,2 'then reset the tick we were using
ELSE
PULSE hourPin, 150 'Play the hourly chime
ENDIF
ENDSUB

Edited by Azure 2018-01-06
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 12:50pm 05 Jan 2018
Copy link to clipboard 
Print this post

Thanks Azure, that's much neater
However I used 24 hour clock to enable easily "not playing" the chimes after 10PM
but that isn't important
I tried your code and used the following
[code]SETPIN 97, DOUT '1/4 hour chime
SETPIN 96, DOUT '2/4 hour chime
SETPIN 95, DOUT '3/4 hour chime
SETPIN 92, DOUT '4/4 hour chime
SETPIN 91, DOUT 'Hour chime
SETPIN 90, DOUT 'Long Hour chime
CONST middayPin = PIN(90)
CONST hourPin = PIN(91)
PIN(97) = 1 'Chime off
PIN(96) = 1 'Chime off
PIN(95) = 1 'Chime off
PIN(92) = 1 'Chime off
PIN(91) = 1 'Chime off
PIN(90) = 1 'Chime off
Hours = Val(left$(Time$,2))
Minutes = Val(Mid$(Time$,4,2))
Seconds = Val(right$(Time$,2))

DO
IF Val(Mid$(Time$,4,2)) = 00 and Val(right$(Time$,2)) = 00 then
PULSE PIN(96), 150 '1/4 hr Chime on
print "Chime 1 ON"
endif

IF (Minutes = 0) AND (Seconds = 0) AND (HourFlag = 0) THEN
HourFlag = (Hours mod 12) + 1 '+1 used to stop midday from multiple triggering
SETTICK 4400, HourChime, 2 'Using tick 2 as hourly chime delay
IF HourFlag = 1 THEN
PULSE middayPin, 150 'Play the midday chime
print "Midday Pin"
ELSE
HourChime 'Play first hourly chime
ENDIF
ENDIF

LOOP
END

SUB HourChime
HourFlag = HourFlag - 1 'count them down
IF HourFlag = 0 THEN 'If we just played the last one needed
SETTICK 0,,2 'then reset the tick we were using
ELSE
PULSE hourPin, 150 'Play the hourly chime
print "Chime on"
ENDIF
END SUB[/code]

It prints the chime on the display when the 1/4 hour arrives
but
PULSE PIN(96), 150 does not turn the sound board on, it doesn't seem to be pulling the pin low which is what's needed

IF I manually pull the pin low using a wire to ground then the board plays the correct chime

Also the hour function isn't working at the moment, I'll go back to that after I figure out the 1/4ly chimes

I can't find PULSE in either of the manuals?


EDIT:
Got the Quarters working by using this code
[code]
IF Val(Mid$(Time$,4,2)) > = 07 and IF Val(Mid$(Time$,4,2)) < = 22 THEN
IF Val(Mid$(Time$,4,2)) = 00 and Val(right$(Time$,2)) = 00 then
SETTICK 150, SecsChime, 2 'Using tick 2 as hourly chime delay
PIN(92) = 0
endif
IF Val(Mid$(Time$,4,2)) = 15 and Val(right$(Time$,2)) = 00 then
SETTICK 150, SecsChime, 2 'Using tick 2 as hourly chime delay
PIN(97) = 0
endif

IF Val(Mid$(Time$,4,2)) = 30 and Val(right$(Time$,2)) = 00 then
SETTICK 150, SecsChime, 2 'Using tick 2 as hourly chime delay
PIN(96) = 0
endif

IF Val(Mid$(Time$,4,2)) = 45 and Val(right$(Time$,2)) = 00 then
SETTICK 150, SecsChime, 2 'Using tick 2 as hourly chime delay
PIN(95) = 0
endif

SUB SecsChime
PIN(97) = 1
PIN(96) = 1
PIN(95) = 1
PIN(92) = 1
SETTICK 0,,2
END SUB[/code]Edited by lew247 2018-01-06
 
Azure

Guru

Joined: 09/11/2017
Location: Australia
Posts: 446
Posted: 02:42pm 05 Jan 2018
Copy link to clipboard 
Print this post

Pulse is on page 58 of the MicroMite MMBASIC User Manual.

I not sure why you changed the CONST and PULSE syntax I typed, it is now incorrect.

Here are the corrected lines that need to be changed:

CONST middayPin = 90
CONST hourPin = 91

'Remove the Hours, Minutes and Seconds lines at the top

PULSE 96, 150

IF (Val(Mid$(Time$,4,2)) = 0) AND (Val(right$(Time$,2)) = 0) AND (HourFlag = 0) THEN


Also the first line of your quarters code above has 2 IF on the first line.
The second one should not be there.

The chime silent time can be added using another IF once it is all working.
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 05:36pm 05 Jan 2018
Copy link to clipboard 
Print this post

I found it thanks
You can also create a pulse using the PULSE command. This will generate very narrow pulses (eg, 20µs) or
long pulses up to several days. Long pulses are run in the background and the program will continue
uninterrupted

trying it now Edited by lew247 2018-01-07
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 11:53am 14 Mar 2018
Copy link to clipboard 
Print this post

I'm stuck again
I want to update the Micromite clock with the time coming from the internet via an ESP8266
I have the code for the ESP and updating the clock working
I'm stuck on how to do it

I'd like to update the clock the FIRST time that COM2 receives data but not any of the other times

I have [code]TIME$ = tim2$ 'Update Micromite clock[/code] which works and tim2$ is the correct time from the ESP8266

How do I run this just once the first time COM2 gets data and not every time it does?
The problem being the Micromite might not get the data from the ESP8266 for up to 20 seconds after it boots up and then transmits the data every 15 seconds
I don't want the clock updating so often ***unless it won't do any harm***
 
lew247

Guru

Joined: 23/12/2015
Location: United Kingdom
Posts: 1702
Posted: 03:01pm 14 Mar 2018
Copy link to clipboard 
Print this post

Alternatively
Does anyone know why this only works SOMETIMES?

tim$ = FieldArray$(18) 'time from ESP2866/ntp SERVER
tim2$ = mid$(tim$,12,8) 'Curent time ready to send to Micromite or RTC
print TIME2$
TIME$ = tim2$ [/CODE}

Some of the time it works
other times randomly it comes up with the error
[quote][187] Time$ = tim2$ 'update Micromite clock
Error: Syntax[/quote]

The string received from the ESP866 is

7.23,04d,10.19,997,5.1,130,62,8.35,11,96,10000,broken clouds,Clouds,987.64,1.72,7.11,8.39,Rain,Wed Mar 14 14:55:47 2018 ,*

 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9493
Posted: 09:12pm 14 Mar 2018
Copy link to clipboard 
Print this post

Syntax error because the string you are trying to ram down the throat of TIME$ is not in the format that MMBASIC wants.

Your example is taking the 8 characters in tim$ from the LEFT of the string, so you are trying to pass ".19,997," to TIME$, and it throws a wobbly.

Try this:

TIME$=MID$(tim2$,LEN(tim2$)-15,8)

That should extract the 8 bytes of the time from tim2$, and that SHOULD be in the format that TIME$ wants. Assuming that the time and date are ALWAYS on the end of the string and in the same format, then it won't matter what length the string is, as the above will always correctly extract the data from the end of the string.

To check this ONCE only, then use a flag along the lines of:


IF TIMEFLAG=0 THEN
TIME$=MID$(tim2$,LEN(tim2$)-15,8)
TIMEFLAG=1
ENDIF


When this runs, it will check the flag, and if it is clear, it will extract the time and pass it to the system clock. If the flag IS set, it will ignore that code(IE: not set the clock) and continue with the next line of code.

Untested, but should work.

Edited by Grogster 2018-03-16
Smoke makes things work. When the smoke gets out, it stops!
 
     Page 20 of 25    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025