Setting Up A Workout (using Demo App)

Post questions and issues with Concept2 PM3 SDK
Post Reply
[old] Daren C

Post by [old] Daren C » September 7th, 2005, 5:44 am

Using tkcmssetCSAFE_command, I can set my workout to be 5km using the following:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1-->86 81 21 3 5 0 21 24 2 0 0 85 <!--c2--></div></td></tr></table><br /><br />I can set it to be 500m using the following:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1-->86 81 21 3 5 0 22 24 2 0 0 85<!--c2--></div></td></tr></table><br /><br />However, I can't see how to set the distance to 5500m. Nor, indeed, can I see how to set it to, say, 250m.<br /><br />I've another question that I can't answer for myself from the API document. On page 23 (Configuring a Programmed Workout), it says this:<br /><br /><i>0x1A 0x07 0x05 0x05 0x80 0xF4 0x01 0x00 0x00 (CSAFE_SETUSERCFG1_CMD, CSAFE_PM_SET_SPLITDURATION, distance, 500m)</i><br /><br />That is, the above should configure 500m splits. <br /><br />CSAFE_SETUSERCFG1_CMD is the 0x1A, that much I follow. CSAFE_PM_SET_SPLITDURATION is 0x05, and 0x80 specifies distance-based splits (128 decimal). However, I'm not sure what the '0x07 0x05' are between CSAFE_SETUSERCFG1_CMD and CSAFE_PM_SET_SPLITDURATION. I think the 0x07 might be the number of bytes to follow, but I'm still not sure of the first 0x05. Is that the number of bytes of data for the specific command that's about to follow? If so, won't it always be 2 less than the previous figure?<br /><br />Thanks for any help.

[old] Daren C

Post by [old] Daren C » September 7th, 2005, 6:06 am

<!--QuoteBegin-Daren C+Sep 7 2005, 10:44 AM--><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td><div class='genmed'><b>QUOTE(Daren C @ Sep 7 2005, 10:44 AM)</b></div></td></tr><tr><td class='quote'><!--QuoteEBegin--><i>0x1A 0x07 0x05 0x05 0x80 0xF4 0x01 0x00 0x00 (CSAFE_SETUSERCFG1_CMD, CSAFE_PM_SET_SPLITDURATION, distance, 500m)</i><br /><br />That is, the above should configure 500m splits.  <br /><br />CSAFE_SETUSERCFG1_CMD is the 0x1A, that much I follow.  CSAFE_PM_SET_SPLITDURATION is 0x05, and 0x80 specifies distance-based splits (128 decimal).  However, I'm not sure what the '0x07 0x05' are between CSAFE_SETUSERCFG1_CMD and CSAFE_PM_SET_SPLITDURATION.  I think the 0x07 might be the number of bytes to follow, but I'm still not sure of the first 0x05.  Is that the number of bytes of data for the specific command that's about to follow?  If so, won't it always be 2 less than the previous figure?<br /><br /> </td></tr></table><br /><br />Regarding this, I am now of the opinion that it's<br /><br /><i>0x1A(CSAFE_SETUSERCFG1_CMD) 0x07 (bytes to follow) 0x05 (CSAFE_PM_SET_SPLITDURATION) 0x05 (bytes to follow) 0x80 0xF4 0x01 0x00 0x00</i><br /><br />So if I used more than one proprietary extension command (e.g. CSAFE_PM_GET_WORKTIME and CSAFE_PM_GET_STROKESTATE), that initial 0.x07 would be the total byte count to follow for the two commands. That makes more sense, and I dare say it's confirmed in the PDF if I read it more closely.<br /><br />However, I still can't work out my first problem - how to set a work distance of 5500m or 250m.

[old] haboustak

Post by [old] haboustak » September 7th, 2005, 12:04 pm

