![]() |
Forum Index : Microcontroller and PC projects : Com Port Problems
Page 1 of 5 ![]() ![]() |
|||||
Author | Message | ||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
I'm stuck with a com port problem On COM1 I have data coming in from the outdoor weather board into the Explore 100 Module On COM2 I have an ESP8266 module parsing weather from openweathermap COM 1 is working perfectly and I have no problems with it COM2 is not working Sometimes it shows data, sometimes it doesn't, it just doesnt make sense to me The OUTPUT from the ESP8266 module is this - which is exactly what I want it to do [quote]Today,shower rain,09d[/quote] The output, is day, description and weather icon for the day The idea is this Com2 the output from the ESP8266 will have 4 sets of weather being transmitted, today, tomorrow, day after and day after that The program sorts it by the 1st section received on the COM2 port Today - goes to SUB T1 which parses the data for today and displays it Tomorrow - goes to the SUB T2 which parses the data for tomorrow and displays it Day2 - goes to the SUB T3 which parses the data for that day and displays it DAY 3 - goes to the SUB T4 which parses the data for that day and displays it For the moment I'm only interested in getting the data from COM2 and parsing todays data and displaying it in the SUB T1 Here is the relevant code - for some reason the COM2 section will not work properly I have tried to make COM2 work similar to COM1 but couldn't get it working/ [code] Open "COM1:19200,1024,isr" As #1 'Open port for HC-12 TX/RX OPEN "COM2:9600, 1024" AS #2 'Open port for ESP8266 '*************Main program section************ DO If head<>tail Then ' collect sentence from buffer B$ = CB$(tail) tail = (tail Mod 10)+1 EndIf if B$<>"" THEN SERIAL1 ' if data GOSUB Serial1 B$ = "" END IF IF LOC(#2) > 0 THEN SERIAL2 'If data in Com1 then GOSUB Serial2 LOOP '************END PROGRAM******************* Sub isr ' serial port handler Local I, S$ length 80 S$ = Input$(72,#1) ' grab data into temporary buffer Z$ = Right$(Z$,255-Len(S$))+S$ ' add to end of linear buffer I = Instr(Z$,Chr$(13)+Chr$(10)) ' check for eol marker (CR+LF) If I<>0 Then CB$(head) = Left$(Z$,I-1) ' transfer into circular buffer head = (head Mod 10)+1 If I<254 Then Z$ = Mid$(Z$,I+2) Else Z$="" EndIf End Sub SUB SERIAL1 N = GetFieldArray(B$) IF Left$(B$,4) = "STXG" THEN PRINT "Searching for gps lock" IF Left$(B$,4) = "STXT" THEN TIME1 IF Left$(B$,4) = "STXW" THEN WEATHER END SUB SUB WEATHER LOCAL N as INTEGER N = GetFieldArray(B$) ctrlval(5)=STR$(humidity,0,1) 'Humidity indoor ctrlval(6)=STR$(VAL(FieldArray$(14)),0,1) 'Humidity outdoor ctrlval(2)=STR$(temp,0,1) 'temp indoor ctrlval(3)=STR$(VAL(FieldArray$(1)),0,1) 'temp outdoor ctrlval(8)=STR$(VAL(FieldArray$(11)),0,2) 'min temp ctrlval(9)=STR$(VAL(FieldArray$(9)),0,2) 'max temp ctrlval(11)=STR$(VAL(FieldArray$(13)),0,2) 'voltage ctrlval(13)=STR$(VAL(FieldArray$(4)),0,1) 'speed ctrlval(15)=STR$(VAL(FieldArray$(5)),0,1) 'gust ctrlval(17)=FieldArray$(7) 'rain ctrlval(20)=FieldArray$(2) 'pressure Heading=VAL(FieldArray$(3)) 'Wind Direction drawpointer(heading) 'draw the pointer END SUB SUB SERIAL2 'Com2 ESP8266 Weather Forecast LOCAL N as INTEGER N = GetFieldArray(D$) IF FieldArray$(0) = "Today" THEN T1 'If the 1st array is "Today" then GOSUB T1 IF FieldArray$(0) = "Day2" THEN T2 'If the 1st array is "Day2" then GOSUB T2 'If the 1st array is "Day2" then GOSUB T2 IF FieldArray$(0) = "Day3" THEN T3 'If the 1st array is "Day3" then GOSUB T3 IF FieldArray$(0) = "Day4" THEN T4 'If the 1st array is "Day4" then GOSUB T4 End Sub SUB T1 'T1 Today desc0$ = FieldArray$(1) ICON$ = FieldArray$(2) Select Case ICON$ 'To Display the CORRECT weather icon for today Case "01d" ICON$ = ICON$ + ".ppm" Case "01n" ICON$ = ICON$ + ".ppm" Case "02d" ICON$ = ICON$ + ".ppm" Case "02n" ICON$ = ICON$ + ".ppm" CASE "03d" ICON$ = ICON$ + ".ppm" CASE "03n" ICON$ = ICON$ + ".ppm" Case "04d" ICON$ = ICON$ + ".ppm" Case "04n" ICON$ = ICON$ + ".ppm" Case "09d" ICON$ = ICON$ + ".ppm" CASE "09n" ICON$ = ICON$ + ".ppm" CASE "10d" ICON$ = ICON$ + ".ppm" CASE "10n" ICON$ = ICON$ + ".ppm" CASE "11d" ICON$ = ICON$ + ".ppm" CASE "11n" ICON$ = ICON$ + ".ppm" CASE "13d" ICON$ = ICON$ + ".ppm" CASE "13n" ICON$ = ICON$ + ".ppm" CASE "50d" ICON$ = ICON$ + ".ppm" CASE "50n" ICON$ = ICON$ + ".ppm" Case Else ICON$ = "50d.ppm" End Select print ICON$ " icon printed" pause 20 DisplayPicture ICON$,280,260 'Weather icon pause 50 Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the Day of week pause 100 print "This SHOULD be working" END SUB Function GetFieldArray( Record$, Delimiter$, KeepQuotes ) 'Function to get array from COM port Local Index, Char, InQuote, Count InQuote = 0 Count = 0 FieldArray$(Count) = "" If Delimiter$ = "" Then Delimiter$ = "," For Index = 1 To Len(Record$) Char = Asc(Mid$(Record$, Index, 1)) If Char = 34 Then InQuote = Not InQuote If Not InQuote And Instr(Delimiter$, Chr$(char)) >= 1 Then Count = Count + 1 FieldArray$(Count) = "" Else If Char <> 34 Or KeepQuotes Then FieldArray$(Count) = FieldArray$(Count) + Chr$(char) EndIf EndIf Next GetFieldArray = Count + 1 End Function[/code] Can anyone spot what is wrong, or have suggestions on how to make COM2 work right? |
||||
Phil23 Guru ![]() Joined: 27/03/2016 Location: AustraliaPosts: 1667 |
Don't have an immediate answer, but I'd change it a bit so it doesn't immediately jump into the SERIAL2 sub. [Code] IF LOC(#2) > 0 THEN SERIAL2 'If data in Com1 then GOSUB Serial2[/code] Don't know exactly the length of the string coming from the ESP, but you could have an interrupt set to either jump straight into the Sub, or set a flag to say the Sub needs to be processed. I use this, but I'm looking for 120+ characters in my block of data [Code] OPEN "COM1:9600,,RecComs,120" AS #1 [/code] Then this, with the DO loop to make sure the coms is completely received. [Code]Sub RecComs Local Integer HdrLoc,FtrLoc,BufLen Local String HdrFnd Timer_L=Timer If LOC(#1)>10 then 'If COM1 serial port buffer is NOT empty.... Was 100 DO WHILE BufLen<> LOC(#1) BufLen=LOC(#1) PAUSE 5 LOOP DataStr=INPUT$(LOC(#1),#1) 'then suck everything in the buffer out and stick it in D$.... [/code] May not be the most correct approach, but it's working. Cheers Phil. |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
The length of the string coming from the ESP varies depending which day the weather is for |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6283 |
I can't see where you fill D$ with the data coming in on com2 VK7JH MMedit |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
Whoops I copied one of the older serial2 subs* [quote]SUB SERIAL2 'Com2 ESP8266 Weather Forecast Local D$ 'Get the data in arrays LOCAL N as INTEGER D$ = Input$(254, #2) 'Suck everything in buffer out N = GetFieldArray(D$) IF FieldArray$(0) = "Today" THEN T1 'If the 1st array is "Today" then GOSUB T1 IF FieldArray$(0) = "Day2" THEN T2 'If the 1st array is "Day2" then GOSUB T2 'If the 1st array is "Day2" then GOSUB T2 IF FieldArray$(0) = "Day3" THEN T3 'If the 1st array is "Day3" then GOSUB T3 IF FieldArray$(0) = "Day4" THEN T4 'If the 1st array is "Day4" then GOSUB T4 End Sub[/quote] It still doesn't work I substituted this for the original and it doesn't print - yet I can see the data on the web page (Yes I am printing to the web page as well as the console on the ESP8266) [quote]SUB T1 Print "Got here" END SUB[/quote] |
||||
Phil23 Guru ![]() Joined: 27/03/2016 Location: AustraliaPosts: 1667 |
So can you take a guess at it's minimum length? Then set the com interrupt for that & then make sure the buffer has finished filling once you are in the sub? 10 or 20 for a trigger maybe? |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
This is the maximum output from the ESP8266 when it sends the data for the forecast [code] Day4,Thu Nov 30 11:00:00 2017 ,sky is clear,01d,-1.17,2.8,1014.68,87,4,sky is clear [/code] I thought I had it set to send all the days in one string, but I have it set to send each day as a seperate output from the ESP This is the minimum from the ESP8166 [code]Today,rain,09n[/code] |
||||
Grogster![]() Admin Group ![]() Joined: 31/12/2012 Location: New ZealandPosts: 9610 |
What happens if you change: [Quote=Lew]IF FieldArray$(0) = "Today" THEN T1 'If the 1st array is "Today" then GOSUB T1[/Quote] To: IF FieldArray$(0) = "Today" THEN T1 ENDIF I have had issues calling SUB's as part of a single-line IF/THEN before in the past - I have no idea why that should be, but when I made them multi-line, with the call to the SUB on a line of it's own, MY problems went away - worth a try. Also, remember that even using LOC to detect if there is data in the COM2 buffer, you need to allow TIME for that data to arrive. If you have a line like IF LOC(#2)>0 THEN type command, the code will execute as soon as there is a single byte in the buffer - but that won't be all your message, as it has not arrived yet! ![]() This caught me out in my first experiments with COM port buffers. I use a simple pause, or you can specifiy how many bytes MUST be there before doing anything else, but if the message will vary in length, a simple pause will catch all of it. DO:LOOP UNTIL LOC(#2) 'Wait for some data to arrive in COM2 buffer PAUSE 150 'Allow all of message to arrive in buffer D$=INPUT$(LOC(#2),#2) 'Suck everything available from the buffer [/Code] Smoke makes things work. When the smoke gets out, it stops! |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
I'm really stumped now I have the com port working but it's NOT printing what's coming in correctly The input to the com port FROM the ESP8266 is this [code]Today,few clouds,02d[/code] This is what's printed on the console when I run the micromite [quote]This SHOULD be working 1 Today,few clouds,02d Today,few clouds,02d 02d Today few clouds This SHOULD be working 2 [/quote] The ICON$ "SHOULD" with the following code attach .ppm to the end of the icon number so it can display properly, but it's not. Also the word "Today" is being pulled from somewhere FieldArray(0) but I'm not asking it to print that and have no idea how it's getting in there AND D$ is being printed twice also? This is the relevant code for parsing the input [code] SUB T1 'T1 Today desc0$ = FieldArray$(1) ICON$ = FieldArray$(2) IF ICON$ = "01d" then ICON$ = "01d.ppm" IF ICON$ = "01n" then ICON$ = "01n.ppm" IF ICON$ = "02d" then ICON$ = "02d.ppm" IF ICON$ = "02n" then ICON$ = "02n.ppm" IF ICON$ = "03d" then ICON$ = "03d.ppm" IF ICON$ = "03n" then ICON$ = "03n.ppm" IF ICON$ = "04d" then ICON$ = "04d.ppm" IF ICON$ = "04n" then ICON$ = "04n.ppm" IF ICON$ = "09d" then ICON$ = "09d.ppm" IF ICON$ = "09n" then ICON$ = "09n.ppm" IF ICON$ = "10d" then ICON$ = "10d.ppm" IF ICON$ = "10n" then ICON$ = "10n.ppm" IF ICON$ = "11d" then ICON$ = "11d.ppm" IF ICON$ = "11n" then ICON$ = "11n.ppm" IF ICON$ = "13d" then ICON$ = "13d.ppm" IF ICON$ = "13n" then ICON$ = "13n.ppm" IF ICON$ = "50d" then ICON$ = "50d.ppm" IF ICON$ = "50n" then ICON$ = "50n.ppm" ENDIF print "This SHOULD be working 1" pause 20 print D$ print icon$ PRINT desc0$ 'DisplayPicture ICON$,280,260 'Weather icon pause 50 Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the Day of week pause 100 print "This SHOULD be working 2" END SUB[/code] |
||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Don't know if it is relevant but you have a spurious ENDIF following a lot of single line IF statments |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
Thanks Peter, I thought the "ENDIF" was needed I removed it but I still get [code]This SHOULD be working 1 Today,few clouds,02d Today,few clouds,02d 02d Today few clouds This SHOULD be working 2 [/code] Which is showing D$ repeated twice and "Today" printed FieldArray$(0) when it wasn't told to print that and the ICON$ isn't being appended with ".ppm" it seems to be ignoring the "THEN" part of the statement The D$ being repeated twice isn't causing a problem, its the PRINT ICON$ which is |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
I changed the print statement so it ONLY prints the ICON PRINT ICON$ now results in this [code]02d Today[/code] It "should" print 02d.ppm and NOT have a line feed and then Today |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
Changed the SUB so it's the way it's "meant to be" in the manual [code]SUB T1 'T1 Today desc0$ = FieldArray$(1) ICON$ = FieldArray$(2) IF ICON$ = "01d" then ICON$ = "01d.ppm" ELSEIF ICON$ = "01n" then ICON$ = "01n.ppm" ELSEIF ICON$ = "02d" then ICON$ = "02d.ppm" ELSEIF ICON$ = "02n" then ICON$ = "02n.ppm" ELSEIF ICON$ = "03d" then ICON$ = "03d.ppm" ELSEIF ICON$ = "03n" then ICON$ = "03n.ppm" ELSEIF ICON$ = "04d" then ICON$ = "04d.ppm" ELSEIF ICON$ = "04n" then ICON$ = "04n.ppm" ELSEIF ICON$ = "09d" then ICON$ = "09d.ppm" ELSEIF ICON$ = "09n" then ICON$ = "09n.ppm" ELSEIF ICON$ = "10d" then ICON$ = "10d.ppm" ELSEIF ICON$ = "10n" then ICON$ = "10n.ppm" ELSEIF ICON$ = "11d" then ICON$ = "11d.ppm" ELSEIF ICON$ = "11n" then ICON$ = "11n.ppm" ELSEIF ICON$ = "13d" then ICON$ = "13d.ppm" ELSEIF ICON$ = "13n" then ICON$ = "13n.ppm" ELSEIF ICON$ = "50d" then ICON$ = "50d.ppm" ELSEIF ICON$ = "50n" then ICON$ = "50n.ppm" ENDIF print icon$ 'DisplayPicture ICON$,280,260 'Weather icon Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the weather description END SUB[/code] However I still have the same problem the ICON hasn't got .ppm appended to it and somehow it also prints a line feed and FieldArray$(0) which is "Today" This is what's printed on the console now [code]02d Today[/code] Full code is attached if anyone wants to check it 2017-11-28_205258_Weather-Display.zip |
||||
CaptainBoing![]() Guru ![]() Joined: 07/09/2016 Location: United KingdomPosts: 2170 |
Hello Lew. A couple of things jump out to me. The use of IF...ELSEIF ... will slow your code down - every test has to be performed every time, even if you have made a change to ICON$ earlier on. It was better when you had SELECT ... CASE... as the code will jump to the END SELECT as soon as the associated CASE has completed - the same as if you had a GOTO on the end of every IF/ELSEIF line.. But... I can't see any difference between ICON$ after each test except that it will get ".ppm" added to it, so instead of all the "expensive" IFs or SELECT, why not simply concatenate it at the beginning of T1? :: SUB T1 'T1 Today desc0$ = FieldArray$(1) ICON$ = FieldArray$(2)+".ppm" print icon$ 'DisplayPicture ICON$,280,260 'Weather icon Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the weather description END SUB Am I missing something? This will be *much* faster, is easier to read and saves a load of program space. This is just a suggestion and in no way intended to be contest - it is technique. If you wanted to do other things with each test then you have to go down the SELECT/CASE route. |
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
Brilliant idea CaptainBoing - I hadn't thought of doing it like that The sub works but I still get the wrong data This is the result of Sub TQ [code]> RUN 04d Today.ppm [/code] I changed T1 to this [code]SUB T1 'T1 Today desc0$ = FieldArray$(1) ICON$ = FieldArray$(2)+".ppm" print icon$ 'DisplayPicture ICON$,280,260 'Weather icon Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the weather description Print "This is what the com port is receiving - " PRINT D$ END SUB[/code] This is the result It's appending the .ppm like it's meant to BUT to the wrong item, an item it's not meant to get seeing [code]> RUN 04d Today.ppm This is what the com port is receiving - Today,broken clouds,04d Today,broken clouds,04d [/code] I really can't see Why or How it's printing the data twice This is the main bit that tells the program to jump to SERIAL2 [code]IF LOC(#2) > 0 THEN SERIAL2 'If data in Com1 then GOSUB Serial2[/code] This is the sub that gets the data from COM2 [code]SUB SERIAL2 'Com2 ESP8266 Weather Forecast LOCAL N as INTEGER DO:LOOP UNTIL LOC(#2) 'Wait for some data to arrive in COM2 buffer PAUSE 150 'Allow all of message to arrive in buffer D$=INPUT$(LOC(#2),#2) 'Suck everything available from the buffer N = GetFieldArray(D$) IF FieldArray$(0) = "Today" THEN T1 'If the 1st array is "Today" then GOSUB T1 IF FieldArray$(0) = "Day2" THEN T2 'If the 1st array is "Day2" then GOSUB T2 'If the 1st array is "Day2" then GOSUB T2 IF FieldArray$(0) = "Day3" THEN T3 'If the 1st array is "Day3" then GOSUB T3 IF FieldArray$(0) = "Day4" THEN T4 'If the 1st array is "Day4" then GOSUB T4 END IF End Sub[/code] And this is the function for sorting the FieldArray [code] '***************************Function to get array from COM port******************************* Function GetFieldArray( Record$, Delimiter$, KeepQuotes ) Local Index, Char, InQuote, Count InQuote = 0 Count = 0 FieldArray$(Count) = "" If Delimiter$ = "" Then Delimiter$ = "," For Index = 1 To Len(Record$) Char = Asc(Mid$(Record$, Index, 1)) If Char = 34 Then InQuote = Not InQuote If Not InQuote And Instr(Delimiter$, Chr$(char)) >= 1 Then Count = Count + 1 FieldArray$(Count) = "" Else If Char <> 34 Or KeepQuotes Then FieldArray$(Count) = FieldArray$(Count) + Chr$(char) EndIf EndIf Next GetFieldArray = Count + 1 End Function '***********End of this function[/code] EDIT: I'm pretty sure FieldArray function is working perfectly because I'm using it on COM1 with no problems to parse the outdoor data I'm wondering do I need a seperate function for COM2? |
||||
Azure![]() Guru ![]() Joined: 09/11/2017 Location: AustraliaPosts: 446 |
I think there might be a problem in your GetFieldArray Function. Could be why you can't get the latter part to work. Have you checked if GetFieldArray is processing the string into the FieldArray correctly by doing a test for loop to print each entry created? PRINT "Test listing of FieldArray$()" FOR check% = 0 to numentries PRINT "FieldArray$(" check% "): " FieldArray$(check%) NEXT PRINT "Test listing end" I did not check precendence but it looks like the "if (NOT variable AND test2) then" in GetFieldArray might need another set of parenthesis "if (NOT variable AND (test2)) then". If Not InQuote And Instr(Delimiter$, Chr$(char)) >= 1 Then might need to be: If (Not InQuote) And (Instr(Delimiter$, Chr$(char)) >= 1) Then |
||||
CaptainBoing![]() Guru ![]() Joined: 07/09/2016 Location: United KingdomPosts: 2170 |
|
||||
lew247![]() Guru ![]() Joined: 23/12/2015 Location: United KingdomPosts: 1702 |
[code]'**************************************Today************************************ SUB T1 'T1 Today local integer nn for nn=1 to 3 ' put the size of the array instead of ?? print nn,FieldArray$(nn) Next desc0$ = FieldArray$(1) ICON$ = FieldArray$(2)+".ppm" print icon$ 'DisplayPicture ICON$,280,260 'Weather icon Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the weather description Print "This is what the com port is receiving - " PRINT D$ END SUB '********************************End of Today*************************************************[/code] This is the result [quote] > RUN 1 light intensity shower rain 2 09d Today 3 light intensity shower rain 09d Today.ppm This is what the com port is receiving - Today,light intensity shower rain,09d Today,light intensity shower rain,09d [/quote] IF I change your modification to [code]SUB T1 'T1 Today local integer nn for nn=0 to 2 ' put the size of the array instead of ?? print nn,FieldArray$(nn) Next desc0$ = FieldArray$(1) ICON$ = FieldArray$(2)+".ppm" print icon$ 'DisplayPicture ICON$,280,260 'Weather icon Text 285,310, desc0$ , LM, 2, 1, RGB(BLACK), RGB(WHITE) 'Print the weather description Print "This is what the com port is receiving - " PRINT D$ END SUB [/code] Then I get this result - which is correct there are 3 items, 1 the word Today,2 weather description, and 3 Icon number [quote]> RUN 0 Today 1 light intensity shower rain 2 09d Today 09d Today.ppm This is what the com port is receiving - Today,light intensity shower rain,09d Today,light intensity shower rain,09d [/quote] |
||||
CaptainBoing![]() Guru ![]() Joined: 07/09/2016 Location: United KingdomPosts: 2170 |
so it looks like there is a newline character(s) in the middle of the (2) element data. change this line ICON$ = FieldArray$(2)+".ppm" to read ICON$ = left$(FieldArray$(2),3)+".ppm" and run then code again and post the results here. I still think there might be some problem with the populating of FieldArray$() |
||||
CaptainBoing![]() Guru ![]() Joined: 07/09/2016 Location: United KingdomPosts: 2170 |
I think the data is getting munged... you are expecting: Today,light intensity shower rain,09d Today,light intensity shower rain,09d but if you are not lining the data up right, you could easily get to light intensity shower rain,09d<nl>Today light intensity shower rain,09d<nl>Today with the newline and the word "today" a hangover from the previous line and becoming joined to the (2) element of the next reading. I think it is all down to how the data is coming in from the port. I will take a look at it in a little while (have to do some work ![]() |
||||
Page 1 of 5 ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |