Jump to content

y2kiah

Members
  • Posts

    418
  • Joined

  • Last visited

Everything posted by y2kiah

  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.
  15. lol, I was wondering how long it would take for the first measurement request to happen. This is going to set a new benchmark for home pits when it's done.
  16. Just as Deadman said, my panel is at 5 deg. It may be on the high side, but looking at photos of the real panel, it seems pretty close. 2 deg looks like it would be a bit on the low side.
  17. Hey sorry for my absence, I haven't had much time lately to think about the pit. I'll get the .dxf file updated before the weekend, and post a pic of the assembly. I'm also planning to release the front panel next, as soon as I can get around to it.
  18. I suppose it is overkill for back lighting. In general I like that the chip offers a cheap way to expand my number of PWM channels, plus it's a constant current sink and I won't have to worry about individual resistors for the LEDs. The breakout board is useful for servos too. Since there are a few lighting zones, each needing individual dimming, the 5 pins from Arduino doesn't seem so bad, and with that I get daisy chaining for even more channels.
  19. JCook I'm fixing the left side plans to include the missing dado. If you don't want to bother with it, you can just cut that piece into 2 pieces to make it work. I'll post a closeup of the throttle assembly when I put the new file up. Both the left and right side are designed to swing. If you don't want it to do that, just extend the bottom of each rib by .75 inches. Another idea would be to omit the platform built on top of the ribs, and instead put a 3/4" base underneath (40.375" x 16.0").
  20. Electronics plan Here's a rundown of my electronics plan: For switches I'm using the Seeeduino Mega with the Wiznet Ethernet Shield. The mega already has lots of pins, but not quite enough for the whole cockpit, so I'm using the Analog/Digital MUX Breakout board from Sparkfun to multiplex 1 analog and 4 digital pins into 16 analog or digital IOs. This breakout board is based on the popular 4067 chip. For 7-segment displays I will be using the MAX7219 chip, and for 14-seg displays the MAX6954. These chips need to be located physically very close to the LEDs, so they will be built onto custom PCBs for those panels that need them. For steppers and servos, I plan to use Pololu boards. I have not checked their product line recently, but some of their board allow you to control up to 8 or 16 motors with a few SPI pins from Arduino. For just about all LED lighting, including general panel back lighting, I plan to use LED driver chips like the TLC5940 to get constant current and PWM dimming capability. I will be using one of these Mega+Ethernet shield combinations per module as a basic hub for each area. One for the left side, one for the right side, and one for the main instrument panel. This way, I only have to run two wires into each module; a CAT5 cable, and a power cable. Some of the more complicated panels will warrant their own micro controller, like the CDU and UFC. I plan to make those panels self-contained units, also with an ethernet interface but possibly USB. The firmware I'm writing for the Arduino will offload most of the pin logic to a central server located on a PC. The firmware will be responsible for pushing pin changes to the server via ethernet, using a very simple protocol based on TCP. All of the pin logic will be done on the server, which will use a configuration to interpret pin changes as switch events. I can easily reconfigure things without having to flash new firmware to the mcu's each time there is a change. The switch events are then sent to/from the simulator via ethernet or TCP/IP loopback. The export.lua script will read those events and translate them into simulator commands. This was a high level overview, I'll get into more detail when I post about each piece of the puzzle.
  21. y2kiah

    The Cube

    not exactly a cube, but... awesome
  22. Hey guys I just want to throw this out there, because I haven't had a lot of time to work on my next long post, including a review of Deadman's knobs, and time is running out on those knobs. These parts runs are often a one-time only offer, and there may never be another opportunity to get a full set of replica knobs ever again. Deadman's work is absolutely top notch, and I have real knobs to compare them to. I have also seen the quality of casts you can get from the "other guys" and there is no comparison. If you are building an A-10 pit, you need these knobs so get them before the offer is closed (if it hasn't already closed).
  23. The plans have sharp corners but the cutting bit itself is round. It also depends on how your tool path is set up, one mode the GCode will decelerate to a stop at corners, then accelerate in the new direction, another mode will try to keep the bit speed up by rounding out corners. Another option if you don't want to chisel out the rounded corners by hand is to create a drill point at each inside corner in your CAM software. The cnc will then take care of all those corners for you, but the extra time may cost too much for what it's worth if you're paying by the minute. One of the things that makes this whole endeavor economical (to some degree) is that many people now have home-built or light commercial cnc machines. If you factor out the cost of having your own machine, the cost of cutting is basically just the plywood. But if you don't already own a cnc, vs. $550 to hire a shop to cut it for you, hands down the $550 is cheaper by a huge margin. You would have to want the cnc anyway, and use it for other projects to justify the additional cost. When all is said and done, it comes down to what your goals are. $550 + maybe $100 shipping can either get you a pile of wood, OR it will get you 1/3 of the way toward a pretty nice CNC machine.
  24. the design is intended to have a dado as you thought, but I may have forgotten to put it into the released plans. I'll take a look and fix the plans if so. Cutting the bottom in two pieces was a good solution, I should have designed it that way from the beginning.
×
×
  • Create New...