<!--QuoteBegin-Daren C+Sep 7 2005, 04:44 AM--><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td><div class='genmed'><b>QUOTE(Daren C @ Sep 7 2005, 04:44 AM)</b></div></td></tr><tr><td class='quote'><!--QuoteEBegin-->Using tkcmssetCSAFE_command, I can set my workout to be 5km using the following:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1-->86 81 21 3 5 0 21 24 2 0 0 85 <!--c2--></div></td></tr></table><br /><br />I can set it to be 500m using the following:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1-->86 81 21 3 5 0 22 24 2 0 0 85<!--c2--></div></td></tr></table><br /> <br /> </td></tr></table><br /><br /><br />The byte that you're changing (0x21 > 0x22), the third byte in the SETHORIZONTAL command, is the byte that corresponds to the units. the value 0x21 is for kilometers, the value of 0x22 is for kilometers divided by 10 (hundred meters).<br /><br />Both of your workouts will fit nicely in a 16-bit integer with the units of meters. The byte that represents meters is 0x24.<br /><br />To setup a 5500m workout you can do:<br />0x21 0x03 0x7C 0x15 0x24<br /><br />For 500m<br />0x21 0x03 0xF4 0x01 0x24<br /><br />You can also set up a workout for 55km/10<br />0x21 0x03 0x37 0x00 0x22<br /><br />The first and second bytes are the actual distance in PM3-native big-endian notation. The first byte of the pair is the least significant byte and the second byte is the most significant. Take the distance you want to row in decimal (5500) and convert it to hex (0x157C) then put the bottom two bytes above the top two (0x7C 0x15) and send it to the PM3 with units meters (0x24)<br /><br />That should take care of it. My C code to build SETHORIZONTAL commands is as follows:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1--><br />   // Send the distance to the PM3<br />   cmd_len=0;<br />   cmd_data[cmd_len++] = CSAFE_SETHORIZONTAL_CMD;<br />   cmd_data[cmd_len++] = 0x03; // number of data fields<br />   cmd_data[cmd_len++] = (unsigned char)(distance & 0xFF);<br />   cmd_data[cmd_len++] = (unsigned char)((distance >> 8) & 0xFF);<br />   cmd_data[cmd_len++] = CSAFE_UNITS_METER;<br /><!--c2--></div></td></tr></table><br /><br />Mike<br />

[old] Daren C

Post by [old] Daren C » September 7th, 2005, 12:17 pm

Cool, thanks Mike, that seems to work as you describe.<br /><br />Now if I can just get C# to call the DLLs correctly, I'll be away... I noted what you wrote in another thread about cdecl versus stdcall, but even having specified the cdecl calling convention on the DllImport, it doesn't seem to be playing ball. I don't suppose you've used C# with the PM3 DLLs, have you? It's odd, because tkcmdsetDDI_init and tkcmdsetCSAFE_init_protocol seem to work fine, but tkcmdsetDDI_discover_pm3s won't. This looks identical in symptoms to the previous poster who was having problems with VB.

[old] haboustak

Post by [old] haboustak » September 7th, 2005, 12:28 pm

Daren,<br /><br />I'm pretty sure that the problem is in the calling convention, even though I've never used the DLLs in VB or a .Net language. RowPro is a .Net program though, so they're certainly using the DLLs from a managed language without problems.<br /><br />I think that when you import a function from an unmanaged library in .Net you can specify the calling convention using the CallingConvention attribute and enumeration. This isn't an option if you're using VB6, but under .Net you can do something like:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1--><br />[DllImport("RPPM3DDI.dll", <br />       EntryPoint="tkcmdsetDDI_discover_pm3s",<br />       CallingConvention=CallingConvention.Cdecl)]<br /><!--c2--></div></td></tr></table><br /><br />I'm typing this off the top of my head, so trust your own DllImport and Visual Studio's auto-complete more than me. I'm pretty sure that Cdecl is the correct convention to use.<br /><br />Mike

[old] Daren C

Post by [old] Daren C » September 7th, 2005, 12:33 pm

You're correct, Mike, that's the syntax. However, I've tried that and it's not working. I dare say the problem lies elsewhere, as I'm a complete novice when it comes to C#. I'll keep on at it though, because I don't really want to use C++ (this is intended as much as an excuse to learn C# & WinForms as anything else).<br /><br />Thanks for your replies.

[old] haboustak

Post by [old] haboustak » September 7th, 2005, 1:08 pm

