Setting Spinneret time from a web page on the SD Card
xanatos
Posts: 1,120
Here's where I get to show how woefully tiny my knowledge is, despite weeks of messing with this thing.
What I want to do is have a webpage on the SD card that someone can enter in the date and time into the appropriate fields for each element, hit the submit button, and have it change the RTC time & date setting.
My RTC stuff is working well, and I can read the time/date info and put it in an email and send it all along just fine. But here's the latest that I've tried, unsuccessfully - obviously - to enter the time and date, and have it set the RTC:
I have created up in the VAR section my CKSetArray:
And I'm running this under the Dispatcher:
Syear, Smonth, etc., are the variable names (the input tag names) in the hrml file on my SD card.
I have, for now, commented out my rtc.writeTime line - which works well on its own. I've got the variables just outputting to the Parallax Serial Terminal for now, and I've tried pst.str, which yields blank spaces, and pst.dec, which yields all sorts of odd characters, but nothing I've tried so far yields anything close to the characters I'm entering.
ALSO... as a side note - I've discovered that my Spinneret will not find any filename on the SD Card longer than 7 characters. testlog.htm works fine, but testfile.htm will not. I get a 404. I've tried this with several filenames - the only thing in common is the 7 character limitation. Any ideas how I managed that? :-) I found this out while trying to get xmlexample.htm to work. Got a 404. Spent an hour swapping things in and out to get it to find the file, eventually had nothing but html and body tags with the text "test" in it. Still got a 404. Just renamed the file to test.htm, and it found it just fine.
Thanks!
Dave
What I want to do is have a webpage on the SD card that someone can enter in the date and time into the appropriate fields for each element, hit the submit button, and have it change the RTC time & date setting.
My RTC stuff is working well, and I can read the time/date info and put it in an email and send it all along just fine. But here's the latest that I've tried, unsuccessfully - obviously - to enter the time and date, and have it set the RTC:
I have created up in the VAR section my CKSetArray:
VAR long StackSpace[20] byte EEArray[10] ' For 24LC256 EEPROM data reads for sending email with data embedded. byte CKSetArray[6] ' For RTC data setting from web interface.
And I'm running this under the Dispatcher:
PRI Dispatcher(id) ''Do some processing before sending the response if(strcomp(Request.GetName(id), string("led"))) Led(id) if(strcomp(Request.GetName(id), string("post"))) Post(id) StaticFileHandler(id) return PRI Post(id) | qstr '' Get the post value CKSetArray[6] := Request.Post(id, string("Syear")) CKSetArray[5] := Request.Post(id, string("Smonth")) CKSetArray[4] := Request.Post(id, string("Sdate")) CKSetArray[3] := Request.Post(id, string("Sday")) CKSetArray[2] := Request.Post(id, string("Shour")) CKSetArray[1] := Request.Post(id, string("Sminute")) CKSetArray[0] := Request.Post(id, string("Ssecond")) pst.str(CKSetArray[6]) pst.str(CKSetArray[5]) pst.str(CKSetArray[4]) pst.str(CKSetArray[3]) pst.str(CKSetArray[2]) pst.str(CKSetArray[1]) pst.str(CKSetArray[6]) 'rtc.writeTime(CKSetArray[0], CKSetArray[1], CKSetArray[2], CKSetArray[3], CKSetArray[4], CKSetArray[5], CKSetArray[6]) 'SET THE RTC (second, minute, hour, day, date, month, year):
Syear, Smonth, etc., are the variable names (the input tag names) in the hrml file on my SD card.
I have, for now, commented out my rtc.writeTime line - which works well on its own. I've got the variables just outputting to the Parallax Serial Terminal for now, and I've tried pst.str, which yields blank spaces, and pst.dec, which yields all sorts of odd characters, but nothing I've tried so far yields anything close to the characters I'm entering.
ALSO... as a side note - I've discovered that my Spinneret will not find any filename on the SD Card longer than 7 characters. testlog.htm works fine, but testfile.htm will not. I get a 404. I've tried this with several filenames - the only thing in common is the 7 character limitation. Any ideas how I managed that? :-) I found this out while trying to get xmlexample.htm to work. Got a 404. Spent an hour swapping things in and out to get it to find the file, eventually had nothing but html and body tags with the text "test" in it. Still got a 404. Just renamed the file to test.htm, and it found it just fine.
Thanks!
Dave
Comments
I verified HttpServer renders 8 character file names. This is an example straight from the online manual that resides on a Spinneret sitting on my desk. This rig has been running for over two years.
http://68.99.247.152:5000/images/pstbinup.png
Here's the html form I'm using, filename=post.htm
Thanks for helping... I'n so far over my head... but I'm still miles ahead of where I was a few weeks ago, many thanks to you.
The Dispatch is looking for "post" so I assume it the file name on the disk is post.htm. The filter above is true regardless of the HTTP action, GET or POST. You don't want that... this is better
I'm not sure how CKSetArray[] is declared but it must be a long. The Request.Post returns a string pointer.
The entire IF block test should look like the following. I verified this functionality.
In the future, please post all your source code. Otherwise I have to guess which it is a bit of a time blackhole.
For the remaining question here, in the interest of full code disclosure, here is teh full HTTPServer.spin that I am running - but it will be followed by the parts that I am pretty sure are the only ones needed to see where I am in error.
HTTPServer.spin as I've modified it, in full:
I won't post "post.htm" because we know that's working - the numbers are unquestionably making it into the Propeller on the Spinneret because I can pst.str them to the PST window.
The question is "What does the rtc.writeTime line expect for data type?"
I have tried my CKSetArray[x] alone, with string(), and with nums.decn(CKSetArray[x], 2))... and several other things that just didn't work. Here's the code I have where the dispatcher DOES parse the values properly, then I set the time with rtc.writeTime(), then I read the individual variables - and they are NOT what I wrote. First the code - then the next window is the display of the PST output.
Code of section in question:
And output of the PST window - the first section is entering time values and date values like 01/23/2045 and 12:34:56; the next one I just entered 11/11/11 and 22:22:22. Note that the output obtained after reading the RTC is the same, with a year of 2099, etc. Note ALSO that if I manually set the rtc at the initialization routine of HTTPServer.spin, comment out the setting line in the post handler, and read the values for the time and date, they DO read correctly. It's ONLY when I try to set the rtc with the values obtained from the post that they go to the values indicated:
I'm guessing that that 12/31/2099 at 23:59:59 is some sort of value that equates to the max time/date of the Spinneret... and I'm assuming that it's due to my sending the numbers in teh wrong format. Note that the reading and writing of the values I am sending from my post.htm form is fine - only the setting of the rtc and reading those values back creates the error.
I'm continuing to try sending various data types, but I just don't yet know enough to know everything I should be sending - or that I can send... and I'm not sure yet of where to find that info. A search of "setting RTC time date Spinneret" hasn't yet yielded the nugget I seem to be missing...
Thanks again...... I hope I can find a way to pay this forward.
Dave
All you're doing is copying a pointer. The pointer is only good for the duration of the request/response. If you need to store the data - so the data persists between requests - then move the bytes to a buffer. Otherwise *poof* unexpected results.
It is much better to update the RTC using SNTP. There are examples on the forum. The newer driver handles SNTP time automatically [nudge nudge hint hint].
http://code.google.com/p/propeller-w5200-driver/source/browse/trunk/#trunk%2F%20propeller-w5200-driver%2FSpinneret
The statement below passes a pointer value to each of the writeTime parameters. The pointer is a string pointer. The string contains ASCII encoded characters which means the values are not numbers. They are codes that represent numbers.
What ya need to do is convert the ASCII strings to numbers. Then pass the numbers to the writeTime method.
It is far easier and less code to use the SNTP object.
I have in fact seen the SNTP stuff in the OBEX and in several posts. My question about this - since I saw one post that seemed to indicate that an SNTP server can go down/disappear - is, How reliable is the SNTP setting function over extended time? Once this whole thing is running, I won't have convenient access to it, so I wouldn't want to have to fly out just to change a server address. Can the SNTP functions be relied on over time without worry?
Lastly - while I am stressing out over when I promised that this would be ready by, I'm really enjoying the learning aspects of all the many areas I'm trying to come up to speed on simultaneously. And while it may turn out that SNTP is the way to go, I still feel there is a lot to learn by completing my understanding of why my time setting routine is not working as expected. I'd like to understand exactly WHAT IS a buffer in this context, and how would I change my CKSetArray[x] values to hold the actual VALUE rather than a pointer to them... this seems to be an important understanding that I feel like I am on the verge of understanding, but I'm just not seeing over that particular hill yet.
Thanks. I hope I'm not annoying with all my requests, but I do learn well and I'm persistent as hell, which is why I have become so proficient with the Stamps. I really want to be that confident with the Propeller, but I still feel like a preschooler - and as far as I can tell, you're the most knowledgeable in these areas, and I really appreciate your willingness to guide me to understanding what I'm doing...
Dave
PS., I posted this while you were responding above... reading now. Can I buy ya a beer or something? :-)
I thought I did that when I used the format:
Where nums is the numbers object I'm using and set in my OBJ block shown in my full code in the first post. So if converting my CKSetArray using nums isn't doing the trick - I am obviously not understanding how nums is working either... :-)
Thanks... feeling way over my depth here.... :-)
Dave
Is this a pointer to a location in memory?
So I guess what I'm looking for is how do I specify CKSetArray[0] := "The value stored at Request.Post(id, string("Ssec"))" instead of CKSetArray[0] := "The location of the value stored at Request.Post(id, string("Ssec"))"
Can you confirm if my understanding of this is correct?
Yes, SNTP is reliable.
The posted code did not contain a call to nums.decn. I'm not familiar with nums.decn so I can't help you there. You'll have to look at the object.
You're not getting it. The value is a character (byte) array. The pointer is a memory location where the byte array starts. The byte array ends with a zero - zero terminated. That's how strings work.
How is it that I can pst.str the data at CKSetArray[5] for example to the PST window, but have it not be able to put that same data into the rtc.writeTime values? You say that CKSetArray[x] contains a pointer to the data, but not the actual data itself. You say the data is a string that is zero-terminated. So my understanding at this point is that the data contained in CKSetArray[x] is a pointer to the location of the actual data I want to use, which begins at the location contained in CKSetArray[x], and ends at the first instance of that zero-terminated string... but it sounds like you are telling me that is wrong. I guess I have no idea what you are saying then - please help me get this....
Dave
It also echoes to the Parallax Serial Terminal properly.
I must be having some sort of contention going on with some other process in the Propeller's program, since when I set the time, it also stops incrementing. The emails sent out from the unit show the time as it was set, but a few minutes later - same time still shows.
However - it must still be keeping time somewhere, because if I reload the RAM (F10), and then have it send an email with the timestamp embedded - the time has been updating internally all along.
I am so far over my head.......
Imagine we have the decimal number 49. 49 fits comfortably in a byte data type being 49 and less than 256. To display 49 on the Parallax Terminal is easy - pst.dec(49).
What if the number 49 is not a number but a ASCII encoded representation of 49. In other words, a string and the kind of item found in the HTTP protocol when using POST. Well, the ASCII equivalent of 49 requires two bytes. One byte for the number 4 and one byte for the number 9. In memory the string 49 looks like; [52 | 57 | 0]. The zero on the end is the end of the string - zero terminated
The decimal value 49 prints as the number 1 and pst.dec(49) parses the decimal value 49. Dec first sends the value 52 then 57 out the serial port.
Code to play with... You must query the RTC when you want to know the current time - readTime. Otherwise you'll get whatever is in the buffer from the last query.
An example being your statement in the posts above that "What ya need to do is convert the ASCII strings to numbers. Then pass the numbers to the writeTime method." Since (I thought) that I had already tried that using the nums.decn(x) structure from SimpleNumbers.spin, and it failed completely, it took a while for me to circle back around and try it using the str.toInteger() method on the array variables. My error originally was apparently to apply the method at the wrong point in the process.
But even now, having found how to make it work, I am still very unclear as to why it would work at one place in the assignment process, but not in the other...
But I've been on this today now for over nine hours. My brain is fried, and I'm guessing you're probably pretty tired of seeing my name in this forum today, so I'm done for tonight.
One thing I can assure you of - I've been working at learning this thing for weeks now, non stop, reading literally hundreds of posts, viewing many, many code examples, going through the OBEX, and trying, trying, and trying all sorts of bits of code. I'm not trying to get one thing working - I'm having to get literally dozens of different functions working and tied together in the framework of the Spinneret. And I'm having to do it all at once. If there's one thing I'm doing - and I know you can't see it, and I'm sure it doesn't look like it - but I am doing the work on my end. I have managed to learn an enormous amount in a relatively short period of time - at least comparatively to where I started - but it's still not even close to a beginning with regards to where I need to be with this stuff in about two weeks.
And I very much recognize the time and effort it takes to help someone learn, especially on a complex subject like Spin and the Propeller. Thank you very much - I hope I can repay the favor some day.
Dave
PS., Adding rtc.readTime before the block of rtc.clockMonth, etc., did the trick - but I can't figure out why it was incrementing the time before, without that statement in there. When I'm not under such tight time constraints, I'll go back to that and figure out what of the things I added made the change.
To add on to what Mike G. said earlier about ASCII conversion and such, I thought I'd share this chart with you.
Here is a nice "conversion" chart you can use with Mikes earlier reply about ASCII encoding. This chart shows you the equivalents of several data encoding schemes as well as their binary representation. Copy it and stuff it into a spin file as a commented section for future reference if you'd like.
The key to remember (using Mike's reply with 49 decimal as the example) is that ASCII characters are SINGLE characters. If it's an ASCII value, it's ONE character, and ALWAYS AND ONLY ONE character. EACH of these characters is represented by ONE byte of data. That's why there are only 256 ASCII characters (8 bits in 1 byte only has 256 possible combinations)
So... as Mike said, the DECIMAL value 49 requires TWO ASCII characters. The first is a 4 and the next is a 9.
If you look at the chart, the DECIMAL value 49 has a binary value of 00110001 which is 8 bits (1 byte) of data. But this binary value equals the character 1 in ASCII. Since ASCII needs 1 byte of data PER CHARACTER, we have to convert between data types to make everything correct.
To create the ASCII value of "49" we actually have to have TWO bytes of data. The first byte would be for the ASCII character 4, which is a binary value of 00110100 when you look up the ASCII value 4. The second byte is ASCII value 9 which is a binary value of 00111001 when you look up the ASCII value of 9.
The same principle applies to all ASCII values equally.
If you wanted to create the ASCII string of characters x a n a t o s you would need to use 7 bytes of data. The first would be the ASCII value x which is binary value 01111000 ... which is 8 bits (1 byte) of data... and on and on.
Now, look at the chart one more time. If you want to convert the ASCII value to the decimal equivalent, look at the chart really closely. Look at the ASCII value 4. Then look at the DECIMAL EQUIVALENT of it. You'll see the DECIMAL EQUIVALENT is 52... which is 48 more than 4. Now look at the rest of the ASCII number characters and their decimal equivalents... (hint, hint)
You can build your own conversion methods to convert from ASCII characters to the corresponding DECIMAL value, and vice versa. However, Mike has an object in HTTPServ for the spinneret called StringMethods which has methods that do this already and they work perfectly. Also, there are other objects on the OBEX that do tasks like this for you.
I hope this'll help you a bit. Hang in there!
Robert
Robert - thank YOU for this - visualization of the data works really well for me and this does that very well. I have a chart similar to this for my web stuff, but it is only for the &#xxx; style encoding - not hex and binary! This is great.
To follow along further in this, I could also look at the high-byte of the string "49", and just take the least significant 4 bits - which is 0100, or 4... and then go to the next byte, and take those LSBs, which are 1001, or 9. So I can either subtract 48, or take the 4 LSBs - if I'm only interested in turning an ASCII number into a DEC anyway - correct?
And lastly, from what I'm understanding now, and to use your example above, if I had x a n a t o s as a STRING - it would really be eight bytes - seven for the ASCII characters, and one byte of all zeros - am I correct on that count?
Now applying this to my understanding for setting my RTC, for example (and yes, I will be using SNTP, but this RTC setting thing has really helped me to understand how to get data from the user into the Spinneret/Propeller via a web page, so it remains a useful training aid for me here) - here's the code line that takes the input fields from my HTML form and places that string into my variable array (or one line of it in this case):
So if the user entered 13 for the year, the actual data being held in CKSetArray[6] (specified as a long in my VAR block) would actually be an ASCII "1" and an ASCII "3", plus a byte of all zeros - am I still correct there?
Then, I'm using one of the StringMethods to convert those strings to integers:
And this works well now. Now StringMethods.spin is written in PASM, and after speaking with Mike this morning, I was able to understand what's happening in that PASM section a bit more (no pun intended...), but if you can bear with my long-winded post just another second - would I be correct that A routine to convert (not necessarily THE routine), would be for the program to take the string value, go to the first character (ASCII character), and either subtract 48, or alternately take the LS 4 Bits, get that number, and multiply it by 10, then go to the next number, take those LS 4 bits, add them to the result of the operation on the previous number, and if there were no subsequent numbers, then that's the answer. If there was another number, it would multiply by 10 again, get the next number, add that in, and so forth, until it finally encountered that terminal 0, correct?? Have I got this finally???? :-)
0
(That's a terminal zero for this post)
Yes.
Yes. You would use 7 bytes for the xanatos characters and then a zero-termination byte, which is simply a byte at the end of the string with a binary value of 0000_0000 (decimal value of 0)
I'm not sure about this one, but it sounds almost correct. I think the actual binary value placed in CKSetArray[6] from this would be 0011_0001 0011_0011 0000_0000 0000_0000 which basically should be fine. One way to find out would be use PST to display the value in binary for you once like this:
But, Mike or someone else might be able to answer this one more accurately. Sorry!
Well, there are many ways to do this. But trying to following your strategy, you could do the following:
Let's say you set up an array of BYTES (not longs) to hold a 4 digit number where each byte is actually an ASCII character, and you do so using your VAR section. You'd want to declare 5 bytes, so that your first 4 bytes hold the data and the last byte is always going to be zero (your zero termination byte.)
You'd use something like this:
Or you could use your DAT section a couple different ways, like this:
One way or another, you've just set aside a "place holder" for 4 bytes of data where each byte will contain an ASCII characters binary value. You've also set up a zero-termination byte.
So, let's suppose the number you want to place into these bytes is "1234" where each digit is actually the ASCII value of that digit.
byte 0 = 0011_0001 = ASCII 1 = DEC 49 (left most byte)
byte 1 = 0011_0010 = ASCII 2 = DEC 50
byte 2 = 0011_0011 = ASCII 3 = DEC 51
byte 3 = 0011_0100 = ASCII 4 = DEC 52
byte 4 = 0000_0000 = zero.
Since it's a 4 digit number, the first byte (byte 0) represents the ASCII value of the digit 1, which is in the the 1000's place. So... subtract 48 decimal from this ASCII value to get the DECIMAL value. Then multiply by 1000.
Do the same for byte 1... subract 48 and multiply by 100.
Do the same for byte 2... subract 48 and multiply by 10.
Do the same for byte 3... subract 48 and multiply by 1. Or not.
Add 'em all together and you've got your brand new shiny decimal value.
Note- to do it this way, you don't actually *need* a terminating zero because we don't actually do anything with it. But if you use Mikes object (which I strongly recommend) like you wrote above with
Yr := 2000 + str.ToInteger(CKSetArray[6]), you DO need the terminating zero because his method requires it. It uses it to say "oh, I've got to the end of the ASCII bytes, so I'm done now."
Thank you for that. I really needed to feel like I was understanding SOMETHING on more than just a superficial level...
And that VAR vs DAT block example of the many different but equivalent ways to set up my byte arrays (or long arrays too I would guess) was very helpful. If I had encountered those different configurations in different programs it would have taken me quite a while to realize they were all saying the same thing.
Unfortunately I can't spend much time to bask in the glory of having finally understood how to work with these items in Spin, because I am trying to figure out why the RESTful Services example isn't working on my Spinneret - when I go to aled.htm, I get redirected to led.htm immediately, and clicking the led on/off links does nothing...
This whole process has been like pulling teeth for me... but it sure feels good when I finally do get some piece of it working well - not just because of a happy accident, but because I genuinely understand it.
Thank you both for your help - very much.
Dave