Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 07:11 02 Aug 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 : uMite + Parallax Hackable Badge

     Page 2 of 4    
Author Message
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 01:14pm 10 Dec 2015
Copy link to clipboard 
Print this post

Not sure what is going on. Below should be the working skeleton of a FUNTION, but when I run the program, I am getting "ERROR: Unknown command". I am not having any problems with the SUB and GOSUB code, but no matter what I try with the FUNCTION, I am getting an error. Anybody have any ideas?


Test

END

FUNCTION Test()

END FUNCTION
Edited by dasyar 2015-12-11
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 4044
Posted: 01:38pm 10 Dec 2015
Copy link to clipboard 
Print this post

A FUNCTION must return a value and be used where a value is appropriate.

As I stated before what you put would be a SUB.

John
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 05:23pm 10 Dec 2015
Copy link to clipboard 
Print this post

[code]
A = Test()
END

FUNCTION Test()
Test = 1
END FUNCTION
[/code]

Microblocks. Build with logic.
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 01:30am 11 Dec 2015
Copy link to clipboard 
Print this post

Thanks MicroBlocks. Below is my version of a simple form FUNCTION, I ran it, and it worked as expected. This is just an easy example of how to use a FUNCTION, you can build on that.

For some reason the example, that is in the manual, just was not sinking in. Maybe I am getting to old for this stuff, but I keep trying. Anyway, I hope this helps somebody else that may have had a problem with this concept.

Now back to my UI experiment...


' Test a function
' Turn on/off LED using FUNCTION call

SETPIN 15, DOUT ' Setup PIN 15 for output


PIN(15) = Otest() ' Turn on Pin 15
PAUSE 3000 ' Pause for three seconds
PIN(15) = Ftest() ' Turn off Pin 15

END

FUNCTION Otest()
Otest = 1 ' On
END FUNCTION

FUNCTION Ftest()
Ftest = 0 ' Off
END FUNCTION

 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 02:31am 11 Dec 2015
Copy link to clipboard 
Print this post

This is what I have with my UI program, it compiles, but does not work as expected.

I am now using a function to capture the incoming data. But it seems that inBound$ contents are not responding to my IF statements, in other words, when I type in 'exit' in the terminal program, the program below is not responding. What am I missing here?

The logic, as I see it, the COM2 buffer is capturing the incoming data, working as an interrupt, as it is being keyed in on the terminal program. The DO ... LOOP keeps running the inBUFF$ function, so the data should be up to date for the inbound$ variable.

This sounds logical, but is not working as expected. It seems like a simple substitution for 'INPUT #2, inBUFF$'. I guess I am missing something here.


' MMbase
' Program to communiate with RPi
' via the COM:2 port

' Open COM:2
OPEN "COM2:19200" AS #2

SETPIN 15, DOUT

PIN(15) = 1 '' On for debug


DO
''INPUT #2, inBUFF$ ' Check for input
inBound$ = inBUFF$() ' inBound$ should contain the contents of inBUFF$
IF inBound$ = "exit" THEN ' If exit, then end
EXIT DO
ELSE IF inBound$ = "help" THEN
GOSUB Menu
ELSE IF inBound$ = "onled" THEN
GOSUB OnLED
ELSE IF inBound$ = "offled" then
GOSUB OffLED
ELSE
PRINT #2, "Invalid Command"
ENDIF
LOOP

PRINT #2, "Good Bye"
PIN(15) = 0 '' Off, for debug

END

Menu:
PRINT #2, "Menu - help, "
RETURN

OnLED:
SETPIN 15, DOUT
PIN(15) = 1
RETURN

OffLED:
SETPIN 15, DOUT
PIN(15) = 0
RETURN