Daren,<br /><br />I see now that as I read your reply more carefully you said you specified the calling convention attribute. I created a very rough C# project that imports the DDI DLL and was able to communicate with it to a level that I think it should work.<br /><br />I imported the function from the DLL in my form class<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1--><br />[DllImport("RPPM3DDI.dll",<br />     EntryPoint="tkcmdsetDDI_discover_pm3s",<br />     CallingConvention=CallingConvention.Cdecl)]<br />public static extern short tkcmdsetDDI_discover_pm3s(String name, short start, short[] ports);<br /><!--c2--></div></td></tr></table><br /><br />and I called the function in a button handler<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1--><br />short[] ports = new short[128];<br /> short result = tkcmdsetDDI_discover_pm3s("Concept2 Performance Monitor 3 (PM3)", 0, ports);<br /><!--c2--></div></td></tr></table><br /><br />At first I got the error message about the INI file, and then it returned an error -10201. So it seems to be calling and working with the DLL ok.<br /><br />Mike<br />

[old] Daren C

Post by [old] Daren C » September 7th, 2005, 1:17 pm

I've found the problem, now. It wasn't the initial problem I thought, it was a problem with one of my parameters.<br /><br />Since tkcmdsetDDI_discover_pm3s takes an int pointer as a parameter (as a place for the number of discovered PM3s to be written), I was trying to use an IntPtr. Even though I duly created and initialised an IntPtr in my code, this was the wrong thing to do. After some Googling, I found someone that described a similar problem, and his fix also worked for me.<br /><br />What I needed was to use "ref" to specify a reference, rather than trying to play around with the IntPtr type.<br /><br />My declaration is now the following:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1--><br />[ DllImport("RPPM3DDI.dll", CallingConvention=CallingConvention.Cdecl) ]<br />public static extern ushort tkcmdsetDDI_discover_pm3s( <br />   String product_name, <br />   Int16 starting_address,<br />   ref Int16 num_units);<br /><!--c2--></div></td></tr></table><br /><br />The calling code is:<br /><br /><!--c1--><table width='95%' cellspacing='1' cellpadding='3' border='0' align='center'><tr><td><b><div class='genmed'>CODE</div></b></td></tr><tr><td class='code'><div><!--ec1--><br />Int16 num_units = 0;<br />ushort ecode = tkcmdsetDDI_discover_pm3s("Concept2 Performance Monitor 3 (PM3)", 0, ref num_units);<!--c2--></div></td></tr></table><br /><br />This works fine, finding the single PM3 connected to my PC. <br /><br />Additional:<br /><br />I previewed this post and noticed your new one as well. I've tried your solution, and that also works. I'm not sure which one is 'the correct one', if either. Thanks once again for your help.

[old] haboustak

Post by [old] haboustak » September 7th, 2005, 1:26 pm

<!--QuoteBegin-Daren C+Sep 7 2005, 12:17 PM--><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td><div class='genmed'><b>QUOTE(Daren C @ Sep 7 2005, 12:17 PM)</b></div></td></tr><tr><td class='quote'><!--QuoteEBegin-->Additional:<br />I previewed this post and noticed your new one as well.  I've tried your solution, and that also works.  I'm not sure which one is 'the correct one', if either.  Thanks once again for your help. <br /> </td></tr></table><br /><br />I think that yours is correct and not mine. It's not actually returning an array of port numbers. I didn't read the docs closely enough for the DDI command. I spend most of my time working with the USB layer, which returns an array of port numbers and not just sequentially numbered devices. But in this case a reference to a short looks just right.<br /><br /><br />Glad you got it working!<br /><br />Mike

[old] Daren C

Post by [old] Daren C » September 7th, 2005, 1:29 pm

Not as glad as I am! =)

[old] ChrisRey

Post by [old] ChrisRey » September 7th, 2005, 2:38 pm

Daren,<br />I got your PM today, sorry but I haven't got the ActiveX stuff working properly. When I was working on it another guy offered to do one in Delphi and I fed some stuff in to him, he did publish his control here but I confess that I couldn't get his to work, but I then (and still do) have too much on to finish the task. Re-reading this forum makes me want to find the time but unless I can slip a PM3 under my desk at work then it isn't happening!<br /><br />I did upload all my work in progress as indicated in this thread:<br /><br /><a href='http://concept2.ipbhost.com/index.php?showtopic=2424' target='_blank'>http://concept2.ipbhost.com/index.php?s ... 424</a><br /><br />but it sounds like you're well on the way, the ints, shorts, by references, by values and cdecls and stdcalls are a minefield aren't they.<br /><br />Good luck<br /><br />Chris<br /><br />(unlucky on the footie at the w/e, I'm just off to watch the NI game)

Post Reply