Peter or others,
Where is the best info on how Tachyon works?
I will have some time on my flight home so thought I would do a little reading on both forth and tachyon.
I have run Tachyon binary a while ago but I didn't do much with it. So much work has been done on Tachyon that it deserves a closer look!
You have so much experience with PASM etc. I'd recommend just have a look at the commented source.
TAchyon.spin + Extend.fth.
Peter gave lot's of explanations so far - unfortunately they are scattered throughout this thread and some others as well. And of course there is the getting started document all from the links in his footer.
go to his dropbox (see his sig) and just download ALL of it
then load ALL into a good editor
then SEARCH is your friend for cross referencing ;-)
I found the Intro but haven't found the links to Tachyon.spin and Extend.fth
Based on Tachyon+Extend (includes PING)
This could be done just in the ESP alone, but I want to extend this with other sensors
and functions later. And even if NodeMCU/Lua is easy to use, it has it's complications,
and Tachyon is so much more natural to me - after some learning curve ;-).
Tachyon Code
{ read water level with PING and transmit to ThingSpeak.com via ESP8266+NodeMCU/Lua }
#P10 == PING_TRIG
#P11 == PING_ECHO
#9600 SERBAUD for ESP8266 (could be higher)
pub SEROUT ( dataByte pin)
0 == serpin pin for Tx to ESP8266
serpin .
pri SEREMIT serpin SEROUT ;
pub SER ' SEREMIT uemit W! ;
pub WATERLEVEL
BEGIN
ESC? NOT
WHILE
0
100 FOR
PING_TRIG PING_ECHO DISTANCE +
NEXT
with placeholders for other data later
SER ." CR $A EMIT sendData(" . ." ,170000,120000)" CR $A EMIT CON
REPEAT ;
WATERLEVEL AUTORUN
BACKUP
ESP8266 / NodeMCU Lua Code
File: init.lua
--init.lua
-- checking pin GPIO14 = (GPIOID = 5)for GND connection
-- if GND then stop script
gpio.mode(5,gpio.INPUT,gpio.PULLUP)
if gpio.read(5) == 1 then
print("now is time for tmr.stop(0) ")
--[[
tmr.stop(0)
]]
cb = function()
print("Setting up WIFI...")
wifi.setmode(wifi.STATION)
--modify according your wireless router settings
wifi.sta.config("Iberbanda-ebac","Who am I")
wifi.sta.connect()
tmr.alarm(1, 1000, 1, function()
if wifi.sta.getip()== nil then
print("IP unavaiable, Waiting...")
else
tmr.stop(1)
print("Config done, IP is "..wifi.sta.getip())
dofile("Deposito_01.lua")
end
end)
end
cb()
tmr.alarm(0,1000000,1,cb())
end
File: Deposito_01.lua
--[[
send deposito level and later also flow and temperature to thingspeak
data is sent via serial from Propeller
]]--
temps = {}
--- Get temp and send data to thingspeak.com
function sendData(level, flow, temp)
local req
local t1, t2
temps[1] = level
temps[2] = flow
temps[3] = temp
print(temps[1],temps[2],temps[3])
-- print("Temp:"..t1 .. "."..string.format("%04d", t2).." C
")
-- conection to thingspeak.com
-- print("Sending data to thingspeak.com")
tmr.wdclr()
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
-- api.thingspeak.com 184.106.153.149
conn:connect(80,'184.106.153.149')
tmr.wdclr()
print(node.heap())
req = "GET /update?key=----channel-key
"
-- assemble the message
for i, d in pairs(temps) do
tmr.wdclr()
t1 = d / 10000
t2 = (d >= 0 and d % 10000) or (10000 - d % 10000)
req = req .. "&field" ..i .."="..t1.."."..string.format("%04d", t2)
end
Only way to tell what the problem is is if you also supply the link you are using etc as I tested the link I gave against the Prop tool and it worked. Did you check with the link I gave?
Has anybody already a MEDIAN or SORT for Tachyon.
I want to filter some data (length 10-20)
thanks MJB
I believe that the best person for this task is a resourceful German who has access to the data to be sorted
If you give it a shot and post what you've got then maybe we can come up with a more refined an elegant solution.
Ok, here s.th. to refine
\ optimized BYTE variant TACHYON style
\ - optimized for code size an simplicity, not for runtime speed,
\ fine for small datasets
: SORTB ( bytearray len -- ) \ array address, len of array
2DUP + ROT ROT
\ 1+ 2/ \ for median we would only need to sort half the array
1- \ for SORT only need to sort n-1 times - last is correct automatically
ADO DUP I 1+ DO I C@ J C@ < \ < sorts ascending
IF I C@ J C@ I C! J C! THEN
LOOP LOOP DROP
;
pub MEDIANB ( bytearray len -- median )
2DUP 2/ + ROT ROT SORTB C@ ;
TABLE 9bytes 13 | 17 | 4 | 7 | 1 | 26 | 45 | 56 | 2 |
9bytes 9 LAP MEDIANB LAP .LAP . \ 706.000us 13 ok
\ 9bytes #10 DUMP
I have the following code:
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO
9 SERIN ' SERBUF I + C! DROP
LOOP ;
pub SerMon ( -- ) \ Continuously read the serial port.
BEGIN GetData 0 UNTIL ;
If I just run GetData, the array populates correctly. When I use the following:
' SerMon TASK? RUN \ Set SerMon to run in background on its own cog.
the array gets corrupted (perhaps overwritten) and I can't figure out why!
The idea is to have a cog continuously monitoring pin 9 for serial data, which occurs at roughly 5 minute intervals. When the data is received, it is placed into the SERBUF array, so another cog can parse and display it.
Just on my phone at the moment but i can see a tick before SERBUF which is not what you want as that returns the address of the code rather than the buffer. So drop the tick and just use serbuf as that returns the address of the buffer.
EDIT: back on the pc but the other thing to watch out for is the data stack as each cog only maintains a 4 level deep stack in cog memory and if you need more then you should create an array (i.e. 8 LONGS mystk) and set the stack pointer at the beginning of the task (i.e. mystk SP!). The other thing to allow register memory for is if your task needs it's own Forth input and output buffers such as when reading a word from the input stream or printing a number etc. So if you need to do this then create an array of say 128 BYTES myregs and also set these up with myregs 7 COGREG! but in this case you probably don't need to do either.
Note: Whenever you use the BYTES or WORDS or LONGS to create an array it is always long aligned.
@FORUM: Where's the source/preview toggle button? I only have source view now. Testing code tags now:
' Registers used by PASM modules to hold parameters such as I/O masks and bit counts etc
REG0 long 0
REG1 long 0
REG2 long 0
REG3 long 0
REG4 long 0
txticks long (sysfreq / baud ) ' set transmit baud rate
txmask long |<txd ' change mask to reassign transmit
' COGREG 7 = TASK REGISTER POINTER
regptr long @registers+s ' used by REG
So when I do " ' SERBUF ", I do get the address of the first byte location of the array, which is what I want (I think). Your point about the stack is well taken and could very well be throwing things off when GetData is running in the background on another cog.
'tis late here, so I'll give these suggestions a try tomorrow and see how things progress!
Use 60 BYTES SERBUF rather than the manual method as besides being easier to read and know that it's a byte array, it also initializes the array, as well as aligning it.
This is how your method looks:
The other way to create arrays that are not inline with code is to use the ORG and DS words. For instance you could have done this:
BUFFERS ORG
60 DS SERBUF
The ORG sets a DS pointer to BUFFERS ($7300) and 60 DS SERBUF creates a constant with the current DS pointer then advances the DS pointer by 60 for the next DS word.
BUFFERS ORG ok
60 DS SERBUF ok
' SERBUF QD
0000_474B: 7E 00 73 00 00 71 47 4B BE BF 0C 0C 00 00 00 00 ~.s..qGK........
0000_475B: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ ok
HERE .WORD 4750 ok
SERBUF QD
0000_7300: F0 49 BF A0 A4 EB BF 00 00 EB FF 68 11 01 7C 5C .I.........h..|\
0000_7310: AE 4F BF 00 01 4E FF 84 7E 4E 7F 85 01 5C F3 80 .O...N..~N...\.. ok
Creating arrays and variables this way allows for the data to be lumped together in one area that may be useful for reading into say from a partition table for instance as all the data fields are contiguous it is possible to deal with individual elements or a range of differing elements.
Is there only Peter here sharing thoughts and some code ideas ?
Anybody built a state machine framework for TACHYON?
Yes - Peter, I could write one ;-)
Your gate-controller gives some inspiration
and your LOOKUP / VECTORS words can help.
A table with adresses of the action words to execute might be a building-block as well.
But exchange and sharing is big part of the fun here -
so I'd like to encourage everybody to increase the fun level here ;-)
5 == speed-i \ Wind speed index
9 == dir-i \ Wind direction index
#13 == temp-i \ Temperature index
#17 == Lrain-i \ Longterm Rainfall index
#41 == date-i \ Day of year index
#45 == tod-i \ Time of day index
#49 == rain-I \ Today's rain index
#P9 == WX-RXD \ Incoming data pin.
8 LONGS mystk
pub InitStack
mystk SP! ;
#2400 SERBAUD \ Ultimeter comm set to 2400 baud.
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO
9 SERIN ' SERBUF I + C! DROP
LOOP ;
pub SerMon ( -- ) \ Continuously read the serial port.
InitStack BEGIN GetData 0 UNTIL ;
\ ' SerMon TASK? RUN \ Set SerMon to run in background on its own cog.
: SERBUF@ ' SERBUF CR 57 DUMP ;
pub ASC2HEX ( char -- hexchar ) \ Convert ASCII hex byte to hex.
DUP $3A <
IF $30 -
ELSE $37 -
THEN ;
pub ConvField ( idx -- decvalue ) \ Index into the serial buffer and convert a field to decimal.
' SERBUF + DUP \ Point to 1st char in field.
C@ ASC2HEX \ Get/convert char to hex.
$1000 * \ Put char into 1st hex posn.
SWAP 1+ \ point to 2nd char in field.
DUP C@ ASC2HEX \ Get/convert char to hex.
$100 * ROT + \ Put char to 2nd hex posn & add to previous value
SWAP 1+ \ Point to 3rd char in field.
DUP C@ ASC2HEX \ Get/convert char to hex
$10 * ROT + \ Put char to 3rd hex posn & add to previous value.
SWAP 1+ \ Point to 4th char in field.
C@ ASC2HEX \ Get/convert char to hex.
+ \ hex value now TOS.
;
pub rose0 0 LOW 1 LOW 2 LOW ;
pub rose1 0 HIGH 1 LOW 2 LOW ;
pub rose2 0 LOW 1 HIGH 2 LOW ;
pub rose3 0 HIGH 1 HIGH 2 LOW ;
pub rose4 0 LOW 1 LOW 2 HIGH ;
pub rose5 0 HIGH 1 LOW 2 HIGH ;
pub rose6 0 LOW 1 HIGH 2 HIGH ;
pub rose7 0 HIGH 1 HIGH 2 HIGH ;
pub W-DIR ( n --- ) \ Light LED n on compass rose.
SWITCH
1 45 SWITCH>< IF rose1 BREAK
46 90 SWITCH>< IF rose2 BREAK
91 135 SWITCH>< IF rose3 BREAK
136 180 SWITCH>< IF rose4 BREAK
181 225 SWITCH>< IF rose5 BREAK
226 270 SWITCH>< IF rose6 BREAK
271 315 SWITCH>< IF rose7 BREAK
316 360 SWITCH>< IF rose0 BREAK
0 0 SWITCH>< IF rose0 BREAK ;
5 == speed-i \ Wind speed index
9 == dir-i \ Wind direction index
#13 == temp-i \ Temperature index
#17 == Lrain-i \ Longterm Rainfall index
#41 == date-i \ Day of year index
#45 == tod-i \ Time of day index
#49 == rain-I \ Today's rain index
#P9 == WX-RXD \ Incoming data pin.
8 LONGS mystk
pub InitStack
mystk SP! ;
#2400 SERBAUD \ Ultimeter comm set to 2400 baud.
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO
9 SERIN ' SERBUF I + C! DROP
LOOP ;
pub SerMon ( -- ) \ Continuously read the serial port.
InitStack #2400 SERBAUD
BEGIN GetData 0 UNTIL ;
\ ' SerMon TASK? RUN \ Set SerMon to run in background on its own cog.
: SERBUF@ ' SERBUF CR 57 DUMP ;
pub ASC2HEX ( char -- hexchar ) \ Convert ASCII hex byte to hex.
DUP $3A <
IF $30 -
ELSE $37 -
THEN ;
pub ConvField ( idx -- decvalue ) \ Index into the serial buffer and convert a field to decimal.
' SERBUF + DUP \ Point to 1st char in field.
C@ ASC2HEX \ Get/convert char to hex.
$1000 * \ Put char into 1st hex posn.
SWAP 1+ \ point to 2nd char in field.
DUP C@ ASC2HEX \ Get/convert char to hex.
$100 * ROT + \ Put char to 2nd hex posn & add to previous value
SWAP 1+ \ Point to 3rd char in field.
DUP C@ ASC2HEX \ Get/convert char to hex
$10 * ROT + \ Put char to 3rd hex posn & add to previous value.
SWAP 1+ \ Point to 4th char in field.
C@ ASC2HEX \ Get/convert char to hex.
+ \ hex value now TOS.
;
pub rose0 0 LOW 1 LOW 2 LOW ;
pub rose1 0 HIGH 1 LOW 2 LOW ;
pub rose2 0 LOW 1 HIGH 2 LOW ;
pub rose3 0 HIGH 1 HIGH 2 LOW ;
pub rose4 0 LOW 1 LOW 2 HIGH ;
pub rose5 0 HIGH 1 LOW 2 HIGH ;
pub rose6 0 LOW 1 HIGH 2 HIGH ;
pub rose7 0 HIGH 1 HIGH 2 HIGH ;
pub W-DIR ( n --- ) \ Light LED n on compass rose.
SWITCH
1 45 SWITCH>< IF rose1 BREAK
46 90 SWITCH>< IF rose2 BREAK
91 135 SWITCH>< IF rose3 BREAK
136 180 SWITCH>< IF rose4 BREAK
181 225 SWITCH>< IF rose5 BREAK
226 270 SWITCH>< IF rose6 BREAK
271 315 SWITCH>< IF rose7 BREAK
316 360 SWITCH>< IF rose0 BREAK
0 0 SWITCH>< IF rose0 BREAK ;
you need to run SERBAUD from within the task code,
because it sets a COG local register with the timing data.
So your new TASK in the seperate COG does NOT get it's BAUDRATE set correctly.
If you give a complete example of your received DATA string,
then we might find a much more FORTH-like way to parse it.
Below is an example of the string received from the WX station. The header is "$ULTW" and every four values that follow are individual fields of data that is to be parsed. An example is the first two fields here indicate 0000 MPH for wind speed and 0000 for a compass degree value (North, in this case). The dashes are fields not currently supported by my current WX station and hence, are ignored.
" $ULTW0001234002E" 20 STRING data
\ data PRINT$ $ULTW0001234002E
pub ASC2HEX ( char -- hexchar ) \ Convert ASCII hex byte to hex.
DUP $3A <
IF $30 -
ELSE $37 -
THEN ;
pub ConvField ( idx -- decvalue ) \ Index into the serial buffer and convert a field to decimal.
data + \ Point to 1st char in field.
0 4 FOR
SWAP C@++ ASC2HEX \ Get/convert char to hex.
ROT 4 SHL OR \ Put char into 1st hex posn.
NEXT NIP
;
8 ConvField .S CR . CR
12 ConvField .S CR . CR
@K6MLE: you can simplify the whole rose and direction thing to a single line:
pub W-DIR ( n --- ) \ Light LED n on compass rose.
1- 45 / 1+ 7 AND 7 OUTCLR OUTSET ;
As you can see there is no testing of ranges and vectoring off to a special routine, just simple maths to generate 0..7 and writing that patten directly to P0..P2.
ASC2HEX can be simplified a little too:
pub ASC2HEX ( char -- hexchar ) \ Convert ASCII hex byte to hex.
$30 - DUP 9 > IF 7 - THEN ;
Use shifts instead of trying to multiply as an 4 << (or SHL) is equivalent to 16 * just as MJB did in ConvField using a FOR NEXT loop.
also remember that SERBUF returns the address of the buffer so your dump word (I favor very short words for debug use) should not use a tick before it and you only use the tick in very special cases anyway:
\ dump serial buffer
: DB SERBUF 64 DUMP ;
The main loop looks like this:
pub SerMon ( -- ) \ Continuously read the serial port.
!SP mystk SP! 2400 SERBAUD BEGIN GetData AGAIN ;
Rather than creating InitStack which only ever gets called once and only contains two words it's better to just integrate that into the start of the main loop plus I did a !SP just to make sure it was reset.
You can use AGAIN rather than a 0 UNTIL which I think is only a PropForth thing. You have BEGIN UNTIL or BEGIN AGAIN or BEGIN WHILE REPEAT structures.
Then again the whole thing can be simplified a whole lot further too but I will show you this later when you have had time to absorb this.
EDIT: I have pretty much written a version that runs in the background and reads and extracts the information from the serial input and formats the information on the VGA screen. The serial receive buffering and display task are combined and preprocesses the raw data so that the string:
$ULTW0000000002E00F76----000086A00001----00DA048D00000000
becomes:
ULTW 0000 0000 02E0 0F76 ---- 0000 86A0 0001 ---- 00DA 048D 0000 0000
and this string is evaluated just like any other serial console input so that we end up with 13 (inc 2 dummies) parameters on the stack and the ULTW sets a deferred action at the end of the line when the parameters are sitting on the stack. These are then saved into variables (for external use) and displayed on the VGA monitor etc.
pub SerMon ( -- ) \ Continuously read the serial port.
!SP mystk SP! 2400 SERBAUD BEGIN GetData AGAIN ;
Rather than creating InitStack which only ever gets called once and only contains two words it's better to just integrate that into the start of the main loop plus I did a !SP just to make sure it was reset.
@K6MLE: Have a look at your stack usage - I think that basic 4 level stack is sufficient. No need to create an extra one.
Just fill your stack with some data
\ in main COG run ...
8 LONGS mystk
mystk BL ADO I 2/ 2/ I ! LOOP
mystk 30 DUMP
\ run GetData in it'S own COG - then check stack again
mystk 30 DUMP
If I use:
64 BYTES SERBUF
as the SERBUF definition,
The word 'GetData', which is defined as such:
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO 9 SERIN SERBUF I + C! DROP LOOP ;
is meant to grab the incoming serial data and begin filling SERBUF. Using SERBUF@, which is defined as:
: SERBUF@ SERBUF CR 57 DUMP ;
However, when GetData (running in another cog) runs, the first two bytes are 0D0A, rather than 2455, which is the "$U" part of the "$ULTW" header. All the rest of the field of incoming data appear to come in correctly, but this has me baffled.
Update ...
Problems with serial data not appearing where it should seems to only happen when running the GetData routine in its own cog. If I run everything in the same cog, this data problem doesn't occur. Could there be some sort of stack problem? Not sure what to look for.
Comments
Where is the best info on how Tachyon works?
I will have some time on my flight home so thought I would do a little reading on both forth and tachyon.
I have run Tachyon binary a while ago but I didn't do much with it. So much work has been done on Tachyon that it deserves a closer look!
You have so much experience with PASM etc. I'd recommend just have a look at the commented source.
TAchyon.spin + Extend.fth.
Peter gave lot's of explanations so far - unfortunately they are scattered throughout this thread and some others as well. And of course there is the getting started document all from the links in his footer.
Is it a tablet/phone thing?
then load ALL into a good editor
then SEARCH is your friend for cross referencing ;-)
I found the Intro but haven't found the links to Tachyon.spin and Extend.fth
https://www.dropbox.com/sh/yzrczasnorqp5i9/AAAFn8MPpVN4jsSQ74mqNBUDa
https://thingspeak.com/channels/39235#publicview
Based on Tachyon+Extend (includes PING)
This could be done just in the ESP alone, but I want to extend this with other sensors
and functions later. And even if NodeMCU/Lua is easy to use, it has it's complications,
and Tachyon is so much more natural to me - after some learning curve ;-).
Tachyon Code
{ read water level with PING and transmit to ThingSpeak.com via ESP8266+NodeMCU/Lua }
#P10 == PING_TRIG
#P11 == PING_ECHO
#9600 SERBAUD for ESP8266 (could be higher)
pub SEROUT ( dataByte pin)
0 == serpin pin for Tx to ESP8266
serpin .
pri SEREMIT serpin SEROUT ;
pub SER ' SEREMIT uemit W! ;
pub WATERLEVEL
BEGIN
ESC? NOT
WHILE
0
100 FOR
PING_TRIG PING_ECHO DISTANCE +
NEXT
with placeholders for other data later
SER ." CR $A EMIT sendData(" . ." ,170000,120000)" CR $A EMIT CON
REPEAT ;
WATERLEVEL AUTORUN
BACKUP
ESP8266 / NodeMCU Lua Code
File: init.lua
--init.lua
-- checking pin GPIO14 = (GPIOID = 5)for GND connection
-- if GND then stop script
gpio.mode(5,gpio.INPUT,gpio.PULLUP)
if gpio.read(5) == 1 then
print("now is time for tmr.stop(0) ")
--[[
tmr.stop(0)
]]
cb = function()
print("Setting up WIFI...")
wifi.setmode(wifi.STATION)
--modify according your wireless router settings
wifi.sta.config("Iberbanda-ebac","Who am I")
wifi.sta.connect()
tmr.alarm(1, 1000, 1, function()
if wifi.sta.getip()== nil then
print("IP unavaiable, Waiting...")
else
tmr.stop(1)
print("Config done, IP is "..wifi.sta.getip())
dofile("Deposito_01.lua")
end
end)
end
cb()
tmr.alarm(0,1000000,1,cb())
end
File: Deposito_01.lua
--[[
send deposito level and later also flow and temperature to thingspeak
data is sent via serial from Propeller
]]--
temps = {}
--- Get temp and send data to thingspeak.com
function sendData(level, flow, temp)
local req
local t1, t2
temps[1] = level
temps[2] = flow
temps[3] = temp
print(temps[1],temps[2],temps[3])
-- print("Temp:"..t1 .. "."..string.format("%04d", t2).." C
")
-- conection to thingspeak.com
-- print("Sending data to thingspeak.com")
tmr.wdclr()
conn=net.createConnection(net.TCP, 0)
conn:on("receive", function(conn, payload) print(payload) end)
-- api.thingspeak.com 184.106.153.149
conn:connect(80,'184.106.153.149')
tmr.wdclr()
print(node.heap())
req = "GET /update?key=----channel-key
"
-- assemble the message
for i, d in pairs(temps) do
tmr.wdclr()
t1 = d / 10000
t2 = (d >= 0 and d % 10000) or (10000 - d % 10000)
req = req .. "&field" ..i .."="..t1.."."..string.format("%04d", t2)
end
print(req)
tmr.wdclr()
conn:on("sent",function(conn)
print("Closing connection")
conn:close()
end)
conn:on("disconnection", function(conn)
print("Got disconnection...")
end)
conn:send(req .."
")
end
I cannot open (Prop Tool) Tachyon V2.7.spin - " no PUB found"
Siri
I cannot open (Prop Tool) Tachyon V2.7.spin - " no PUB found"
Siri
The dropbox file works in BST and Prop tool
I should have given the correct info,I amm sorry.
The problem is when I try ot get the info to Load RAM or EEPROM.
At first get Expected " } " when I fix that I get " No PUB routine found "
Siri
I have a drop box account and I down- loaded from your drop box.I also down loaded Tachyon_ V 2.4 from
the dropbox and it works well.
Siri
You are right ,your program works.My desk top's USB port is broken and Tachyon loaded properly on my lap top.
Sorry for the inconvienance.
Siri
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO
9 SERIN ' SERBUF I + C! DROP
LOOP ;
pub SerMon ( -- ) \ Continuously read the serial port.
BEGIN GetData 0 UNTIL ;
If I just run GetData, the array populates correctly. When I use the following:
' SerMon TASK? RUN \ Set SerMon to run in background on its own cog.
the array gets corrupted (perhaps overwritten) and I can't figure out why!
The idea is to have a cog continuously monitoring pin 9 for serial data, which occurs at roughly 5 minute intervals. When the data is received, it is placed into the SERBUF array, so another cog can parse and display it.
Any help is greatly appreciated!!
Thanks,
Michael
K6MLE
EDIT: back on the pc but the other thing to watch out for is the data stack as each cog only maintains a 4 level deep stack in cog memory and if you need more then you should create an array (i.e. 8 LONGS mystk) and set the stack pointer at the beginning of the task (i.e. mystk SP!). The other thing to allow register memory for is if your task needs it's own Forth input and output buffers such as when reading a word from the input stream or printing a number etc. So if you need to do this then create an array of say 128 BYTES myregs and also set these up with myregs 7 COGREG! but in this case you probably don't need to do either.
Note: Whenever you use the BYTES or WORDS or LONGS to create an array it is always long aligned.
@FORUM: Where's the source/preview toggle button? I only have source view now. Testing code tags now:
SERBUF is defined as such:
CREATE SERBUF 60 ALLOT \ Serial input buffer
So when I do " ' SERBUF ", I do get the address of the first byte location of the array, which is what I want (I think). Your point about the stack is well taken and could very well be throwing things off when GetData is running in the background on another cog.
'tis late here, so I'll give these suggestions a try tomorrow and see how things progress!
Regards,
Michael
K6MLE
This is how your method looks:
But this works the same but it also cleaner:
The other way to create arrays that are not inline with code is to use the ORG and DS words. For instance you could have done this:
BUFFERS ORG
60 DS SERBUF
The ORG sets a DS pointer to BUFFERS ($7300) and 60 DS SERBUF creates a constant with the current DS pointer then advances the DS pointer by 60 for the next DS word.
Creating arrays and variables this way allows for the data to be lumped together in one area that may be useful for reading into say from a partition table for instance as all the data fields are contiguous it is possible to deal with individual elements or a range of differing elements.
Is there only Peter here sharing thoughts and some code ideas ?
Anybody built a state machine framework for TACHYON?
Yes - Peter, I could write one ;-)
Your gate-controller gives some inspiration
and your LOOKUP / VECTORS words can help.
A table with adresses of the action words to execute might be a building-block as well.
But exchange and sharing is big part of the fun here -
so I'd like to encourage everybody to increase the fun level here ;-)
Markus
I'm not quite following the proper management of separate cogs and tasks. Could you expand upon the correct usage of myregs 7 COGREG! ?
Although I've implemented the changes you suggested (that I can understand), the SERBUF array is still getting overwritten somehow.
I've posted the entirety of my code below:
\ FORTH support routines for Ultimeter 100 Wall display using Tachyon FORTH on the Propeller.
\ FORGET ULTIMETER
pub ULTIMETER ;
\ CREATE SERBUF 60 ALLOT \ Serial input buffer
64 BYTES SERBUF
5 == speed-i \ Wind speed index
9 == dir-i \ Wind direction index
#13 == temp-i \ Temperature index
#17 == Lrain-i \ Longterm Rainfall index
#41 == date-i \ Day of year index
#45 == tod-i \ Time of day index
#49 == rain-I \ Today's rain index
#P9 == WX-RXD \ Incoming data pin.
8 LONGS mystk
pub InitStack
mystk SP! ;
#2400 SERBAUD \ Ultimeter comm set to 2400 baud.
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO
9 SERIN ' SERBUF I + C! DROP
LOOP ;
pub SerMon ( -- ) \ Continuously read the serial port.
InitStack BEGIN GetData 0 UNTIL ;
\ ' SerMon TASK? RUN \ Set SerMon to run in background on its own cog.
: SERBUF@ ' SERBUF CR 57 DUMP ;
pub ASC2HEX ( char -- hexchar ) \ Convert ASCII hex byte to hex.
DUP $3A <
IF $30 -
ELSE $37 -
THEN ;
pub ConvField ( idx -- decvalue ) \ Index into the serial buffer and convert a field to decimal.
' SERBUF + DUP \ Point to 1st char in field.
C@ ASC2HEX \ Get/convert char to hex.
$1000 * \ Put char into 1st hex posn.
SWAP 1+ \ point to 2nd char in field.
DUP C@ ASC2HEX \ Get/convert char to hex.
$100 * ROT + \ Put char to 2nd hex posn & add to previous value
SWAP 1+ \ Point to 3rd char in field.
DUP C@ ASC2HEX \ Get/convert char to hex
$10 * ROT + \ Put char to 3rd hex posn & add to previous value.
SWAP 1+ \ Point to 4th char in field.
C@ ASC2HEX \ Get/convert char to hex.
+ \ hex value now TOS.
;
pub rose0 0 LOW 1 LOW 2 LOW ;
pub rose1 0 HIGH 1 LOW 2 LOW ;
pub rose2 0 LOW 1 HIGH 2 LOW ;
pub rose3 0 HIGH 1 HIGH 2 LOW ;
pub rose4 0 LOW 1 LOW 2 HIGH ;
pub rose5 0 HIGH 1 LOW 2 HIGH ;
pub rose6 0 LOW 1 HIGH 2 HIGH ;
pub rose7 0 HIGH 1 HIGH 2 HIGH ;
pub W-DIR ( n --- ) \ Light LED n on compass rose.
SWITCH
1 45 SWITCH>< IF rose1 BREAK
46 90 SWITCH>< IF rose2 BREAK
91 135 SWITCH>< IF rose3 BREAK
136 180 SWITCH>< IF rose4 BREAK
181 225 SWITCH>< IF rose5 BREAK
226 270 SWITCH>< IF rose6 BREAK
271 315 SWITCH>< IF rose7 BREAK
316 360 SWITCH>< IF rose0 BREAK
0 0 SWITCH>< IF rose0 BREAK ;
you need to run SERBAUD from within the task code,
because it sets a COG local register with the timing data.
So your new TASK in the seperate COG does NOT get it's BAUDRATE set correctly.
If you give a complete example of your received DATA string,
then we might find a much more FORTH-like way to parse it.
Below is an example of the string received from the WX station. The header is "$ULTW" and every four values that follow are individual fields of data that is to be parsed. An example is the first two fields here indicate 0000 MPH for wind speed and 0000 for a compass degree value (North, in this case). The dashes are fields not currently supported by my current WX station and hence, are ignored.
0000_3E47: 24 55 4C 54 57 30 30 30 30 30 30 30 30 30 32 45 $ULTW0000000002E
0000_3E57: 30 30 46 37 36 2D 2D 2D 2D 30 30 30 30 38 36 41 00F76----000086A
0000_3E67: 30 30 30 30 31 2D 2D 2D 2D 30 30 44 41 30 34 38 00001----00DA048
0000_3E77: 44 30 30 30 30 30 30 30 30 00 00 00 00 00 00 00 D00000000.......
ASC2HEX can be simplified a little too: Use shifts instead of trying to multiply as an 4 << (or SHL) is equivalent to 16 * just as MJB did in ConvField using a FOR NEXT loop.
also remember that SERBUF returns the address of the buffer so your dump word (I favor very short words for debug use) should not use a tick before it and you only use the tick in very special cases anyway:
The main loop looks like this: Rather than creating InitStack which only ever gets called once and only contains two words it's better to just integrate that into the start of the main loop plus I did a !SP just to make sure it was reset.
You can use AGAIN rather than a 0 UNTIL which I think is only a PropForth thing. You have BEGIN UNTIL or BEGIN AGAIN or BEGIN WHILE REPEAT structures.
Then again the whole thing can be simplified a whole lot further too but I will show you this later when you have had time to absorb this.
EDIT: I have pretty much written a version that runs in the background and reads and extracts the information from the serial input and formats the information on the VGA screen. The serial receive buffering and display task are combined and preprocesses the raw data so that the string:
$ULTW0000000002E00F76----000086A00001----00DA048D00000000
becomes:
ULTW 0000 0000 02E0 0F76 ---- 0000 86A0 0001 ---- 00DA 048D 0000 0000
and this string is evaluated just like any other serial console input so that we end up with 13 (inc 2 dummies) parameters on the stack and the ULTW sets a deferred action at the end of the line when the parameters are sitting on the stack. These are then saved into variables (for external use) and displayed on the VGA monitor etc.
there he is running a serial input in a seperate COG.
Just fill your stack with some data
I cant find your Gate Scheduler example code any more.
@Anybody: I am looking for a Tachyon implementation of a hierarchical menu system
for LCD or terminal / VGA ??
From Brian Riley I found a code to use a rotary quadrature encoder as input device -
a great start for interacting with the menu.
thanks,
Markus
64 BYTES SERBUF
as the SERBUF definition,
The word 'GetData', which is defined as such:
pub GetData ( -- ) \ Read serial port and place WX data into array SERBUF.
#57 0 DO 9 SERIN SERBUF I + C! DROP LOOP ;
is meant to grab the incoming serial data and begin filling SERBUF. Using SERBUF@, which is defined as:
: SERBUF@ SERBUF CR 57 DUMP ;
However, when GetData (running in another cog) runs, the first two bytes are 0D0A, rather than 2455, which is the "$U" part of the "$ULTW" header. All the rest of the field of incoming data appear to come in correctly, but this has me baffled.
Problems with serial data not appearing where it should seems to only happen when running the GetData routine in its own cog. If I run everything in the same cog, this data problem doesn't occur. Could there be some sort of stack problem? Not sure what to look for.