FUNCTION inBUFF$()
NumberOfBytes = LOC(#2)
IF NumberOfBytes > 0 THEN ' The buffer has some stuff
inBUFF$ = INPUT$(NumberOfBytes, #2) ' Move it into inBUFF$
END IF
END FUNCTION

 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 02:44am 11 Dec 2015
Copy link to clipboard 
Print this post

The inBuff$() function you have now will return characters very quickly, probably one by one.
You can not be sure however. You either need to collect the characters that you get and test for a specific sequence, or much easier you change your 'commands' to single characters.
For example: E for exit, H for help, O for onLed and F for offLed


Microblocks. Build with logic.
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 03:09am 11 Dec 2015
Copy link to clipboard 
Print this post


  Quote  
The inBuff$() function you have now will return characters very quickly, probably one by one.

That is the part that I do not understand, I thought that the INPUT command holds until a CR shows up, so technically the inBuff$ variable should contain everything up to the CR. Also what is the difference between INPUT and INPUT$?

Since the COM2 buffer collects the data, at what point does it go back to being empty? Or does it have to flushed after every INPUT request?
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 04:42am 11 Dec 2015
Copy link to clipboard 
Print this post

Try this, it will make it more clear what happens.

[code]
FUNCTION inBUFF$()
NumberOfBytes = LOC(#2)
IF NumberOfBytes > 0 THEN ' The buffer has some stuff
PRINT NumberOfBytes;
inBUFF$ = INPUT$(NumberOfBytes, #2) ' Move it into inBUFF$
PRINT "->" + inBuff$
END IF
END FUNCTION
[/code]

Microblocks. Build with logic.
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 10:24am 11 Dec 2015
Copy link to clipboard 
Print this post

One question answered another question comes up.

After adding some debug statements which MicroBlocks provided, one problem got solved. It appears that when I am sending a string, from the terminal program, it gets sent with an "\n", which is needed for the INPUT command. That was verified by showing 5 as the number of chars, with a four char string received. I also checked what string the inBound$ variable was getting, was the same as the inBUFF$. So, what I did was add an LEFT$ function to trim off the CR, which is what I thought could be the problem. Yep, now it works as expected, it was passing along a string with a CR.

Now, originally, when I used 'INPUT #2, inBUFF$', the program ran as expected. It seems that inBUFF$ was a string without a CR attached. Is the INPUT command stripping off the CR, while the INPUT$ command is not stripping off the CR?

If I have to strip off the CR with a function, which function would I use? It seems that the functions that are available, will not allow for just a char to stripped off, while keeping the main string in tact, if that makes any sense.


' MMbase
' Program to communiate with RPi
' via the COM:2 port

' Open COM:2
OPEN "COM2:19200" AS #2

SETPIN 15, DOUT

PIN(15) = 1 '' On for debug


DO
''INPUT #2, inBUFF$ ' Check for input
inBound$ = inBUFF$() ' inBound$ should contain the contents of inBUFF$
IF inBound$ > " " THEN '' Debug
PRINT inBound$ '' Debug
ENDif '' Debug
IF inBound$ = "exit" THEN ' If exit, then end
EXIT DO
ELSE IF inBound$ = "help" THEN
GOSUB Menu
ELSE IF inBound$ = "onled" THEN
GOSUB OnLED
ELSE IF inBound$ = "offled" then
GOSUB OffLED
ELSE
PRINT #2, "Invalid Command"
ENDIF
LOOP

PRINT #2, "Good Bye"
PIN(15) = 0 '' Off, for debug

END

Menu:
PRINT #2, "Menu - help, "
RETURN

OnLED:
SETPIN 15, DOUT
PIN(15) = 1
RETURN

OffLED:
SETPIN 15, DOUT
PIN(15) = 0
RETURN

FUNCTION inBUFF$()
NumberOfBytes = LOC(#2)
IF NumberOfBytes > 0 THEN ' The buffer has some stuff
PRINT NumberOfBytes; '' Debug
inTemp$ = INPUT$(NumberOfBytes, #2) ' Move it into inBUFF$
inTemp1$ = LEFT$(inTemp$,4) '' Debug
inBUFF$ = inTemp1$
PRINT "->" + inBUFF$ '' Debug
END IF
END FUNCTION

 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6283
Posted: 11:50am 11 Dec 2015
Copy link to clipboard 
Print this post



' to trim of any trailing CR
IF RIGHT$(mystring$,1) = CHR$(13) THEN
mystring$=LEFT$(mystring$,LEN(mystring$)-1)
ENDIF


' to trim of any trailing CR and or LF in any order
DO WHILE RIGHT$(mystring$,1) <> CHR$(13) AND RIGHT$(mystring$,1) <> CHR$(10)
mystring$=LEFT$(mystring$,LEN(mystring$)-1)
LOOP


I would put the second option into a function called RTRIM$

Jim
VK7JH
MMedit
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 12:46pm 11 Dec 2015
Copy link to clipboard 
Print this post

Has anybody else verified what I am observing, in terms of how INPUT and INPUT$ are working?

I looked in the manual, the description for INPUT and INPUT$, and there is no mention as to what it does with the CR. So, the question is, does INPUT$ have a bug, or is it done design. If by design, then maybe it should be mentioned in the manual?
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6283
Posted: 01:02pm 11 Dec 2015
Copy link to clipboard 
Print this post

It is by design.
INPUT reads the file or input stream for variables separated by commas or end of line.

INPUT$ read a specific number of bytes including any CR or other 'non printing' character.

Two different functions doing slightly different things.

Jim
VK7JH
MMedit
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 05:39pm 11 Dec 2015
Copy link to clipboard 
Print this post

Also don't expect that a string will be read in one single time when using INPUT$.
For instance if 'help\n' is send it can be read in parts.
Like the first time you get "hel" and the second time you get "p" en the third time you get the "\n".

If two commands are send then you could get the first time "help\nonle" and the second time "d\n".
That is why you need to 'collect' the strings you receive and then parse it.

[code]
inBuff$= inBuff$ + INPUT$(NumberOfBytes, #2)
[/code]

Then in main you check if inBuff$ contains a "\n"
[code]
p = INSTR(inBuff$, chr$(10))
[/code]

If it does you take the left part until position p
[code]
if p > 0 then
inBound$ = LEFT$(inBuff$,p-1)
'strip the first command from inBuff$
inBuff$ = MID$(inBuff$, p+1)
'parse inBound$
endif
[/code]


Microblocks. Build with logic.
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 01:14am 12 Dec 2015
Copy link to clipboard 
Print this post

Thanks guys, this is really interesting, and a good way to learn mmbasic.

  Quote  
Also don't expect that a string will be read in one single time when using INPUT$.

I can make an assumption that what is causing that is the COM: interrupt and the way it works. This could add a problem to my program if I decide to add more tics. It could possibly slow the program down to a crawl, checks and balances are in affect, or it could start picking up erroneous data.

The intent of my original program was to have the mmbasic do a comm with the HB via the IR, one interrupt, then have it do a comm with the RPi, another interrupt. I was also considering having the uMite handle at least a couple of sensors, a couple of tics (interrupts). With this scheme in mind, I am getting the feeling that the uMite will get overwhelmed, is this a correct feeling?

Giving it some spontaneous thought, could I put the UI part, comm with RPi, into a tick, and have the sensor(s) in a DO ... LOOP, where I could probably have at least four, and have the program run efficiently? Just a couple of thoughts, now back to thinking about a data accumulator ("collector"), and what that entails, plus the RTRIM function.
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 02:04am 12 Dec 2015
Copy link to clipboard 
Print this post

Once you start transmitting data over a serial port it CAN get overwhelmed when the speed is to high.
19200 baud would be fine.

The trick is to spend as little time as possible in an interrupt.
Calling the InBUFF$ function from a main loop will just waste a lot of time because probably 99% of the time there is nothing in the com port buffer anyway.

The next little modification should be changing the com port handling using interrupts.
[code]
CONST BUFFERSIZE = 128
OPEN "COM2:19200, 128, ComInterrupt, 1" AS #5

'main
DO
if len(buffer$) > 1 then ProcessBuffer
LOOP
[/code]

Your interrupt routine would then be something like:
[code]
FUNCTION ComInterrupt()
CharInBuffer = LOC(#2)
if CharInBuffer + LEN(buffer$)> 255 then
'buffer overflow will occur when reading it now
if CharInBuffer = BUFFERSIZE then
'Com port buffer is full have to make room
'by deleting from the start of our buffer.
'This can be acceptable or not,decide here!
buffer$ = right$(buffer$, 255 - CharInBuffer)
else
'Comport buffer not full yet,
'Force a process of our buffer to make room
ProcessBuffer
'and wait until the next interrupt
EXIT FUNCTION
endif
endif
buffer$ = buffer$ + INPUT$(CharInBuffer, #2)
END FUNCTION
[/code]

ProcessBuffer would then be a subroutine with the code to check the commands send over COM2.

Calling ProcessBuffer from the interrupt routine is normally not done as then more time then absolutely necessary is taking place inside the interrupt routine.
However a choice has to be made, if you already have a buffer overrun situation (and only then) you can try to rescue this by forcing the processing of the buffer. This will free up some buffer space and that might be enough to hold the next sequence of characters that arrive, hopefully in time before the next interrupt.

ProcessBuffer should check for commands that are captured in the buffer.
Easiest way todo that is to find the location of the CR (or LF) and extract that (left$) part from the buffer. You then remove that part from the buffer at the front (substr$)and by doing that you create room at the end of the buffer.
After that parse the string.

If you add other sensors you can read them regularly by using a SETTICK, or if the sensor has interrupt support you could use that.
Just structure it so that all the routines collect (sensor) data en you process it by calling the processing routine from main.
Last part of the main loop you could update the screen with new values, and check for a keypress (inkey$) if you also want to support keyboard input.



Microblocks. Build with logic.
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 02:38am 12 Dec 2015
Copy link to clipboard 
Print this post

While I am giving the previous post some thought, I wanted to finish up with the current thought process.

I made these changes to the program, shown below, and I get the following error when run:
  Quote  
[65] mystring$=Left$(mystring$,Len(mystring$)-1)
Error: Number out of bounds

Not sure what the "Number out of bounds" means.



FUNCTION inBUFF$()
NumberOfBytes = LOC(#2)
IF NumberOfBytes > 0 THEN ' The buffer has some stuff
PRINT NumberOfBytes; '' Debug
inTemp$ = INPUT$(NumberOfBytes, #2) ' Move it into inBUFF$
'inTemp1$ = LEFT$(inTemp$,4) '' Debug
inTemp1$ = RTRIM$(inTemp$) <-- <--
inBUFF$ = inTemp1$
PRINT "->" + inBUFF$ '' Debug
END IF
END FUNCTION



FUNCTION RTRIM$(mystring$)
DO WHILE RIGHT$(mystring$,1) <> CHR$(13) AND RIGHT$(mystring$,1) <> CHR$(10)
mystring$=LEFT$(mystring$,LEN(mystring$)-1)
LOOP
END FUNCTION
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 03:11am 12 Dec 2015
Copy link to clipboard 
Print this post

Your DO WHILE loop needs another condition.
It also has to check if the length of the string is not zero.

Out of bound means it is going beyond the 'bounds' of the string
LEN(mystring$) -1 could evaluate to -1 which would be 'out of bounds' as the minimum value for a LEFT$ function is 1.
Edited by MicroBlocks 2015-12-13
Microblocks. Build with logic.
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 04:31am 12 Dec 2015
Copy link to clipboard 
Print this post

The below code works as expected, no restriction on the incoming string length.

Still looking at a RTRIM$() function, that could be a handy function if you could add a number of chars you want to trim off.


DO
''INPUT #2, inBUFF$ ' Check for input
inBound$ = inBUFF$() ' inBound$ should contain the contents of inBUFF$

p = INSTR(inBound$, chr$(10)) '' What position is '\n'
if p > 0 then
inTemp$ = LEFT$(inBound$,p-1)
'strip the first command from inBuff$
inTemp1$ = MID$(inTemp$, p+1)
'parse inBound$
inBound$ = inTemp$
PRINT inBound$
endif

IF inBound$ = "exit" THEN ' If exit, then end
EXIT DO
ELSE IF inBound$ = "help" THEN
GOSUB Menu
ELSE IF inBound$ = "onled" THEN
GOSUB OnLED
ELSE IF inBound$ = "offled" then
GOSUB OffLED
ELSE
PRINT #2, "Invalid Command"
ENDIF
LOOP
 
dasyar
Regular Member

Joined: 13/04/2015
Location: United States
Posts: 58
Posted: 09:37am 12 Dec 2015
Copy link to clipboard 
Print this post

The program below works as expected. I put in some comments, for those that are following along, and I added a blinking LED interrupt. I just wanted to see if the new interrupt code would mess things up.

Unfortunately the only sensor that I have on hand is a sht11 temp/humidity module, but I do not have any mmbasic code to use it. I guess what I could do now is put my HB in the mix, and see it I can turn on/off PIN(14) LED attached to the uMite, using the Parallax Hackable Badge.

I still keep thinking about the uMite IR, but not sure what the first step would be, besides attaching the IR receiver and sender to the uMite.


' MMbase
' December 12, 2015
' Program to communiate with RPi
' via the COM:2 port

' Open COM:2
OPEN "COM2:19200" AS #2

' Start testtick interrupt
SETTICK 1000, testtick, 1

' Setup PIN 14 LED for debug and testing
SETPIN 14, DOUT

' Light up LED for debug
PIN(14) = 1 '' On for debug

' Start of Main program LOOP
DO
''INPUT #2, inBUFF$ ' Check for input
inBound$ = inBUFF$() ' inBound$ should contain the contents of inBUFF$
' Check the inBound$ string for '\n' and then remove it
p = INSTR(inBound$, chr$(10)) '' What position is '\n'
if p > 0 then
inTemp$ = LEFT$(inBound$,p-1) 'strip the first command from inBuff$
inTemp1$ = MID$(inTemp$, p+1) 'parse inBound$
inBound$ = inTemp$ '' ???
'parse inBound$
endif
' Command check
IF inBound$ = "exit" THEN ' If exit, then end
EXIT DO
ELSE IF inBound$ = "help" THEN
GOSUB Menu
ELSE IF inBound$ = "onled" THEN
GOSUB OnLED
ELSE IF inBound$ = "offled" then
GOSUB OffLED
ELSE
PRINT #2, "Invalid Command"
ENDIF
LOOP

PRINT #2, "Good Bye"
PIN(14) = 0 '' Off, for debug

END
'' *********************************************
' SUBs and FUNCTIONs
Menu:
PRINT #2, "Menu - help, "
RETURN

OnLED:
SETPIN 14, DOUT
PIN(14) = 1
RETURN

OffLED:
SETPIN 14, DOUT
PIN(14) = 0
RETURN

FUNCTION inBUFF$()
NumberOfBytes = LOC(#2)
IF NumberOfBytes > 0 THEN ' The buffer has some stuff
'PRINT NumberOfBytes; '' Debug
inTemp$ = INPUT$(NumberOfBytes, #2) ' Move it into inBUFF$

'inTemp1$ = RTRIM$(inTemp$)
inBUFF$ = inTemp$
'PRINT "->" + inBUFF$ '' Debug
END IF
END FUNCTION

' Not functional yet
FUNCTION RTRIM$(mystring$)
DO WHILE RIGHT$(mystring$,1) <> CHR$(13) AND RIGHT$(mystring$,1) <> CHR$(10)
mystring$=LEFT$(mystring$,LEN(mystring$)-1)
LOOP
END FUNCTION

' SUB for interrupt routine
SUB testtick:
SETPIN 15, DOUT
PIN(15) = 1
PAUSE 300
PIN(15) = 0
PAUSE 300
END SUB
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6283
Posted: 07:13pm 12 Dec 2015
Copy link to clipboard 
Print this post

Here is a more complete rtrim$() finction

You can change the characters to trim to suit.


PRINT "*"+rtrim$("Fred")+"*"
PRINT "*"+rtrim$("Fred ")+"*"
PRINT "*"+rtrim$("Fred "+CHR$(13)+CHR$(10))+"*"
PRINT "*"+rtrim$("Fred"+CHR$(0)+" ")+"*"
PRINT "*"+rtrim$("Fred"+CHR$(13)+CHR$(10)+"Wilma")+"*"
PRINT "*"+rtrim$("")+"*"

END

FUNCTION rtrim$(txt$)
LOCAL cull$, k
cull$=CHR$(0)+CHR$(10)+CHR$(13)+" "+CHR$(9)
IF txt$<>"" THEN
FOR k = LEN(txt$) TO 1 STEP -1

IF INSTR(cull$,MID$(txt$,k,1))=0 THEN
EXIT FOR
ENDIF
NEXT k
rtrim$=LEFT$(txt$,k)
ENDIF
END FUNCTION


Getting different device to talk nicely can be difficult.

Jim
VK7JH
MMedit
 
     Page 2 of 4    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025