Jump to content

y2kiah

Members
  • Posts

    418
  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. All of the control information is available in the DCS-BIOS project. This file for the A-10 (for example): https://github.com/dcs-bios/dcs-bios/blob/master/Scripts/DCS-BIOS/doc/json/A-10C.json Basically I just read that file in and split it into input controls and output controls, the outputs are what you care about parsing. The last function in the source code I posted (buildOutputAddressMap) re-maps that structure for fast lookup by the address, which is faster and more convenient when you are parsing the output stream. One thing to be aware of is DCS-BIOS maintains a buffer of all control values in Lua memory, and it transmits updates to that information in small segments. You should maintain a shadow copy of the entire buffer on your side and apply the updates to your copy as they stream in. Then, you can interpret the values from the buffer as you need them. This is necessary because in some cases you will get partial updates to string data, so you cannot form the full picture from just the network stream alone. The first function I posted (applyUpdates) updates the local buffer, and the second function (mapUpdatesToControls) maps the updated raw values from the buffer to the more "friendly" control structure that is used thereafter. This process involves some manipulation of the raw value based on the mask, shift_by, etc. in the control metadata.
  2. Your primary resource should be the documentation here: https://github.com/dcs-bios/dcs-bios/blob/master/Scripts/DCS-BIOS/doc/developerguide.adoc. I've pasted my javascript code to parse the DCS-BIOS stream, it won't give you all the answers but it will give you a good example, and should be straightforward to port to C#. This is pulled from a NodeRed environment, so there is a lot going on outside of these functions, but they are the important parts. You can also study the DCS-BIOS arduino code available on github. /** * Parse the DCS BIOS export message from the UDP client. * Apply updates to the retained buffer in flow context. * Transform msg.payload into an array of modified addresses. */ function applyUpdates(msg, flow) { // the original message payload, binary buffer from dcs-bios let buf = msg.payload; // msg.payload transforms into an array of updated addresses msg.payload = flow.get("updateAddresses") || []; msg.payload.length = 0; /** * Get (or create) the stored output buffer that we will apply updates to. * RawData is stored in binary format. */ let rawData = flow.get("rawData") || Buffer.alloc(65536); // check for valid message format, the first 4 bytes are always 0x55 if (buf.length < 4 || buf.readUInt32LE(0) !== 0x55555555) { node.error("Malformed DCS-BIOS message received!", msg); return; } let offset = 4; // Go through each update block in the message. while (buf.length - offset > 0) { let startAddress = buf.readUInt16LE(offset); offset += 2; let dataLenB = buf.readUInt16LE(offset); offset += 2; buf.copy(rawData, startAddress, offset, offset + dataLenB); offset += dataLenB; msg.payload.push({ startAddress, dataLenB }); } flow.set("rawData", rawData); flow.set("updateAddresses", msg.payload); return msg; } /** * Converts the array of update addresses to the corresponding set of * output control addresses. * Integer data updates yield the same address, while partial * string updates must step backward until the nearest control base * address is found. * * Updated values are deserialized and written to the output * object's val property. * * The returned msg.payload is an array of outputs to trigger change events. */ function mapUpdatesToControls(msg, flow) { let addressMap = flow.get("outputAddressMap"); let rawData = flow.get("rawData"); let now = Date.now(); let changedOutputs = []; msg.payload.forEach(({ startAddress, dataLenB }) => { // do some asserts to check for errors, remove later if they are never encountered if (Math.abs(startAddress % 2) === 1 || Math.abs(dataLenB % 2) === 1 || dataLenB === 0) { node.error(`Unexpected update address (${startAddress}) or length (${dataLenB})`); } // start at the end of the update data stream to walk backwards, possibly through multiple controls // Note: subtract 2 instead of 1 since we want even addresses, and dataLenB is always even let controlAddr = startAddress + dataLenB - 2; // make sure we find all controls back to the start of the data stream while (controlAddr >= startAddress) { // if not at a base control address, step backward to find nearest control while (!addressMap.has(controlAddr)) { controlAddr -= 2; } let outputsAtAddress = addressMap.get(controlAddr); outputsAtAddress.forEach((output) => { let hasChanged = false; // if this is an integer switch (output.type.charAt(0)) { case "i": { output.prevVal = output.val; let intVal = rawData.readUInt16LE(controlAddr); // get the bits representing this value using mask and shift_by output.val = (intVal & output.mask) >> output.shift_by; hasChanged = (output.val !== output.prevVal); break; } case "s": { //output.prevVal = output.rawVal; output.prevVal = output.hexVal; output.val = rawData.toString("utf8", controlAddr, controlAddr + output.max_length); //output.asciiVal = rawData.toString("ascii", controlAddr, controlAddr + output.max_length); output.hexVal = rawData.toString("hex", controlAddr, controlAddr + output.max_length); output.rawVal = rawData.slice(controlAddr, controlAddr + output.max_length); //hasChanged = !output.rawVal.equals(output.prevVal); hasChanged = (output.hexVal !== output.prevVal); break; } default: node.warn("unknown output type: " + output.type); } // add the output to trigger a change event downstream, prevent duplicates if ((hasChanged || now - output.lastUpdate >= 1000) && !changedOutputs.some((o) => Object.is(o, output))) { changedOutputs.push(output); } // capture update time for telemetry output.updateDelta = now - output.lastUpdate; output.lastUpdate = now; }); controlAddr -= 2; } }); msg.payload = changedOutputs; return msg; } /** * Pivots the flow.outputs object to a Map indexed by output address. * This is convenient for quickly updating based on the output data stream. * Sets msg.payload to the new Map object, flow.outputAddressMap. */ function buildOutputAddressMap(msg, flow) { let outputs = flow.get("outputs"); let addressMap = Object.values(outputs).reduce( (map, output) => { let addr = output.address; // map multiple controls per address, since many controls are bitfields // and share the same base address let outputList = map.get(addr) || []; outputList.push(output); map.set(addr, outputList); return map; }, new Map()); flow.set("outputAddressMap", addressMap); msg.payload = addressMap; return msg; }
  3. @lesthegrngo the button matrix connects to the Pi through a chip called the MCP23017, breakout boards for this chip can be found on ebay for under a dollar (U.S.). This connects to the Pi through the I2C bus, so it only takes a couple pins to run the whole matrix. The MCP chip gives you 16 inputs in 2 groups of 8, so that's 8 rows and 8 columns for 64 buttons. The other 3 buttons on the CDU are handled separately with direct input to RPi pins. The code for controlling the MCP chip and scanning the key matrix on the Pi is in this file, mostly right in the "main" function. Regarding the buttons, I made them myself on my laser cutter out of several layers of acrylic sandwiched together and bonded by solvent. The problem with doing buttons this way is it's very laborious, and the results aren't very good. Laser cutters don't make a perfectly vertical cut through the material, so there can be a visible ridge between layers even if they are aligned perfectly. I try to mitigate that by alternating the pieces so wide edges meet other wide edges, and narrow edges meet narrow edges, but it is still a challenge to align them perfectly when bonding. The last challenge I found was that painting them to look good with back-lighting is actually a huge pain, you need to go much thicker with the paint than you might think, and there is no way to tell if your coverage is good without testing. I ended up making hundreds of touch-ups to all of my buttons with a brush and not-quite-perfectly-matching paint. In the end it looks good to the naked eye, and I am fine with the results, but when you get nice close-ups with a camera, you can really start to see all of the flaws in the buttons. The next time around, I may try cutting over-sized pieces and then sanding them down to size. I will try painting the buttons by dipping them in paint instead of spraying.
  4. my CDU is built this way, some information here: https://github.com/y2kiah/A-10C-CDU-Display pictures of the build are posted in my thread here.
  5. Great idea and great work sir!
  6. CDU Demo Thanks for the tip Hansolo, that would help for sure. I may try that on my upcoming UFC build. I fixed some of the CDU light bleed with touch up paint on the buttons and electrical tape around the screen perimeter, and the pictures actually make it look a lot worse than it does in person, so I'm pretty satisfied with it for now. If it works out for the UFC, I'll probably come back and work it into the CDU. Here is a demo from CDU boot to shutdown:
  7. Absolutely fantastic, what an inspiration, and thank you for sharing!
  8. Thanks Bubbles and Duckling! CDU data is extracted with DCS-BIOS and then run through my NodeRED program. From there it goes to the Pi as MQTT messages over ethernet. The CDU display, buttons and backlighting is run from a single python script, available on github https://github.com/y2kiah/A-10C-CDU-Display. I added some setup instructions to the project to further explain how it's all set up.
  9. CDU Build Here are some shots of my CDU build. The guts are driven by a raspberry pi and some custom software that is available on my github here https://github.com/y2kiah/A-10C-CDU-Display. I will post a more complete tutorial IF there is enough interest, on how to turn an out-of-the-box raspberry pi into a working CDU using my interfacing solution. In a nuthell, data is extracted with DCS-BIOS and sent to a Pi running NodeRED. The NodeRED "node" is the heart of my interfacing solution, and there is logic built into the software running on there to parse the DCS-BIOS stream, filter it down to just what is needed, and transform everything out into MQTT messages. The MQTT server (Mosquitto) also runs on the Pi. Then, all interfacing from the NodeRED hub to the panels happens via MQTT messages over ethernet. I will eventually release all of my interfacing software, hopefully as an official "alternative" set of libraries under the DCS-BIOS project. But before I go into any more of that, here is my build. I still have some light bleed to resolve, and I will be remaking the buttons, but other than that, the dimming is fully functional in the sim. It gets a bit too bright at the highest setting, so I will limit the max PWM value in software. The bottom of the case shows slots for Molex power and the Raspberry Pi SD card. Only the ethernet port is used for interfacing. The back of the perf board, mounted to the base plate shows the crazy amount of soldering. Somehow I managed to get it right the first time, phew... The male pin headers connect key rows and columns to the MCP23017 board. The female pin header connects the LCD screen to the RPi. The wire labeled HDD connects the LED back lighting to the dimmer module. The buttons feel fantastic, with no wobble and a nice long travel, but they look like hell. I need to find a better way to make them. The N,E,S,W,5 and MK buttons are built a bit taller than the rest. Most LCDs at this size are 3.5" or 4.3", but neither are suitable for this panel. This screen is a 4" made for the RPi, which allowed me to make fewer deviations from the original design with my button positions. I ended up with a very close replica in the end. In order to get perfect alignment of the LSK buttons with their corresponding row on the LCD, .1" spacing would not work, so I had to do some tricky off-center positioning of the hole for the key cap to sit on the buttons. Shots of the top of the perf board with and without the light plate. The innards of the case shows the Pi with a pin breakout board, and above it the power input board with MCP23017 attached. Over top of the power board is the LED dimmer which receives PWM signal from the Pi. The Pi itself gives out a very inconsistent PWM signal and cannot dim the LEDs directly (I originally tried this through a MOSFET). The dimmer board smooths out the Pi's signal and sends its own clean PWM to drive the LED strips. The light panel is made from laser cut styrene sheet, and sprayed black on the back side. Light strips and wires are held on with the help of some CA glue. You can also see clear acrylic triangle pieces which act as a fulcrum for the rocker switches. When one side of the switch is pressed, it's not possible to press the other side. Some more shots of the individual pieces. The bottom of the perf board connects rows of the key matrix, and brings them up to the top row of header pins. There is a pull-down resistor connecting each row to ground so the GPIOs do not float. The MCP board does not have built in pull-up or pull-down resistors. The top of the perf board connects key columns. There is a diode at each button to, so I am able to press unlimited simultaneous keys and each one is picked up correctly. One important thing when soldering up a button matrix like this is that no two buttons should share both the same row and column with each other. Well that about wraps it up. My next post will be a video showing the screen and buttons in action with the sim.
  10. I'm back baby! After many years on hiatus, I am restarting my pit build. I have a new, simplified set of plans based on a modified version of Deadman's outer tub, and a complete redesign of my original inner consoles. This new pit should now be easier and cheaper to build, and more accurate in several places. My first modification was to split the tub lengthwise into 3 parts so it can be disassembled and fit through a standard doorway, as well as providing a hinge point for swinging the sides open to enter and exit the pit without climbing. The next modification was the shorten the pit on both ends so that it fits within a 5ft. x 5ft. profile. The pit measures 60" x 53.75" with some spill-over from the seat and HUD frame. I also lowered the pit by a couple inches and plan to use 2x6 lumber for the floor rails. Finally, I cut corners (literally!), so that I could snug the whole thing up to a corner of my room, get the screens closer, and still be able to swing the side open by about 15-20 degrees. My inner side consoles were redesigned to do away with the cross-sectional ribs and go with a mostly longitudinal structure, inspired by the Lynx design. This simplified the whole thing, especially around the Warthog throttle mount. The whole front panel structure was also redesigned to replace the aluminum angle construction with 1/2" plywood. I left room behind the center pedestal for my new TM TPRs. For visuals, I was thinking about starting with 3 vertically mounted TVs on a custom 80/20 hinged frame. I also have an Oculus Rift, and although it will probably not be my preferred way to fly, the immersion is undeniable, so I will be experimenting with VR in the pit as well. The ultimate setup, I think, would be to use 3d glasses to get a bit of both worlds. I'm sorry for the terrible renders, I'm still using Sketchup for this project and it doesn't seem to have a decent renderer anymore.
  11. Ok I have updated post 295 with new links. Still missing are a few pictures, the rails.7z file, and the front extension, but the rest of the cut layouts should be there. http://forums.eagle.ru/showpost.php?p=1336760&postcount=295
  12. Yeah sorry about that I switched web hosting providers and lost all of my screenshots in the process, thinking I have them stashed somewhere else though. I still have the dxf files from the released plans of course and will upload them to my new server. I'll try to get the rest of the screenshots back if possible to get my thread back in shape.
  13. I cleared out some messages so my PM box should be able to accept some now. IIRC my rails.7z file, released along with the plywood plans, includes the dimensions of each panel on the left and right as a simple wireframe outline. I'm 99.9% sure that all of the panel dimensions are accurate, because I did a lot of crosschecking with real A-10 photos and not necessarily the sim itself. I also have several real panels for reference including a AN/ARC-186, which is the most commonly "oversized" panel that I see others design, because ED did not get the height dimension right, so screenshots will throw you off. Anyway, thought I would mention that since it might help someone.
  14. Hi guys, sorry I've been an absentee for a while, but I still stalk every now and then. I'm intending to post my front console files and get some of the minor issues with the current files fixed up. It motivates me to get them done knowing that somebody is waiting. I'll check back in soon to post the front module.
×
×
  • Create New...