![]() |
Forum Index : Microcontroller and PC projects : uMite + Parallax Hackable Badge
![]() ![]() ![]() ![]() |
|||||
Author | Message | ||||
dasyar Regular Member ![]() Joined: 13/04/2015 Location: United StatesPosts: 58 |
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 |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 4044 |
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: ThailandPosts: 2209 |
[code] A = Test() END FUNCTION Test() Test = 1 END FUNCTION [/code] Microblocks. Build with logic. |
||||
dasyar Regular Member ![]() Joined: 13/04/2015 Location: United StatesPosts: 58 |
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 StatesPosts: 58 |
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: ThailandPosts: 2209 |
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 StatesPosts: 58 |
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: ThailandPosts: 2209 |
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 StatesPosts: 58 |
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: AustraliaPosts: 6283 |
' 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 StatesPosts: 58 |
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: AustraliaPosts: 6283 |
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: ThailandPosts: 2209 |
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 StatesPosts: 58 |
Thanks guys, this is really interesting, and a good way to learn mmbasic. 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: ThailandPosts: 2209 |
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 StatesPosts: 58 |
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: 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: ThailandPosts: 2209 |
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. Microblocks. Build with logic. |
||||
dasyar Regular Member ![]() Joined: 13/04/2015 Location: United StatesPosts: 58 |
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 StatesPosts: 58 |
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: AustraliaPosts: 6283 |
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 |
||||
![]() ![]() ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |