Bluetooth keyboard
Pages: 1 2
Dave Higton (1515) 3526 posts |
Just supposing I were able to add some sort of Bluetooth capability to RISC OS. (I’m not, but I have had some experimental communications with Bluetooth devices working with RISC OS.) How would I interface a keyboard with the rest of RISC OS? I can get messages from a Bluetooth keyboard that represent key presses and releases, and they are clearly pretty much the same as you’d get from a USB wired keyboard, once the myriad Bluetooth layers are stripped off. But I have no idea how to put the messages into RISC OS without re-inventing a keyboard handler – and I’m sure that isn’t necessary. I presume the communication must be bidirectional, too, in order to permit turning the keyboard LEDs on and off. Also, I can’t see any mechanism that chooses between multiple keyboards; the Configure app allows to set parameters but seems to assume just one keyboard. |
Sprow (202) 1158 posts |
There are a handful of places to look they all jump on KeyV, and have a big table mapping key up/down on your keyboard to internal key numbers.
I think the configure plugin in intended to select how the writing on the keys map to things appearing in your document, that’s much higher level than you need here. I believe you can have multiple keyboards plugged in at once (try it: two USB keyboards?) which can be useful. RISC OS doesn’t need to select between them. |
Jeffrey Lee (213) 6048 posts |
The basic procedure is:
KeyV 3 is used for that; the OS will call it whenever it wants you to update the LEDs.
Correct. The implementation of the keyboard driver in the kernel & InternationalKeyboard means that although multiple keyboards can be used in unison, they won’t work very well if they need differing requirements (different keyboard numbers/layouts, or different key debounce settings). And all keyboards will share the key repeat settings, whether the user wants them to or not. |
Dave Higton (1515) 3526 posts |
OK, thanks, both. I’ll give it a go – eventually. This may take some time… |
Dave Higton (1515) 3526 posts |
Well, I’ve got a skeleton module that finds a Bluetooth controller and opens its interrupt and bulk endpoints. Now the real work starts… |
Dave Higton (1515) 3526 posts |
Not much success yesterday. There needs to be an occasional window opened for pairing PINs, and there will have to be multiple state machines that really have no business being run from TickerV, so the module wants to be runnable. I implemented it with tboxlibs wimp and wimplib, but the module crashed as soon as I double-clicked it. Looking at the library, I saw the name didn’t end in z or zm, and there isn’t source, which made me think the library wasn’t compiled suitably, so I implemented my own swix (Wimp_Initialise…). The same crash. Adding a debug string before and after the call to WimpInitialise, only the before one appears in the log. The module was fine before adding the module-is-runnable and main() code. Clearly there’s something I don’t know but need to know about how to do this. Any ideas would be welcome. I can post any needed code this evening from home. |
Jeffrey Lee (213) 6048 posts |
I’m fairly certain the toolbox libs will work correctly from module code, but if there’s no z/zm versions then maybe I’m wrong. However I do know a likely cause of the crash you’re seeing. C modules which run as tasks (and assembler modules, if they want to use a wimp slot) need to listen out for Service_Memory in order to say “yes, really, I’m using application space”. Take a look at BatMgrHAL for a simple, non-toolbox C module which starts a wimp task. The service call handler just needs to check the supplied CAO pointer against the module base address, and you’ll also need an assembler file to export the base address as a C variable (disappointingly, I don’t think CMHG will export a variable for it, so it has to be done manually) One extra thing to be aware of is how malloc() behaves. If called in a privileged mode it’ll alloc from the RMA. If called from user mode it’ll alloc from application space. So you need to be careful not to allocate something from your wimp task and then try and make use of the data in a ticker event or anything else which might be running when your task is paged out. Or, go with what I think is the more common approach, and use a utility library like ModMalloc instead of standard malloc/free. ModMalloc also has the advantage that it builds a linked list of all allocations, ensuring they get freed when your module exits (something which regular malloc() won’t do; allocations from application space will implicitly get freed when your task/app quits, but RMA allocations need to be freed manually). |
Dave Higton (1515) 3526 posts |
Thanks, Jeffrey. This is, of course, new territory to me! I take it that there is an “extern module_base” declaration somewhere in one of the included files? |
Jeffrey Lee (213) 6048 posts |
BTW, that Service_StartWimp, Service_StartedWimp stuff is just to allow the task to start itself when the desktop is entered. Your bluetooth module probably won’t be needing that (at least not until you get to the point where you want to include it in a ROM image) |
Dave Higton (1515) 3526 posts |
Another question: if I declare a global variable in the source module that contains the module initialisation and finalisation code, will that variable be in the RMA? Does responding to Service_Memory as you’ve described above change the answer? |
Jeffrey Lee (213) 6048 posts |
All the global/static variables will be located in the RMA, except for const stuff, which will be referenced straight from the module body (so could be RMA or ROM). The veneers that CMHG produces (or maybe it’s the C library init function?) will ensure they all get set up prior to any of your code being called. |
Dave Higton (1515) 3526 posts |
Well, thanks to your help, I have some progress. Now the module runs without crashing. I can quit it from the Task Manager (via Wimp Message_Quit). If I double-click the module, the new one replaces the old one because I call Wimp_CloseDown from the module_final code, the task handle being held in RMA. So far, so good. But some bits remain tantalisingly just out of reach. I was surprised to see that the module is listed under Application tasks, and has inherited a wimp slot of the size of Next. I was expecting it to be listed under Module tasks. I don’t understand the stuff you have to do to respond to Service_StartWimp. It seems to require the module to accept a star command, if I understand it correctly. I hadn’t expected to need one or to have to respond to one. Is that the way that it moves from being an Application task to a Module task? I’ve found several things to read, but I can’t make sufficient sense of them yet. I need to have another read tomorrow. Sorry to need so much help. Modules as tasks is all new to me. I’ve written simple modules before – in assembly language until a few months ago – but never attempted a runnable module. |
Jeffrey Lee (213) 6048 posts |
It looks like it’s only tasks which don’t use application space which will show up as being a module task.
I’m not really sure why Service_StartWimp operates the way it does. I think it might just down to limitations over where it’s safe/valid to call Wimp_StartTask. The way it works is that when the desktop is first entered, the desktop module (which is the first task to start) will sit in a loop making calls to Service_StartWimp until no modules are left responding to it. For each response it calls Wimp_StartTask using the returned *command. So I’m assuming that it had to be implemented this way for a reason, e.g. maybe it’s not safe to call Wimp_StartTask from within a service call handler, and so there’s no way for a module to start itself when Service_StartWimp is received. |
Dave Higton (1515) 3526 posts |
The driver is now at a stage where it goes quite a long way through initialising the Bluetooth dongle. Some of the code is wing-and-a-prayer stuff, which will have to get tidied up soon, but at least it’s good enough that I know that the dongle understands and responds correctly to what I’m doing. |
Dave Higton (1515) 3526 posts |
Last night I had two (different) dongles, each visible to my phone under the host names of the Iyonix and BBxM that they were connected to. So it’s a bit more progress. |
Dave Higton (1515) 3526 posts |
Sorry, I’m getting bogged down in more unfamiliar territory; in this case, it’s how to get an icon sprite available to the module. In normal application programming, I’d have an associated sprite file, I’d issue a *iconsprites command from an Obey file, and then things would be ready for me to reference the sprite by Wimp_CreateIcon. A module doesn’t have other resources associated with it, so I thought I’d build the sprite file data into the module as constant data (which I’ve done), then save the data as a file in Wimp$ScrapDir, and issue the *iconsprites command. The last step is easier said than done when you’re in a module. Reading about Wimp_SpriteOp and OS_SpriteOp, it looks as if I can get the effect of a *iconsprites command, but I can’t work out the details. Any help would be appreciated. All I need is an icon to put on the icon bar for the runnable Wimp part of the module. |
Rick Murray (539) 13840 posts |
Shouldn’t that be a private icon, not a WimpPool one? You should be setting the icon to be an indirected sprite-only icon with +20 being the sprite name or a pointer, +24 being a pointer to the sprite area, and +28 being a length (name) or zero (pointer). In this case, just hold the sprite in your module someplace and point the Wimp at it when creating that icon… If it really must be in the Wimp pool, try adding it to ResourceFS – see PRM 2-415. Note that the “files” must be in a specific format (described) and they should be deregistered when the module exits. Oh, and…
The Wimp only permits read-type SpriteOp calls (and by sprite name (no pointers)) to protect itself from having its sprite pool arbitrarily messed with1; the exception bing call 11 to merge a file into a sprite area (this, I presume, is the same basic behaviour as *IconSprites). 1 And then offers us Wimp_BaseOfSprites which exists purely to permit one to mess with the Wimp’s internal sprite areas. Maybe this made sense back in the Arthur 0.xx days? |
Jeffrey Lee (213) 6048 posts |
The traditional way of adding resources to modules would be to embed the files as binary data (as you have done), but instead of saving them out to Wimp$ScrapDir, add them to ResourceFS. Unsurprisingly, there’s a tool to help with this in the build system (resgen) Of course, that doesn’t help with your question of how to *iconsprites the file. For that, I think you have three options:
|
Jeffrey Lee (213) 6048 posts |
Hmm, I should have probably refreshed the page in the long gap between me reading Dave’s post and starting that reply. It didn’t take me half an hour to write the above, honest! |
Dave Higton (1515) 3526 posts |
@Rick:
No. This is a legitimate use for one in the Wimp pool. In fact if you look at PRM 3-101 you’ll see that it has to be from the Wimp pool unless you’re doing an indirected sprite-only icon. @Jeffrey: Thanks. I had trouble originally using “OS_CLI iconsprites”, but I was using it from module start code. Now I’ve moved it to the Wimp part after Wimp_Initialise but before anything else, it works. Now I just have to find some other bugs… |
Rick Murray (539) 13840 posts |
I think we might have to agree to disagree on this. You are a module with the sprite embedded within and thus don’t have a need for that sprite to be used externally (for example, by the Filer) . . . isn’t this the very definition of a “private” sprite?
The other way of looking at this is… that’s what the indirected sprite-only icon is there for. ;-) If you were pointing me to the list of the meaning of icon data (IST) bits, that is p3-98 in my (PDF) PRM, and physical page 101 is the end of CreateWindow. I mention this so you know that my (PDF related; as supplied by ROOL) page references and yours don’t appear to match up. Are you referring to the physical printed volumes?
Doesn’t it seem a little…. awkward…. to take a sprite resource, save it to disc as a temp file, then *IconSprites it…. By the way – how are you getting on with this? Having some sort of Bluetooth support in RISC OS would be kind of nice. I’m assuming that in order to get a keyboard working, the lower level protocols have to be implemented (in some manner). 1 Which is more than my iPad can manage. I have to email files to my Android phone to Bluetooth to the PC. How dumb is that? |
Dave Higton (1515) 3526 posts |
I hope to convince you that I’m right.
Yes. Look at the description of the IST bits (printed PRM 3-101), and you’ll see this text: “Note that the icon bar’s sprite area pointer is set to +1, so icons there use Wimp sprites.” Three out of four ways of creating an icon require you to use Wimp sprites. That should tell you what is the natural sprite pool to use.
Yes…
At some stage I will very likely want text too, so I’m ruling out that method. There is another way to get the effect of iconsprites, and the vague top-level description sounds very simple, but I haven’t namaged to put the detail on it yet. For a 2504 byte sprite, I would end up having to link in significant other resource, AFAICS. It’s no palaver the way I’ve done it. It’s easy, straightforward and obvious. If I could issue an iconsprites command with a memory address as argument, that would be neatest of all. One other thing: it is the direct equivalent of an application icon. I’m only having to do this because a module doesn’t have a collection of application directory, !Boot file, !Run file and !Sprites file that an application does. |
Dave Higton (1515) 3526 posts |
Slowly. If you’ve ever looked at the Bluetooth core spec (2300+ pages for 4.0+EDR) and analysed USB captures from Windows communications with a dongle, you’ll realise that it’s very complex. It makes TCP/IP look flat (number of layers) and simple. I’ve got the module to initialise a dongle so that other equipment can see it enough to attempt to pair with it, although the module has no pairing code yet. I’ve had pairing working in a BASIC app, but that’s not of practical use – it has to be a module. I’ve got a bug or two to find and remove right now, but I’ve started the infrastructure to do Inquiry (what we as users call scanning for devices), then pairing will follow on naturally. At that point I’ll need to add a small GUI. Then there will be the business of opening communication over the bulk endpoints. The extra layers and protocol involved (Bluetooth, not USB) have been what hurts my head most so far. I have never seen a protocol anywhere near as complicated as Bluetooth. And that’s just to get a keyboard or mouse working. |
Steve Fryatt (216) 2105 posts |
You can, sort of, and you’ve already been told how to: register the file in ResourceFS, then do
(where you’ve registered “Bluetooth” – or an appropriate name – with ROOL first, of course). There’s an overhead of 24 bytes plus the filename for the file metadata, and you need some service call handlers to make it work, but it’s simple enough to do. The PRMs cover it in detail, IIRC; you can also find example code in my MsgMon module if that’s any help. |
Dave Higton (1515) 3526 posts |
A short update: last night I got a device to pair with the host. It’s not a proper job yet, but it does work. I’ve decided to split the thing out into a module that will work (to some extent, anyway) on its own, plus a management app with the GUI. I decided that trying to roll the GUI into the module as well as the state machines that it has to have, was heading me into a world of pain. Even having achieved pairing, there are still at least as many steps again before I can get useful input from keyboards. |
Pages: 1 2